在 buuoj 上看到的这个比赛题目,期间平台关了,就拿了 Dockerfile 本地做了,web 题目感觉还不错
encode_and_encode [100]
打开靶机,前两个页面都是 html 页面,第三个给了页面源码
源码如下
<?php
error_reporting(0);
if (isset($_GET['source'])) {
show_source(__FILE__);
exit();
}
function is_valid($str) {
$banword = [
// no path traversal
'\.\.',
// no stream wrapper
'(php|file|glob|data|tp|zip|zlib|phar):',
// no data exfiltration
'flag'
];
$regexp = '/' . implode('|', $banword) . '/i';
if (preg_match($regexp, $str)) {
return false;
}
return true;
}
$body = file_get_contents('php://input');
$json = json_decode($body, true);
if (is_valid($body) && isset($json) && isset($json['page'])) {
$page = $json['page'];
$content = file_get_contents($page);
if (!$content || !is_valid($content)) {
$content = "<p>not found</p>\n";
}
} else {
$content = '<p>invalid request</p>';
}
// no data exfiltration!!!
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{<censored>}', $content);
echo json_encode(['content' => $content]);
file_get_contents('php://input')
获取 post 的数据,json_decode($body, true)
用 json 格式解码 post 的数据,然后is_valid($body)
对 post 数据检验,大概输入的格式如下is_valid($body)
对 post 数据检验,导致无法传输$banword
中的关键词,也就无法传输flag
,这里在 json 中,可以使用 Unicode 编码绕过,flag
就等于\u0066\u006c\u0061\u0067
通过检验后,获取
page
对应的文件,并且页面里的内容也要通过is_valid
检验,然后将文件中HarekazeCTF{}
替换为HarekazeCTF{<censored>}
,这样就无法明文读取 flag这里传入
/\u0066\u006c\u0061\u0067
后,由于flag
文件中也包含 flag 关键字,所以返回not found
,这也无法使用file://
file_get_contents
是可以触发php://filter
的,所以考虑使用伪协议读取,对php
的过滤使用Unicode
绕过即可可以看出,json 在传输时是 Unicode 编码的
Avatar Uploader 1 [100]
给了源码,打开靶机,登录之后,是一个文件上传
首先
config.php
中定义了一些常量然后在
upload.php
中判断文件大小,并使用FILEINFO
判断上传图片类型,上传图片只能是 png 类型后面再用
getimagesize
判断文件像素大小,并且再进行一次类型判断,如果不是 png 类型就给出 flag在这两种判断上传图片类型的函数中,有一个很有趣的现象,
FILEINFO
可以识别 png 图片( 十六进制下 )的第一行,而getimagesize
不可以,代码如下
<?php
$file = finfo_open(FILEINFO_MIME_TYPE);
var_dump(finfo_file($file, "test"));
$f = getimagesize("test");
var_dump($f[2] === IMAGETYPE_PNG);
结果,16进制文件也在下面
直接上传这个文件就可以获取 flag 了
Easy Notes [200]
给了源码,打开靶机,是一个笔记系统
在登陆处进行了匹配,只允许输入 4 到 64 位规定字符,且不是前端验证
登陆成功后,可以进行增删查和导出为 zip 或 tar 的功能,点击
Get flag
提示不是 admin既然拿到源码就先看看全局配置
config.php
,就写了一行,定义临时文件目录
define('TEMP_DIR', '/var/www/tmp');
进入
page/flag.php
看一下给出 flag 的条件,要满足is_admin()
函数跟进
is_admin()
函数,没有发现什么可以利用的地方看到有个导出功能,它会将添加的 note 导出为 zip,这个文件存放的位置在
TEMP_DIR
,和session
信息保存在同一个位置,那么是不是可以考虑伪造 sessionsession 文件以
sess_
开头,且只含有a-z
,A-Z
,0-9
,-
看到
$filename
处可以满足所有的条件构造
user
为sess_
,type
为.
,经过处理之后,$path
就是TEMP_DIR/sess_0123456789abcdef
这就伪造了一个 session 文件然后向这个文件写入 note 的
title
php 默认的 session 反序列化方式是
php
,其存储方式为键名+竖线+经过serialize函数序列处理的值
,这就可以伪造admin
了在最后,它会将构造的
$filename
返回,这样就可以拿到构造出的 admin 的 session 数据