Hi y’all,
I REd PackClient and identified a TCP stream robustness issue in the existing ET PackClient Launcher signatures.
The current PLH1 / PLC1 / PLA1 / PLK1 detections (ET SIDs 2069878–2069882) rely on packet-boundary assumptions that hold for the observed capture layout, but equivalent PackClient traffic can be segmented or coalesced differently during normal TCP transport.
I reproduced this under Suricata 7.0.3 and prepared stream-aware equivalents that retain the existing PackClient protocol anchors while matching against the reassembled TCP stream.
I also included an optional PackClient Core startup correlation based on traffic recovered during my reverse engineering. It correlates:
SYS|Q|EXT|STARTUP|PROBE|
with:
SYS|R|EXT|STARTUP|OK|tags=
on the same established TCP flow before alerting.
I originally submitted this to support@emergingthreats.net on September 11, but I’m posting it here as well for visibility and community review.
Proposed rules
The SIDs in the rules below are temporary submission/validation identifiers only. For ET SIDs 2069878–2069882, the existing upstream SID/message/reference/metadata can be retained while replacing the packet-boundary-dependent predicates with the corresponding stream-aware logic.
# PackClient — ET Open stream robustness submission
#
# Mapping:
# 9900101 -> ET 2069878 (PLH1 length prefix)
# 9900102 -> ET 2069879 (PLH1 check-in body)
# 9900103 -> ET 2069880 (PLC1 C2 response)
# 9900104 -> ET 2069881 (PLA1 payload request)
# 9900105 -> ET 2069882 (PLK1 C2 response)
# 9900110/9900111/9900112 -> optional Core startup correlation
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"PackClient PLH1 length prefix stream"; flow:established,to_server,only_stream; content:"|24 00 40 5a|"; fast_pattern; reference:url,www.proofpoint.com/us/blog/threat-insight/carry-compromise-ta4922-packs-packclient; reference:url,ivanimmanuel-dev.github.io/PackClient/; classtype:trojan-activity; sid:9900101; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_09_11, deployment Perimeter, malware_family PackClient, confidence High, signature_severity Major, updated_at 2026_09_11;)
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"PackClient PLH1 stream"; flow:established,to_server,only_stream; content:"|24 00 40 5a 15 00 00 00|PLH1"; fast_pattern; reference:url,www.proofpoint.com/us/blog/threat-insight/carry-compromise-ta4922-packs-packclient; reference:url,ivanimmanuel-dev.github.io/PackClient/; classtype:trojan-activity; sid:9900102; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_09_11, deployment Perimeter, malware_family PackClient, confidence High, signature_severity Major, updated_at 2026_09_11;)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"PackClient PLC1 stream"; flow:established,to_client,only_stream; content:"|1c 00 40 5a 15 00 00 00|PLC1"; fast_pattern; reference:url,www.proofpoint.com/us/blog/threat-insight/carry-compromise-ta4922-packs-packclient; reference:url,ivanimmanuel-dev.github.io/PackClient/; classtype:trojan-activity; sid:9900103; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_09_11, deployment Perimeter, malware_family PackClient, confidence High, signature_severity Major, updated_at 2026_09_11;)
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"PackClient PLA1 stream"; flow:established,to_server,only_stream; content:"|2c 00 40 5a 15 00 00 00|PLA1"; fast_pattern; reference:url,www.proofpoint.com/us/blog/threat-insight/carry-compromise-ta4922-packs-packclient; reference:url,ivanimmanuel-dev.github.io/PackClient/; classtype:trojan-activity; sid:9900104; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_09_11, deployment Perimeter, malware_family PackClient, confidence High, signature_severity Major, updated_at 2026_09_11;)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"PackClient PLK1 stream"; flow:established,to_client,only_stream; content:"|3c 00 40 5a 15 00 00 00|PLK1"; fast_pattern; reference:url,www.proofpoint.com/us/blog/threat-insight/carry-compromise-ta4922-packs-packclient; reference:url,ivanimmanuel-dev.github.io/PackClient/; classtype:trojan-activity; sid:9900105; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_09_11, deployment Perimeter, malware_family PackClient, confidence High, signature_severity Major, updated_at 2026_09_11;)
# Optional Core startup correlation. Match each semantic message on reassembled
# stream data, then alert once both states are present on the same TCP flow.
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"PackClient Core startup probe state"; flow:established,to_client,only_stream; content:"SYS|7c|Q|7c|EXT|7c|STARTUP|7c|PROBE|7c|"; fast_pattern; flowbits:set,ET.packclient.core_startup_probe; flowbits:noalert; reference:url,ivanimmanuel-dev.github.io/PackClient/; classtype:trojan-activity; sid:9900110; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_09_11, deployment Perimeter, malware_family PackClient, confidence High, signature_severity Major, updated_at 2026_09_11;)
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"PackClient Core startup response state"; flow:established,to_server,only_stream; content:"SYS|7c|R|7c|EXT|7c|STARTUP|7c|OK|7c|tags="; fast_pattern; flowbits:set,ET.packclient.core_startup_response; flowbits:noalert; reference:url,ivanimmanuel-dev.github.io/PackClient/; classtype:trojan-activity; sid:9900111; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_09_11, deployment Perimeter, malware_family PackClient, confidence High, signature_severity Major, updated_at 2026_09_11;)
alert tcp $HOME_NET any <> $EXTERNAL_NET any (msg:"PackClient Core startup probe and response"; flow:established; flowbits:isset,ET.packclient.core_startup_probe; flowbits:isset,ET.packclient.core_startup_response; flowbits:unset,ET.packclient.core_startup_probe; flowbits:unset,ET.packclient.core_startup_response; reference:url,ivanimmanuel-dev.github.io/PackClient/; classtype:trojan-activity; sid:9900112; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_09_11, deployment Perimeter, malware_family PackClient, confidence High, signature_severity Major, updated_at 2026_09_11;)
Validation
The submitted rules parse successfully under Suricata 7.0.3.
Engine-level testing confirmed that:
- the observed four-byte split layout satisfies the existing packet-oriented predicates;
- equivalent PackClient frames delivered as coalesced TCP payloads can bypass those predicates;
- segmentation within the PackClient framing can also prevent packet-oriented matching;
- the stream-aware Launcher signatures continue to match after TCP reassembly;
- the Core startup alert requires both the probe and response on the same TCP flow;
- a response without the corresponding probe does not produce the correlated alert;
- the Core startup correlation survives segmentation in both directions.
References
PackClient reverse-engineering research:
Original Proofpoint PackClient research:
Happy to adjust the signatures or provide additional captures/validation if useful.
Thanks,
Ivan Immanuel Shaji