Last active
August 24, 2026 15:59
-
-
Save PeterGabaldon/e5b9a6c4ad4e3278481fb5cb02d67a8c to your computer and use it in GitHub Desktop.
YARA rules — bincrypter-packed gsocket / gs-netcat implants
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| YARA rules — bincrypter-packed gsocket / gs-netcat implants | |
| ------------------------------------------------------------------ | |
| Derived from analysis of a live intrusion sample ("netd.sh"). | |
| Chain: bincrypter shell dropper | |
| -> AES+gzip -> UPX-packed ELF (UPX! magics zeroed) | |
| -> gs-netcat 1.4.42beta5 (--enable-stealth) | |
| + 664-byte operator config appended to the packed file | |
| Tooling is public (The Hacker's Choice): the rules below therefore flag a | |
| dual-use toolkit. Treat HKTL_* hits as "hacktool present" and MAL_* / | |
| SUSP_* hits as intrusion indicators. | |
| License: MIT | |
| */ | |
| import "math" | |
| rule SUSP_Bincrypter_Shell_Dropper | |
| { | |
| meta: | |
| description = "bincrypter-obfuscated shell dropper (THC bincrypter)" | |
| reference = "https://github.com/hackerschoice/bincrypter" | |
| author = "Peter Gabaldon" | |
| date = "2026-08-23" | |
| severity = "high" | |
| strings: | |
| /* _bc_obbell(): empty command substitutions spliced inside keywords | |
| so eval/echo/perl/openssl never appear as literal tokens. */ | |
| $ob_bs = { 60 21 20 3A 26 26 08 23 60 } // `! :&&<BS>#` | |
| $ob_bel = { 60 3A 7C 7C 07 60 } // `:||<BEL>` | |
| /* fragments of the loader that survive the splicing */ | |
| $lang = "LANG=C pe" | |
| $print = ":pr" | |
| condition: | |
| uint16(0) == 0x2123 // "#!" | |
| and filesize > 3KB | |
| and ( #ob_bs + #ob_bel ) > 10 | |
| and $lang | |
| and $print | |
| } | |
| rule SUSP_Memfd_Perl_Loader | |
| { | |
| meta: | |
| description = "Fileless memfd_create + argv[0]-spoofing Perl loader" | |
| reference = "bincrypter hook / gsocket deploy.sh _config2bin_memexec()" | |
| author = "Peter Gabaldon" | |
| date = "2026-08-23" | |
| severity = "high" | |
| note = "Matches the decrypted stage-2 stub, gsocket's deploy.sh, and process memory" | |
| strings: | |
| $fd = "$^F=255" | |
| $sysc = "for(319,279,385,4314,4354)" | |
| $memfd = "syscall$_,$\"," | |
| $procfd = "/proc/$$/fd/" | |
| $exec = "exec{\"/proc/$$/fd/" | |
| condition: | |
| filesize < 10MB | |
| and $sysc | |
| and 2 of ($fd, $memfd, $procfd, $exec) | |
| } | |
| rule SUSP_Bincrypter_Decrypt_Hook | |
| { | |
| meta: | |
| description = "bincrypter decryption hook (decoded stage 2, or unobfuscated build)" | |
| reference = "https://github.com/hackerschoice/bincrypter" | |
| author = "Peter Gabaldon" | |
| date = "2026-08-23" | |
| severity = "medium" | |
| strings: | |
| $esc = "s/B3/\\n/g" // LF <- B3 | |
| $esc2 = "s/B1/" // NUL <- B1 | |
| $esc3 = "s/B2/B/g" // B <- B2 | |
| $enc = "s/B/B2/g" // packer side | |
| $aes = "openssl enc -d -aes-256-cbc -md sha256 -nosalt -k" | |
| $fn = "_bc_dec" | |
| $err = "BC_FN=FileName source FileName" | |
| condition: | |
| filesize < 10MB | |
| and ( ( $esc and $esc2 and $esc3 ) or $enc ) | |
| and 1 of ($aes, $fn, $err) | |
| } | |
| rule HKTL_UPX_Packed_Magic_Zeroed | |
| { | |
| meta: | |
| description = "UPX-packed ELF64 with the UPX! magics zeroed so upx -d refuses it" | |
| author = "Peter Gabaldon" | |
| date = "2026-08-23" | |
| severity = "high" | |
| note = "Observed in THC gsocket beta-channel binaries; also a generic anti-unpacking trick" | |
| /* l_info sits immediately after the program header table: | |
| l_info = e_phoff + e_phentsize * e_phnum | |
| (e_phoff is read as uint32: YARA has no uint64, and the high word | |
| of e_phoff is zero for every realistic ELF) | |
| layout: +0 u32 l_checksum | |
| +4 u32 l_magic <- "UPX!", zeroed here | |
| +8 u16 l_lsize | |
| +10 u8 l_version | |
| +11 u8 l_format | |
| +12 p_info { u32 progid; u32 filesize; u32 blocksize } | |
| +24 b_info { u32 sz_unc; u32 sz_cpr; u8 method; u8 ftid; ... } */ | |
| strings: | |
| $upx = "UPX!" | |
| condition: | |
| uint32(0) == 0x464c457f // \x7fELF | |
| and uint8(4) == 2 // ELFCLASS64 | |
| and filesize > 4KB and filesize < 200MB | |
| // l_magic slot is zeroed ... | |
| and uint32(uint32(0x20) + uint16(0x36) * uint16(0x38) + 4) == 0 | |
| // ... but the surrounding UPX structure is intact | |
| and uint32(uint32(0x20) + uint16(0x36) * uint16(0x38)) != 0 // l_checksum | |
| and uint32(uint32(0x20) + uint16(0x36) * uint16(0x38) + 12) == 0 // p_progid | |
| and uint32(uint32(0x20) + uint16(0x36) * uint16(0x38) + 16) > filesize // p_filesize | |
| and uint32(uint32(0x20) + uint16(0x36) * uint16(0x38) + 20) > 0 // p_blocksize | |
| // first b_info: a real compression method, no filter | |
| and ( uint8(uint32(0x20) + uint16(0x36) * uint16(0x38) + 32) == 2 or | |
| uint8(uint32(0x20) + uint16(0x36) * uint16(0x38) + 32) == 8 or | |
| uint8(uint32(0x20) + uint16(0x36) * uint16(0x38) + 32) == 14 ) | |
| and uint8(uint32(0x20) + uint16(0x36) * uint16(0x38) + 33) == 0 | |
| and uint32(uint32(0x20) + uint16(0x36) * uint16(0x38) + 24) > 0 // sz_unc | |
| and uint32(uint32(0x20) + uint16(0x36) * uint16(0x38) + 28) > 0 // sz_cpr | |
| // and the marker really is gone everywhere | |
| and not $upx | |
| and math.entropy(0, filesize) > 7.0 | |
| } | |
| rule INFO_UPX_Packed_Magic_Intact | |
| { | |
| meta: | |
| description = "Ordinary UPX-packed ELF64 (magic present) — informational baseline" | |
| author = "Peter Gabaldon" | |
| date = "2026-08-23" | |
| severity = "informational" | |
| strings: | |
| $upx = "UPX!" | |
| condition: | |
| uint32(0) == 0x464c457f and uint8(4) == 2 | |
| and uint32(uint32(0x20) + uint16(0x36) * uint16(0x38) + 4) == 0x21585055 // "UPX!" | |
| and $upx | |
| } | |
| rule HKTL_Gsocket_Config_Blob | |
| { | |
| meta: | |
| description = "gs-netcat embedded operator config (struct gsnc_config, 664 bytes, XOR 0xAB)" | |
| reference = "gsocket beta branch, tools/gsnc-utils.h GSNC_CONFIG_MAGIC_STR" | |
| author = "Peter Gabaldon" | |
| date = "2026-08-23" | |
| severity = "critical" | |
| note = "Present in the UPX-packed file. Carries relay host, port, secret and masquerade name." | |
| strings: | |
| /* "8xKd12TX" ^ 0x1F (field) ^ 0xAB (whole struct) == ^ 0xB4 */ | |
| $magic_on_disk = { 8C CC FF D0 85 86 E0 EC } | |
| /* the same magic as it appears in memory after the 0xAB pass */ | |
| $magic_in_mem = { 27 67 54 7B 2E 2D 4B 47 } | |
| condition: | |
| $magic_on_disk at (filesize - 8) | |
| or $magic_on_disk | |
| or $magic_in_mem | |
| } | |
| rule INFO_Gsocket_Config_Capable | |
| { | |
| meta: | |
| description = "gs-netcat build that supports embedded configs (magic constant in .text)" | |
| author = "Peter Gabaldon" | |
| date = "2026-08-23" | |
| severity = "informational" | |
| note = "Does NOT mean a config is present - see HKTL_Gsocket_Config_Blob for that" | |
| strings: | |
| $magic_plain = "8xKd12TX" | |
| condition: | |
| $magic_plain | |
| } | |
| rule HKTL_GsNetcat_Stealth_Build | |
| { | |
| meta: | |
| description = "gs-netcat compiled with --enable-stealth and embedded-config support" | |
| reference = "https://github.com/hackerschoice/gsocket (beta branch)" | |
| author = "Peter Gabaldon" | |
| date = "2026-08-23" | |
| severity = "high" | |
| note = "Match on the UNPACKED binary or on process memory" | |
| strings: | |
| $c1 = "GS_CONFIG_SECRET='%s'" | |
| $c2 = "GS_CONFIG_PROC_HIDDENNAME='%s'" | |
| $c3 = "GSOCKET_CONFIG_CHECK" | |
| $c4 = "GS_CONFIG_MEMEXEC=1" | |
| $c5 = "GSOCKET_PROC_HIDDENNAME" | |
| $s1 = "gs.thc.org" | |
| $s2 = "-stealth" | |
| $s3 = "_GS_REEXEC_DONE" | |
| $s4 = "_GS_SYSTEMD_RUN=1 exec systemd-run --quiet --scope --user" | |
| condition: | |
| uint32(0) == 0x464c457f | |
| and 3 of ($c*) | |
| and 1 of ($s*) | |
| } | |
| rule MAL_Gsocket_Proc_Masquerade | |
| { | |
| meta: | |
| description = "gsocket artifact carrying one of deploy.sh's process-masquerade names" | |
| reference = "gsocket deploy.sh proc_name_arr[] (beta branch, line 224)" | |
| author = "Peter Gabaldon" | |
| date = "2026-08-23" | |
| severity = "critical" | |
| note = "deploy.sh picks one of these 14 at RANDOM unless GS_NAME= overrides it. Kernel threads have an EMPTY /proc/<pid>/cmdline; a bracketed argv[0] with content is anomalous." | |
| strings: | |
| /* verbatim from proc_name_arr[] in deploy.sh */ | |
| $n01 = "[kthreadd]" ascii wide | |
| $n02 = "[kstrp]" ascii wide | |
| $n03 = "[watchdogd]" ascii wide | |
| $n04 = "[ksmd]" ascii wide | |
| $n05 = "[kswapd0]" ascii wide | |
| $n06 = "[card0-crtc8]" ascii wide | |
| $n07 = "[mm_percpu_wq]" ascii wide | |
| $n08 = "[rcu_preempt]" ascii wide | |
| $n09 = "[kworker]" ascii wide | |
| $n10 = "[raid5wq]" ascii wide | |
| $n11 = "[slub_flushwq]" ascii wide | |
| $n12 = "[netns]" ascii wide | |
| $n13 = "[kaluad]" ascii wide | |
| $n14 = "sshd:" ascii wide | |
| /* gsocket anchor - required, the names alone are far too generic */ | |
| $g1 = "GS_PROC_HIDDENNAME" | |
| $g2 = { 8C CC FF D0 85 86 E0 EC } | |
| $g3 = "8xKd12TX" | |
| $g4 = "GSOCKET_PROC_HIDDENNAME" | |
| condition: | |
| 1 of ($n*) and 1 of ($g*) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment