[漏洞复现]thinkphp5代码执行漏洞

个人博客地址

http://www.darkerbox.com

欢迎大家学习交流

参考网址:

https://paper.seebug.org/760/

漏洞代码

https://github.com/vulnspy/thinkphp-5.1.29

漏洞概述

程序未对控制器进行过滤,导致攻击者可以通过引入命名空间\符号来调用任意类的任意方法。

漏洞影响版本:
ThinkPHP 5.0.5-5.0.22
ThinkPHP 5.1.0-5.1.30

最好先看上面的参考网址再回来看下面的漏洞利用。因为下面直接分析的exp的数据流走向

漏洞利用

搭建好后,先用exp打一下,看看是否成功。

http://127.0.0.1/tp/public/index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

[漏洞复现]thinkphp5代码执行漏洞_第1张图片
利用成功后即可分析其原理了。

程序入口点

先主要分析一下我们传入的参数是怎么到程序里的。

public目录下的Index.php是程序的入口文件,文件里调用了App.php中的run方法

[漏洞复现]thinkphp5代码执行漏洞_第2张图片

这里的App.php是指thinkphp\libray\think目录下的App.php。

找到run方法,这里其实就是整个程序的入口了。
[漏洞复现]thinkphp5代码执行漏洞_第3张图片

在这个文件下的第403行。路由检测,
因为我加了几行注释。实际行数应该在403行附近。

[漏洞复现]thinkphp5代码执行漏洞_第4张图片

跟进$dispatch = $this->routeCheck()->init();中的routeCheck函数,在App.php的600行获取应用调度信息。

[漏洞复现]thinkphp5代码执行漏洞_第5张图片

继续跟进$path = $this->request->path();的path方法,跳转到Request.php的716行的path方法。
该方法中调用了pathinfo方法
[漏洞复现]thinkphp5代码执行漏洞_第6张图片

继续跟进pathinfo方法。同样在request.php的680行,找到了pathinfo方法,。

[漏洞复现]thinkphp5代码执行漏洞_第7张图片
$this->config[‘var_pathinfo’]的值就是config目录下的app.php文件中var_pathinfo的值,[漏洞复现]thinkphp5代码执行漏洞_第8张图片

那么拼接起来就是$_GET[‘s’]。通过$pathinfo = $_GET[$this->config['var_pathinfo']];将exp中的s参数的值传给了$pathinfo变量。那么此时$pathinfo的值是/index/\think\app/invokefunction

pathinfo方法最后返回的时候,值还是/index/\think\app/invokefunction
[漏洞复现]thinkphp5代码执行漏洞_第9张图片

这就成功获取s参数的值。之后function参数的值之后会说。

获取s参数后

获取参数之后,也就是$path = $this->request->path();执行完了

[漏洞复现]thinkphp5代码执行漏洞_第10张图片
注意上图603行的是否强制路由模式。config目录下的App.php。默认为false

[漏洞复现]thinkphp5代码执行漏洞_第11张图片

