文章总结: 文章记录了一次针对电商企业小程序的授权渗透测试。作者通过反编译小程序源码,发现云函数鉴权存在严重漏洞:管理员的openid被硬编码在前端代码中,且云函数直接信任前端传参的openid,未进行二次校验。利用此漏洞,攻击者可伪造管理员身份调用所有云函数,导致全量用户数据(手机号、地址、订单等)泄露。文章还提供了挖掘小程序云函数漏洞的实用技巧,如关键词搜索、硬编码openid利用、云函数命名规律猜测等。核心结论是云函数鉴权必须使用微信内部可信的wxContext.OPENID,而非前端传入参数。 综合评分: 90 文章分类: 渗透测试,漏洞分析,实战经验,WEB安全,红队
记录一次小程序挖洞经历,感谢小程序,不然这单得黄!!!
原创
是老A 是老A
老A搞安全
2026年7月23日 07:58 贵州
在小说阅读器读本章
去阅读
一位深耕网络安全的老兵,目前是一家安全公司的技术分管,擅长渗透测试及安全培训方向,老A的愿望是大家没烦恼,一切顺心!
接了个电商企业的授权项目,客户给的范围是”尽量测,别打挂就行”。Web端搞了一圈,WAF+云防护,基本没什么缝
这下不好交差了……/笑哭
客户说:”我们小程序应该没啥问题,就几个商品展示页面。”
好好好(心想,小程序争取搞个交差咯)
确实!
小程序往往比Web站更容易出问题,因为开发资源都堆在前端UI上了,后端逻辑经常是”能跑就行”。
打开微信开发者工具,连上Burp的代理,开始抓包
所有请求都指向一个域名:
https://xxx.cloudfunctions.net/(示例,数据已脱敏)
云函数,微信小程序的云开发环境
这说明整个小程序的后端逻辑都跑在云函数上,没有自己的服务器。好处是开发快,坏处是,如果你在云函数里写了什么敏感逻辑,或者依赖前端传什么参数来做鉴权,那就等于把保险柜的密码贴在了保险柜外面
但是第一步就卡住了。我只知道一个根域名,不知道有哪些云函数路径,也没地方去看代码。总不能盲猜吧?
虽然有时候真的能猜中,但那是撞大运,不是正经打法/笑哭
于是开始搞小程序源码
流程是这样的,拿了一台安卓手机,从微信的缓存目录里把小程序的wxapkg包扒出来。路径大概是:
/data/data/com.tencent.mm/MicroMsg/[32位hash]/appbrand/pkg/
微信会把运行过的小程序包缓存到这里,直接复制出来就行。然后用到wxappUnpacker解包:
node wuWxapkg.js xxx.wxapkg
解出来一堆文件,js、json、wxml混在一起。大部分js都被混淆过,变量名全是_、a、b这种,读起来头疼。但好消息是,字符串常量是明文的,不会被混淆
我开始一个文件一个文件地翻。在app-service.js里搜admin,在cloudfunctions.js里搜openid,心里默念:让我看到一个硬编码的openid,求求了,不然真不好交差了,总不能说,领导,你们的系统”灰常安全”吧
在一个叫adminConfig.js的文件里,我看到了这样的代码
const ADMIN_LIST = { 'oXXXXXXXXXXXXXXXXXXXXXXXXXXX': '超级管理员', 'oYYYYYYYYYYYYYYYYYYYYYYYYYYY': '运营管理员'};if (ADMIN_LIST[event.openid]) { return await getAllUsers();}
写死了。管理员的openid直接硬编码在前端代码里。我盯着屏幕,脑子里有一个念头:”这跟把管理员密码贴在登录框下面有什么区别?”
更离谱的是,云函数接收openid的方式是这样的:
exports.main = async (event, context) => { const { openid } = event; // 直接用了前端传过来的openid去做权限判断}
没有任何二次校验。意味着什么?意味着服务端完全信任了客户端传来的openid。你传什么,它就认什么
而且别忘了,小程序是跑在用户手机上的。任何人都可以反编译,拿到这些硬编码的openid。然后伪造请求,以管理员的身份调用任何云函数
我当时的第一反应是:赶紧翻一翻还有哪些云函数
在一个叫cloudfunctionMap.js的文件里,找到了所有云函数的映射表,整整写了二十多个:
module.exports = { getAllUsers: 'getAllUsers_prod_v2', getOrders: 'getOrders_prod_v2', getUserInfo: 'getUserInfo_prod_v2', getSensitiveLogs: 'getSensitiveLogs_prod_v2', batchExport: 'batchExport_prod_v2', // ... 还有十几个}
我打开了Postman
先试getAllUsers_prod_v2:
POST https://xxx.cloudfunctions.net/getAllUsers_prod_v2{ "openid": "oXXXXXXXXXXXXXXXXXXXXXXXXXXX"}
返回了千多条用户数据。手机号、收货地址、订单记录、积分余额,全都有。我随手翻了几条,有个用户的收货地址精确到了门牌号,还有个用户半年下了26单
再试getSensitiveLogs_prod_v2:
POST https://xxx.cloudfunctions.net/getSensitiveLogs_prod_v2{ "openid": "oXXXXXXXXXXXXXXXXXXXXXXXXXXX"}
返回了管理员的操作日志,里面包含了内部系统的接口地址和参数格式
再试batchExport_prod_v2:
POST https://xxx.cloudfunctions.net/batchExport_prod_v2{ "openid": "oXXXXXXXXXXXXXXXXXXXXXXXXXXX", "type": "all_users"}
返回了一个下载链接,点开就是全量用户数据的Excel。我当时甚至有点心虚,这要是客户看到他们小程序后台长这样,怕是要连夜开除整个技术团队
后面和运维团队确认了漏洞,一起分析了漏洞是怎么造成的
云函数的鉴权逻辑分两层:
第一层:微信自带的身份认证。微信会在请求里带上调用者的openid,这是可信的,伪造不了
第二层:开发者自己的鉴权逻辑。开发者从请求里取出openid,判断它是不是管理员
问题出在哪里?出在开发者混淆了这两个来源
正确做法应该是:云函数内部通过wxContext.OPENID或者云函数自带的变量去拿微信传递的openid,这是可信的。但这位开发者用了event.openid——这个字段是前端传进来的,任何人都能伪造
也就是说,他只是”看起来”做了鉴权,实际上这个鉴权是空气。你传超级管理员的openid,你就是管理员;你传普通用户的openid,你就是普通用户。一个openid校验,硬生生被玩成了选择题
好好好……
这里补充几个挖小程序云函数的习惯动作:
1、优先看app-service.js。这是小程序的业务逻辑大本营,虽然被混淆,但关键词搜索依然有效。变量名变了,字符串常量不会变。
2、搜关键词的顺序:先搜admin(权限判断的核心)、再搜cloud(调用云函数的入口)、然后搜openid(硬编码的管理员标识)、最后搜云函数的名字(getAllUsers、getOrders、batchExport这种)
3、碰到硬编码的openid,不要只试一个。把所有硬编码的管理员openid都找出来,然后用Burp Intruder批量去调所有云函数。经常能发现不同的openid对应不同的权限等级
4、云函数的命名规律:很多开发者喜欢加后缀区分环境,_prod、_v2、_test是常见的。如果你找到了一个getAllUsers_prod,大概率也存在一个getAllUsers_prod_v2
5、没找到源码怎么办?有些小程序做了加固,解包解不出来。这时候走抓包路线,把所有云函数的请求都抓下来,观察URL的命名规律,然后自己拼路径盲测。虽然效率低,但也常有意外收获
6、别忘了云存储:很多小程序会把图片、配置文件传到云存储里,如果云存储的权限配置是public,直接就能遍历下载
收工收工!!!
感谢小程序,不然这单得黄
⚠️ 法律声明:以上内容仅供安全学习和授权测试使用。未经授权的漏洞利用属于违法行为,请勿对未经授权的目标进行测试。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:老A搞安全 是老A 是老A《记录一次小程序挖洞经历,感谢小程序,不然这单得黄!!!》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论