# 代码审计入门总结

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2016-02-15T01:31:00Z
Canonical: https://wepostx.com/topics/72

> 乌云历史资料归档
>
> **原始作者：** sixwhale
> **原始编号：** superkieran-wooyundrops:1037
> **原始发布时间：** 2016-02-15 09:31
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

# 0x00 简介

之前看了seay写的PHP代码审计的书，全部浏览了一遍，作为一个代码审计小白，希望向一些和我一样的小白的人提供一下我的收获，以及一个整体的框架和常见漏洞函数。这也算是这本书的一个学习笔记吧，可以结合我捋顺的思路来看这本书。: )

# 0x01 整体

学习代码审计的目标是能够独立完成对一个CMS的代码安全监测。其通用的思路有：

- 通读全文代码，从功能函数代码开始阅读，例如include文件夹下的common_fun.php，或者有类似关键字的文件。

- 看配置文件，带有config关键字的文件，找到mysql.class.php文件的connect()函数，查看在数据库连接时是否出现漏洞。

- 继续跟读首页文件,index.php,了解程序运作时调用了哪些函数和文件 以index.php文件作为标线，一层一层去扩展阅读所包含的文件，了解其功能，之后进入其功能文件夹的首页文件，进行扩展阅读。

# 0x02 各种洞洞

### a.文件操作漏洞

- 能不用文件名参数就不用 尽量不要让用户可控

- 平行用户的权限 管理员的权限 操作权限

- 禁止传入参数类似于这种 .. ,/,\ 检查传入的参数,做出限制,停止程序往下执行

#### 1.文件包含漏洞：
(1) 本地文件包含：

- 一般存在于模块加载，模板加载，cache调用

- 包括函数：include()/include_once()，require()/require_once()寻找可控变量

![原文图片](/media/2016/06/2622a931d154d3f0985f402138f2098d)

![原文图片](/media/2016/06/69833c234e7a823c36188eec9af7bf38)

(2) 远程文件包含：

- 前提条件：allow_url_include = on

- 出现频率不如本地包含
(3) 文件包含截断：

- %00截断(php版本小于5.3)

- 问号截断(问号后面相当于请求的参数，伪截断)

- 英文(.) 反斜杠(/) 截断

#### 2.文件读取(下载)漏洞：
搜索关键函数:
file_get_contents(),highlight_file(),fopen(),read file(),fread(),fgetss(), fgets(),parse_ini_file(),show_source(),file()等

#### 3.文件上传漏洞：
搜索关键函数:
move_uploaded_file() 接着看调用这个函数的代码是否存在为限制上传格式或者可以绕过。
(1) 未过滤或本地过滤：服务器端未过滤，直接上传PHP格式的文件即可利用。
(2) 黑名单扩展名过滤：

- 限制不够全面：IIS默认支持解析.asp,.cdx, .asa,.cer等。

- 扩展名可绕过：

![原文图片](/media/2016/06/26e19683a6ff52af2e1e236e6b3615e6)

不被允许的文件格式.php，但是我们可以上传文件名为1.php(注意后面有一个空格)
(3) 文件头 content-type验证绕过：

- getimagesize()函数：验证文件头只要为GIF89a，就会返回真。

- 限制$_FILES["file"]["type"]的值 就是人为限制content-type为可控变量。
(4) 防范：

- 使用in_array()或 利用三等于===对比扩展名。

- 保存上传文件是重命名，规则采用时间戳拼接随机数：md5(time() + rand(1,1000))。

#### 4.文件删除漏洞：
搜索关键函数：

- unlink()利用回溯变量的方式

- 老版本下的session_destroy()，可以删除文件，现已基本被修复。
Metinfo的任意文件删除漏洞：

![原文图片](/media/2016/06/97e2a899695891b14d9c7e8ac2de69c0)

$action = delete即可删除.sql的文件，如果文件不是sql直接删除提交的文件名
target.com/recovery.php?&action=delete&filename=../../index.php

