From 73c5d361c84aa662e87b5907059f107101e44b69 Mon Sep 17 00:00:00 2001 From: Carl Flottmann Date: Mon, 17 Nov 2025 15:55:49 +1000 Subject: [PATCH] chore: reduce FPs in decode-and-execute by removing file write and network connection sinks Signed-off-by: Carl Flottmann --- .../pypi_malware_rules/obfuscation.yaml | 156 ----- .../obfuscation/decode_and_execute.py | 18 +- .../obfuscation/expected_results.json | 542 +++++++++--------- 3 files changed, 268 insertions(+), 448 deletions(-) diff --git a/src/macaron/resources/pypi_malware_rules/obfuscation.yaml b/src/macaron/resources/pypi_malware_rules/obfuscation.yaml index debb8c2e3..81b2f08f8 100644 --- a/src/macaron/resources/pypi_malware_rules/obfuscation.yaml +++ b/src/macaron/resources/pypi_malware_rules/obfuscation.yaml @@ -111,135 +111,6 @@ rules: pattern-sinks: - pattern-either: - # remote connection - # using socket module - - patterns: - - pattern-either: - - patterns: - - pattern-either: - - pattern-inside: | - $SOC = socket.socket(...) - ... - - pattern-inside: | - with socket.socket(...) as $SOC: - ... - - pattern-either: - - pattern-inside: | - $SOC.connect(...) - ... - - pattern-inside: | - $SOC.connect_ex(...) - ... - - pattern-inside: | - $SOC.bind(...) - ... - # socket.socket and socket.connect in one call - - pattern-inside: | - $SOC = socket.create_connection(...) - ... - - pattern-inside: | - with socket.create_connection(...) as $SOC: - ... - # socket.socket and socket.bind in one call - - pattern-inside: | - $SOC = socket.create_server(...) - ... - - pattern-inside: | - with socket.create_server(...) as $SOC: - ... - - pattern-either: - # Assume that .accept, .listen was called somewhere if needed - - pattern: $SOC.send(...) - - pattern: $SOC.recv(...) - - pattern: $SOC.recvfrom(...) - - pattern: $SOC.recvmsg(...) - - pattern: $SOC.recvmsg_into(...) - - pattern: $SOC.recvfrom_into(...) - - pattern: $SOC.recv_into(...) - - pattern: $SOC.sendall(...) - - pattern: $SOC.sendto(...) - - pattern: $SOC.sendmsg(...) - - pattern: $SOC.sendmsg_afalg(...) - - pattern: $SOC.sendfile(...) - # using requests module - - pattern: requests.get(...) - - pattern: requests.post(...) - - pattern: requests.put(...) - - pattern: requests.delete(...) - - pattern: requests.head(...) - - pattern: requests.options(...) - - pattern: requests.patch(...) - - pattern: requests.Session(...).get(...) - - pattern: requests.Session(...).delete(...) - - pattern: requests.Session(...).head(...) - - pattern: requests.Session(...).options(...) - - pattern: requests.Session(...).patch(...) - - pattern: requests.Session(...).post(...) - - pattern: requests.Session(...).put(...) - - pattern: requests.Session(...).request(...) - - pattern: requests.Session(...).send(...) - - pattern: requests.Request(...) - # using urllib3 module - - pattern: urllib3.request(...) - # object creation here is included as decoded values may be passed as parameters - - pattern: urllib3.PoolManager(...) - - pattern: urllib3.PoolManager(...).request(...) - - pattern: urllib3.PoolManager(...).request_encode_body(...) - - pattern: urllib3.PoolManager(...).request_encode_url(...) - - pattern: urllib3.PoolManager(...).urlopen(...) - - pattern: urllib3.HTTPConnectionPool(...) - - pattern: urllib3.HTTPConnectionPool(...).urlopen(...) - - pattern: urllib3.HTTPConnectionPool(...).request(...) - - pattern: urllib3.HTTPConnectionPool(...).request_encode_body(...) - - pattern: urllib3.HTTPConnectionPool(...).request_encode_url(...) - - pattern: urllib3.HTTPSConnectionPool(...) - - pattern: urllib3.HTTPSConnectionPool(...).urlopen(...) - - pattern: urllib3.HTTPSConnectionPool(...).request(...) - - pattern: urllib3.HTTPSConnectionPool(...).request_encode_body(...) - - pattern: urllib3.HTTPSConnectionPool(...).request_encode_url(...) - - pattern: urllib3.HTTPConnection(...) - - pattern: urllib3.HTTPConnection(...).request(...) - - pattern: urllib3.HTTPConnection(...).request_chunked(...) - - pattern: urllib3.HTTPSConnection(...) - - pattern: urllib3.HTTPSConnection(...).request(...) - - pattern: urllib3.HTTPSConnection(...).request_chunked(...) - - pattern: urllib3.ProxyManager(...).urlopen(...) - # using urllib - - pattern: urllib.request(...) - - pattern: urllib.request.urlopen(...) - # using httpx - - pattern: httpx.request(...) - - pattern: httpx.get(...) - - pattern: httpx.post(...) - - pattern: httpx.put(...) - - pattern: httpx.delete(...) - - pattern: httpx.head(...) - - pattern: httpx.options(...) - - pattern: httpx.stream(...) - - pattern: httpx.patch(...) - - pattern: httpx.AsyncClient(...) - - pattern: httpx.AsyncClient(...).request(...) - - pattern: httpx.AsyncClient(...).get(...) - - pattern: httpx.AsyncClient(...).post(...) - - pattern: httpx.AsyncClient(...).put(...) - - pattern: httpx.AsyncClient(...).delete(...) - - pattern: httpx.AsyncClient(...).head(...) - - pattern: httpx.AsyncClient(...).options(...) - - pattern: httpx.AsyncClient(...).stream(...) - - pattern: httpx.AsyncClient(...).patch(...) - - pattern: httpx.AsyncClient(...).send(...) - - pattern: httpx.Client(...) - - pattern: httpx.Client(...).request(...) - - pattern: httpx.Client(...).get(...) - - pattern: httpx.Client(...).post(...) - - pattern: httpx.Client(...).put(...) - - pattern: httpx.Client(...).delete(...) - - pattern: httpx.Client(...).head(...) - - pattern: httpx.Client(...).options(...) - - pattern: httpx.Client(...).stream(...) - - pattern: httpx.Client(...).patch(...) - - pattern: httpx.Client(...).send(...) - # process spawning # using subprocess module - pattern: subprocess.check_output(...) @@ -285,33 +156,6 @@ rules: - pattern: __import__('builtins').exec(...) - pattern: __import__('builtins').eval(...) - # file write - - patterns: - - pattern-either: - - pattern-inside: | - with open(...) as $FILE: - ... - - pattern-inside: | - with builtins.open(...) as $FILE: - ... - - pattern-inside: | - with __import__('builtins').open(...) as $FILE: - ... - - pattern-inside: | - $FILE = open(...) - ... - - pattern-inside: | - $FILE = builtins.open(...) - ... - - pattern-inside: | - $FILE = __import__('builtins').open(...) - ... - - pattern: $FILE.write(...) - - pattern: os.write(...) - - pattern: os.writev(...) - - pattern: os.pwrite(...) - - pattern: os.pwritev(...) - - id: obfuscation_excessive-spacing metadata: description: Detects the use of excessive spacing in code, which may indicate obfuscation or hidden code. diff --git a/tests/malware_analyzer/pypi/resources/sourcecode_samples/obfuscation/decode_and_execute.py b/tests/malware_analyzer/pypi/resources/sourcecode_samples/obfuscation/decode_and_execute.py index 114072a52..6f29c1f58 100644 --- a/tests/malware_analyzer/pypi/resources/sourcecode_samples/obfuscation/decode_and_execute.py +++ b/tests/malware_analyzer/pypi/resources/sourcecode_samples/obfuscation/decode_and_execute.py @@ -32,11 +32,6 @@ def marshal_flow(): def marshal_inline_flow(): exec(__import__('marshal').loads(b'\xe3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00@\x00\x00\x00s\x0c\x00\x00\x00e\x00d\x00\x83\x01\x01\x00d\x01S\x00)\x02z\x0cHello world!N)\x01\xda\x05print\xa9\x00r\x02\x00\x00\x00r\x02\x00\x00\x00\xfa\x08\xda\x08\x01\x00\x00\x00\xf3\x00\x00\x00\x00')) - def bytes_eval_to_soc_bind(): - import socket - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as soc: - soc.bind(__import__('builtins').eval(b'("127.0.0.1", 0)'.decode())) - def map_b64_to_request(): import requests as req @@ -54,14 +49,5 @@ def zlib_ast_subprocess(): # just decodes to ["echo", "Hello world!"] subprocess.Popen(literal_eval(zeeee.decompress(b'x\x9c\x8bVOM\xce\xc8W\xd7QP\xf7H\xcd\xc9\xc9W(\xcf/\xcaIQT\x8f\x05\x00]\xa0\x07\x9d').decode())) - def propagation_to_write(): - import os as e - - # symbol propagations should detect assign of os as e to o and bytes to b and still trigger - o = e - b = bytes - # just decodes to "Hello world!" - contents = b.fromhex("48656C6C6F20776F726C6421") - # just decodes to "some_path" - file = o.open(''.join(chr(c) for c in [115, 111, 109, 101, 95, 112, 97, 116, 104]), o.O_RDWR) - o.pwritev(file, contents, 0) + def b64_reversed_decompressed_ioc(): + _ = lambda __ : __import__('zlib').decompress(__import__('base64').b64decode(__[::-1]));exec((_)(b'something_malicious_here')) diff --git a/tests/malware_analyzer/pypi/resources/sourcecode_samples/obfuscation/expected_results.json b/tests/malware_analyzer/pypi/resources/sourcecode_samples/obfuscation/expected_results.json index 008bd1eb6..5fb7c3965 100644 --- a/tests/malware_analyzer/pypi/resources/sourcecode_samples/obfuscation/expected_results.json +++ b/tests/malware_analyzer/pypi/resources/sourcecode_samples/obfuscation/expected_results.json @@ -1,280 +1,270 @@ { - "enabled_sourcecode_rule_findings": { - "src.macaron.resources.pypi_malware_rules.obfuscation_decode-and-execute": { - "message": "Detected the flow of a decoded primitive value to a remote endpoint, process, code evaluation, or file write", - "detections": [ - { - "file": "obfuscation/decode_and_execute.py", - "start": 30, - "end": 30 - }, - { - "file": "obfuscation/decode_and_execute.py", - "start": 33, - "end": 33 - }, - { - "file": "obfuscation/decode_and_execute.py", - "start": 38, - "end": 38 - }, - { - "file": "obfuscation/decode_and_execute.py", - "start": 47, - "end": 47 - }, - { - "file": "obfuscation/decode_and_execute.py", - "start": 55, - "end": 55 - }, - { - "file": "obfuscation/decode_and_execute.py", - "start": 67, - "end": 67 - } - ] - }, - "src.macaron.resources.pypi_malware_rules.obfuscation_inline-imports": { - "message": "Found an instance of a suspicious API in a hardcoded inline import", - "detections": [ - { - "file": "obfuscation/decode_and_execute.py", - "start": 33, - "end": 33 - }, - { - "file": "obfuscation/decode_and_execute.py", - "start": 38, - "end": 38 - }, - { - "file": "obfuscation/decode_and_execute.py", - "start": 44, - "end": 44 - }, - { - "file": "obfuscation/excessive_spacing.py", - "start": 24, - "end": 24 - }, - { - "file": "obfuscation/excessive_spacing.py", - "start": 25, - "end": 25 - }, - { - "file": "obfuscation/excessive_spacing.py", - "start": 26, - "end": 26 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 23, - "end": 23 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 24, - "end": 24 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 25, - "end": 25 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 26, - "end": 26 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 27, - "end": 27 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 28, - "end": 28 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 29, - "end": 29 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 31, - "end": 31 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 32, - "end": 32 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 69, - "end": 69 - } - ] - }, - "src.macaron.resources.pypi_malware_rules.obfuscation_excessive-spacing": { - "message": "Hidden code after excessive spacing", - "detections": [ - { - "file": "obfuscation/excessive_spacing.py", - "start": 24, - "end": 24 - }, - { - "file": "obfuscation/excessive_spacing.py", - "start": 25, - "end": 25 - }, - { - "file": "obfuscation/excessive_spacing.py", - "start": 25, - "end": 25 - }, - { - "file": "obfuscation/excessive_spacing.py", - "start": 26, - "end": 26 - }, - { - "file": "obfuscation/inline_imports.py", - "start": 27, - "end": 27 - } - ] - }, - "src.macaron.resources.pypi_malware_rules.obfuscation_obfuscation-tools": { - "message": "Found an indicator of the use of a python code obfuscation tool", - "detections": [ - { - "file": "obfuscation/obfuscation_tools.py", - "start": 23, - "end": 23 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 25, - "end": 31 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 26, - "end": 26 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 27, - "end": 27 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 28, - "end": 28 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 30, - "end": 31 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 33, - "end": 33 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 37, - "end": 37 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 39, - "end": 45 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 40, - "end": 40 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 41, - "end": 41 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 42, - "end": 42 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 44, - "end": 45 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 47, - "end": 47 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 51, - "end": 51 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 53, - "end": 59 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 54, - "end": 54 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 55, - "end": 55 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 56, - "end": 56 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 58, - "end": 59 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 61, - "end": 61 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 65, - "end": 65 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 68, - "end": 68 - }, - { - "file": "obfuscation/obfuscation_tools.py", - "start": 68, - "end": 68 - } - ] + "enabled_sourcecode_rule_findings": { + "src.macaron.resources.pypi_malware_rules.obfuscation_decode-and-execute": { + "message": "Detected the flow of a decoded primitive value to a remote endpoint, process, code evaluation, or file write", + "detections": [ + { + "file": "obfuscation/decode_and_execute.py", + "start": 30, + "end": 30 + }, + { + "file": "obfuscation/decode_and_execute.py", + "start": 33, + "end": 33 + }, + { + "file": "obfuscation/decode_and_execute.py", + "start": 50, + "end": 50 + } + ] + }, + "src.macaron.resources.pypi_malware_rules.obfuscation_inline-imports": { + "message": "Found an instance of a suspicious API in a hardcoded inline import", + "detections": [ + { + "file": "obfuscation/decode_and_execute.py", + "start": 33, + "end": 33 + }, + { + "file": "obfuscation/decode_and_execute.py", + "start": 39, + "end": 39 + }, + { + "file": "obfuscation/decode_and_execute.py", + "start": 53, + "end": 53 + }, + { + "file": "obfuscation/decode_and_execute.py", + "start": 53, + "end": 53 + }, + { + "file": "obfuscation/excessive_spacing.py", + "start": 24, + "end": 24 + }, + { + "file": "obfuscation/excessive_spacing.py", + "start": 25, + "end": 25 + }, + { + "file": "obfuscation/excessive_spacing.py", + "start": 26, + "end": 26 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 23, + "end": 23 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 24, + "end": 24 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 25, + "end": 25 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 26, + "end": 26 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 27, + "end": 27 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 28, + "end": 28 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 29, + "end": 29 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 31, + "end": 31 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 32, + "end": 32 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 69, + "end": 69 + } + ] + }, + "src.macaron.resources.pypi_malware_rules.obfuscation_excessive-spacing": { + "message": "Hidden code after excessive spacing", + "detections": [ + { + "file": "obfuscation/excessive_spacing.py", + "start": 24, + "end": 24 + }, + { + "file": "obfuscation/excessive_spacing.py", + "start": 25, + "end": 25 + }, + { + "file": "obfuscation/excessive_spacing.py", + "start": 25, + "end": 25 + }, + { + "file": "obfuscation/excessive_spacing.py", + "start": 26, + "end": 26 + }, + { + "file": "obfuscation/inline_imports.py", + "start": 27, + "end": 27 } + ] }, - "disabled_sourcecode_rule_findings": {} + "src.macaron.resources.pypi_malware_rules.obfuscation_obfuscation-tools": { + "message": "Found an indicator of the use of a python code obfuscation tool", + "detections": [ + { + "file": "obfuscation/obfuscation_tools.py", + "start": 23, + "end": 23 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 25, + "end": 31 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 26, + "end": 26 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 27, + "end": 27 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 28, + "end": 28 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 30, + "end": 31 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 33, + "end": 33 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 37, + "end": 37 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 39, + "end": 45 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 40, + "end": 40 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 41, + "end": 41 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 42, + "end": 42 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 44, + "end": 45 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 47, + "end": 47 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 51, + "end": 51 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 53, + "end": 59 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 54, + "end": 54 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 55, + "end": 55 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 56, + "end": 56 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 58, + "end": 59 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 61, + "end": 61 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 65, + "end": 65 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 68, + "end": 68 + }, + { + "file": "obfuscation/obfuscation_tools.py", + "start": 68, + "end": 68 + } + ] + } + }, + "disabled_sourcecode_rule_findings": {} }