Skip to content

Commit d17ebf3

Browse files
committed
python/smb - let dionaea talk to WannaCry, MS17-010 honeypot and collect Double Pulsar payload
- dionaea will act and respond as it had the Double Pulsar implanted - once WannaCry try to scan and found “hey, you have implanted with DoublePulsar. Let me start to deliver my payload", dionaea will capture the payload nicely - modify SMB_Sessionsetup_AndX_Response to make dionaea a Windows 7 machine (it was Windows 2000 for years) - update SMB_COM_TRANSACTION2 packet handling, collecting payload to disk and logging - add new classes: SMB_NT_Trans_Request, SMB_NT_Trans_Response, SMB_Trans2_Secondary_Request
1 parent 13fd187 commit d17ebf3

3 files changed

Lines changed: 151 additions & 6 deletions

File tree

‎modules/python/scripts/smb/include/smbfields.py‎

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -839,8 +839,10 @@ class SMB_Sessionsetup_AndX_Response2(Packet):
839839
XLEShortField("Action",1),
840840
MultiFieldLenField("ByteCount", None, fmt='<H', length_of=("Padding","NativeOS", "NativeLanManager", "PrimaryDomain")),
841841
ConditionalField(StrFixedLenField("Padding", b'\0', 1), lambda x:x.underlayer.Flags2 & SMB_FLAGS2_UNICODE),
842-
SMBNullField("NativeOS","Windows 5.1", utf16=lambda x:x.underlayer.Flags2 & SMB_FLAGS2_UNICODE),
843-
SMBNullField("NativeLanManager","Windows 2000 LAN Manager", utf16=lambda x:x.underlayer.Flags2 & SMB_FLAGS2_UNICODE),
842+
SMBNullField("NativeOS","Windows 7 Professional 7600", utf16=lambda x:x.underlayer.Flags2 & SMB_FLAGS2_UNICODE),
843+
SMBNullField("NativeLanManager","Windows 7 Professional 6.1", utf16=lambda x:x.underlayer.Flags2 & SMB_FLAGS2_UNICODE),
844+
# SMBNullField("NativeOS","Windows 5.1", utf16=lambda x:x.underlayer.Flags2 & SMB_FLAGS2_UNICODE),
845+
# SMBNullField("NativeLanManager","Windows 2000 LAN Manager", utf16=lambda x:x.underlayer.Flags2 & SMB_FLAGS2_UNICODE),
844846
SMBNullField("PrimaryDomain","WORKGROUP", utf16=lambda x:x.underlayer.Flags2 & SMB_FLAGS2_UNICODE),
845847
]
846848

@@ -1190,6 +1192,14 @@ class SMB_Trans_Response(Packet):
11901192
ByteField("Reserved2",0),
11911193
]
11921194

1195+
class SMB_Trans_Response_Simple(Packet):
1196+
name = "SMB Trans Response Simple"
1197+
smb_cmd = SMB_COM_TRANSACTION #0x25
1198+
fields_desc = [
1199+
ByteField("WordCount",0),
1200+
LEShortField("ByteCount",0),
1201+
]
1202+
11931203
# page 45
11941204
class SMB_Trans2_Request(Packet):
11951205
name = "SMB Trans2 Request"
@@ -1256,6 +1266,54 @@ class SMB_Trans2_Response(Packet):
12561266
LEShortField("ByteCount",0),
12571267
]
12581268

