├── .gitignore
├── README.md
├── RIG
├── landing.py
└── rigswf.py
├── Sundown
└── landing.py
├── angler
└── landing.py
├── neutrino
└── neutrino.py
└── nuclear
├── chain.py
└── landing.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | *.egg-info/
23 | .installed.cfg
24 | *.egg
25 |
26 | # PyInstaller
27 | # Usually these files are written by a python script from a template
28 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
29 | *.manifest
30 | *.spec
31 |
32 | # Installer logs
33 | pip-log.txt
34 | pip-delete-this-directory.txt
35 |
36 | # Unit test / coverage reports
37 | htmlcov/
38 | .tox/
39 | .coverage
40 | .coverage.*
41 | .cache
42 | nosetests.xml
43 | coverage.xml
44 | *,cover
45 |
46 | # Translations
47 | *.mo
48 | *.pot
49 |
50 | # Django stuff:
51 | *.log
52 |
53 | # Sphinx documentation
54 | docs/_build/
55 |
56 | # PyBuilder
57 | target/
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ekdeco
2 | Scripts for dealing with various ek's
3 |
--------------------------------------------------------------------------------
/RIG/landing.py:
--------------------------------------------------------------------------------
1 | import ast
2 | import pprint
3 | import hashlib
4 | import argparse
5 | import sys
6 | import re
7 | import os
8 | import itertools
9 |
10 | apr = argparse.ArgumentParser(description='RIG landing page decoder')
11 | apr.add_argument('file', type=str, nargs='?', help='File path')
12 | apr.add_argument('-d', '--dir', help='Output dir', default='/tmp')
13 | apr.add_argument(
14 | '-o', '--out', help="prefix for filename used to store decoded chunks")
15 | args = None
16 |
17 |
18 | def clear_comments(d):
19 | return re.sub('/\*[^/]*\*/', '', d).replace('"+"', '')
20 |
21 |
22 | def decode_fdata(data):
23 | alphabet = ('za1sd0123456789abcdefghijklmnopqrstuvwxyz'
24 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[5:])
25 | mixtbl = ('za1sdLMNOPWXY3defghiQRSTUVjklmnABC012'
26 | 'DEFopq456789abcrstuvwxyzGHIJKZ'[5:])
27 | r = ''.join([alphabet[mixtbl.index(c)]
28 | for c in data]).decode('hex').strip('\x00')
29 | x = ord(r[1]) ^ ord(';')
30 | r = ''.join(chr(ord(c) ^ x) for c in r)
31 | return r.strip("\x00")
32 |
33 |
34 | def write_payload(t, payload):
35 | if args and args.out:
36 | h = hashlib.sha256(payload).hexdigest()
37 | with open(args.dir + os.sep + '.'.join([args.out, h, t]), 'w') as f:
38 | f.write(payload)
39 |
40 |
41 | def extract_from_swfpage(d):
42 | r = {}
43 | data = d.split('"')
44 | r['flash_url'] = data[-6]
45 | r['flash_drop_1'] = data[-4]
46 | r['drop_key'] = data[-2]
47 | return r
48 |
49 |
50 | def do_rige(d):
51 | r = {'vb_drops': []}
52 | for s in re.findall('', d, re.S):
53 | # if not s: continue
54 |
55 | d = clear_comments(s)
56 | xxx = re.findall(
57 | '"([/A-Za-z0-9=+]+)"\.replace\((/|"|\')([^/"\']+)("|/|\')', d)
58 | if xxx:
59 | basep, _, g, _ = xxx[0]
60 | else:
61 | # last month changes...
62 | basep = re.findall('"([/A-Za-z0-9=+]{65,})";', d)[0]
63 | g = ''
64 | payload = clear_comments(basep.replace(g, '').decode('base64'))
65 | if 'Dim' in payload:
66 | x = 'http' + re.findall('"http(.*?)"', payload)[0]
67 | r['vb_drops'].append(x)
68 | t = 'exploit.vbs'
69 | elif 'application/x-shockwave-flash' in payload:
70 | d = payload.split(';')[-2].split('"')
71 | flash_url = d[1]
72 | r['flash_url'] = flash_url
73 | try:
74 | id, key, url = decode_fdata(d[-2]).split(';')
75 | r['flash_drop_%s' % id] = url
76 | r['drop_key'] = key
77 | except:
78 | r.update(extract_from_swfpage(payload))
79 | t = 'flash_drop.html'
80 | else:
81 | t = 'exploit.js'
82 |
83 | write_payload(t, payload)
84 | r['type'] = 'rig-e'
85 | return r
86 |
87 |
88 | def do_rigv(d):
89 | r = {'vb_drops': []}
90 | # this gonna be fun...
91 | for s in re.findall('', d, re.S):
92 |
93 | vars = re.findall('="([^"]+)".split\((\'|")([^\'"]+)(\'|")', s)
94 | repl = re.findall('="(.+)";for', s)[0]
95 | x = []
96 | for v, _, e, _ in vars:
97 | x.append(v.split(e))
98 | payload = ''.join(
99 | a + b for a, b in zip(x[1], itertools.cycle(x[0][::-1])))
100 | for i in range(0, len(repl) - 4, 2):
101 | payload = payload.replace(repl[i], repl[i + 1])
102 |
103 | x = re.findall('var [a-z]+ = ([^;,]+)(;|,)', payload)[0][0]
104 | payload = ast.literal_eval(''.join(x.split("'+'"))).decode('base64')
105 | if 'redim' in payload.lower():
106 | x = 'http' + re.findall('"http(.*?)"', payload)[0]
107 | r['vb_drops'].append(x)
108 | t = 'exploit.vbs'
109 |
110 | elif 'application/x-shockwave-flash' in payload:
111 | r.update(extract_from_swfpage(payload))
112 | t = 'flash_drop.html'
113 |
114 | else:
115 | t = 'exploit.js'
116 |
117 | write_payload(t, payload)
118 | r['type'] = 'rig-v'
119 |
120 | return r
121 |
122 |
123 | def doit(d):
124 | try:
125 | r = do_rige(d)
126 | except Exception as e:
127 | print repr(e)
128 | r = do_rigv(d)
129 | return r
130 |
131 |
132 | if __name__ == '__main__':
133 | args = apr.parse_args()
134 | with open(sys.argv[1]) as f:
135 | d = f.read()
136 | r = doit(d)
137 | pprint.pprint(r)
138 |
--------------------------------------------------------------------------------
/RIG/rigswf.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import struct
4 | from StringIO import StringIO
5 | from swf.stream import SWFStream
6 | from swf.movie import SWF, SWFHeader
7 |
8 | from Crypto.Cipher import ARC4 as RC4
9 | from Crypto.Cipher import AES
10 |
11 |
12 | def get_keys(data):
13 | f = StringIO(data) if isinstance(data, basestring) else data
14 | ret = []
15 | for i in range(0, ord(f.read(1))):
16 | ret.append(f.read(16))
17 | return ret
18 |
19 |
20 | class xSWF(SWF):
21 |
22 | class SS(SWFStream):
23 |
24 | def _read_bytes_aligned(self, bytes):
25 | buf = map(ord, self.f.read(bytes))
26 | return reduce(lambda x, y: x << 8 | y, buf, 0)
27 |
28 | @property
29 | def binary_data(self):
30 | if not hasattr(self, '_bd'):
31 | self._bd = self.build_dictionary()
32 | return self._bd
33 |
34 | @property
35 | def symbols(self):
36 | if not hasattr(self, '_sc'):
37 | for s in self.tags:
38 | if s.name == 'SymbolClass':
39 | self._sc = s
40 | break
41 | return self._sc.symbols
42 |
43 | @property
44 | def script(self):
45 | if not hasattr(self, '_s'):
46 | for s in self.tags:
47 | if s.name in ['DoABC', 'DoAction']:
48 | self._s = s
49 | break
50 | return self._s
51 |
52 | @property
53 | def strings(self, data=None):
54 | """get a list of strings from abcFile, whose length larger than 5
55 | """
56 | if not hasattr(self, '_ss'):
57 | self._ss = re.findall('[a-zA-Z0-9]{5,}', data or self.script.bytes)
58 | return self._ss
59 |
60 | def tag_by_name(self, name):
61 | for s in self.symbols:
62 | if s.name.endswith(name):
63 | return self.binary_data[s.tagId]
64 | return None
65 |
66 | # fix problem with pyswf...
67 | def parse(self, data):
68 | import io
69 | self._data = data = data if isinstance(
70 | data, SWFStream) else self.SS(data)
71 | self._header = SWFHeader(self._data)
72 | if self._header.compressed:
73 | temp = io.BytesIO()
74 | if self._header.compressed_zlib:
75 | import zlib
76 | data = data.f.read()
77 | zip = zlib.decompressobj()
78 | temp.write(zip.decompress(data))
79 | else:
80 | import pylzma
81 | data.readUI32() # consume compressed length
82 | data = data.f.read()
83 | temp.write(pylzma.decompress(data))
84 | temp.seek(0)
85 | data = self.SS(temp)
86 | self._header._frame_size = data.readRECT()
87 | self._header._frame_rate = data.readFIXED8()
88 | self._header._frame_count = data.readUI16()
89 | self.parse_tags(data)
90 |
91 |
92 | class RIG(xSWF):
93 | # assume
94 | # 1 - strings
95 | # 2 - key for string
96 | # 3 - shellcode
97 | # 4 - key for shellcode
98 |
99 | def read_blob(self, data):
100 | f = StringIO(data) if isinstance(data, basestring) else data
101 | cnt = struct.unpack('>I', f.read(4))[0]
102 | for i in range(cnt):
103 | size = struct.unpack('>I', f.read(4))[0]
104 | yield f.read(size)
105 |
106 | @property
107 | def type(self):
108 | if not hasattr(self, '_type'):
109 | self._type = 'rig-v' if len(rig.binary_data) == 2 else 'rig-e'
110 | return self._type
111 |
112 | @property
113 | def enc_strings(self):
114 | if not hasattr(self, 'rc4keys'):
115 | self.rc4keys = get_keys(self.binary_data[2].data)
116 | if not hasattr(self, '_enc_strings'):
117 | s = []
118 | for i, _s in enumerate(self.read_blob(self.binary_data[1].data)):
119 | s.append(RC4.new(self.rc4keys[i %
120 | len(self.rc4keys)]).decrypt(_s))
121 | self._enc_strings = s
122 | return self._enc_strings
123 |
124 | def get_clean_shellcode(self):
125 | if not hasattr(self, '_shellcode'):
126 | raise Exception('[-] decrypt it frist')
127 | if self._shellcode[9:13] != "\x49\x80\x34\x08":
128 | raise Exception('[-] Diffrent obfuscation your are on your own')
129 |
130 | xorb = ord(self._shellcode[13])
131 | size = struct.unpack('I', self._shellcode[5:9])[0]
132 | hdr = "\x90" * 0x19
133 | sh = ''.join(
134 | chr(ord(self._shellcode[0x19 + i]) ^ xorb) for i in range(size))
135 | rest = self._shellcode[0x19 + size:]
136 | self.shellcode = hdr + sh + rest
137 |
138 | @property
139 | def config(self):
140 | if self.type == 'rig-v':
141 | print '[-] for know i dont kwnow where config is'
142 | return None
143 |
144 | if not hasattr(self, '_cfgkeys'):
145 | self._cfgkeys = get_keys(self.binary_data[4].data)
146 |
147 | if not hasattr(self, '_config'):
148 | cfg = list(self.read_blob(self.binary_data[3].data))
149 | if len(cfg) == 1:
150 | print '[*] new version, only shelcode is here'
151 | self._config = None
152 | self._shellcode = RC4.new(
153 | self._cfgkeys[0]).decrypt(cfg[0]).decode('hex')
154 | self.get_clean_shellcode()
155 | else:
156 | c = []
157 | for i, x in enumerate(cfg):
158 | c.append(
159 | unpad(AES.new(
160 | self._cfgkeys[i % len(self._cfgkeys)]).decrypt(x)))
161 | self._config = c
162 | return self._config
163 |
164 |
165 | def unpad(d):
166 | return d[:-ord(d[-1])]
167 |
168 |
169 | if __name__ == '__main__':
170 | rig = xSWF(open(sys.argv[1]))
171 | data = rig.binary_data[1].data.decode('zlib')
172 | x = ord(data[1]) ^ ord('W')
173 | y = ord(data[2]) ^ ord('S')
174 |
175 | if x != y:
176 | print '[-] cant do it'
177 | sys.exit(-1)
178 |
179 | emb1 = 'CWS' + ''.join(map(lambda x: chr(ord(x) ^ y), data[3:]))
180 | with open('a.swf', 'w') as f:
181 | f.write(emb1)
182 | rig = xSWF(StringIO(emb1))
183 | data = rig.binary_data[31].data.decode('zlib')
184 | key = data[-16:]
185 | data = ''.join((chr(ord(c) ^ ord(key[i % 16]))
186 | for i, c in enumerate(data[:-16].decode('base64'))))
187 | with open('b.swf', 'w') as f:
188 | f.write(data)
189 | rig = RIG(StringIO(data))
190 |
191 | print rig.type
192 | print rig.enc_strings
193 | if rig.config:
194 | print rig.config
195 | with open('s.bin', 'w') as f:
196 | f.write(rig._shellcode)
197 | with open('sc.bin', 'w') as f:
198 | f.write(rig.shellcode)
199 |
--------------------------------------------------------------------------------
/Sundown/landing.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import hashlib
4 | import argparse
5 | from lxml.etree import HTML
6 | from Crypto.Cipher import ARC4 as RC4
7 |
8 | def decode_payloads(js):
9 | funs = re.findall('function ([a-z]+)\(',js,re.I)
10 | data = key = None
11 | for f in funs:
12 | if data:
13 | key = re.findall(f + '\("([a-z0-9]+)",',js,re.I)
14 | else:
15 | data =re.findall(f + '\(\s*([a-z0-9]+)\s*\)\s*\)\s*;',js,re.I)
16 | data = re.findall('var\s+%s\s*=\s*"([a-zA-Z0-9+/=]+)";'%data[0],js)[0]
17 |
18 | if key and data:
19 | payload = RC4.new(key[0]).decrypt(data.decode('base64'))
20 | yield payload
21 | key = data = None
22 |
23 | def decode_first(d):
24 | h = HTML(d)
25 | inner_js=''.join(h.xpath('//div/text()')).replace('_','')
26 | inner_js=inner_js.replace('&','').replace('%','')
27 | inner_js=inner_js.replace('=','').replace('undefined','')
28 | inner_js=inner_js.decode('hex')
29 | return inner_js
30 |
31 | apr = argparse.ArgumentParser(description='Sundown landig page decoder')
32 | apr.add_argument('file',type=str, nargs='?', help='File path')
33 | apr.add_argument('-d','--dir',help='Output dir',default='/tmp')
34 | apr.add_argument('-s','--save',help='save exploits',default=False,action='store_true')
35 | args = None
36 |
37 | def doit(d):
38 | if '
old_l:
41 | ## this is the same script with encoded data...
42 | ## there are three posibilites as seens so far,
43 | ### 1. separet variables
44 | ### 2. array initialized index by index
45 | ### 3. array initialized at once
46 |
47 | for enc in re.findall("([_a-z0-9]+(\[[0-9]+\])?\s*=\s*('|\")[-_. %a-z0-9/+=:]+(\"|')\s*)(,|;)",scr,re.I):
48 | ## this covers 1 and 2
49 | var,val = enc[0].split('=',1)
50 | var = var.strip()
51 | val = val.strip()[1:-1]
52 | #print `var`,`val`
53 | print '[+] found posible configuration var', var
54 | ENC_DATA[var]=val
55 | for enc in re.findall("var\s+([_a-z0-9]+)\s+=\s+\[((\s*('|\")[-_. %a-z0-9/+=:]+(\"|'),?)+)\s*\];",scr,re.I):
56 | ### this takes care of 3rd case
57 | var = enc[0]
58 | val = enc[1]
59 | sep = enc[-1] + ',' + enc[-1]
60 | print '[+] found configuration table', var
61 |
62 | for i,e in enumerate(val.split(sep)):
63 | ENC_DATA[var+'[%d]'%i] = e.strip().strip(enc[-1])
64 |
65 |
66 | # print '-'*20
67 | # print scr
68 | return r
69 |
70 |
71 | def xtea_decrypt_block(key,block,n=32,endian="<"):
72 | v0,v1 = struct.unpack(endian+"2L",block)
73 | k = struct.unpack(endian+"4L",key)
74 | delta,mask = 0x9e3779b9L,0xffffffffL
75 | sum = (delta * n) & mask
76 | for round in range(n):
77 | v1 = (v1 - (((v0<<4 ^ v0>>5) + v0) ^ (sum + k[sum>>11 & 3]))) & mask
78 | sum = (sum - delta) & mask
79 | v0 = (v0 - (((v1<<4 ^ v1>>5) + v1) ^ (sum + k[sum & 3]))) & mask
80 | return struct.pack(endian+"2L",v0,v1)
81 | def xtea_worker(f,data):
82 | _len = len(data)
83 | assert len(data)% 8 == 0
84 | return ''.join(map(f,chunks(data,8)))
85 |
86 | def xtea_decrypt(data,xkey):
87 | return xtea_worker(lambda c:xtea_decrypt_block(xkey,c),data)
88 |
89 |
90 | def shuffle(data,key):
91 | key = [ key.index(c) for c in sorted(key) ]
92 | ks = len(key)
93 | data += ' ' * (ks - len(data)%ks)
94 | r = []
95 | for ch in chunks(data,ks):
96 | r.append(''.join([ ch[key[i]] for i in range(len(ch))]))
97 | return ''.join(r).strip()
98 |
99 |
100 | def method_0(text,key):
101 | txt = re.sub(r'\s+|\.','',text)
102 | txt = txt.decode('base64')
103 | txt = xor(txt,key.decode('base64'))
104 | return txt
105 |
106 | def method_1(text,key):
107 | txt = re.sub(r'\s+','%',text)
108 | txt = unquote(txt)
109 | txt = xtea_decrypt(txt,key[:16])
110 | #@ print `txt`
111 | return txt
112 |
113 | def method_2(text,key):
114 | txt = dehtml(text.replace(u"\xa0",' '))
115 | txt = shuffle(txt,key)
116 | # print txt
117 | return txt
118 |
119 | def method_3(text,key):
120 | key = key.decode('hex')
121 | text = text.decode('hex')
122 | return xor(text,key)
123 |
124 | def method_4(text,key):
125 | text = re.sub(r"(:+|=|\]\[)",'%',text.replace("\r\n",''))
126 | text = re.sub('%[A-F0-9]{2}',lambda g: g.group(0)[1:].decode('hex'),text)
127 | text = xtea_decrypt(text,key[:16])
128 | return text
129 |
130 |
131 | DEOBF_METHODS = sorted(filter(lambda x:x.startswith('method_'),globals()))
132 | def decode_page(t,k):
133 | # print globals()
134 | tlen = len(t)
135 | epsi = tlen /2
136 | for f in DEOBF_METHODS:
137 | f = globals()[f]
138 | try:
139 | x=f(t,k)
140 | #print len(t),len(x)
141 | ## there are some constant names in
142 | ## angler payload lets look for them
143 | if 'cryptKey' in x:
144 | return x
145 | elif 'getKolaio' in x:
146 | return x
147 | elif 'xTrue' in x and 'xFalse' in x:
148 | return x
149 |
150 | ## some basic heuristic....
151 | # if tlen - len(x) < epsi:
152 | # print 'fop'
153 | # return x
154 | except:
155 | pass
156 | return None
157 |
158 | def get_num(x):
159 | return int(re.search('[0-9]+$',x).group(0))
160 |
161 |
162 | if __name__ == '__main__':
163 | args = apr.parse_args()
164 | h = HTML(open(args.file).read().replace('
',''))
165 | key_var = None
166 | for key in get_keys(h):
167 | print '[*] testing key:',key
168 | stream = ''; txt = None
169 | for el in h.xpath('//*[@id or @ui or @di]'):
170 | if el.text:
171 | txt = decode_page(el.text,key)
172 | # print txt
173 | if not txt:
174 | continue
175 |
176 | if 'cryptKey' in txt:
177 |
178 | key_var = re.findall('var cryptKey = ([_a-z0-9]+(\[\s*[0-9]+\s*\])?),',txt,re.I)[0][0]
179 | key_var = re.sub('\s+','',key_var)
180 | print '[+] found key_var',key_var
181 | #txt = method_3(stream,key)
182 | #print txt
183 | if args.out:
184 | uid = el.attrib.get('id',None)
185 | uid = uid or el.attrib.get('di',None)
186 | uid = uid or el.attrib.get('ui',None)
187 | fname = os.path.join(args.dir,'.'.join([args.out,uid,'js']))
188 | print '[*] saving decoded chunk to',fname
189 | with open(fname,'w') as f: f.write(txt)
190 |
191 | if key_var: break
192 | #print txt
193 |
194 |
195 | if not key_var or key_var not in ENC_DATA:
196 | print '[-] err cant find key variable'
197 | sys.exit(1)
198 |
199 | print '[+] decoding using %s as key' % ENC_DATA[key_var]
200 |
201 | #ENC_DATA[key_var] = ENC_DATA[key_var].decode('base64')
202 | for k in ENC_DATA:
203 | if k == key_var: continue
204 | if not key_var.startswith(k[:2]) and \
205 | not key_var[1:].startswith(k[1:3]):
206 | continue
207 |
208 | v = ENC_DATA[k]
209 | data=shuffle(v,ENC_DATA[key_var])
210 | try:
211 | data_b= unquote(data).decode('base64')
212 | except:
213 | data_b = None
214 |
215 | if data_b and isprintable(data_b):
216 | data = data_b
217 | print '[+] decoded data:', data
218 |
219 |
220 |
221 | ## this looks strange and needs more testing...
222 | # elif el.attrib.get('value',None):
223 | # if not re.match('^[0-9a-f]+$',key,re.I):
224 | # break
225 | # val = el.attrib['value']
226 | # ns = stream+val
227 | # if len(ns)%2:
228 | # stream = ns
229 | # continue
230 |
231 | # tmp = method_3(ns,key)
232 | # if not isprintable(tmp):
233 | # txt = method_3(stream,key)
234 | # stream = val
235 | # else:
236 | # txt = ''
237 | # stream =ns
238 |
--------------------------------------------------------------------------------
/neutrino/neutrino.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import zlib
4 | import json
5 | import hashlib
6 | import argparse
7 | import StringIO
8 | from swf.movie import SWF
9 | from Crypto.Cipher import ARC4
10 |
11 | rc4_decrypt = lambda d,k : ARC4.new(k).decrypt(d)
12 | apr = argparse.ArgumentParser(description='Neutrino swf decoder')
13 | apr.add_argument('file',type=str, nargs='?', help='File path')
14 | apr.add_argument('-d','--dir',help='Output dir',default='/tmp')
15 | apr.add_argument('-e','--exploits',help='save exploits',default=False,action='store_true')
16 | apr.add_argument('-i','--intermediate',help='save second swf',default=False,action='store_true')
17 | apr.add_argument('-s','--second',help='second swf',default=False,action='store_true')
18 |
19 | class Neutrino(SWF):
20 |
21 |
22 | @property
23 | def binary_data(self):
24 | if not hasattr(self,'_bd'):
25 | self._bd = self.build_dictionary()
26 | return self._bd
27 |
28 |
29 | @property
30 | def symbols(self):
31 | if not hasattr(self,'_sc'):
32 | for s in self.tags:
33 | if s.name == 'SymbolClass':
34 | self._sc = s
35 | break
36 | return self._sc.symbols
37 |
38 | @property
39 | def script(self):
40 | if not hasattr(self,'_s'):
41 | for s in self.tags:
42 | if s.name in ['DoABC','DoAction']:
43 | self._s = s
44 | break
45 | return self._s
46 |
47 | @property
48 | def strings(self):
49 | """get a list of strings from abcFile, whose length larger than 5
50 | """
51 | if not hasattr(self,'_ss'):
52 | self._ss = re.findall('[a-zA-Z0-9]{5,}', self.script.bytes)
53 | return self._ss
54 |
55 | def tag_by_name(self,name):
56 | for s in self.symbols:
57 | if s.name.endswith(name):
58 | return self.binary_data[s.tagId]
59 | return None
60 |
61 | def get_exploits(self):
62 | for sym in self.symbols:
63 | if not self.binary_data.has_key(sym.tagId):
64 | continue
65 | n = sym.name[:sym.name.find('$')]
66 | d = rc4_decrypt(self.binary_data[sym.tagId].data,self.ek_key)
67 | if d[:3] not in ['ZWS','CWS','FWS']:
68 | try:
69 | d = zlib.decompress(d,-15)
70 | except Exception, e:
71 | pass
72 | yield n, d
73 |
74 | def get_keys(self, cfg=None):
75 | the_cfg = cfg if cfg else self.get_cfg()
76 | data_id = [ s.tagId for s in self.symbols if 'html_rc4' in s.name ][0]
77 | for k in self.strings:
78 | if not hasattr(self, 'ek_key'):
79 | try:
80 | d = rc4_decrypt(self.binary_data[data_id].data,k)
81 | d = zlib.decompress(d,-15)
82 | if '= 3:
114 | strs = re.findall('[a-zA-Z0-9]{5,}',s.script.bytes)
115 | beg = strs.index('writeBytes')
116 | old = True
117 | try:
118 | end = strs.index('getDefinitionByName')
119 | old= False
120 | except:
121 | end = strs.index('Loader')
122 | resources = [strs[beg-1]] + strs[beg+1:end]
123 | if old and len(resources) < 5:
124 | ## this is older version with one letter-names...
125 | idx = self.script.bytes.index('writeBytes')
126 | h= re.findall('([a-z])\nwriteBytes((\x01[a-z])+)\x06Loader',self.script.bytes,re.M)[0]
127 | resources = [h[0]] + h[1].split("\x01")[1:]
128 |
129 | swf_bytes = ''.join([ get_data(r) for r in resources])
130 |
131 | for k in [self.binary_data[k].data for k in self.binary_data if len(self.binary_data[k].data) < 0x50]:
132 | d = rc4_decrypt(swf_bytes,k)
133 | if d[:3] in ['ZWS','CWS','FWS']:
134 | return d
135 | elif len(self.symbols) == 2 and ('Loader' in self.strings or self.symbols[0].name == 'EmbedFile'):
136 | # new version(2b19b310887bb1beac421843671e5541f2e369b2815fcc3dc4dfdfc71e059a0d) only has two symbols:
137 | # [, ]
138 | ### beter use funcion name which is using to set some bitmap, that is used to store payload
139 | ## see 7cb253e6997cf06e33eb8d0c74da9e10
140 |
141 | for k in self.strings:
142 | try:
143 | d = rc4_decrypt(self.binary_data[1].data, k)
144 | if d[:3] in ['ZWS','CWS','FWS']:
145 | return d
146 | except Exception, e:
147 | pass
148 | else:
149 | return None
150 |
151 | def get_cfg(self):
152 | for i in self.binary_data:
153 | x=self.binary_data[i].data[:3]
154 | try:
155 | return self.binary_data[i].data[3:int('0x'+x,16)+3]
156 | except:
157 | pass
158 |
159 |
160 | if __name__ == '__main__':
161 | args = apr.parse_args()
162 | s = Neutrino(open(args.file))
163 | cfg_r = s.get_cfg()
164 |
165 | if not args.second:
166 | swf = s.get_second_swf()
167 |
168 | if not swf: sys.exit("[-] can't extract second swf, bailing")
169 | h = hashlib.sha256(swf).hexdigest()
170 | sys.stderr.write('[+] embeded swf (SHA256: %s) extracted'%h)
171 | if args.intermediate:
172 | p = '%s/%s.swf' % (args.dir,h)
173 | with open(p,'w') as f: f.write(swf)
174 | sys.stderr.write(',and saved to %s\n' % p)
175 | else:
176 | sys.stderr.write('\n')
177 |
178 | s2 = Neutrino(StringIO.StringIO(swf))
179 | else:
180 | s2 = s
181 |
182 | if not s2.get_keys(cfg_r):
183 | print >> sys.stderr,'[-] failed to get keys'
184 | exit(-1)
185 |
186 | if not cfg_r:
187 | cfg_r = s2.get_cfg() # get config from 2nd layer swf
188 | print >> sys.stderr,'[+] cfg key: %s, exploit key: %s' % (s2.cfg_key,s2.ek_key)
189 | cfg = json.loads(rc4_decrypt(cfg_r,s2.cfg_key))
190 | import pprint
191 | pprint.pprint(cfg)
192 | if args.exploits:
193 | for n, ek in s2.get_exploits():
194 | h = hashlib.sha256(ek).hexdigest()
195 | p = '%s/%s_%s.ek.bin' % (args.dir,n,h)
196 | with open(p,'w') as f: f.write(ek)
197 | print >> sys.stderr, '[+] Exploit saved to %s' %p
198 |
--------------------------------------------------------------------------------
/nuclear/chain.py:
--------------------------------------------------------------------------------
1 | import sys,os
2 | import hashlib
3 | #import requests
4 | import urlparse
5 | import argparse
6 | ###
7 | sys.path.append(os.path.dirname(__file__))
8 | import landing
9 | import requesocks as requests
10 |
11 |
12 | apr = argparse.ArgumentParser(description='Nuclear payload downloader')
13 | apr.add_argument('url',type=str, nargs='?', help='fist url that is starting infection chain')
14 | apr.add_argument('-d','--dir',help='Output dir',default='/tmp')
15 | apr.add_argument('-o','--out',help="prefix for filename used to store decoded chunks")
16 | args = apr.parse_args()
17 |
18 |
19 | def xor(x,y):
20 | if len(x)>len(y):
21 | y = y * (len(x)/len(y))
22 | return ''.join(map(lambda x: chr(ord(x[0])^ord(x[1])),zip(x,y)))
23 |
24 |
25 |
26 |
27 | UA = 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)'
28 |
29 | hdrs = {}
30 | #hdrs['Accept-Language']='pl'
31 | hdrs['Accept']='image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*'
32 | hdrs['User-Agent'] = UA
33 |
34 | s = requests.Session()
35 | #s.proxies={'http':'http://localhost:8118','https':'http://localhost:8118'}
36 |
37 | s.headers = hdrs
38 |
39 | ## lets do it...
40 | BASE_URL = sys.argv[1]
41 | r=s.get(BASE_URL)
42 | if r.status_code == 444:
43 | print "[-] zed's dead, either blackisted ip or wrong country..."
44 | sys.exit(1)
45 |
46 | url = urlparse.urlparse(r.url)
47 | payload = {}
48 | ## decode landing
49 | for txt in landing.decode_first_js(r.content):
50 | payload.update(landing.analyze_payload(txt))
51 |
52 | if 'encryption' not in payload:
53 | print '[-] sorry, i cant deal with it...'
54 | sys.exit(1)
55 |
56 | ## this looks like a bug in nuclerear we dont need to
57 | ## be exploited to get payload...
58 | if payload['encryption'] == 'xor':
59 | ## right now only xor is handled
60 | r = s.get(payload['sh-url'])
61 | if r.ok:
62 | data = xor(r.content,payload['sh-key'])
63 | h = hashlib.md5(data).hexdigest()
64 | if data[:2] == 'MZ':
65 | print '[+] hooray, we got our payload, saving to %s'
66 | with open(fname,'w') as f:
67 | f.write(data)
68 |
69 | elif payload['encryption'] == 'DH+rc4':
70 | print '[-] not dh for now'
71 | sys.exit(1)
72 | else:
73 | print '[-] imposible...'
74 |
--------------------------------------------------------------------------------
/nuclear/landing.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import sys
4 | import struct
5 | import string
6 | import argparse
7 | from urllib import unquote
8 |
9 | from lxml.etree import HTML
10 |
11 | printset = set(string.printable)
12 | isprintable = lambda yourstring: set(yourstring).issubset(printset)
13 | xor = lambda xk,d : ''.join(map(lambda x: chr(ord(x)^xk),d))
14 |
15 | apr = argparse.ArgumentParser(description='Nuclear landing page decoder')
16 | apr.add_argument('file',type=str, nargs='?', help='File path')
17 | apr.add_argument('-d','--dir',help='Output dir',default='/tmp')
18 | apr.add_argument('-o','--out',help="prefix for filename used to store decoded chunks")
19 |
20 |
21 | def get_off(h):
22 | r = []
23 | for el in h.xpath('//script'):
24 | for hit in re.finditer("('|\")>('|\")\s*\)\s*\+\s*([0-9]+)",el.text):
25 | try:
26 | r.append(int(hit.group(3))-1)
27 | except:
28 | pass
29 | return r
30 | def decode64(p):
31 | for s in ['','=','==']:
32 | try:
33 | r = (p.replace('-','/').replace('_','+').replace('!','0')+s).decode('base64')
34 | return r
35 | except:
36 | pass
37 | return None
38 |
39 |
40 | def decode_payload(offsets,p):
41 | for off in offsets:
42 | r = decode64(p[off:])
43 | if r and isprintable(r):
44 | return r
45 | return None
46 |
47 | def decode_bin_payload(sh):
48 | sh = sh.decode('hex')
49 | if sh.startswith("\x60\xeb\x11\x58\xb9"):
50 | off = struct.unpack('I',sh[5:9])[0]+4
51 | xk = ord(sh[off+0x19+1]) ^ ord(';')
52 | if ord(sh[off+0x19])^xk in [0x32,0x31]:
53 | r=xor(xk,sh[off+0x19:]).split("\x00")[0].split(';')[:3]
54 | return r
55 | return None
56 |
57 | def decode_shellcode(pay,r):
58 | for _line in pay.splitlines():
59 | try:
60 | data = _line.split('=')[1].strip().split(';')[0][1:-1]
61 | if re.match("^[0-9A-Fa-f]+$",data):
62 | id,key,url = decode_bin_payload(data)
63 | if id == '1':
64 | typ = 'standalone'
65 | elif id == '2':
66 | typ = 'service'
67 | r[0]={'sh-key':key,'sh-url':url,'type':typ}
68 | print '[+] %s binary from %s - with key:%s' % (typ,url,key)
69 | return True
70 | except Exception as e:
71 | pass
72 | return False
73 |
74 | def analyze_payload(txt,args=None):
75 | enc = None
76 | ret = {}
77 | for line in txt.splitlines():
78 | ext = None
79 | if line.startswith('var') and ("'" in line or '"' in line):
80 | try:
81 | if 'decodeBase64' in line:
82 | line = line.replace('decodeBase64(','').replace(')','')
83 |
84 | name,pay = line.split('=',1)
85 | pay = pay.strip()[1:-2].decode('base64')
86 | except:
87 | continue
88 |
89 | if 'class' in pay or 'Randomize' in pay or 'end sub' in pay or 'Dim' in pay:
90 | print '[*] found vbs payload'
91 | ext = 'vbs'
92 |
93 | elif 'catch' in pay:
94 | print '[*] found js payload'
95 | ext = 'js'
96 | r = [{}]
97 | if 'powMod' in pay and 'rc4' in pay and 'str2bigInt' in pay:
98 | enc = 'DH+rc4'
99 | elif decode_shellcode(pay,r):
100 | enc = 'xor'
101 | ret.update(r[0])
102 |
103 | elif line.startswith('return') and re.match(r"^return\s+('|\")[A-Z0-9+/=]+(\"|');?$",line,re.I):
104 | data=line.split(' ',1)[1].lstrip(';')[1:-1].decode('base64')
105 | if data.startswith('http'):
106 | print '[+] payload-data',data
107 |
108 | elif line.startswith('flash_run'):
109 | data= line.lstrip(';')[11:-2].split(',')
110 | flsh_u = data[0][1:-1]
111 | param1 = decode64(data[1][1:-1])#.decode('base64')
112 | print 'flash-payload',flsh_u
113 | print 'p1 (%d)'%len(param1),`param1`
114 |
115 | if len(data) >2:
116 | param2 = unquote(data[2][1:-1]).decode('base64')
117 | ret['binary'] = param2
118 | print 'binary-url',param2
119 |
120 | ret['flash'] = flsh_u
121 |
122 | if ext and args and args.out:
123 | uid = name.split(' ')[1]
124 | fname = os.path.join(args.dir,'.'.join([args.out,'inner',uid,ext]))
125 | print '[*] saving decoded chunk to',fname
126 | with open(fname,'w') as f: f.write(pay)
127 | if enc:
128 | print '[*] payload delivery encryption:',enc
129 | ret['encryption']=enc
130 | return ret
131 |
132 | def decode_first_js(data):
133 | h = HTML(data)
134 | off = get_off(h)
135 | off.append(0)
136 | for el in h.xpath('//*[@id]'):
137 | if el.text:
138 | txt = decode_payload(off,el.text)
139 | if not txt:
140 | continue
141 | yield txt
142 |
143 |
144 |
145 | if __name__ == '__main__':
146 | args = apr.parse_args()
147 | for txt in decode_first_js(open(args.file).read()):
148 | if args.out:
149 | fname = os.path.join(args.dir,'.'.join([args.out,el.attrib['id'],'js']))
150 | print '[*] saving decoded chunk to',fname
151 | with open(fname,'w') as f: f.write(txt)
152 | analyze_payload(txt,args)
153 |
154 |
155 |
--------------------------------------------------------------------------------