PHP代码审计之taocms(一)

简介: PHP代码审计之taocms

环境搭建:

d63b76643ebf21bb7b9ffabd2f5f7cfc_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

e1352b730d352aa21c605b52a05fdab3_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

5213ee85b6abc9e83c2256408d70a59f_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

首先我们来分析该系统的路由信息,以及如何进行参数的构造。

路由分析:

该系统有两个路由,一是前台功能点路由,二后台功能点路由,但两个路由代码类似只不过后台路由添

加了session校验,我们先来看看前台路由是怎么构造的。

前台路由放在api.php文件中。

75ab287628460501a288ab09477f4c3a_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

在common.php中22行代码处中调用__autoload() 魔术方法来加载 Model 文件夹下的功能代码,方便后续路由的调用。在代码30行去除 get_magic_quotes_gpc() 方法对特殊字符加载的反斜杠,这可能是为了代码的兼容性。

7ea369a258449facb9a4e8c4154fee00_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

016867e775582123d64886430c309f1c_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

代码5、6两行传入两个参数 ctrl 、 action ,第7行代码其实就是将 action 传过来的参数首字母转换为

大写,因为类名首字母都是大写的,第8行判断该类是否为 Api 或 Comment。

f059f68d546f7e4ab69246adf057b079_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

后台路由代码 admin.php 文件与前台路由代码基本类似,只是在上面添加了session校验,检测是否为

登录状态。

0476a3bfdabf948788ac4d21d3635098_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

漏洞审计

1.任意文件读取/下载

通过上面的路由信息我们知道功能点文件存放在Model文件夹下,我们去翻找Model文件夹发现

file.php 文件也就是File这个类下存在一个 download() 方法。

在这个类中的第85行代码处,我们一目了然的看到了 file_get_contents() 函数,看到这个函数想要

利用,我们会下意思的想到两个点:第一该函数的参数是否可控;第二该函数是没有回显的,如果想要

利用是需要使用 echo 等函数配合。我们只需要查看这里file_get_contents() 中参数是否可控就可以了。

7b7faee92e414d2dc73739da321e4156_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

漏洞复现:

由于在上面我们已经分析过路由的构造,所以我们可以不用特意去找功能点就能构造出利用路由。

在路由中 action 传入的是我们要实例化的类名 file , ctrl 则对应我们需要调用的方法 download 。

9cfe3243fc918535047e4c32c272b068_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

2.任意文件上传


首先我们去创建一个.php后缀的文件

0de2fe5c0e794ed201848e085dc2758a_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

通过这里我们发现 executeupload() 方法中调用了Upload类下的 upload() 方法,这里的上传主要调

用了upload()方法,我们主要去看下他是如何进行过滤的。

caf41c5066216584acf0e07c11bcf61c_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

在该方法的最上面定义了 $upext 变量,这里包含了可以上传的后缀名,也就是白名单,大致看了这些后缀没有可利用的。然后下面通过 $_FILES 接收上传文件,

通过 pathinfo() 获取上传文件名。

d035068d515c2fd8235732a30d0a9f00_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

关键在于代码106行通过 [extension] 获取后缀名,然后到代码107行进行正则匹配如果上传的文件名

不在 $upext 白名单中,则返回下面的提示信息。

e8fdc07b47c14169a4c02daa44458df9_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

a37902133dfc47a1287d2754b2d26f51_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

这里上传是走不通的,但是在上传的右边有一个创建文件的功能点,我们发现这里竟然没有限制可以上传任意文件

67c050c444d2624935e9b2c6bcd4975d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

在 create() 方法中,首先接收文件名 name ,然后通过 isdir 来判断创建的是目录还是文件,然后分别做不同的操作进行创建。

80661379ae647d1d147015922d2fef5c_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

然后我们在看看他是如何进行文件写入的,其实下面的功能点就可以直接写入文件内容

cb8da2809cb5389e3d914141a8f64e7f_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

其实这里写入内容的代码也在 File 这个类中,在 save() 方法中只是对该文件是否具有写入权限进行判断,就直接将内容写入到文件中。

bfc675bf215012d16402eb7fa4b4a349_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

漏洞复现:

21d19af3a3f1dc2b0c67aa18426e10b5_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

11b2b2b24f40bbf48fb795f1a25ec976_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

3.mysql日志文件getshell

5c80968ab2f4324077413856bf43da72_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

在Sql类下的 excute() 方法,依旧的简洁明了。通过14行传入 $sqltext 参数也就是我们的SQL语句,

在15行实例化 Dbclass 类调用其中 query() 方法直接执行SQL语句。最后18行将我们SQL语句结果进行

打印输出。

8a80a5d0d0e661a1e24b6bed088f1f09_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

漏洞复现:

MySQL日志文件getshell

