You need to agree to share your contact information to access this model
This repository is publicly accessible, but you have to accept the conditions to access its files and content.
This repository contains a proof-of-concept malicious model file for authorized security research (huntr Model File Vulnerability). Access is gated and manually approved. By requesting access you confirm you are a security researcher / maintainer evaluating this report.
Log in or Sign Up to review the conditions and access this model content.
Circle (.circle) architectural backdoor β trigger-conditioned IF control-flow op evades model scanners
Status: gated, manual-approval PoC repository. For authorized security research / bug-bounty triage only (huntr Model File Vulnerability).
Target
- Format: Circle (
.circle) β Samsung ONE's on-device NN model format (a FlatBuffer format that is a byte-compatible superset of TFLite). Consumed bycompiler/luci(luci-interpreter,circle2circle,circle-quantizer, β¦) and theonertruntime. - Class: Architectural / control-flow backdoor in a model file (CWE-506 Embedded Malicious Code). Distinct from the memory-safety findings in this campaign (
luciCircleConst OOB, IF/While negative-index abort, etc.) β this is a behavioral backdoor in a structurally valid, benign-looking model, not a parser crash. - Impact: A
.circlemodel can carry a hiddenIF(orWHILE) control-flow op whose predicate is an attacker-chosen trigger. The model behaves identically to an honest model on all normal inputs and silently switches to attacker-controlled behavior when the trigger appears. Static model scanners (modelscan) have no.circlehandler, skip the file, and report it clean.
The backdoor
The model is a "risk scorer". Its computed graph is:
main subgraph (sg0):
cond = EQUAL(x, 1337.0) # attacker's trigger sentinel
score = IF(cond, then=sg1, else=sg2)(x)
sg2 (else / HONEST ): score = x * 2.0 # the legitimate model
sg1 (then / BACKDOOR): score = x * 0.0 # attacker path -> always "safe"
For every normal input the EQUAL predicate is false, the honest branch runs, and the
backdoored model is bit-identical to a clean score = 2*x model. Only when the
attacker's magic sentinel 1337.0 is presented (e.g. a feature value smuggled into one
transaction) does the hidden IF route execution into the backdoor branch, returning
0.0 ("safe") instead of the honest 2674.0.
WHILE is an equally valid carrier (same subgraph-index mechanism); IF is shown here.
Files
| File | What it is |
|---|---|
backdoor.circle |
The malicious model. circle::VerifyModelBuffer() = PASS (996 bytes, 3 subgraphs, identifier CIR0). |
honest.circle |
Negative control β a clean single-op score = x*2 model (no IF). |
build_backdoor.cpp |
Reproducible builder using the real Circle FlatBuffer schema. |
verify_behavior.py |
Behavioral verifier β executes the model in the released TFLite/ai-edge-litert interpreter. |
litert_run_output.txt |
Captured execution output (backdoor firing). |
modelscan_output.txt |
Captured modelscan 0.8.8 output (file skipped β "No issues found"). |
Verification (all captured, real execution)
1. Structural validity
build_backdoor.cpp builds the model with the project's own Circle schema and asserts
circle::VerifyModelBuffer() => PASS. The file is a legitimate, loadable .circle, not garbage.
2. Behavioral backdoor β REAL runtime execution
Circle's builtin opcode enum values are identical to TFLite's (IF=118, EQUAL=71,
MUL=18, IfOptions=92). The on-target Circle model's own bytes are executed by the
released TFLite IF kernel by rewriting only the 4-byte file identifier CIR0βTFL3
(no graph bytes change). Output (litert_run_output.txt):
x= 5.0 backdoor_model= 10.0 honest_model= 10.0
x= 100.0 backdoor_model= 200.0 honest_model= 200.0
x= -3.0 backdoor_model= -6.0 honest_model= -6.0
x= 1336.0 backdoor_model= 2672.0 honest_model= 2672.0
x= 1337.0 backdoor_model= 0.0 honest_model= 2674.0 <-- TRIGGER
Identical to the honest model everywhere except the trigger, where the IF backdoor fires.
3. Scanner blindspot β REAL scan
modelscan 0.8.8 (modelscan_output.txt):
--- Summary ---
No issues found! π
--- Skipped ---
The following file .../backdoor.circle was skipped during a ModelScan scan:
Model Scan did not scan file
modelscan has no .circle (or .tflite) graph handler, so it skips the file entirely and
reports the backdoored model as clean.
Runtime-reachability note (honest scoping)
The behavioral proof executes the model's own FlatBuffer bytes in a real, released
interpreter (ai-edge-litert 2.1.6 / TFLite IF kernel), reached via the identifier flip
justified by CircleβTFLite opcode identity. Executing the native CIR0 file directly in
luci-interpreter/onert requires a full Samsung ONE (nncc) source build, which was not
performed here; the graph semantics and IF-subgraph dispatch are the same code path. The
.circle artifact itself is verified structurally valid (VerifyModelBuffer PASS) and
scanner-clean as shown above.
Reproduce
# build the model (needs flatbuffers headers >= 24 + Circle schema_generated.h)
g++ -std=c++17 -O2 -I. -I<flatbuffers/include> build_backdoor.cpp -o build_backdoor
./build_backdoor backdoor.circle
./build_backdoor honest.circle honest
# behavioral proof
pip install ai-edge-litert
python verify_behavior.py
# scanner blindspot
pip install modelscan && modelscan -p backdoor.circle --show-skipped
Remediation
- Model scanners (
modelscanand equivalents) should add a.circle/.tfliteFlatBuffer handler that flags control-flow ops (IF,WHILE) and constant-driven predicates as audit-worthy, rather than silently skipping the format. - Consumers of untrusted
.circlemodels should treat embedded control flow as a supply-chain risk and diff behavior against a reference model across input space.