概述
本月早些时候,Sam Curry 和原文作者发现了他们在实战中遇到的首批可利用的 ORM 注入漏洞,并利用该漏洞从一个在线游戏中实现了加密货币窃取。
事件背景
该游戏是一款即将上线的“付费生成(pay-to-spawn)”射击类游戏,属于大逃杀类型,玩家需要支付一定数量的加密货币才能生成,最后胜者通吃。
遗憾的是,目前这款游戏尚未正式发布,处于封闭测试阶段,没有公开邀请方式,也找不到游戏的二进制文件,但另一位朋友 Justin Rhinehart 在 VirusTotal 找到了上传的游戏文件。
研究过程
取得游戏二进制文件后,启动时由于服务器尚未运行,屏幕几乎空白,于是他们将注意力转向驱动游戏运行的网站。
- 账号注册功能可用
- 创建设备账户并在网站上探索后取得部分进展
- 通过修改客户端逻辑,将“isAdmin”标记从“false”替换为“true”,在网站仪表盘中出现了一个隐藏的管理员面板菜单。
虽然替换标记使管理员功能可见,但由于权限限制,无法进行数据访问,相关的 API 调用均返回 401 未授权错误,无法在主站点获得权限升级。
进一步探索
发现一个子域名“dev.”,其内容与主站点完全相同,但启用了 Django debug 模式。
API 请求失败时返回详细错误信息,这帮助他们进一步了解网站内部架构,虽然机密密码和关键配置被 Django 屏蔽,但调试堆栈中仍有部分源代码片段可抓取。
关键漏洞实验
API 请求如下,暴露了对后台 ORM 查询的过滤器控制:
POST /api/race/queue HTTP/2
Host: dev.xxx.com
Content-Type: text/plain;charset=UTF-8
{"queue":"default","action":"remove_users","query":{"start_filters":{"id":"123"},"filters":{},"order":[]}}
尝试使用带有 password__contains
过滤器泄露密码哈希:
POST /api/race/queue HTTP/2
Host: dev.xxx.com
Content-Type: text/plain;charset=UTF-8
{"queue":"default","action":"remove_users","query":{"start_filters":{"password__contains":"a"},"filters":{},"order":[]}}
返回如下响应,表示有 140 名用户的密码哈希中包含字母“a”:
{"queue": "default", "deleted": 140}
数据库信息和权限发现
- Django 默认使用 PBKDF2 方式存储密码,破译密码难度大
- 未发现会话 cookie、JWT 或密码重置令牌等其它敏感数据
- 模型包含
is_superuser
和is_staff
字段,可用于识别管理员账户 - 利用复合过滤条件,结合
__startswith
查询,暴力猜测管理员邮箱地址
编写脚本自动化邮件地址暴力猜测:
进一步利用邮件日志
发现 mail_log
模型,包含 subject
和 message
字段,类似发送给用户的邮件记录。
请求密码重置后,通过 ORM 注入查询 mail_log.message
字段,检索重置链接字符串,成功泄露密码重置链接。
权限提升与攻击成功
结合管理员邮箱和重置链接,重置管理员密码后成功登录后台。
获得完全管理员权限后,尝试将游戏的钱包中的少量 USDC 转账至自己账户,测试成功。
结论与善后
获得了对游戏管理员面板的完全访问权限,包括控制加密货币钱包,转移资金等。
所有发现均已负责任披露,相关公司已发布补丁修复漏洞。
原文:https://blog.p1.gs/writeup/2025/07/06/Hacking-a-crypto-game/