ronin-web-session_cookie 0.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|