1269+
class SMB_Trans2_Secondary_Request(Packet):
1270+
name = "SMB Trans2 Secondary Request"
1271+
smb_cmd = SMB_COM_TRANSACTION2_SECONDARY # 0x33
1272+
fields_desc = [
1273+
ByteField("WordCount",0),
1274+
LEShortField("TotalParamCount",0),
1275+
LEShortField("TotalDataCount",0),
1276+
LEShortField("MaxParamCount",0),
1277+
LEShortField("MaxDataCount",0),
1278+
ByteField("MaxSetupCount",0),
1279+
ByteField("Reserved",0),
1280+
XLEShortField("Flags",0),
1281+
LEIntField("Timeout",0),
1282+
ShortField("Reserved2",0),
1283+
FieldLenField("ParamCount", 0, fmt='<H', count_of="Data"),
1284+
StrFixedLenField("Data", "", length_from=lambda pkt: pkt.ParamCount),
1285+
]
1286+
1287+
class SMB_NT_Trans_Request(Packet):
1288+
name = "SMB NT Trans Request"
1289+
smb_cmd = SMB_COM_NT_TRANSACT #0xa0
1290+
fields_desc = [
1291+
ByteField("WordCount",0),
1292+
ByteField("MaxSetupCount",0),
1293+
ShortField("Reserved",0),
1294+
LEIntField("TotalParamCount",0),
1295+
LEIntField("TotalDataCount",0),
1296+
LEIntField("MaxParamCount",0),
1297+
LEIntField("MaxDataCount",0),
1298+
FieldLenField("ParamCount", 0, fmt='<I', count_of="Params"),
1299+
LEIntField("ParamOffset",0),
1300+
LEIntField("DataCount",0),
1301+
LEIntField("DataOffset",0),
1302+
FieldLenField("SetupCount", 0, fmt='B', count_of="Setup"),
1303+
ShortField("Function",0),
1304+
#TODO: need more work on this part
1305+
FieldListField("Param", 0, XByteField("", 0), count_from = lambda pkt: pkt.ParamCount),
1306+
StrFixedLenField("Data", b"", length_from=lambda pkt: pkt.DataCount),
1307+
]
1308+
1309+
class SMB_NT_Trans_Response(Packet):
1310+
name = "SMB NT Trans Response"
1311+
smb_cmd = SMB_COM_NT_TRANSACT #0xa0
1312+
fields_desc = [
1313+
ByteField("WordCount",0),
1314+
LEShortField("ByteCount",0),
1315+
]
1316+
12591317
# [MS-CIFS].pdf - 2.2.5 Transaction Subcommands
12601318
# http://msdn.microsoft.com/en-us/library/ee441557%28v=PROT.13%29.aspx
12611319
TRANS_NMPIPE_SET_STATE = 0x0001
@@ -1522,6 +1580,7 @@ class RAP_Response(Packet):
15221580
bind_bottom_up(SMB_Header, SMB_NTcreate_AndX_Response, Command=lambda x: x==0xa2, Flags=lambda x: x&0x80)
15231581
bind_bottom_up(SMB_Header, SMB_Trans_Request, Command=lambda x: x==0x25, Flags=lambda x: not x&0x80)
15241582
bind_bottom_up(SMB_Header, SMB_Trans2_Request, Command=lambda x: x==0x32, Flags=lambda x: not x&0x80)
1583+
bind_bottom_up(SMB_Header, SMB_Trans2_Secondary_Request, Command=lambda x: x==0x33, Flags=lambda x: not x&0x80)
15251584

15261585
bind_bottom_up(SMB_Header, SMB_Write_AndX_Request, Command=lambda x: x==0x2f, Flags=lambda x: not x&0x80)
15271586
bind_bottom_up(SMB_Header, SMB_Write_AndX_Response, Command=lambda x: x==0x2f, Flags=lambda x: x&0x80)
@@ -1568,6 +1627,7 @@ class RAP_Response(Packet):
15681627
bind_top_down(SMB_Header, SMB_Read_AndX_Response, Command=0x2e)
15691628
bind_top_down(SMB_Header, SMB_Trans_Request, Command=0x25)
15701629
bind_top_down(SMB_Header, SMB_Trans2_Request, Command=0x32)
1630+
bind_top_down(SMB_Header, SMB_Trans2_Secondary_Request, Command=0x33)
15711631
bind_top_down(SMB_Header, SMB_Open_AndX_Request, Command=0x2d)
15721632
bind_top_down(SMB_Read_AndX_Response, SMB_Data)
15731633

‎modules/python/scripts/smb/smb.py‎

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import tempfile
3535
import binascii
3636
import os
37+
import hashlib
38+
from dionaea.util import xor
3739
from uuid import UUID
3840