之后在606行进入check方法,传入了两个参数。KaTeX parse error: Undefined control sequence: \think at position 22: …我们s参数的值`/index/\̲t̲h̲i̲n̲k̲\app/invokefunc…must上面说过了是false。

[漏洞复现]thinkphp5代码执行漏洞_第12张图片
跟进check方法。
[漏洞复现]thinkphp5代码执行漏洞_第13张图片
在check方法中,如果$must为true,则会抛出异常。在上面说过了$must是false。默认是fasle。

[漏洞复现]thinkphp5代码执行漏洞_第14张图片

函数最后实例化UrlDispatch对象。UrlDispatch继承了Dispathch。

[漏洞复现]thinkphp5代码执行漏洞_第15张图片
跟进之后就到了Dispathch的构造函数。$dispatch是可控的。

[漏洞复现]thinkphp5代码执行漏洞_第16张图片

构造函数执行完,接着返回到App.php的403换行,此时routecheck函数的返回值就是一个urldispatch对象。接着又调用了init方法。

[漏洞复现]thinkphp5代码执行漏洞_第17张图片

跟进init方法。此时$this->dispatch的值为index|\think\app|invokefunction(之前做过字符串替换)。也就是模块|控制器|操作。那么此时模块:index,控制器:think\app,操作:invokefunction。

跟进parseUrl。

[漏洞复现]thinkphp5代码执行漏洞_第18张图片
在parseurl中调用了parseUrlPath方法,此时$url的值index|\think\app|invokefunction

[漏洞复现]thinkphp5代码执行漏洞_第19张图片

这个parseUrlPath方法可以跟进去看看。
[漏洞复现]thinkphp5代码执行漏洞_第20张图片

这个方法主要是分开index|\think\app|invokefunction为一个数组。放到$path变量中,此时$path的值如下图。很明显了。0就是模块,1是控制器,2是操作。
在这里插入图片描述
之后返回$path和$var。$var为空。
[漏洞复现]thinkphp5代码执行漏洞_第21张图片

返回之后,又回到了parseurl函数,然后再执行到返回。回到了Init函数。

[漏洞复现]thinkphp5代码执行漏洞_第22张图片
此时$result的值如下图
[漏洞复现]thinkphp5代码执行漏洞_第23张图片

此时执行到了return (new Module($this->request, $this->rule, $result))->init();。开始new一个Model对象调用Init方法。

这才是重点

在Init方法中的70行,获取控制器名字。

这是一个关键点,这里没有做过滤。漏洞就出现在这个地方。没有对控制器做过滤导致可以执行任意类的任意方法。
[漏洞复现]thinkphp5代码执行漏洞_第24张图片

init方法执行完之后,会返回当前对象
[漏洞复现]thinkphp5代码执行漏洞_第25张图片

此时,返回到了App.php。终于把$dispatch = $this->routeCheck()->init();执行完毕了。

在App.php的436行调用了dispatch方法,
[漏洞复现]thinkphp5代码执行漏洞_第26张图片

跟进。

[漏洞复现]thinkphp5代码执行漏洞_第27张图片

一直跟进,。会在dispatch.php中的168行调用exec方法。

[漏洞复现]thinkphp5代码执行漏洞_第28张图片

又是一个重点

进入该方法。该方法里实例化控制器

[漏洞复现]thinkphp5代码执行漏洞_第29张图片

进入contrloller方法,$name就是控制器名think\app

[漏洞复现]thinkphp5代码执行漏洞_第30张图片
跟进parseModuleAndClass方法。又是一个重点方法。

如下图。该方法判断KaTeX parse error: Can't use function '\`' in math mode at position 9: name是否有`\̲`̲。如果有的话。直接将值赋给class,然后返回。众所周知,$name的值是\think\app,有\,所以直接返回。
[漏洞复现]thinkphp5代码执行漏洞_第31张图片
返回之后回到controller方法。

判断$class类是否存在,此时$class的值是\think\app。这个类是存在的。就是App.php。
[漏洞复现]thinkphp5代码执行漏洞_第32张图片

如果存在,则return $this->__get($class)。这应该就是具体的实例化操作了。里面用了反射。虽然我不是很懂。
之后一直返回,回到了model.php。此时$instace已经实例化完毕了。

[漏洞复现]thinkphp5代码执行漏洞_第33张图片

到了105行,105行之后就是在获取操作名invokefunction。并且在112行new了一个反射方法对象。

[漏洞复现]thinkphp5代码执行漏洞_第34张图片

此时$reflect的值如下图。可以发现原来我们的think\app变成了think\Container

这是由于app继承了Contatiner。invokefunciton方法是属于Containter的。
在这里插入图片描述
118行之后,会获取function参数值和vars参数值。
[漏洞复现]thinkphp5代码执行漏洞_第35张图片
在136行执行了方法。
[漏洞复现]thinkphp5代码执行漏洞_第36张图片
此时参数值如下图。$instance一直是很多实例的数组。

在这里插入图片描述
在这里插入图片描述

进入这个invokeReflectMethod方法。。绑定参数,

[漏洞复现]thinkphp5代码执行漏洞_第37张图片

再进入invokArgs方法。

直接跳转到invokeFunction方法内部。这个invokdefunction方法对应着exphttp://127.0.0.1/tp/public/index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1中的invokdefunction。exp中的function参数值就是方法中的第一个参数值。vars参数是放到第二个参数值。

[漏洞复现]thinkphp5代码执行漏洞_第38张图片

然后调用了call_user_func_array。此时参数值如下图
[漏洞复现]thinkphp5代码执行漏洞_第39张图片

实际就是执行了

call_user_func_array('call_user_func_array',['phpinfo',['1']]);

执行后就是一直的返回,然后输出结果到页面。

如有不正确的地方请指出

你可能感兴趣的:(漏洞复现)