-
Notifications
You must be signed in to change notification settings - Fork 337
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
382 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
本文由红日安全成员: **七月火** 编写,如有不当,还望斧正。 | ||
|
||
## 前言 | ||
|
||
大家好,我们是红日安全-代码审计小组。最近我们小组正在做一个PHP代码审计的项目,供大家学习交流,我们给这个项目起了一个名字叫 [**PHP-Audit-Labs**](https://github.com/hongriSec/PHP-Audit-Labs) 。现在大家所看到的系列文章,属于项目 **第一阶段** 的内容,本阶段的内容题目均来自 [PHP SECURITY CALENDAR 2017](https://www.ripstech.com/php-security-calendar-2017/) 。对于每一道题目,我们均给出对应的分析,并结合实际CMS进行解说。在文章的最后,我们还会留一道CTF题目,供大家练习,希望大家喜欢。下面是 **第10篇** 代码审计文章: | ||
|
||
## Day 10 - Anticipation | ||
|
||
题目叫做预期,代码如下: | ||
|
||
![1](1.png) | ||
|
||
**漏洞解析** : | ||
|
||
这道题目实际上讲的是当检测到攻击时,虽然有相应的防御操作,但是程序未立即停止退出,导致程序继续执行的问题。程序在 **第一行处** 使用 **extract** 函数,将 **POST** 请求的数据全都注册成变量, **extract** 函数的定义如下: | ||
|
||
>[ extract ](http://php.net/manual/zh/function.extract.php) :(PHP 4, PHP 5, PHP 7) | ||
> | ||
>**功能** :从数组中将变量导入到当前的符号表 | ||
> | ||
>**定义** : `int extract ( array &$array [, int $flags = EXTR_OVERWRITE [, string $prefix = NULL ]] )` | ||
> | ||
该函数实际上就是把数组中的键值对注册成变量,具体可以看如下案例: | ||
|
||
![2](2.png) | ||
|
||
这样我们就可以控制 **第7行** 处的 **pi** 变量。程序对 **pi** 变量进行简单的验证,如果不是数字或者没有设置 **pi** 变量,程序就会执行 **goAway** 方法,即记录错误信息并直接重定向到 **/error/** 页面。看来程序员这里是对非法的操作进行了一定的处理。但是关键在于,程序在处理完之后,没有立即退出,这样程序又会按照流程执行下去,也就到了 **第11行** 的 **assert** 语句。由于前面 **pi** 变量可以被用户控制,所以在这一行存在远程代码执行漏洞。 | ||
|
||
例如我们的payload为:**pi=phpinfo()** (这里为POST传递数据),然后程序就会执行这个 **phpinfo** 函数。当然,你在浏览器端可能看不到 **phpinfo** 的页面,而是像下面这样的图片: | ||
|
||
![3](3.png) | ||
|
||
但是用 **BurpSuite** ,大家就可以清晰的看到程序执行了 **phpinfo** 函数: | ||
|
||
![4](4.png) | ||
|
||
为了方便大家理解,笔者这里录制了debug程序的过程: | ||
|
||
![5](5.gif) | ||
|
||
实际上,这种案例在真实环境下还不少。例如有些CMS通过检查是否存在install.lock文件,从而判断程序是否安装过。如果安装过,就直接将用户重定向到网站首页,却忘记直接退出程序,导致网站重装漏洞的发生。下面我们来看两个真实的案例。 | ||
|
||
## 实例分析 | ||
|
||
### FengCms 1.32 网站重装漏洞 | ||
|
||
本次实例分析,我们选取的是 **[FengCms 1.32](http://pan.baidu.com/s/1i33gNVR)** 。对于一个已经安装好的 **FengCms** ,当用户再次访问 **install/index.php** 时,就会导致网站重装。我们来具体看下程序的逻辑: | ||
|
||
![6](6.png) | ||
|
||
我们可以看到,如果是第一次安装网站,程序会在 **upload** 目录下生成一个 **INSTALL** 文件,用于表示该网站已经安装过(对应上图 **25-28行** 代码)。当我们再次访问该文件时,程序会先判断 **upload** 目录下是否有 **INSTALL** 文件。如果存在,则弹窗提示你先删除 **INSTALL** 文件才能进行网站重装(对应上图 **1-4行** 代码)。但是这里注意了,网站在弹出告警信息后,并没有退出,而是继续执行,所以我们在不删除 **INSTALL** 文件的情况下,仍可以重装网站。 | ||
|
||
比较有趣的是,原本网站网站成功后,程序会自动删除 **upload** 目录下的所有文件,来防止攻击者重装网站,然而这段代码却在注释当中,具体原因不得而知。 | ||
|
||
![7](7.png) | ||
|
||
### Simple-Log1.6网站重装漏洞 | ||
|
||
我们再来看 **[Simple-Log1.6](http://down.admin5.com/php/42012.html#link)** 网站重装的例子。其 **install\index.php** 文件中,对网站安装成功的处理有问题,其代码是在下图 **17-20行** ,程序只是用 **header** 函数将其重定向到网站首页,然而程序还是会继续执行下去。 | ||
|
||
![8](8.png) | ||
|
||
而且程序的安装逻辑其实是有问题的,安装步骤由 **$setup** 变量控制,而 **$setup** 变量可以被用户完全控制(如上图 **第10行** 代码),攻击者完全可以控制网站的安装步骤。 | ||
|
||
## 漏洞利用 | ||
|
||
漏洞利用就极其简单了,我们先来看一下 **FengCms** ,我们直接访问 **install/index.php** 页面,无视弹出来的警告: | ||
|
||
![9](9.png) | ||
|
||
![10](10.png) | ||
|
||
可以看到程序仍然可以继续安装。 | ||
|
||
我们再来看一下 **Simple-Log** 的重装利用: | ||
|
||
![11](11.png) | ||
|
||
直接post以上数据,即可重装网站数据。 | ||
|
||
## 修复建议 | ||
|
||
实际上,要修复这一类型的漏洞,我们只要在正确的地方退出程序即可。拿这次的例题举例,我们只需要在检查到非法操作的时候,直接添加退出函数,即可避免漏洞发生。例如使用 **die** 、 **exit** 等函数都是可以的,具体修复代码如下: | ||
|
||
![12](12.png) | ||
|
||
## 结语 | ||
|
||
看完了上述分析,不知道大家是否对 **未正确退出程序** 导致的攻击有了更加深入的理解,文中用到的 **CMS** 可以从这里( [**FengCms 1.32**](http://pan.baidu.com/s/1i33gNVR) 、 **[Simple-Log1.6](http://down.admin5.com/php/42012.html#link)** )下载,当然文中若有不当之处,还望各位斧正。如果你对我们的项目感兴趣,欢迎发送邮件到 **[email protected]** 联系我们。**Day10** 的分析文章就到这里,我们最后留了一道CTF题目给大家练手,题目如下: | ||
|
||
```php | ||
// index.php | ||
<?php | ||
include 'config.php'; | ||
function stophack($string){ | ||
if(is_array($string)){ | ||
foreach($string as $key => $val) { | ||
$string[$key] = stophack($val); | ||
} | ||
} | ||
else{ | ||
$raw = $string; | ||
$replace = array("\\","\"","'","/","*","%5C","%22","%27","%2A","~","insert","update","delete","into","load_file","outfile","sleep",); | ||
$string = str_ireplace($replace, "HongRi", $string); | ||
$string = strip_tags($string); | ||
if($raw!=$string){ | ||
error_log("Hacking attempt."); | ||
header('Location: /error/'); | ||
} | ||
return trim($string); | ||
} | ||
} | ||
$conn = new mysqli($servername, $username, $password, $dbname); | ||
if ($conn->connect_error) { | ||
die("连接失败: "); | ||
} | ||
if(isset($_GET['id']) && $_GET['id']){ | ||
$id = stophack($_GET['id']); | ||
$sql = "SELECT * FROM students WHERE id=$id"; | ||
$result = $conn->query($sql); | ||
if($result->num_rows > 0){ | ||
$row = $result->fetch_assoc(); | ||
echo '<center><h1>查询结果为:</h1><pre>'.<<<EOF | ||
+----+---------+--------------------+-------+ | ||
| id | name | email | score | | ||
+----+---------+--------------------+-------+ | ||
| {$row['id']} | {$row['name']} | {$row['email']} | {$row['score']} | | ||
+----+---------+--------------------+-------+</center> | ||
EOF; | ||
} | ||
} | ||
else die("你所查询的对象id值不能为空!"); | ||
?> | ||
``` | ||
|
||
```php | ||
// config.php | ||
<?php | ||
$servername = "localhost"; | ||
$username = "fire"; | ||
$password = "fire"; | ||
$dbname = "day10"; | ||
?> | ||
``` | ||
|
||
```sql | ||
# 搭建CTF环境使用的sql语句 | ||
create database day10; | ||
use day10; | ||
create table students ( | ||
id int(6) unsigned auto_increment primary key, | ||
name varchar(20) not null, | ||
email varchar(30) not null, | ||
score int(8) unsigned not null ); | ||
|
||
INSERT INTO students VALUES(1,'Lucia','[email protected]',100); | ||
INSERT INTO students VALUES(2,'Danny','[email protected]',59); | ||
INSERT INTO students VALUES(3,'Alina','[email protected]',66); | ||
INSERT INTO students VALUES(4,'Jameson','[email protected]',13); | ||
INSERT INTO students VALUES(5,'Allie','[email protected]',88); | ||
|
||
create table flag(flag varchar(30) not null); | ||
INSERT INTO flag VALUES('HRCTF{tim3_blind_Sql}'); | ||
``` | ||
|
||
题解我们会阶段性放出,如果大家有什么好的解法,可以在文章底下留言,祝大家玩的愉快! |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
本文由红日安全成员: **七月火** 编写,如有不当,还望斧正。 | ||
|
||
## 前言 | ||
|
||
大家好,我们是红日安全-代码审计小组。最近我们小组正在做一个PHP代码审计的项目,供大家学习交流,我们给这个项目起了一个名字叫 [**PHP-Audit-Labs**](https://github.com/hongriSec/PHP-Audit-Labs) 。现在大家所看到的系列文章,属于项目 **第一阶段** 的内容,本阶段的内容题目均来自 [PHP SECURITY CALENDAR 2017](https://www.ripstech.com/php-security-calendar-2017/) 。对于每一道题目,我们均给出对应的分析,并结合实际CMS进行解说。在文章的最后,我们还会留一道CTF题目,供大家练习,希望大家喜欢。下面是 **第9篇** 代码审计文章: | ||
|
||
## Day 9 - Rabbit | ||
|
||
题目叫做兔子,代码如下: | ||
|
||
![1](1.png) | ||
|
||
**漏洞解析** : | ||
|
||
这一题考察的是一个 **str_replace** 函数过滤不当造成的任意文件包含漏洞。在上图代码 **第18行** 处,程序仅仅只是将 **../** 字符替换成空,这并不能阻止攻击者进行攻击。例如攻击者使用payload:**....//** 或者 **..././** ,在经过程序的 **str_replace** 函数处理后,都会变成 **../** ,所以上图程序中的 **str_replace** 函数过滤是有问题的。我们来看一下PHP手册对 **str_replace** 函数的具体定义: | ||
|
||
>[ str_replace ](http://php.net/manual/zh/function.str-replace.php):(PHP 4, PHP 5, PHP 7) | ||
> | ||
>**功能** :子字符串替换 | ||
> | ||
>**定义** : `mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )` | ||
> | ||
>该函数返回一个字符串或者数组。如下: | ||
> | ||
>str_replace(字符串1,字符串2,字符串3):将字符串3中出现的所有字符串1换成字符串2。 | ||
> | ||
>str_replace(数组1,字符串1,字符串2):将字符串2中出现的所有数组1中的值,换成字符串1。 | ||
> | ||
>str_replace(数组1,数组2,字符串1):将字符串1中出现的所有数组1一一对应,替换成数组2的值,多余的替换成空字符串。 | ||
![2](2.png) | ||
|
||
## 实例分析 | ||
|
||
本次实例分析,我们选取的是 **Metinfo 6.0.0** 版本。漏洞文件在 **app/system/include/module/old_thumb.class.php** 中,我们发现程序将变量 **$dir** 中出现的 **../** 和 **./** 字符替换成空字符串(下图第6行处),猜想开发者应该是有考虑到路径穿越问题,所以做了此限制。具体代码如下: | ||
|
||
![3](3.png) | ||
|
||
接着在第8行处,用 **strstr** 函数判断 **$dir** 变量中是否含有 **http** 字符串,如果有,则读取加载 **$dir** 变量,并以图片方式显示出来。这里猜测开发者的意图是,加载远程图片。关于 **strstr** 函数,定义如下: | ||
|
||
>[ strstr ](http://php.net/manual/zh/function.strstr.php):(PHP 4, PHP 5, PHP 7) | ||
> | ||
>**功能** :查找字符串的首次出现 | ||
> | ||
>**定义** : `string strstr ( string $haystack , mixed $needle [, bool $before_needle = FALSE ] )` | ||
> | ||
>返回 `haystack` 字符串从 `needle` 第一次出现的位置开始到 `haystack` 结尾的字符串。 | ||
> | ||
>```php | ||
>domain = strstr('[email protected]', '@'); | ||
>// 上面输出:@gmail.com | ||
>user = strstr('[email protected], '@', true); // 从 PHP 5.3.0 起 | ||
>// 上面输出:hongrisec | ||
>``` | ||
然而这段代码是可以绕过的,例如我们使用 **payload:.....///http/.....///.....///.....///.....///etc/passwd** ,过滤后实际就变成: **../http/../../../../etc/passwd** ,效果如下: | ||
![4](4.png) | ||
![5](5.png) | ||
接下来,我们要做的就是搜索程序在哪里调用了这个文件。用 **phpstorm** 加载整个项目文件,按住 `Ctrl+Shift+F` 键,搜索关键词 **old_thumb** ,发现在 **include/thumb.php** 文件中调用 **old_thumb** 类,搜索结果如下图: | ||
![6](6.png) | ||
我们在 **include/thumb.php** 文件中,可以看到 **M_CLASS** 定义为 **old_thumb** ,而 **M_ACTION** 定义为 **doshow** 。我们接着跟进到 **app/system/entrance.php** 文件中,在该文件的末尾可以看包含了 **app/system/include/class/load.class.php** 文件,引入了 **load** 类,然后调用了 **load** 类的 **module** 方法。 | ||
```php | ||
// app/system/include/class/load.class.php | ||
require_once PATH_SYS_CLASS.'load.class.php'; | ||
load::module(); | ||
``` | ||
我们跟进 **module** 方法,并查看各个变量的赋值情况( **app/system/include/class/load.class.php** 文件): | ||
|
||
![7](7.png) | ||
|
||
上图程序最后调用了 **load** 类的 **_load_class** 方法,我们跟进该方法,详细代码如下: | ||
|
||
![8](8.png) | ||
|
||
可以看到上图代码第16行处实例化了一个 **old_thumb** 类对象,然后在第25行处调用了 **old_thumb** 类的 **doshow** 方法, **doshow** 方法中的 **$dir** 变量就是用户可以控制的。以上便是完整的攻击过程分析,下面我们看看具体如何进行攻击。 | ||
|
||
## 漏洞利用 | ||
|
||
实际上攻击的话就很简单了,因为 **$dir** 变量是直接通过 **GET请求** 获取的,然后用 **str_replace** 方法处理,而 **str_replace** 方法处理又有问题,所以我们构造 **payload** 如下: | ||
|
||
```bash | ||
http://localhost/metInfo/include/thumb.php?dir=.....///http/.....///最终用户授权许可协议.txt | ||
``` | ||
|
||
![9](9.png) | ||
|
||
成功读取 **最终用户授权许可协议.txt** 文件。 | ||
|
||
## 修复建议 | ||
|
||
关于修复建议,这里先抛出个问题给大家,针对这个案例,下面的修复代码是否可行? | ||
|
||
```php | ||
$dir = str_replace(array('..','//'), '', $_GET['dir']); | ||
``` | ||
|
||
咋一看,这个代码好像完美地修复了路径穿越问题,但是,我们在修复代码的时候一定要结合实际情况。比如在metinfo中,程序这里原来的功能是加载远程图片,使用上面的修复代码,会导致正常的图片链接无法加载,这种修复肯定是无效的。这里给出我的修复代码,如下图: | ||
|
||
![10](10.png) | ||
|
||
## 结语 | ||
|
||
看完了上述分析,不知道大家是否对 **str_replace()** 函数过滤路径符号有了更加深入的理解,文中用到的CMS可以从 [这里](https://www.metinfo.cn/upload/file/MetInfo6.0.0.zip) 下载,当然文中若有不当之处,还望各位斧正。如果你对我们的项目感兴趣,欢迎发送邮件到 **[email protected]** 联系我们。**Day9** 的分析文章就到这里,我们最后留了一道CTF题目给大家练手,题目如下: | ||
|
||
```php | ||
// index.php | ||
<?php | ||
include 'config.php'; | ||
include 'function.php'; | ||
|
||
$conn = new mysqli($servername,$username,$password,$dbname); | ||
if($conn->connect_error){ | ||
die('连接数据库失败'); | ||
} | ||
|
||
$sql = "SELECT COUNT(*) FROM users"; | ||
$result = $conn->query($sql); | ||
if($result->num_rows > 0){ | ||
$row = $result->fetch_assoc(); | ||
$id = $row['COUNT(*)'] + 1; | ||
} | ||
else die($conn->error); | ||
|
||
if(isset($_POST['msg']) && $_POST['msg'] !==''){ | ||
$msg = addslashes($_POST['msg']); | ||
$msg = replace_bad_word(convert($msg)); | ||
$sql = "INSERT INTO users VALUES($id,'".$msg."')"; | ||
$result = $conn->query($sql); | ||
if($conn->error) die($conn->error); | ||
} | ||
echo "<center><h1>Welcome come to HRSEC message board</center></h1>"; | ||
echo <<<EOF | ||
<center> | ||
<form action="index.php" method="post"> | ||
<p>Leave a message: <input type="text" name="msg" /><input type="submit" value="Submit" /></p> | ||
</form> | ||
</center> | ||
EOF; | ||
$sql = "SELECT * FROM users"; | ||
$result = $conn->query($sql); | ||
if($result->num_rows > 0){ | ||
echo "<center><table border='1'><tr><th>id</th><th>message</th><tr></center>"; | ||
while($row = $result->fetch_row()){ | ||
echo "<tr><th>$row[0]</th><th>$row[1]</th><tr>"; | ||
} | ||
echo "</table></center>"; | ||
} | ||
$conn->close(); | ||
?> | ||
``` | ||
|
||
```php | ||
// function.php | ||
<?php | ||
function replace_bad_word($str){ | ||
global $limit_words; | ||
foreach ($limit_words as $old => $new) { | ||
strlen($old) > 2 && $str = str_replace($old,trim($new),$str); | ||
} | ||
return $str; | ||
} | ||
|
||
function convert($str){ | ||
return htmlentities($str); | ||
} | ||
|
||
$limit_words = array('造反' => '造**', '法轮功' => '法**'); | ||
|
||
foreach (array('_GET','_POST') as $method) { | ||
foreach ($$method as $key => $value) { | ||
$$key = $value; | ||
} | ||
} | ||
?> | ||
``` | ||
|
||
```php | ||
// config.php | ||
<?php | ||
$servername = "localhost"; | ||
$username = "hongrisec"; | ||
$password = "hongrisec"; | ||
$dbname = "day9"; | ||
?> | ||
``` | ||
|
||
```sql | ||
# 搭建CTF环境使用的sql语句 | ||
create database day9; | ||
use day9; | ||
create table users( | ||
id integer auto_increment not null primary key, | ||
message varchar(50) | ||
); | ||
create table flag( flag varchar(40)); | ||
insert into flag values('HRCTF{StR_R3p1ac3_anD_sQ1_inJ3ctIon_zZz}'); | ||
``` | ||
|
||
题解我们会阶段性放出,如果大家有什么好的解法,可以在文章底下留言,祝大家玩的愉快! | ||
|
||
## 相关文章 | ||
|
||
[Metinfo 6.0.0 任意文件读取漏洞](http://badcode.cc/2018/05/26/Metinfo-6-0-0-%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96%E6%BC%8F%E6%B4%9E/) | ||
|
||
[MetInfo 任意文件读取漏洞的修复与绕过](https://paper.seebug.org/676/) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters