Skip to content

Commit 099582f

Browse files
committed
Added support for new type of Ursnif
1 parent f9064f5 commit 099582f

File tree

2 files changed

+34
-16
lines changed

2 files changed

+34
-16
lines changed

utils/ursnifscan.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@
4545
$c8 = "soft=%u"\
4646
$d1 = "%s://%s%s"\
4747
$d2 = "PRI \x2A HTTP/2.0"\
48-
condition: $a1 or ($b1 and 3 of ($c*)) or (5 of ($c*)) or ($b1 and all of ($d*))}'
48+
$e1 = { A1 ?? ?? ?? 00 35 E7 F7 8A 40 50 }\
49+
$e2 = { 56 56 56 6A 06 5? FF ?? ?? ?? ?? 00 }\
50+
$f1 = { 56 57 BE ?? ?? ?? ?? 8D ?? ?? A5 A5 A5 }\
51+
$f2 = { 35 8F E3 B7 3F }\
52+
$f3 = { 35 0A 60 2E 51 }\
53+
condition: $a1 or ($b1 and 3 of ($c*)) or (5 of ($c*)) or ($b1 and all of ($d*)) or all of ($e*) or all of ($f*)}'
4954
}
5055

5156
# Magic pattern
@@ -74,7 +79,7 @@
7479
0x510f22d2: ["c2_tor_domain", DT_STR],
7580
0x556aed8f: ["server", DT_STR],
7681
0x584e5925: ["SetWaitableTimer_value", DT_STR],
77-
0x602c2c26: ["capture_window_title?(CRC_KEYLOGLIST)"],
82+
0x602c2c26: ["capture_window_title?(CRC_KEYLOGLIST)", DT_STR],
7883
0x656b798a: ["botnet", DT_STR],
7984
0x6de85128: ["not_use(CRC_BCTIMEOUT)", DT_STR],
8085
0x73177345: ["dga_base_url", DT_STR],
@@ -161,34 +166,39 @@ def parse_joinned_data(self, data):
161166
magic_dword = data[m.start():m.start() + 4]
162167
if (magic_dword[0:1] == "J1" or magic_dword[3] == "\0"):
163168
(flags, crc32_name, addr, size) = unpack_from("<LLLL", data, m.start() + 4)
164-
print("[+] magic: {0} flags: 0x{1:X} crc32_name: 0x{2:X} addr: 0x{3:X} size: 0x{4:X}\n".format(
169+
print("[+] magic: {0} flags: 0x{1:X} crc32_name: 0x{2:X} addr: 0x{3:X} size: 0x{4:X}".format(
165170
repr(magic_dword), flags, crc32_name, addr, size))
166171
elif (magic_dword[0:1] == "JJ" or (ord(magic_dword[3]) & 1) == 1):
167172
(xor_dword, crc32_name, addr, size) = unpack_from("<LLLL", data, m.start() + 4)
168-
print("[+] magic: {0} xor: 0x{1:X} crc32_name: 0x{2:X} addr: 0x{3:X} size: 0x{4:X}\n".format(
173+
print("[+] magic: {0} xor: 0x{1:X} crc32_name: 0x{2:X} addr: 0x{3:X} size: 0x{4:X}".format(
169174
repr(magic_dword), xor_dword, crc32_name, addr, size))
170175
else:
171176
break
172177

173178
if size > 0x80000:
174-
print("[!] size is too large, skipped this entry\n")
179+
print("[!] size is too large, skipped this entry")
175180
continue
176181

177182
try:
178183
offset = addr
179184
except:
180-
print("[!] This PE is old Ursnif (not DreamBot)\n")
185+
print("[!] This PE is old Ursnif (not DreamBot)")
181186
(addr, size, crc32_name, flags) = unpack_from(
182187
"<LLLL", data, m.start() + 4)
183-
print("[+] magic: {0} addr: 0x{1:X} size: 0x{2:X} crc32_name: 0x{3:X} flags: 0x{4:X}\n".format(
188+
print("[+] magic: {0} addr: 0x{1:X} size: 0x{2:X} crc32_name: 0x{3:X} flags: 0x{4:X}".format(
184189
repr(magic_dword), addr, size, crc32_name, flags))
185190
offset = addr
186-
joined_res = data[offset:offset + size]
191+
187192
try:
193+
joined_res = data[offset:offset + size]
188194
dec_data = aplib.decompress(joined_res).do()[0]
189195
except:
190-
print("[!] Cann't decode data.\n")
191-
continue
196+
pe = pefile.PE(data=data)
197+
offset = pe.get_offset_from_rva(addr)
198+
joined_res = data[offset:offset + size]
199+
dec_data = aplib.decompress(joined_res).do()[0]
200+
#print("[!] Cann't decode data.")
201+
#continue
192202

193203
if (xor_dword != 0):
194204
mod_data = ""
@@ -201,22 +211,22 @@ def parse_joinned_data(self, data):
201211
if crc32_name in (0x4f75cea7, 0x9e154a0c):
202212
fname = "ursnif_client32.bin"
203213
open(fname, "wb").write(dec_data)
204-
print("[+] dumped 32 bit client dll: {0}\n".format(fname))
214+
print("[+] dumped 32 bit client dll: {0}".format(fname))
205215
fnames.append(dec_data)
206216
elif crc32_name in (0x90f8aab4, 0x41982e1f):
207217
fname = "ursnif_client64.bin"
208218
open(fname, "wb").write(dec_data)
209-
print("[+] dumped 64 bit client dll: {0}\n".format(fname))
219+
print("[+] dumped 64 bit client dll: {0}".format(fname))
210220
# fnames.append(fname)
211221

212222
elif crc32_name in (0xe1285e64,):
213223
fname = "ursnif_public_key.bin"
214224
open(fname, "wb").write(dec_data)
215-
print("[+] dumped public key: {0}\n".format(fname))
225+
print("[+] dumped public key: {0}".format(fname))
216226
elif crc32_name in (0xd722afcb, 0x8365b957, 0x8fb1dde1):
217227
fname = "ursnif_st_config.bin"
218228
open(fname, "wb").write(dec_data)
219-
print("[+] dumped static config: {0}\n".format(fname))
229+
print("[+] dumped static config: {0}".format(fname))
220230
config_data.append(self.parse_config(dec_data))
221231
else:
222232
fname = "ursnif_" + hex(addr) + "_ap32_dec.bin"
@@ -262,7 +272,10 @@ def calculate(self):
262272
if not config_data:
263273
p_data = OrderedDict()
264274
data = self.pe_magic_check(data)
265-
pe = pefile.PE(data=data)
275+
try:
276+
pe = pefile.PE(data=data)
277+
except:
278+
continue
266279
imagebase = pe.NT_HEADERS.OPTIONAL_HEADER.ImageBase
267280
for pattern in CONFIG_PATTERNS:
268281
m = re.search(pattern, data)

yara/rule.yara

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,13 @@ rule Ursnif {
148148
$c8 = "soft=%u"
149149
$d1 = "%s://%s%s"
150150
$d2 = "PRI \x2A HTTP/2.0"
151+
$e1 = { A1 ?? ?? ?? 00 35 E7 F7 8A 40 50 }
152+
$e2 = { 56 56 56 6A 06 5? FF ?? ?? ?? ?? 00 }
153+
$f1 = { 56 57 BE ?? ?? ?? ?? 8D ?? ?? A5 A5 A5 }
154+
$f2 = { 35 8F E3 B7 3F }
155+
$f3 = { 35 0A 60 2E 51 }
151156
152-
condition: $a1 or ($b1 and 3 of ($c*)) or (5 of ($c*)) or ($b1 and all of ($d*))
157+
condition: $a1 or ($b1 and 3 of ($c*)) or (5 of ($c*)) or ($b1 and all of ($d*)) or all of ($e*) or all of ($f*)
153158
}
154159

155160
rule Emotet {

0 commit comments

Comments
 (0)