Skip to content

Instantly share code, notes, and snippets.

@PeterGabaldon
Last active August 24, 2026 15:59
Show Gist options
  • Select an option

  • Save PeterGabaldon/e5b9a6c4ad4e3278481fb5cb02d67a8c to your computer and use it in GitHub Desktop.

Select an option

Save PeterGabaldon/e5b9a6c4ad4e3278481fb5cb02d67a8c to your computer and use it in GitHub Desktop.
YARA rules — bincrypter-packed gsocket / gs-netcat implants
/*
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