3941
from .include.smbfields import *
@@ -70,10 +72,12 @@ def __init__ (self):
7072
'stop': False,
7173
}
7274
self.buf = b''
75+
self.buf2 = b'' # ms17-010 SMB_COM_TRANSACTION2
7376
self.outbuf = None
7477
self.fids = {}
7578
self.printer = b'' # spoolss file "queue"
7679

80+
7781
def handle_established(self):
7882
# self.timeouts.sustain = 120
7983
self.timeouts.idle = 120
@@ -121,9 +125,9 @@ def handle_io_in(self,data):
121125
r = self.process(p)
122126
smblog.debug('packet: {0}'.format(p.summary()))
123127

124-
if p.haslayer(Raw):
125-
smblog.warning('p.haslayer(Raw): {0}'.format(p.getlayer(Raw).build()))
126-
p.show()
128+
# if p.haslayer(Raw):
129+
# smblog.warning('p.haslayer(Raw): {0}'.format(p.getlayer(Raw).build()))
130+
# p.show()
127131

128132
# i = incident("dionaea.module.python.smb.info")
129133
# i.con = self
@@ -305,6 +309,8 @@ def process(self, p):
305309
if Service.startswith('\\\\'):
306310
Service = Service[1:]
307311
Service = Service.split('\\')[-1]
312+
if Service[-1] == '\x00':
313+
Service = Service[:-1]
308314
if Service[-1] == '$':
309315
Service = Service[:-1]
310316
r.Service = Service + '\x00'
@@ -525,15 +531,80 @@ def process(self, p):
525531

526532
rdata.ByteCount = dceplen
527533
rdata.Bytes = self.outbuf
534+
535+
if socket.htons(h.Setup[0]) == TRANS_NMPIPE_PEEK:
536+
SetupCount = h.SetupCount
537+
if SetupCount > 0:
538+
smblog.info('MS17-010 - SMB RCE exploit scanning..')
539+
r = SMB_Trans_Response_Simple()
540+
# returned #STATUS_INSUFF_SERVER_RESOURCE as we not being patched
541+
rstatus = 0xc0000205 #STATUS_INSUFF_SERVER_RESOURCES
528542

