详细介绍:CTFshow系列——PHP特性Web113-115(123)
今天继续给大家带来php特性的CTF题目讲解,说实话每次回头看写的文章,总能学到新东西;
文章目录
- Web113
- 代码分析
- 方法二:利用函数所能处理的长度限制进行目录溢出(官方WP)
- Web114
- Web115(新题型)
- 代码分析
- 解题思路
- Web123
- 代码分析
- payload
- 总结
Web113
直接看源代码:
<?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://协议了:

# payload
?file=compress.zlib://flag.php
–
方法二:利用函数所能处理的长度限制进行目录溢出(官方WP)
# 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
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:编码也不要了

# payload
?file=php://filter/resource=flag.php
(之前的目录溢出在这关失效了。。。)
Web115(新题型)
<?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
# payload
?num=%0c36
我真的没招了。

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

所以还是先看代码:
<?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’])
# payload
POST: CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

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