ronin-web-session_cookie 0.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +4 -0
- data/.github/workflows/ruby.yml +43 -0
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.rubocop.yml +20 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +13 -0
- data/Gemfile +35 -0
- data/README.md +137 -0
- data/Rakefile +36 -0
- data/gemspec.yml +29 -0
- data/lib/ronin/web/session_cookie/cookie.rb +160 -0
- data/lib/ronin/web/session_cookie/django.rb +169 -0
- data/lib/ronin/web/session_cookie/jwt.rb +151 -0
- data/lib/ronin/web/session_cookie/rack.rb +132 -0
- data/lib/ronin/web/session_cookie/version.rb +26 -0
- data/lib/ronin/web/session_cookie.rb +84 -0
- data/ronin-web-session_cookie.gemspec +62 -0
- metadata +128 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
|
4
|
+
#
|
5
|
+
# ronin-web-session_cookie is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Lesser General Public License as published
|
7
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# ronin-web-session_cookie is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
16
|
+
# along with ronin-web-session_cookie. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'ronin/web/session_cookie/cookie'
|
20
|
+
require 'ronin/support/encoding/base64'
|
21
|
+
require 'ronin/support/encoding/base62'
|
22
|
+
|
23
|
+
require 'python/pickle'
|
24
|
+
|
25
|
+
module Ronin
|
26
|
+
module Web
|
27
|
+
module SessionCookie
|
28
|
+
#
|
29
|
+
# Represents a Django signed session cookie (JSON or Pickle serialized).
|
30
|
+
#
|
31
|
+
# ## Examples
|
32
|
+
#
|
33
|
+
# Parse a Django JSON session cookie:
|
34
|
+
#
|
35
|
+
# Ronin::Web::SessionCookie.parse('sessionid=eyJmb28iOiJiYXIifQ:1pQcTx:UufiSnuPIjNs7zOAJS0UpqnyvRt7KET7BVes0I8LYbA')
|
36
|
+
# # =>
|
37
|
+
# # #<Ronin::Web::SessionCookie::Django:0x00007f29bb9c6b70
|
38
|
+
# # @hmac=
|
39
|
+
# # "R\xE7\xE2J{\x8F\"3l\xEF3\x80%-\x14\xA6\xA9\xF2\xBD\e{(D\xFB\x05W\xAC\xD0\x8F\va\xB0",
|
40
|
+
# # @params={"foo"=>"bar"},
|
41
|
+
# # @salt=1676070425>
|
42
|
+
#
|
43
|
+
# Parse a Django Pickled session cookie:
|
44
|
+
#
|
45
|
+
# Ronin::Web::SessionCookie.parse('sessionid=gAWVEAAAAAAAAAB9lIwDZm9vlIwDYmFylHMu:1pQcay:RjaK8DKN4xXQ_APIXXWEyFS08Q-PGo6UlRBFpedFk9M')
|
46
|
+
# # =>
|
47
|
+
# # #<Ronin::Web::SessionCookie::Django:0x00007f29b7aa6dc8
|
48
|
+
# # @hmac=
|
49
|
+
# # "F6\x8A\xF02\x8D\xE3\x15\xD0\xFC\x03\xC8]u\x84\xC8T\xB4\xF1\x0F\x8F\x1A\x8E\x94\x95\x10E\xA5\xE7E\x93\xD3",
|
50
|
+
# # @params={"foo"=>"bar"},
|
51
|
+
# # @salt=1676070860>
|
52
|
+
#
|
53
|
+
# @see https://docs.djangoproject.com/en/4.1/topics/http/sessions/#using-cookie-based-sessions
|
54
|
+
#
|
55
|
+
class Django < Cookie
|
56
|
+
|
57
|
+
# The salt used to sign the cookie.
|
58
|
+
#
|
59
|
+
# @return [Integer]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
attr_reader :salt
|
63
|
+
|
64
|
+
# The SHA256 HMAC of the Base64 encoded serialized {#params}.
|
65
|
+
#
|
66
|
+
# @return [String]
|
67
|
+
#
|
68
|
+
# @api public
|
69
|
+
attr_reader :hmac
|
70
|
+
|
71
|
+
#
|
72
|
+
# Initializes the Django cookie.
|
73
|
+
#
|
74
|
+
# @param [Hash{String => Object}] params
|
75
|
+
# The deserialized params of the session cookie.
|
76
|
+
#
|
77
|
+
# @param [Integer] salt
|
78
|
+
# The Base62 decoded timestamp that is used to salt the HMAC.
|
79
|
+
#
|
80
|
+
# @param [Integer] hmac
|
81
|
+
# The SHA256 HMAC of the Base64 encoded serialized {#params}.
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
#
|
85
|
+
def initialize(params,salt,hmac)
|
86
|
+
super(params)
|
87
|
+
|
88
|
+
@salt = salt
|
89
|
+
@hmac = hmac
|
90
|
+
end
|
91
|
+
|
92
|
+
# Regular expression to match Django session cookies.
|
93
|
+
REGEXP = /\A(?:sessionid=)?#{URL_SAFE_BASE64_REGEXP}:#{URL_SAFE_BASE64_REGEXP}:#{URL_SAFE_BASE64_REGEXP}\z/
|
94
|
+
|
95
|
+
#
|
96
|
+
# Identifies if the cookie is a Django session cookie.
|
97
|
+
#
|
98
|
+
# @param [String] string
|
99
|
+
# The raw session cookie value.
|
100
|
+
#
|
101
|
+
# @return [Boolean]
|
102
|
+
# Indicates whether the session cookie value is a Django session
|
103
|
+
# cookie.
|
104
|
+
#
|
105
|
+
# @api public
|
106
|
+
#
|
107
|
+
def self.identify?(string)
|
108
|
+
string =~ REGEXP
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Parses a Django session cookie.
|
113
|
+
#
|
114
|
+
# @param [String] string
|
115
|
+
# The raw session cookie string to parse.
|
116
|
+
#
|
117
|
+
# @return [Django]
|
118
|
+
# The parsed and deserialized session cookie
|
119
|
+
#
|
120
|
+
# @api public
|
121
|
+
#
|
122
|
+
def self.parse(string)
|
123
|
+
# remove any 'sessionid' prefix.
|
124
|
+
string = string.sub(/\Asessionid=/,'')
|
125
|
+
|
126
|
+
# split the cookie
|
127
|
+
params, salt, hmac = string.split(':',3)
|
128
|
+
|
129
|
+
params = Support::Encoding::Base64.decode(params, mode: :url_safe)
|
130
|
+
params = if params.start_with?('{') && params.end_with?('}')
|
131
|
+
# JSON serialized cookie
|
132
|
+
JSON.parse(params)
|
133
|
+
else
|
134
|
+
# unpickle the Python Pickle serialized session cookie
|
135
|
+
Python::Pickle.load(params)
|
136
|
+
end
|
137
|
+
|
138
|
+
salt = Support::Encoding::Base62.decode(salt)
|
139
|
+
hmac = Support::Encoding::Base64.decode(hmac, mode: :url_safe)
|
140
|
+
|
141
|
+
return new(params,salt,hmac)
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# Extracts the Django session cookie from the HTTP response.
|
146
|
+
#
|
147
|
+
# @param [Net::HTTPResponse] response
|
148
|
+
# The HTTP response object.
|
149
|
+
#
|
150
|
+
# @return [Django, nil]
|
151
|
+
# The parsed Django session cookie, or `nil` if there was no
|
152
|
+
# `Set-Cookie` header containing a Django session cookie.
|
153
|
+
#
|
154
|
+
# @api public
|
155
|
+
#
|
156
|
+
def self.extract(response)
|
157
|
+
if (set_cookie = response['Set-Cookie'])
|
158
|
+
cookie = set_cookie.split(';',2).first
|
159
|
+
|
160
|
+
if identify?(cookie)
|
161
|
+
return parse(cookie)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
|
4
|
+
#
|
5
|
+
# ronin-web-session_cookie is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Lesser General Public License as published
|
7
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# ronin-web-session_cookie is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
16
|
+
# along with ronin-web-session_cookie. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'ronin/web/session_cookie/cookie'
|
20
|
+
|
21
|
+
require 'base64'
|
22
|
+
require 'json'
|
23
|
+
|
24
|
+
module Ronin
|
25
|
+
module Web
|
26
|
+
module SessionCookie
|
27
|
+
#
|
28
|
+
# Represents a [JSON Web Token (JWT)][JWT].
|
29
|
+
#
|
30
|
+
# [JWT]: https://jwt.io
|
31
|
+
#
|
32
|
+
# ## Examples
|
33
|
+
#
|
34
|
+
# Ronin::Web::SessionCookie.parse('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c')
|
35
|
+
# # =>
|
36
|
+
# # #<Ronin::Web::SessionCookie::JWT:0x00007f18d5a45e58
|
37
|
+
# # @header={"alg"=>"HS256", "typ"=>"JWT"},
|
38
|
+
# # @hmac=
|
39
|
+
# # ":\x93\x92K\x0E\xDE\xE3\xCEK8\xFEO\xAF4\x9C\xC4v\xFBI\x1E\xAC\x00\xE3\x11rG\xC5\xC2.+\xA7\xBA",
|
40
|
+
# # @params={"id"=>123456789, "name"=>"Joseph"}>
|
41
|
+
#
|
42
|
+
# @see https://jwt.io/
|
43
|
+
#
|
44
|
+
class JWT < Cookie
|
45
|
+
|
46
|
+
# The parsed JWT header information.
|
47
|
+
#
|
48
|
+
# @return [Hash{String => Object}]
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
attr_reader :header
|
52
|
+
|
53
|
+
# The SHA256 HMAC of the encoded {#header} + `.` + the encoded
|
54
|
+
# {#payload}.
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
attr_reader :hmac
|
60
|
+
|
61
|
+
alias payload params
|
62
|
+
|
63
|
+
#
|
64
|
+
# Initializes the parsed JWT session cookie.
|
65
|
+
#
|
66
|
+
# @param [Hash{String => Object}] header
|
67
|
+
# The parsed header information.
|
68
|
+
#
|
69
|
+
# @param [Hash{String => Object}] payload
|
70
|
+
# The parsed JWT payload.
|
71
|
+
#
|
72
|
+
# @param [String] hmac
|
73
|
+
# The SHA256 HMAC of the encoded header + `.` + the encoded payload.
|
74
|
+
#
|
75
|
+
# @api private
|
76
|
+
#
|
77
|
+
def initialize(header,payload,hmac)
|
78
|
+
@header = header
|
79
|
+
|
80
|
+
super(payload)
|
81
|
+
|
82
|
+
@hmac = hmac
|
83
|
+
end
|
84
|
+
|
85
|
+
# Regular expression to match JWT session cookies.
|
86
|
+
REGEXP = /\A(Bearer )?#{URL_SAFE_BASE64_REGEXP}\.#{URL_SAFE_BASE64_REGEXP}\.#{URL_SAFE_BASE64_REGEXP}\z/
|
87
|
+
|
88
|
+
#
|
89
|
+
# Identifies whether the string is a JWT session cookie.
|
90
|
+
#
|
91
|
+
# @param [String] string
|
92
|
+
# The raw session cookie value to identify.
|
93
|
+
#
|
94
|
+
# @return [Boolean]
|
95
|
+
# Indicates whether the session cookie value is a JWT session cookie.
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
#
|
99
|
+
def self.identify?(string)
|
100
|
+
string =~ REGEXP
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Parses a JWT session cookie.
|
105
|
+
#
|
106
|
+
# @param [String] string
|
107
|
+
# The raw session cookie string to parse.
|
108
|
+
#
|
109
|
+
# @return [JWT]
|
110
|
+
# The parsed and deserialized session cookie
|
111
|
+
#
|
112
|
+
# @api public
|
113
|
+
#
|
114
|
+
def self.parse(string)
|
115
|
+
# remove any 'Bearer ' prefix.
|
116
|
+
string = string.sub(/\ABearer /,'')
|
117
|
+
|
118
|
+
# split the string
|
119
|
+
header, payload, hmac = string.split('.',3)
|
120
|
+
|
121
|
+
header = JSON.parse(Base64.decode64(header))
|
122
|
+
payload = JSON.parse(Base64.decode64(payload))
|
123
|
+
hmac = Base64.decode64(hmac)
|
124
|
+
|
125
|
+
return new(header,payload,hmac)
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Extracts the JWT session cookie from the HTTP response.
|
130
|
+
#
|
131
|
+
# @param [Net::HTTPResponse] response
|
132
|
+
# The HTTP response object.
|
133
|
+
#
|
134
|
+
# @return [JWT, nil]
|
135
|
+
# The parsed JWT session cookie, or `nil` if there was no
|
136
|
+
# `Authorization` header containing a JWT session cookie.
|
137
|
+
#
|
138
|
+
# @api public
|
139
|
+
#
|
140
|
+
def self.extract(response)
|
141
|
+
if (authorization = response['Authorization'])
|
142
|
+
if (match = authorization.match(REGEXP))
|
143
|
+
return parse(match[0])
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
|
4
|
+
#
|
5
|
+
# ronin-web-session_cookie is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Lesser General Public License as published
|
7
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# ronin-web-session_cookie is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
16
|
+
# along with ronin-web-session_cookie. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'ronin/web/session_cookie/cookie'
|
20
|
+
|
21
|
+
require 'base64'
|
22
|
+
require 'delegate'
|
23
|
+
require 'rack/session/cookie'
|
24
|
+
|
25
|
+
module Ronin
|
26
|
+
module Web
|
27
|
+
module SessionCookie
|
28
|
+
#
|
29
|
+
# Represents a [Rack][rack-session] session cookie.
|
30
|
+
#
|
31
|
+
# [rack-session]: https://github.com/rack/rack-session
|
32
|
+
#
|
33
|
+
# ## Examples
|
34
|
+
#
|
35
|
+
# Ronin::Web::SessionCookie.parse('rack.session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVG86HVJhY2s6OlNlc3Npb246OlNlc3Npb25JZAY6D0BwdWJsaWNfaWRJIkUyYWJkZTdkM2I0YTMxNDE5OThiYmMyYTE0YjFmMTZlNTNlMWMzYWJlYzhiYzc4ZjVhMGFlMGUwODJmMjJlZGIxBjsARkkiCWNzcmYGOwBGSSIxNHY1TmRCMGRVaklXdjhzR3J1b2ZhM2xwNHQyVGp5ZHptckQycjJRWXpIZz0GOwBGSSINdHJhY2tpbmcGOwBGewZJIhRIVFRQX1VTRVJfQUdFTlQGOwBUSSItOTkxNzUyMWYzN2M4ODJkNDIyMzhmYmI5Yzg4MzFmMWVmNTAwNGQyYwY7AEY%3D--02184e43850f38a46c8f22ffb49f7f22be58e272')
|
36
|
+
# # =>
|
37
|
+
# # #<Ronin::Web::SessionCookie::Rack:0x00007ff67455ee30
|
38
|
+
# # @params=
|
39
|
+
# # {"session_id"=>"2abde7d3b4a3141998bbc2a14b1f16e53e1c3abec8bc78f5a0ae0e082f22edb1",
|
40
|
+
# # "csrf"=>"4v5NdB0dUjIWv8sGruofa3lp4t2TjydzmrD2r2QYzHg=",
|
41
|
+
# # "tracking"=>{"HTTP_USER_AGENT"=>"9917521f37c882d42238fbb9c8831f1ef5004d2c"}}>
|
42
|
+
#
|
43
|
+
# @see https://github.com/rack/rack-session
|
44
|
+
#
|
45
|
+
class Rack < Cookie
|
46
|
+
|
47
|
+
# The HMAC for the deserialized and Base64 encoded session cookie.
|
48
|
+
#
|
49
|
+
# @return [String]
|
50
|
+
attr_reader :hmac
|
51
|
+
|
52
|
+
#
|
53
|
+
# Initializes the parsed Rack session cookie.
|
54
|
+
#
|
55
|
+
# @param [Hash{String => Object}] params
|
56
|
+
# The parsed params for the session cookie.
|
57
|
+
#
|
58
|
+
# @param [String] hmac
|
59
|
+
# The HMAC for the serialized and Base64 encoded session cookie.
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
#
|
63
|
+
def initialize(params,hmac)
|
64
|
+
super(params)
|
65
|
+
|
66
|
+
@hmac = hmac
|
67
|
+
end
|
68
|
+
|
69
|
+
# Regular expression to match Rack session cookies.
|
70
|
+
REGEXP = /\A(rack\.session=)?(?:#{STRICT_BASE64_REGEXP}|#{URI_ENCODED_BASE64_REGEXP})--[0-9a-f]{40}\z/
|
71
|
+
|
72
|
+
#
|
73
|
+
# Identifies if the cookie is a Rack session cookie.
|
74
|
+
#
|
75
|
+
# @param [String] string
|
76
|
+
# The raw session cookie value to identify.
|
77
|
+
#
|
78
|
+
# @return [Boolean]
|
79
|
+
# Indicates whether the session cookie is a Rack session cookie.
|
80
|
+
#
|
81
|
+
# @api public
|
82
|
+
#
|
83
|
+
def self.identify?(string)
|
84
|
+
string =~ REGEXP
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Parses a Django session cookie.
|
89
|
+
#
|
90
|
+
# @param [String] string
|
91
|
+
# The raw session cookie string to parse.
|
92
|
+
#
|
93
|
+
# @return [Rack]
|
94
|
+
# The parsed and deserialized session cookie
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
#
|
98
|
+
def self.parse(string)
|
99
|
+
# remove any 'rack.session' prefix.
|
100
|
+
string = string.sub(/\Arack\.session=/,'')
|
101
|
+
|
102
|
+
payload, hmac = string.split('--',2)
|
103
|
+
|
104
|
+
return new(Marshal.load(Base64.decode64(payload)),hmac)
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# Extracts the Rack session cookie from the HTTP response.
|
109
|
+
#
|
110
|
+
# @param [Net::HTTPResponse] response
|
111
|
+
# The HTTP response object.
|
112
|
+
#
|
113
|
+
# @return [Rack, nil]
|
114
|
+
# The parsed Rack session cookie, or `nil` if there was no
|
115
|
+
# `Set-Cookie` header containing a Rack session cookie.
|
116
|
+
#
|
117
|
+
# @api public
|
118
|
+
#
|
119
|
+
def self.extract(response)
|
120
|
+
if (set_cookie = response['Set-Cookie'])
|
121
|
+
cookie = set_cookie.split(';',2).first
|
122
|
+
|
123
|
+
if identify?(cookie)
|
124
|
+
return parse(cookie)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
|
4
|
+
#
|
5
|
+
# ronin-web-session_cookie is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Lesser General Public License as published
|
7
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# ronin-web-session_cookie is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
16
|
+
# along with ronin-web-session_cookie. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
module Ronin
|
20
|
+
module Web
|
21
|
+
module SessionCookie
|
22
|
+
# ronin-web-session_cookie version
|
23
|
+
VERSION = '0.1.0.rc1'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
|
4
|
+
#
|
5
|
+
# ronin-web-session_cookie is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Lesser General Public License as published
|
7
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# ronin-web-session_cookie is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
16
|
+
# along with ronin-web-session_cookie. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'ronin/web/session_cookie/rack'
|
20
|
+
require 'ronin/web/session_cookie/django'
|
21
|
+
require 'ronin/web/session_cookie/jwt'
|
22
|
+
|
23
|
+
module Ronin
|
24
|
+
module Web
|
25
|
+
#
|
26
|
+
# Namespace for `ronin-web-session_cookie`.
|
27
|
+
#
|
28
|
+
module SessionCookie
|
29
|
+
# All session cookie classes.
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
CLASSES = [
|
33
|
+
Rack,
|
34
|
+
JWT,
|
35
|
+
Django
|
36
|
+
]
|
37
|
+
|
38
|
+
#
|
39
|
+
# Parses the session cookie.
|
40
|
+
#
|
41
|
+
# @param [String] string
|
42
|
+
# The raw session cookie to parse.
|
43
|
+
#
|
44
|
+
# @return [Rack, Django, JWT, nil]
|
45
|
+
# The parsed and deserialized session cookie data.
|
46
|
+
# Returns `nil` if the session cookie did not match any of the supported
|
47
|
+
# formats.
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
#
|
51
|
+
def self.parse(string)
|
52
|
+
CLASSES.each do |klass|
|
53
|
+
if klass.identify?(string)
|
54
|
+
return klass.parse(string)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
return nil
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Extracts and parses the session cookie from the HTTP response.
|
63
|
+
#
|
64
|
+
# @param [Net::HTTPResponse] response
|
65
|
+
# The HTTP response object.
|
66
|
+
#
|
67
|
+
# @return [Rack, Django, JWT, nil]
|
68
|
+
# The parsed session cookie or `nil` if no session cookie could be
|
69
|
+
# detected.
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
#
|
73
|
+
def self.extract(response)
|
74
|
+
CLASSES.each do |klass|
|
75
|
+
if (session_cookie = klass.extract(response))
|
76
|
+
return session_cookie
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
return nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gemspec = YAML.load_file('gemspec.yml')
|
7
|
+
|
8
|
+
gem.name = gemspec.fetch('name')
|
9
|
+
gem.version = gemspec.fetch('version') do
|
10
|
+
lib_dir = File.join(File.dirname(__FILE__),'lib')
|
11
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
12
|
+
|
13
|
+
require 'ronin/web/session_cookie/version'
|
14
|
+
Ronin::Web::SessionCookie::VERSION
|
15
|
+
end
|
16
|
+
|
17
|
+
gem.summary = gemspec['summary']
|
18
|
+
gem.description = gemspec['description']
|
19
|
+
gem.licenses = Array(gemspec['license'])
|
20
|
+
gem.authors = Array(gemspec['authors'])
|
21
|
+
gem.email = gemspec['email']
|
22
|
+
gem.homepage = gemspec['homepage']
|
23
|
+
gem.metadata = gemspec['metadata'] if gemspec['metadata']
|
24
|
+
|
25
|
+
glob = ->(patterns) { gem.files & Dir[*patterns] }
|
26
|
+
|
27
|
+
gem.files = `git ls-files`.split($/)
|
28
|
+
gem.files = glob[gemspec['files']] if gemspec['files']
|
29
|
+
gem.files += Array(gemspec['generated_files'])
|
30
|
+
# exclude test files from the packages gem
|
31
|
+
gem.files -= glob[gemspec['test_files'] || 'spec/{**/}*']
|
32
|
+
|
33
|
+
gem.executables = gemspec.fetch('executables') do
|
34
|
+
glob['bin/*'].map { |path| File.basename(path) }
|
35
|
+
end
|
36
|
+
|
37
|
+
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
38
|
+
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
39
|
+
|
40
|
+
gem.require_paths = Array(gemspec.fetch('require_paths') {
|
41
|
+
%w[ext lib].select { |dir| File.directory?(dir) }
|
42
|
+
})
|
43
|
+
|
44
|
+
gem.requirements = gemspec['requirements']
|
45
|
+
gem.required_ruby_version = gemspec['required_ruby_version']
|
46
|
+
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
47
|
+
gem.post_install_message = gemspec['post_install_message']
|
48
|
+
|
49
|
+
split = ->(string) { string.split(/,\s*/) }
|
50
|
+
|
51
|
+
if gemspec['dependencies']
|
52
|
+
gemspec['dependencies'].each do |name,versions|
|
53
|
+
gem.add_dependency(name,split[versions])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if gemspec['development_dependencies']
|
58
|
+
gemspec['development_dependencies'].each do |name,versions|
|
59
|
+
gem.add_development_dependency(name,split[versions])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|