amarck commited on
Commit
b6f4a96
·
1 Parent(s): 702e402

MCP server: heaptrm as Model Context Protocol tool

Browse files

4 tools for any MCP-compatible LLM agent:
heap_start — launch binary with instrumentation
heap_send — send data, get structured heap state
heap_check — force corruption validation
heap_observe — get current state

Works with Claude Code, SWE-agent, EnIGMA, or any MCP client.
Returns structured JSON with addresses, bins, risk score, corruptions.

Files changed (2) hide show
  1. heaptrm/mcp_config.json +9 -0
  2. heaptrm/mcp_server.py +166 -0
heaptrm/mcp_config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "mcpServers": {
3
+ "heaptrm": {
4
+ "command": "python3",
5
+ "args": ["heaptrm/mcp_server.py"],
6
+ "description": "Heap exploitation observability — structured heap state for exploit development"
7
+ }
8
+ }
9
+ }
heaptrm/mcp_server.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ heaptrm MCP server — structured heap observation for LLM agents.
4
+
5
+ Provides 4 tools via Model Context Protocol:
6
+ - heap_start: Launch a binary with heap instrumentation
7
+ - heap_send: Send data to binary, get heap state back
8
+ - heap_check: Force corruption validation (catches UAF/overflow)
9
+ - heap_observe: Get current heap state without sending data
10
+
11
+ Usage with Claude Code:
12
+ claude --mcp-server "python heaptrm/mcp_server.py"
13
+
14
+ Usage with any MCP client:
15
+ Run as stdio server: python heaptrm/mcp_server.py
16
+ """
17
+
18
+ import json
19
+ import os
20
+ import subprocess
21
+ import signal
22
+ import time
23
+ from pathlib import Path
24
+ from mcp.server.fastmcp import FastMCP
25
+
26
+ mcp = FastMCP("heaptrm", instructions="""
27
+ You have access to heaptrm, a heap exploitation observability tool.
28
+ It instruments a binary's heap allocator and gives you structured
29
+ observations: chunk addresses, sizes, states, freelist contents,
30
+ corruption events, and exploit-readiness risk scores.
31
+
32
+ Typical workflow:
33
+ 1. heap_start("./vulnerable_binary") — launch with instrumentation
34
+ 2. heap_send("1 0 64\\n") — send menu commands, get heap state
35
+ 3. heap_observe() — check current state
36
+ 4. heap_check() — validate for corruption after writes
37
+ 5. Use the addresses and bin info to compute your exploit
38
+
39
+ The heap state includes safe-linking XOR keys derivable from chunk
40
+ addresses (key = address >> 12). Use this for tcache poisoning.
41
+ """)
42
+
43
+ # Global state for the heaptrm subprocess
44
+ _proc = None
45
+ _last_state = None
46
+
47
+
48
+ def _find_heaptrm_binary():
49
+ """Locate the compiled heaptrm Rust binary."""
50
+ candidates = [
51
+ Path(__file__).parent.parent / "heaptrm-cli" / "target" / "release" / "heaptrm",
52
+ Path("heaptrm-cli/target/release/heaptrm"),
53
+ Path("heaptrm"),
54
+ ]
55
+ for c in candidates:
56
+ if c.exists():
57
+ return str(c.resolve())
58
+ return None
59
+
60
+
61
+ def _cmd(action: str, data: str = "") -> dict:
62
+ """Send a command to the heaptrm subprocess."""
63
+ global _proc, _last_state
64
+ if _proc is None:
65
+ return {"error": "No binary running. Call heap_start first."}
66
+
67
+ try:
68
+ _proc.stdin.write(json.dumps({"action": action, "data": data}) + "\n")
69
+ _proc.stdin.flush()
70
+ line = _proc.stdout.readline()
71
+ if not line:
72
+ return {"error": "heaptrm process closed"}
73
+ result = json.loads(line)
74
+ if result.get("heap"):
75
+ _last_state = result
76
+ return result
77
+ except Exception as e:
78
+ return {"error": str(e)}
79
+
80
+
81
+ @mcp.tool()
82
+ def heap_start(binary: str, args: str = "") -> str:
83
+ """Launch a binary with heap instrumentation.
84
+
85
+ Args:
86
+ binary: Path to the target binary
87
+ args: Optional space-separated arguments
88
+
89
+ Returns heap state after binary starts, or error message.
90
+ """
91
+ global _proc, _last_state
92
+
93
+ # Kill existing process
94
+ if _proc is not None:
95
+ try:
96
+ _proc.kill()
97
+ _proc.wait(timeout=2)
98
+ except:
99
+ pass
100
+ _proc = None
101
+ _last_state = None
102
+
103
+ heaptrm_bin = _find_heaptrm_binary()
104
+ if not heaptrm_bin:
105
+ return json.dumps({"error": "Cannot find heaptrm binary. Build with: cd heaptrm-cli && cargo build --release"})
106
+
107
+ cmd = [heaptrm_bin, binary]
108
+ if args:
109
+ cmd.extend(args.split())
110
+
111
+ try:
112
+ _proc = subprocess.Popen(
113
+ cmd,
114
+ stdin=subprocess.PIPE,
115
+ stdout=subprocess.PIPE,
116
+ stderr=subprocess.DEVNULL,
117
+ text=True,
118
+ )
119
+ time.sleep(0.1)
120
+ return json.dumps({"status": "started", "binary": binary, "pid": _proc.pid})
121
+ except Exception as e:
122
+ return json.dumps({"error": f"Failed to start: {e}"})
123
+
124
+
125
+ @mcp.tool()
126
+ def heap_send(data: str) -> str:
127
+ """Send data to the binary's stdin and get heap state back.
128
+
129
+ Args:
130
+ data: Data to send (e.g., "1 0 64\\n" for alloc). Must end with \\n.
131
+
132
+ Returns structured heap observation with chunks, bins, risk score,
133
+ addresses, and any corruption events.
134
+ """
135
+ if not data.endswith("\n"):
136
+ data += "\n"
137
+ result = _cmd("send", data)
138
+ return json.dumps(result, indent=2)
139
+
140
+
141
+ @mcp.tool()
142
+ def heap_check() -> str:
143
+ """Force heap validation to detect corruption from writes.
144
+
145
+ Call this after sending data that might corrupt the heap (edit/write
146
+ commands). Detects UAF writes, metadata corruption, and overflow.
147
+
148
+ Returns heap state with any newly detected corruptions.
149
+ """
150
+ result = _cmd("check")
151
+ return json.dumps(result, indent=2)
152
+
153
+
154
+ @mcp.tool()
155
+ def heap_observe() -> str:
156
+ """Get the current heap state without sending any data.
157
+
158
+ Returns the last observed heap state including chunks, bins,
159
+ risk score, and any detected corruption events.
160
+ """
161
+ result = _cmd("observe")
162
+ return json.dumps(result, indent=2)
163
+
164
+
165
+ if __name__ == "__main__":
166
+ mcp.run(transport="stdio")