529543
r /= rdata
530-
elif p.getlayer(SMB_Header).Command == SMB_COM_TRANSACTION2:
544+
elif Command == SMB_COM_TRANSACTION2:
545+
h = p.getlayer(SMB_Trans2_Request)
546+
if h.Setup[0] == SMB_TRANS2_SESSION_SETUP:
547+
smblog.info('Possible DoublePulsar connection attempts..')
548+
# make sure the payload size not larger than 10MB
549+
if len(self.buf2) > 10485760:
550+
self.buf2 = ''
551+
elif len(self.buf2) == 0 and h.DataCount == 4096:
552+
self.buf2 = self.buf2 + h.Data
553+
elif len(self.buf2) != 0 and h.DataCount == 4096:
554+
self.buf2 = self.buf2 + h.Data
555+
elif len(self.buf2) != 0 and h.DataCount < 4096:
556+
smblog.info('DoublePulsar payload receiving..')
557+
self.buf2 = self.buf2 + h.Data
558+
key = bytearray([0x52,0x73,0x36,0x5E])
559+
xor_output = xor(self.buf2, key)
560+
hash_buf2 = hashlib.md5(self.buf2);
561+
smblog.info('DoublePulsar payload - MD5 (before XOR decryption): %s' %(hash_buf2.hexdigest()))
562+
hash_xor_output = hashlib.md5(xor_output);
563+
smblog.info('DoublePulsar payload - MD5 (after XOR decryption ): %s' %(hash_xor_output.hexdigest()))
564+
565+
#f = tempfile.NamedTemporaryFile(delete=False, prefix="xorfull-"+hash_xor_output.hexdigest()+"-", suffix="", dir=g_dionaea.config()['downloads']['dir'])
566+
dir=g_dionaea.config()['downloads']['dir'] + "/"
567+
f = open(dir+hash_xor_output.hexdigest(),'wb')
568+
f.write(xor_output)
569+
f.close
570+
571+
offset = 0
572+
for i, c in enumerate(xor_output):
573+
if ((xor_output[i] == 0x4d and xor_output[i+1] == 0x5a) and xor_output[i+2] == 0x90):
574+
offset = i
575+
smblog.info('DoublePulsar payload - MZ header found...')
576+
break
577+
578+
hash_xor_output_mz = hashlib.md5(xor_output[offset:]);
579+
smblog.info('DoublePulsar payload - MD5 final: %s. Save to disk' %(hash_xor_output_mz.hexdigest()))
580+
#f1 = tempfile.NamedTemporaryFile(delete=False, prefix=hash_xor_output_mz.hexdigest()+"-", suffix="", dir=g_dionaea.config()['downloads']['dir'])
581+
f1 = open(dir+hash_xor_output_mz.hexdigest(),'wb')
582+
f1.write(xor_output[offset:])
583+
f1.close
584+
self.buf2 = b''
585+
xor_output = b''
586+
587+
icd = incident("dionaea.download.complete")
588+
icd.path = dir+hash_xor_output_mz.hexdigest()
589+
icd.url = self.remote.host
590+
icd.con = self
591+
icd.report()
531592
r = SMB_Trans2_Response()
593+
rstatus = 0xc0000002 #STATUS_NOT_IMPLEMENTED
594+
532595
elif Command == SMB_COM_DELETE:
533596
# specific for NMAP smb-enum-shares.nse support
534597
h = p.getlayer(SMB_Delete_Request)
535598
if h.FileName == b'nmap-test-file\0':
536599
r = SMB_Delete_Response()
600+
elif Command == SMB_COM_TRANSACTION2_SECONDARY:
601+
h = p.getlayer(SMB_Trans2_Secondary_Request)
602+
# TODO: need some extra works
603+
pass
604+
elif Command == SMB_COM_NT_TRANSACT:
605+
h = p.getlayer(SMB_NT_Trans_Request)
606+
r = SMB_NT_Trans_Response()
607+
rstatus = 0x00000000 #STATUS_SUCCESS
537608
else:
538609
smblog.critical('...unknown SMB Command. bailing out.')
539610
p.show()
@@ -542,9 +613,18 @@ def process(self, p):
542613
smbh = SMB_Header(Status=rstatus)
543614
smbh.Command = r.smb_cmd
544615
smbh.Flags2 = p.getlayer(SMB_Header).Flags2
616+
if Command == SMB_COM_TRANSACTION:
617+
smbh.Flags2 = p.getlayer(SMB_Header).Flags2 | SMB_FLAGS2_ERR_STATUS
618+
smblog.info('MS17-010 - responded with legitimate SMB with #STATUS_INSUFF_SERVER_RESOURCES')
619+
smblog.info('MS17-010 - waiting for next incoming request.. ')
545620
# smbh.Flags2 = p.getlayer(SMB_Header).Flags2 & ~SMB_FLAGS2_EXT_SEC
546621
smbh.MID = p.getlayer(SMB_Header).MID
547622
smbh.PID = p.getlayer(SMB_Header).PID
623+
# Deception for DoublePulsar, we fix the XOR key first as 0x5273365E
624+
# WannaCry will use the XOR key to encrypt and deliver next payload, so we can decode easily later
625+
if Command == SMB_COM_TRANSACTION2:
626+
smbh.MID = p.getlayer(SMB_Header).MID + 16
627+
smbh.Signature = 0x000000009cf9c567
548628
rp = NBTSession()/smbh/r
549629

550630
if Command in SMB_Commands:

‎modules/python/scripts/util.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,8 @@ def hashfile(filename, digest):
4646
fh.close()
4747
return digest.hexdigest()
4848

49+
def xor(data, key):
50+
l = len(key)
51+
return bytearray((
52+
(data[i] ^ key[i % l]) for i in range(0,len(data))
53+
))

0 commit comments

Comments
 (0)