### b.代码执行漏洞

#### 1.代码执行函数：
搜索关键函数：eval(), assert(), preg_replace(), call_user_func(), call_user_func_array(), array_map()
(1) preg_replace()函数：
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
当$pattern处存在e修饰符时，$replacement 会被当做php代码执行。
(2)mixed call_user_func( callable $callbank [ , mixed $parameter [ , mixed $…)：
第一个参数为回调函数，第二个参数是回调函数的参数

![原文图片](/media/2016/06/881f31735935e9b7bf14921b823db048)

![原文图片](/media/2016/06/68874790769aea74e8179e8f40e7bbd0)

(3)eval()和assert()：
当assert()的参数为字符串时 可执行PHP代码
【区分】：

```text
eval(" phpinfo(); ");【√】 eval(" phpinfo() ");【X】 assert(" phpinfo(); ");【√】 assert(" phpinfo() ");【√】
```

#### 2.动态函数执行：
动态函数后门：

```text
#!php <?php $_GET['a']($_GET['b']); ?>
```

![原文图片](/media/2016/06/dd3cfc6c37d7c5c16e8c30acb283a187)

#### 3.命令执行函数：
搜索关键函数：system(), exec(), shell_exec(), passthru() ,pcntl_exec(), popen(),proc_open()
(1) popen和proc_open()：

```text
#!php <?php popen( 'whoami >> /Users/bingdaojueai/Desktop/1.txt', 'r' ); ?>
```
所在路径就会出现一个1.txt 里面的内容为命令执行后的结果
(2) 反引号命令执行：

- echo whoami; 直接就可以执行命令

- 双引号和单引号的区别：

```text
#!php $a = 1 echo " $a " output：1 echo ' $a ' output：$a
```

双引号时，可以直接解析变量，造成代码执行漏洞，过狗绕过。

### c.变量覆盖漏洞

#### 1.函数使用不当：

- int extract( array &$var_array , int $extract_type = EXTR_OVERWRITE , string $prefix = null )

- void parse_str( string $str , array &$arr )

- bool import_request_variables( string $type , string $prefix )

#### 2.$$变量覆盖：

![原文图片](/media/2016/06/230c8596240be4d6ece2cf3c2f12dfec)

![原文图片](/media/2016/06/8d3bd76d4ced011454183f679cff0859)

### d.逻辑漏洞
需要思考的问题：

- 程序是否可以重复安装

- 修改密码是否存在越权修改其他用户密码

- 找回密码验证码是否可以暴力破解

- cookie是否可以预测 验证存在绕过

#### 1.等于与存在判断绕过：
(1) in_array(): 比较之前会自动转换类型

![原文图片](/media/2016/06/ca71dd3cecfc87c256dde412ebcb2f74)

![原文图片](/media/2016/06/be4e1848056e449e5bed859eda6de1e7)

(2)is_numeric():当传入参数为hex时 直接通过并返回true 并且MYSQL可以直接使用hex编码代替字符串明文 可以二次注入 并且可能造成XSS漏洞
(3)双等于==和三等于===：

- 双等于会在变量比较时，进行类转换，与in_array()是一样的问题。

- 三等于是type和value的双重比较，相比之下更加安全。

#### 2.账户体系中的越权问题：

- 水平越权：A用户能够以B用户的身份，进行B用户的全部权限操作。前提A用户和B用户拥有相同的权限。

- 垂直越权：A用户能够以C用户的身份，进行C用户的全部权限操作，前提C用户比A用户拥有更高的权限。
(1) 未exit/return/die：

```text
#!php <?php if(file_exists('install.lock)){ header("Location:xxx.com"); //exit(); } echo "test"; ?>
```
test 依旧会被输出，替换成安装流程，PHP依旧会进行。
(2) 支付漏洞：

- 客户端修改单价

- 客户端修改总价和购买数量

- 服务端未校验严格

- 重复发包利用时间差：

```text
#!php <?php if (check_money($price)){ //Do something //花费几秒 $money = $money - $price; } ?>
```

可能导致漏洞函数： str_replace()

![原文图片](/media/2016/06/f27ffb50cd446a3d4f519a5726522173)

```text
#!php <?php $a = addslashes($_GET['a']); $b = addslashes($_GET['b']); echo "$a<br>$b<br>"; $c = str_replace($a,'',$b); echo trim($c); ?>
```

![原文图片](/media/2016/06/1c6b578729526898e58632cc678dc47a)

### e.会话认证漏洞

- COOKIE验证：没有使用SESSION验证，将信息直接保存在COOKIE中

- 找到传入sql语句的参数的传递过程 回溯变量到最原始的函数 看它保存在cookie的算法 是否可逆

- 和MD5比起 sha1更安全 解密sha1的网站更少

- 限制一个用户只能同时在一个IP上登录

- 审计代码时，查看登录处代码

### f.二次漏洞

#### 1.类型：

- 不是逻辑问题，是可信问题。

- 业务逻辑复杂度，与二次漏洞触发率 成正比。

- 购物车 / 订单 / 引用数据 / 文章编辑 / 草稿 ==> SQL注入 / XSS

![原文图片](/media/2016/06/084c3a31d1fe2f682e93bd9492606b51)

![原文图片](/media/2016/06/917a934867a1008c33b57a575837b2f5)

#### 2.技巧：
(1) 钻GPC等转义的空子：

- 不受GPC保护的$_SERVER变量：PHP5以后，$_SERVER取到的header不再受GPC影响，就算开启特殊字符也不会被转义，存在注入

- 编码问题转换：

- GBK的宽字节注入：%df ' 单引号自动被转义成(%5c)，同时%df与%5c连在一起组合成運字单引号依然在，成功闭合。【php与mysql交互过程中发生的编码转换问题】

- mb_convert_encoding()：

```text
#!php <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> <?php $sql = "WHERE id='".urldecode("-1%df%5c' == ")."'"; print_r(mb_convert_encoding($sql,"UTF-8","GBK")); ?>
```

(2)字符串问题：

- 利用报错，找到敏感信息

- 字符串截断：

- %00空字符截断：【PHP版本小于5.3】

```text
#!php <?php include($_GET['file'].'.php'); //1.php?file=2.txt%00 //2.txt里面是 <?php phpinfo()?> ?>
```

- iconv函数字符编码转换截断：【对PHP版本有要求】

```text
#!php chr(128)—chr(255)可以截断字符 <?php $a = '1'.chr(130).'2’; echo $a."<br>"; //1�2 echo iconv("UTF-8", "GBK", $a); //1 ?>
```

- php:// 输入输出流：

```text
#!php <?php include($_GET[‘file']); ?> 1.php?file=php://filter/convert.base64-encode(内容被base64编码)/resource=example.txt(远程文件)
```

- php代码解析标签：

- <script language="php">…</script>

- <?…?>：php3.0.4版本后可用

- <%…%>：asp标签，需要asp_tags=on，默认是off

- 正则表达式：

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

- 特殊字符未转义：

- 报错注入：

![原文图片](/media/2016/06/54e77472eade64b05a4dc5ecd4bb9eb4)

![原文图片](/media/2016/06/46ef54e94ea1876843561455e908808c)

- windows findfirstfile 利用：若要搜索12345.txt文件，可使用1<<来代替或者12<<，不可以单独使用一个"<"或">"，因为单独一个只是代表了一个字符，两个代表多个字符。

# 0x03 End

自己走上安全这条路既是兴趣也是偶然，选择白盒完全是因为喜欢php，毕竟是初识代码审计，seay的书确实帮了我不少，抱作者大腿（我是萌妹纸），希望这篇文章能够帮助像我一样小白的人，花了两天总结的，如果有什么缺陷也等着大家指出。
不会开发的谈审计都是耍流氓！：)

## Replies
