Skip to content

Commit 37bf688

Browse files
committed
Add scanner for the open proxy from 'SharknAT&To'
1 parent a5be16f commit 37bf688

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import socket, re, functools
2+
import asyncio
3+
4+
from metasploit import module
5+
from async_timeout import timeout
6+
7+
8+
def make_study(payload='', pattern='', onmatch=None, connect_timeout=3, read_timeout=10):
9+
return lambda args: start_study(payload, pattern, args, onmatch, connect_timeout=connect_timeout, read_timeout=read_timeout)
10+
11+
12+
def start_study(payload, pattern, args, onmatch, **timeouts):
13+
loop = asyncio.get_event_loop()
14+
loop.run_until_complete(run_study(payload, pattern, args, onmatch, **timeouts))
15+
16+
17+
async def run_study(payload, pattern, args, onmatch, **timeouts):
18+
runs = [study_host(host, int(args['rport']), payload, **timeouts) for host in args['rhosts']]
19+
async for (target, res) in Study(runs):
20+
if isinstance(res, Exception):
21+
module.log('{}:{} - Error connecting: {}'.format(*target, res), level='error')
22+
elif res and re.search(pattern, res):
23+
module.log('{}:{} - Matches'.format(*target), level='good')
24+
module.log('{}:{} - Matches with: {}'.format(*target, res), level='debug')
25+
onmatch(target, res)
26+
else:
27+
module.log('{}:{} - Does not match'.format(*target), level='info')
28+
module.log('{}:{} - Does not match with: {}'.format(*target, res), level='debug')
29+
30+
31+
class Study:
32+
def __init__(self, runs):
33+
self.queue = asyncio.queues.Queue()
34+
self.total = len(runs)
35+
self.done = 0
36+
37+
for r in runs:
38+
f = asyncio.ensure_future(r)
39+
args = r.cr_frame.f_locals
40+
target = (args['host'], args['port'])
41+
f.add_done_callback(functools.partial(self.__queue_result, target))
42+
43+
44+
def __queue_result(self, target, f):
45+
res = None
46+
47+
try:
48+
res = f.result()
49+
except Exception as e:
50+
res = e
51+
52+
self.queue.put_nowait((target, res))
53+
54+
55+
async def __aiter__(self):
56+
return self
57+
58+
59+
async def __anext__(self):
60+
if self.done == self.total:
61+
raise StopAsyncIteration
62+
63+
res = await self.queue.get()
64+
self.done += 1
65+
return res
66+
67+
68+
async def study_host(host, port, payload, connect_timeout, read_timeout):
69+
r = None
70+
w = None
71+
buf = bytearray()
72+
73+
async with timeout(connect_timeout):
74+
r, w = await asyncio.open_connection(host, port)
75+
remote = w.get_extra_info('peername')
76+
if remote[0] == host:
77+
module.log('{}:{} - Connected'.format(host, port), level='debug')
78+
else:
79+
module.log('{}({}):{} - Connected'.format(host, *remote), level='debug')
80+
w.write(payload)
81+
await w.drain()
82+
83+
try:
84+
async with timeout(read_timeout):
85+
while len(buf) < 4096:
86+
data = await r.read(4096)
87+
if data:
88+
module.log('{}:{} - Received {} bytes'.format(host, port, len(data)), level='debug')
89+
buf.extend(data)
90+
else:
91+
break
92+
except asyncio.TimeoutError:
93+
if buf:
94+
pass
95+
else:
96+
raise
97+
98+
w.close()
99+
return buf
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env python3
2+
3+
from metasploit import module, sonar
4+
5+
6+
metadata = {
7+
'name': 'Open WAN-to-LAN proxy on AT&T routers',
8+
'description': '''
9+
The Arris NVG589 and NVG599 routers configured with AT&T U-verse
10+
firmware 9.2.2h0d83 expose an un-authenticated proxy that allows
11+
connecting from WAN to LAN by MAC address.
12+
''',
13+
'authors': [
14+
'Joseph Hutchins' # Initial disclosure
15+
'Jon Hart <jon_hart[AT]rapid7.com>', # Dummy payload and response pattern
16+
'Adam Cammack <adam_cammack[AT]rapid7.com>' # Metasploit module
17+
],
18+
'date': '2017-08-31',
19+
'references': [
20+
{'type': 'cve', 'ref': '2017-14117'},
21+
{'type': 'url', 'ref': 'https://www.nomotion.net/blog/sharknatto/'},
22+
{'type': 'url', 'ref': 'https://blog.rapid7.com/2017/09/07/measuring-sharknat-to-exposures/#vulnerability5port49152tcpexposure'},
23+
{'type': 'aka', 'ref': 'SharknAT&To'},
24+
{'type': 'aka', 'ref': 'sharknatto'}
25+
],
26+
'type': 'scanner.multi',
27+
'options': {
28+
'rhosts': {'type': 'address_range', 'description': 'The target address', 'required': True, 'default': None},
29+
'rport': {'type': 'port', 'description': 'The target port', 'required': True, 'default': 49152},
30+
},
31+
}
32+
33+
34+
def report_wproxy(target, response):
35+
module.report_vuln(target[0], 'wproxy', port=target[0])
36+
37+
38+
if __name__ == "__main__":
39+
module.run(metadata, sonar.make_study(payload = b'\x2a\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00', pattern = b'^\\*\xce.{3}$', onmatch = report_wproxy))

0 commit comments

Comments
 (0)