[HFCTF2020]BabyUpload
1 | <?php |
前面部分设置了session存储路径,启动了session并根目录下包含flag
1 | error_reporting(0); |
接下来判断session的username是否为admin,如果是,则判断/var/babyctf下是否存在
success.txt,如果存在,删除文件并输出flag,否则设置usename为guest
1 | if($_SESSION['username'] ==='admin') |
然后设置两个post参数direction、attr,$dir_path拼接路径,若$attr为private,
在$dir_path的基础上再拼接一个username
1 | $direction = filter_input(INPUT_POST, 'direction'); |
如果direction设置为upload,首先判断是否正常上传,通过则在$dir_path下拼接文件名,之后再拼接一个_,提示加上文件名的sha256值,之后限制墨绿穿越,创建相应目录,把文件上传到目录下
1 | if($direction === "upload"){ |
若idrection设置为download,读取上传上来的文件,拼接位$file_path,限制目录穿越,判断是否存在,存在则返回文件内容
1 | elseif ($direction === "download") {//如果direction设置为download |
通过以上可知,要获取flag需满足
1 | $_SESSION[‘username’] ===‘admin’ |
也就是说我们伪造自己的username是admin,并穿件一个success.txt的文件
伪造session
php的session默认存储文件名是sess_+PHPSESSID的值,我们先看一下session文件内容。
查看cookie中PHPSESSID

构造direction=download&attr=&filename=sess_e5212a8144f0d61543352a8bc5b4434fpost传入,在返回内容中读到内容

我们发现还有一个不可见字符
参考https://blog.spoock.com/2016/10/16/php-serialize-problem/
不同的引擎所对应的session的存储方式有
php_binary:存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值
php:存储方式是,键名+竖线+经过serialize()函数序列处理的值
php_serialize(php>5.5.4):存储方式是,经过serialize()函数序列化处理的值
我们可以判断这里session处理器为php_binary,那么我们可以在本地利用php_binary生成我们要伪造的session文件
1 | <?php |
运行生成session文件

将文件名改为sess并计算sha256

这样,如果我们见sess文件上传,服务器存储该文件的文件名就是
1 | sess_432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4 |

成功伪造session,文件名设置不了,直接创建目录也符合条件,将attr设置为success.txt创建目录,再将sess上传到该目录下即可绕过判断
我们先修改PHPSESSID为432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4
再给attr`传参为success.txt
1 | direction=download&attr=success.txt&filename=sess_432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4 |

额外解:
使用脚本
1 | import requests |