Mysql 5.6.34版本以后无法通过into outfile、into dumpfile进行文件写入

我们通过日志文件写shell即可

set global general_log = on;

set global general_log_file = '网站绝对路径';

fa58afbd4f93eca0ee1844079d887558_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

e8e81c6eca8fdaeca15031416bb9c211_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

4.通过修改配置文件getshell

在后台有这么一个 网站设置 功能点,大致一看这里的内容和config.php文件中内容是一致的

首先我们去查看代码该功能点代码,这里调用 upload() 方法,第53行直接判断config.php是否可写,

然后通过POST接收参数,但是这里参数值会被57行代码处的 safeword() 方法进行过滤,跟进该方法看

看是如何对输入内容进行过滤的。

af96a101bf859a016aeb8681473ba0c5_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

在 safeword() 方法中需要传入两个参数,一个是需要过滤的字符串,另一个参数则决定走哪个case。

上面没有给出第二个参数则直接走默认level,也就是154行下面的代码,在155行判断了数据库类型是否

为 Sqlite 是的话执行Sqlite的过滤代码,如果不是则走158行的else,,调用 Base::_addslashs() 方法,跟进该方法。

6863588ff4372c968a791ee4592e9dd8_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

f673f3c239ef645fc7771d5556003832_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

将传入的字符通过 addslashes() 函数将特殊字符添加反斜杠,无法绕过限制。

08e3167cf822c90b9bef09c17eb78d2f_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

所以我们只能走上面的if条件,这里只要数据库为 Sqlite ,下面的单引号会被替换为两个单引号(当时以

为将单引号替换为双引号了),而这个替换方式是可以被绕过的。

7e76f647a5eb9ec938a70fc329148219_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

然后我们返回 upload() 方法的第60行,直接将过滤后的内容通过 file_put_contents() 写入到config.php中。

07e5472737bfd7beb33205a9cfd96b26_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


通过分析源码,我们知道输入的单引号会替换为两个双引号,如果我们输入 \' 这样在替换为两个双引号的时候第一个双引号前会有一个反斜杠,那么我们就可以闭合前面的双引号,我们的payload可以构造为:

\');@eval($_REQUEST[1]);/*

453b4984f8206caa5345a7226c90ae76_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png6f06080d02a80d519a67a2ff94369d28_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

0f4ded02ccc7a38b6c7c3fb6721ec632_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


相关文章
|
11月前
|
安全 小程序 PHP
PHP代码审计(七)Rips源代码审计系统使用教程
上一篇中提到的Seay源代码审计系统是由C#编写的winform程序,现在已经停止更新了,但是,还是比较好用的。 PHP代码审计还有另一个工具,也是一个神器Rips
296 0
|
3月前
|
JSON PHP Apache
[GFCTF 2021]Baby_Web(CVE-2021-41773) 从一道题入门PHP代码审计 (保姆级)
[GFCTF 2021]Baby_Web(CVE-2021-41773) 从一道题入门PHP代码审计 (保姆级)
30 1
|
3月前
|
SQL 安全 API
PHP代码审计示例(一)——淡然点图标系统SQL注入漏洞审计
PHP代码审计示例(一)——淡然点图标系统SQL注入漏洞审计
87 4
|
3月前
|
前端开发 网络安全 PHP
PHP代码审计之MVC与ThinkPHP简介
PHP代码审计之MVC与ThinkPHP简介
38 2
|
3月前
|
监控 安全 网络安全
PHP代码审计之简单思路方法
PHP代码审计之简单思路方法
138 1
|
5月前
|
SQL 监控 安全
代码审计-PHP原生开发篇&SQL注入&数据库监控&正则搜索&文件定位&静态分析
代码审计-PHP原生开发篇&SQL注入&数据库监控&正则搜索&文件定位&静态分析
|
6月前
|
PHP
【攻防世界】easyphp(PHP代码审计)
【攻防世界】easyphp(PHP代码审计)
|
11月前
|
XML 开发框架 .NET
代码审计之PHP基础铺垫
代码审计之PHP基础铺垫
88 0
|
11月前
|
安全 小程序 PHP
PHP代码审计(五)PHP代码审计方法与步骤
(1):获取到网站源码(这就是废话……) (2):将网站部署到你自己的环境中,能运行。 这里特殊说明一下我的习惯,我本地的环境与线上的环境基本上保持一致,这样在本地审计及线上部署的时候能发现更多的问题。不建议说是随便搭个环境能跑起来就行,这样不是很严谨。 (3):拿到源码之后,浏览大概的项目结构。
121 0
|
11月前
|
Shell PHP Windows
PHP代码审计(四)PHP文件操作函数(2)
改变文件所有者。如果成功则返回 TRUE,如果失败则返回 FALSE。 语法:chown(file,owner)
73 0