[PHP代码审计] Chinaz

发布于 2021-08-12  111 次阅读


前言

刚刚接触代码审计,PHPStorm的动调使用的还不是很流畅,开始审计的是一个非常简单的chinaz,但是即便跟着别的师傅的思路走,很多地方的函数调用还是弄不明白,可以说是非常的痛苦。代码审计真的很吃基本功,要对危险函数敏感,这也是我CTF一直打不好的原因吧,面对危险函数太不敏感了,一定要培养自己扎实的基本功。黄沙百战穿金甲,不破楼兰誓不还

思路

  • 通读全文法:寻找入口文件,通读全文代码,查找跟踪可控变量
  • 回溯法:定位危险函数,查找跟踪可控变量

审计

结构分析

这个cms的体量非常小,没有使用MVC框架,因此分析起来不是很困难,具体结构如图:

image-20210812164820999

入口分析

这是个但单入口的的cms,直接进入index.php开始分析:

  • 首先包含了公共文件的函数和方法
  • 对包含的view.php文件中的类进行实例化
  • 第五行声明一个数组$data
  • 接着,对page参数使用了filter函数,进行处理,并赋值到$data[page]
  • 将经过处理page,传递到类内方法echoContent
image-20210812165422589

跟踪一下filter(ctrl + 鼠标左键,直接跳转了):

  • filtercommon.php的一个函数,用来将传递的参数进行过滤,将. 替换为空,由此可以避免../../这样的路径穿越
image-20210812170030076
  • 这里感觉可以绝对路径进行绕过,打断点,验证思路,确实可以通过绝对路径绕过
image-20210812174538837

跟踪一下echoContent:

  • 将传递进来的参数,拼接views/.php后放入到loadFile
  • 然后调用三个类内方法,处理loadFile返回值
image-20210812170540742

跟踪一个loadFile:

  • 判断文件是否存在:
    • 如果文件不存在,就调用wirte_log,并返回报错页面路径($cfg_basedir,跟踪一下是提前声明好的返回路径的全局变量)
    • 如果文件存在,通过fopenfreadfclose读取文件信息,并返回
image-20210812170806745

跟踪一下write_log

  • 将访问错误路径,格式化为字符串,写入日志中
image-20210812171550206

到这里入口分析完毕

seay扫描

  • 个人感觉Seay不是很好用,我这套cms,本身是一套AWD题目,有个预留后门,这都扫不出来,玩个屁。这也间接告诉我们,不要迷信工具。或者说,重构一下Seay的正则。
  • 但是对于现在我这个菜逼来说够用了,以后接触到更好的工具咱再换
  • 这里Seay扫描的结果是危险的函数,还需要手工去验证
image-20210812172156048

漏洞分析

action.php文件包含

根据Seay的扫描结果对action.php进行分析:

  • 包含两个公用函数和方法
  • 获取postpage传参,使用filter函数进行处理,并拼接后缀.php
  • 将所有post型传参都存储到数组$post_data
  • 如果文件存在进行包含。
  • 综合入口分析的结果,我们可以使用绝对路径绕过filter对路径穿越的处理
  • 由此我们可以读取所有的php文件
  • 如果当PHP版本<5.3.4的时候,甚至可以使用%00截断,达到任意文件包含的效果
image-20210812175315670
  • 打断点进行测试,发现传递的值是正常的
image-20210812185212104
  • 提前写好 phpinfo.php文件,运行断点进行测试,成功显示phpinfo页面说明,分析正确
image-20210812185329528

normaliz.php变量覆盖

  • 从26行开始看起,首先实例化一个对象
  • 创建三个变量并赋值,调用action函数,将结果保存在$data['res']
  • 接着调用echoContent处理页面
  • 跟踪一下action函数
    • $post_data哪来的,跟踪一下发现,是action.php里面的数组
    • 接下来就是对数组的键值对,转化为变量和值的形式
    • 如果我们在$post_data里面传递包含$ip_replacement$mail_replacement的键值对,当数组转化为变量和值的形式,是不是就相当于对函数接收的形参,进行了重新赋值,达到了变量覆盖的效果
    • 这里应该如何利用呢,就需要一个小Tips:preg_replace函数在e模式下,匹配成功会造成任意代码执行(但是这个必须要在PHP<7.0,才可以)
    • 因此我们需要让method/xxx/e开启e模式,mail_replacement函数为我们要执行的代码,$source为任意值即可
image-20210812185649001
  • 因为要7.0 以下版本才可以,我的版本太高,因此这里只能演示动调结果:
image-20210812192354431
image-20210812192947539
image-20210812192809294
image-20210812193152564
image-20210812193322131

common.php任意文件写入

  • 这里存在一个write_log函数,在入口分析时,知道他会把错误路径写入到logs/logfile.php
image-20210812193945159
  • 全局搜索看在哪里调用了这个函数(PHPStrom 直接Ctrl + shift + F,我这里热键冲突了,使用seay进行查找),发现有两处调用了它,其中一处可疑,进行跟踪。
image-20210812194508977
  • 也就是我们入口分析时,已经提到过的loadFile
  • 也就是说,只要我们传递错误的路径,这个错误的路径就会被写入到日志中,在通过action.php的文件包含就可以达到任意文件写入的目的
image-20210812194645245
  • 全局搜索,看看哪里调用了loadFile函数
  • 这里我们发现有三处调用了该函数,但是前两处参数都是不可控的。只有第三处可控,跟踪第三处
image-20210812195318904
  • 这里任然是我们入口分析处,提到的函数,由之前分析可知,$vId是我们可控的,也就是index.php
  • 整理漏洞里利用过程 index.php中利用page参数访问我们想要写入的文件信息,因为文件不存在会被写入日志。我这里没有禁止对logfile.php的访问,要是被禁止了,使用action.php进行文件包含即可。
image-20210812195448956
  • 利用过程演示
image-20210812200213513
image-20210812200320221

view.php任意代码执行

  • 剩下为验证的只有view.phpeval,对其进行跟踪
image-20210812200435270
  • 我们发现所有的这些eval 全部是出现在view.phpparseSubIf方法中
  • 而且我们发现,eval里面的可控参数strIf是和$conntect关联起来的,要想控制$strIf必须通过$content正则替代出来
image-20210812201848536
  • 全局搜索,发现只有函数本身和echoContent进行了调用,跟踪echoContent
image-20210812202240937
  • 这个函数在四个文件进行了调用,挨个文件进行分析
image-20210812202613714
  • 发现只有md5.php文件中的$data[page]通过变量覆盖,可以控制
image-20210812203104607
  • 利用md5.php文件构造payload进行动调
image-20210812203445057
image-20210812203914346
image-20210812204131689
image-20210812204805996
image-20210812204957450

总结

  1. 第一次做代码审计,审计的cms也比较小,总体来说很累也很有成就感
  2. 代码审计一定要对危险函数敏感,看到这个函数就知道要怎么利用,基础不牢地动山摇
  3. 后续要完善Seay的正则,现在用着非常不流畅
  4. 代码审计一定要有思路,跟踪好危险函数才行。

参考

https://rj45mp.github.io/php-chinaz
https://ca01h.top/code_audit/PHP/2.PHP%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E5%A4%8D%E7%8E%B0%E2%80%94%E2%80%94chinaz/