Overview
Machine author: rastating. Focus: privilege escalation (misconfiguration / service abuse, and a ret2libc BOF).
| |
Skills: API enumeration, credential reuse, service abuse through a database, SUID binary analysis (ltrace/Ghidra), ret2libc, NX + ASLR bypass, blacklist filter bypass.
Reconnaissance
| |
-sC can misreport port 3000 as Hadoop. When a result looks odd, verify with another
scan (-sV); do not trust one tool blindly.
The Express application (port 3000):
- The AngularJS client code in
/assets/js/app/controllers/*reveals backend endpoints/api/.... - Key endpoints:
/api/users/latest: returns users together with password hashes (SHA-256, unsalted)/api/users/<username>: a single user’s data/api/users/(empty username): returns everyone, including the hidden adminmyP14ceAdm1nAcc0uNTwithis_admin: true
An API that exposes hashes is broken access control / excessive data exposure. Always test
path variants: /api/users/, /api/users/<x>, /api/users/latest.
Cracking: unsalted SHA-256, so check online (CrackStation) first, then locally.
| |
Initial Access
Log into the web admin account; the file
myplace.backupbecomes available.It is one long base64 string:
1 2cat myplace.backup | base64 -d > myplace.backup.zip file myplace.backup.zip # Zip archive dataThe zip is password protected:
1 2zip2john myplace.backup.zip > zip.hash john zip.hash --wordlist=rockyou.txt --format=PKZIP # -> magicwordThe extracted source (
var/www/myplace/app.js) contains a Mongo connection string:1mongodb://mark:5AYRft73VtFpc84k@localhost:27017/myplace?...Credential reuse: the same password works over SSH:
1sshpass -p '5AYRft73VtFpc84k' ssh [email protected]
Passwords from connection strings and configuration files should always be tested over SSH and other services.
Lateral Movement
mark to tom, service abuse via MongoDB
The pattern: a service running as another user, fed data you control.
| |
Two processes run as tom:
| |
/var/scheduler/app.js:
| |
Every 30 seconds it reads the tasks collection and runs exec(doc.cmd) as tom, then
deletes the task.
The Mongo password for mark is reused on the scheduler database:
| |
Reverse shell:
| |
On the listener (nc -lnvp 443) you get a shell as tom within 30 seconds.
Stabilisation:
| |
Service-abuse checklist:
ps auxww: processes of other users, especially root and target users- scripts/services that read data from a database, file or queue and execute it
- do you have access to that data source? (credential reuse)
- cron jobs (
/etc/crontab,/etc/cron.*,crontab -l) - writable files/scripts run by privileged processes
Privilege Escalation
tom to root, SUID + ret2libc BOF
| |
A GID above 1000 is a non-system group added by an admin, so it is suspicious. Find its files:
| |
SUID root means the binary runs with root privileges regardless of who runs it. Force it to execute your code and the code runs as root.
What backup does (dynamic analysis with ltrace). Called as
backup <flag> <token> <path>:
geteuid()thensetuid(...).Compares arg 1 with
-q(quiet mode: print base64 only).Reads tokens from
/etc/myplace/keysand compares with arg 2. A valid token is any from the file, or the empty string""(there is a blank line).Blacklist filter on arg 3 (the path): rejects
..,/root,;,&, backtick,$,|,//,/etc.Builds a command and runs it via
system():1 2/usr/bin/zip -r -P magicword <tmpfile> <path> > /dev/null /usr/bin/base64 -w0 <tmpfile>
Correct use:
| |
Finding the vulnerability, an unsafe strcpy
In ltrace (without -q):
| |
| |
The vulnerable strcpy only occurs without -q. Do not use -q for exploitation.
Protections, checksec backup
| |
Plus cat /proc/sys/kernel/randomize_va_space = 2, so ASLR is enabled (libc is
randomized).
| Protection | Effect | Response |
|---|---|---|
| No canary | No buffer guard | Overwrite the return address directly |
| NX enabled | Cannot execute stack code | ret2libc (reuse libc code) |
| No PIE | Binary at a fixed address | Less relevant, we target libc anyway |
| ASLR (libc) | libc randomized | Brute force (low entropy on 32-bit) |
What ret2libc is
NX blocks executing your code on the stack, so call code that is already in memory and
executable: the system() function from libc, with the argument "/bin/sh".
On x86 32-bit (cdecl), arguments are passed on the stack, and ret pops an address and
jumps there. Build a fake stack frame as if someone had normally called system("/bin/sh").
| |
When ret jumps to system, it assumes a normal call: a return address just above it
(we give exit, to exit cleanly), and above that the first argument (a pointer to
"/bin/sh").
Finding the offset to EIP
| |
libc addresses (offsets from base)
| |
Runtime address = libc base + offset.
Bypassing ASLR, why brute force is enough
| |
- Always prefix
0xf7 - Always suffix
000(page alignment, 0x1000) - Only the middle digits vary, in a narrow range (~0x540-0x614)
Effective entropy is roughly 9 bits (~512 possibilities). Each failed attempt takes a fraction of a second, so guess a fixed base address and loop:
- one attempt succeeds roughly 0.1-0.2% of the time
- ~500 attempts is ~63%+ chance of success
Payload script (Python 3) plus brute force
| |
| |
ret2libc micro-checklist
checksec: confirm no canary, NX on, 32-bit.- Find a vulnerable
strcpy/gets/sprintf(ltrace/Ghidra). - Offset to EIP:
pattern_create-> crash ->pattern_offset. - Addresses:
system,exit,"/bin/sh"(readelf + strings), added to the libc base. - Payload:
padding + &system + &exit + &binsh. - If ASLR: assess entropy; on 32-bit, brute force in a loop.
Unintended paths to root (a lesson about blacklist filters)
The author filtered /root, .., ;, &, backtick, $, |. A blacklist almost
always leaks.
The ~ environment variable:
| |
Wildcards defeat the literal /root:
| |
Command injection via newline (no ;/|, but a newline in system() acts as a new
command):
| |
When you see a blacklist filter before system()/exec(), immediately test wildcards
(* ? [ ]), ~, $HOME and other env vars, newline (\n), quotes and encodings.
Predicting the temp-file name does not work: /tmp/.backup_<rand> comes from
srand(mix(pid, time, clock)). pid and time are guessable, but clock() (process
CPU time) is not, so a symlink/race attack is out.
Detection and Mitigation
- Do not expose password hashes through an API; enforce authorization on every path variant.
- Do not reuse database passwords for system accounts.
- Do not let a service execute unvalidated data from a database.
- SUID binaries: use safe string functions, pass arguments as a list to
exec(no shell), and validate paths with an allow-list regex rather than a blacklist. - Stack protections: enable canaries, PIE and full RELRO.
Lessons Learned
- NX on means ret2libc; reuse
system()from libc. - cdecl (32-bit): arguments on the stack; layout
padding + &system + &exit + &"/bin/sh". - No canary means you can overwrite the return address freely.
- ASLR on 32-bit has low entropy (~9 bits), so brute force in a loop.
- Do not use
-qfor thebackupBOF (no vulnerablestrcpywith it). - Blacklists leak:
~,$HOME, wildcards? *, newline, quotes. - GID > 1000 is a custom group;
find / -group <name>finds its files.
Command Reference
| |