# frozen_string_literal: true # # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com) # # ronin-web-session_cookie is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # ronin-web-session_cookie is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with ronin-web-session_cookie. If not, see . # require 'ronin/web/session_cookie/cookie' require 'ronin/support/encoding/base64' require 'ronin/support/encoding/base62' require 'python/pickle' module Ronin module Web module SessionCookie # # Represents a Django signed session cookie (JSON or Pickle serialized). # # ## Examples # # Parse a Django JSON session cookie: # # Ronin::Web::SessionCookie.parse('sessionid=eyJmb28iOiJiYXIifQ:1pQcTx:UufiSnuPIjNs7zOAJS0UpqnyvRt7KET7BVes0I8LYbA') # # => # # #"bar"}, # # @salt=1676070425> # # Parse a Django Pickled session cookie: # # Ronin::Web::SessionCookie.parse('sessionid=gAWVEAAAAAAAAAB9lIwDZm9vlIwDYmFylHMu:1pQcay:RjaK8DKN4xXQ_APIXXWEyFS08Q-PGo6UlRBFpedFk9M') # # => # # #"bar"}, # # @salt=1676070860> # # @see https://docs.djangoproject.com/en/4.1/topics/http/sessions/#using-cookie-based-sessions # class Django < Cookie # The salt used to sign the cookie. # # @return [Integer] # # @api public attr_reader :salt # The SHA256 HMAC of the Base64 encoded serialized {#params}. # # @return [String] # # @api public attr_reader :hmac # # Initializes the Django cookie. # # @param [Hash{String => Object}] params # The deserialized params of the session cookie. # # @param [Integer] salt # The Base62 decoded timestamp that is used to salt the HMAC. # # @param [Integer] hmac # The SHA256 HMAC of the Base64 encoded serialized {#params}. # # @api private # def initialize(params,salt,hmac) super(params) @salt = salt @hmac = hmac end # Regular expression to match Django session cookies. REGEXP = /\A(?:sessionid=)?#{URL_SAFE_BASE64_REGEXP}:#{URL_SAFE_BASE64_REGEXP}:#{URL_SAFE_BASE64_REGEXP}\z/ # # Identifies if the cookie is a Django session cookie. # # @param [String] string # The raw session cookie value. # # @return [Boolean] # Indicates whether the session cookie value is a Django session # cookie. # # @api public # def self.identify?(string) string =~ REGEXP end # # Parses a Django session cookie. # # @param [String] string # The raw session cookie string to parse. # # @return [Django] # The parsed and deserialized session cookie # # @api public # def self.parse(string) # remove any 'sessionid' prefix. string = string.sub(/\Asessionid=/,'') # split the cookie params, salt, hmac = string.split(':',3) params = Support::Encoding::Base64.decode(params, mode: :url_safe) params = if params.start_with?('{') && params.end_with?('}') # JSON serialized cookie JSON.parse(params) else # unpickle the Python Pickle serialized session cookie Python::Pickle.load(params) end salt = Support::Encoding::Base62.decode(salt) hmac = Support::Encoding::Base64.decode(hmac, mode: :url_safe) return new(params,salt,hmac) end # # Extracts the Django session cookie from the HTTP response. # # @param [Net::HTTPResponse] response # The HTTP response object. # # @return [Django, nil] # The parsed Django session cookie, or `nil` if there was no # `Set-Cookie` header containing a Django session cookie. # # @api public # def self.extract(response) if (set_cookie = response['Set-Cookie']) cookie = set_cookie.split(';',2).first if identify?(cookie) return parse(cookie) end end end end end end end