Coverage for src/alprina_cli/tools/security/exploit.py: 21%

117 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-11-14 11:27 +0100

1""" 

2Exploit Testing Tool 

3 

4Context Engineering: 

5- Safe exploitation testing for educational/authorized use 

6- Returns structured exploit results 

7- Built-in safety checks and consent verification 

8- Educational mode by default 

9 

10Educational exploitation testing - authorized use only. 

11""" 

12 

13from typing import Dict, Any, List, Literal 

14from pydantic import BaseModel, Field 

15from loguru import logger 

16from pathlib import Path 

17import re 

18 

19from alprina_cli.tools.base import AlprinaToolBase, ToolOk, ToolError 

20 

21 

22class ExploitParams(BaseModel): 

23 """ 

24 Parameters for exploit testing. 

25 

26 Context: Focused schema for exploitation testing. 

27 """ 

28 target: str = Field( 

29 description="Target to test (file, directory, or URL)" 

30 ) 

31 exploit_type: Literal["safe", "poc", "educational"] = Field( 

32 default="safe", 

33 description="Exploit mode: safe (non-destructive), poc (proof-of-concept), educational (learning)" 

34 ) 

35 vulnerability: str = Field( 

36 default="", 

37 description="Specific vulnerability to test (e.g., 'sql_injection', 'xss', 'path_traversal')" 

38 ) 

39 max_attempts: int = Field( 

40 default=5, 

41 description="Maximum exploit attempts" 

42 ) 

43 

44 

45class ExploitTool(AlprinaToolBase[ExploitParams]): 

46 """ 

47 Exploit testing tool for authorized security testing. 

48 

49 Context Engineering Benefits: 

50 - Safe mode by default (non-destructive) 

51 - Educational focus 

52 - Structured exploit results 

53 - Built-in safety checks 

54 

55 IMPORTANT: This tool is for: 

56 - Educational purposes 

57 - Authorized penetration testing 

58 - CTF competitions 

59 - Security research with proper authorization 

60 

61 Exploit Types: 

62 - safe: Non-destructive testing only 

63 - poc: Proof-of-concept demonstrations 

64 - educational: Learning and training 

65 

66 Supported Vulnerabilities: 

67 - sql_injection: SQL injection testing 

68 - xss: Cross-site scripting testing 

69 - path_traversal: Path traversal testing 

70 - command_injection: Command injection testing 

71 

72 Usage: 

73 ```python 

74 tool = ExploitTool() 

75 result = await tool.execute(ExploitParams( 

76 target="./test_app", 

77 exploit_type="educational", 

78 vulnerability="sql_injection" 

79 )) 

80 ``` 

81 """ 

82 

83 name: str = "Exploit" 

84 description: str = """Exploit testing for authorized security testing. 

85 

86AUTHORIZED USE ONLY - For education, pentesting, CTF, research. 

87 

88Capabilities: 

89- SQL injection testing 

90- XSS testing 

91- Path traversal testing 

92- Command injection testing 

93 

94Returns: Structured exploit results with safety information""" 

95 params: type[ExploitParams] = ExploitParams 

96 

97 async def execute(self, params: ExploitParams) -> ToolOk | ToolError: 

98 """ 

99 Execute exploit testing. 

100 

101 Context: Returns structured, educational results. 

102 """ 

103 logger.info(f"Exploit: {params.target} (type={params.exploit_type}, vuln={params.vulnerability})") 

104 

105 try: 

106 # Safety check: Ensure safe mode 

107 if params.exploit_type not in ["safe", "poc", "educational"]: 

108 return ToolError( 

109 message="Only safe, poc, and educational modes are supported", 

110 brief="Invalid exploit mode" 

111 ) 

112 

113 # Determine target type 

114 target_path = Path(params.target).expanduser() 

115 is_local = target_path.exists() 

116 

117 if is_local: 

118 results = await self._exploit_local(params, target_path) 

119 else: 

120 results = await self._exploit_remote(params) 

121 

122 # Limit attempts 

123 if len(results) > params.max_attempts: 

124 results = results[:params.max_attempts] 

125 truncated = True 

126 else: 

127 truncated = False 

128 

129 # Calculate success rate 

130 successful = sum(1 for r in results if r.get("successful", False)) 

131 success_rate = (successful / len(results) * 100) if results else 0 

132 

133 return ToolOk( 

134 content={ 

135 "target": params.target, 

136 "exploit_type": params.exploit_type, 

137 "vulnerability": params.vulnerability or "general", 

138 "results": results, 

139 "summary": { 

140 "total_attempts": len(results), 

141 "successful": successful, 

142 "success_rate": success_rate, 

143 "truncated": truncated, 

144 "target_type": "local" if is_local else "remote" 

145 }, 

146 "safety_notice": "This tool is for authorized testing only. Always obtain proper authorization." 

147 } 

148 ) 

149 

150 except Exception as e: 

151 logger.error(f"Exploit testing failed: {e}") 

152 return ToolError( 

153 message=f"Exploit testing failed: {str(e)}", 

154 brief="Exploit failed" 

155 ) 

156 

157 async def _exploit_local( 

158 self, 

159 params: ExploitParams, 

160 target_path: Path 

161 ) -> List[Dict[str, Any]]: 

162 """ 

163 Test exploits on local target. 

164 

165 Context: Safe, educational exploitation testing. 

166 """ 

167 results = [] 

168 

169 if target_path.is_file(): 

170 results.extend(self._test_file_exploits(target_path, params)) 

171 else: 

172 # Test directory 

173 files = list(target_path.rglob("*")) 

174 for file_path in files: 

175 if file_path.is_file(): 

176 results.extend(self._test_file_exploits(file_path, params)) 

177 

178 # Limit attempts 

179 if len(results) >= params.max_attempts: 

180 break 

181 

182 return results 

183 

184 def _test_file_exploits( 

185 self, 

186 file_path: Path, 

187 params: ExploitParams 

188 ) -> List[Dict[str, Any]]: 

189 """Test exploits on individual file""" 

190 results = [] 

191 

192 try: 

193 # Skip binary files 

194 if self._is_binary(file_path): 

195 return results 

196 

197 content = file_path.read_text(errors="ignore") 

198 lines = content.splitlines() 

199 

200 # Test specific vulnerability or all 

201 if not params.vulnerability or params.vulnerability == "sql_injection": 

202 results.extend(self._test_sql_injection(file_path, content, lines)) 

203 

204 if not params.vulnerability or params.vulnerability == "xss": 

205 results.extend(self._test_xss(file_path, content, lines)) 

206 

207 if not params.vulnerability or params.vulnerability == "path_traversal": 

208 results.extend(self._test_path_traversal(file_path, content, lines)) 

209 

210 if not params.vulnerability or params.vulnerability == "command_injection": 

211 results.extend(self._test_command_injection(file_path, content, lines)) 

212 

213 except Exception as e: 

214 logger.warning(f"Could not test {file_path}: {e}") 

215 

216 return results 

217 

218 def _test_sql_injection( 

219 self, 

220 file_path: Path, 

221 content: str, 

222 lines: List[str] 

223 ) -> List[Dict[str, Any]]: 

224 """Test SQL injection vulnerabilities (educational)""" 

225 results = [] 

226 

227 # Look for vulnerable SQL patterns 

228 vulnerable_patterns = [ 

229 (r"execute\([^)]*\+[^)]*\)", "String concatenation in SQL"), 

230 (r'SELECT.*"\s*\+\s*', "SQL query with concatenation"), 

231 (r"\.format\(.*SELECT", "format() with SQL query"), 

232 ] 

233 

234 for pattern, description in vulnerable_patterns: 

235 matches = re.finditer(pattern, content, re.IGNORECASE) 

236 for match in matches: 

237 line_num = content[:match.start()].count('\n') + 1 

238 

239 # Educational exploit payloads 

240 test_payloads = [ 

241 "' OR '1'='1", 

242 "1' UNION SELECT NULL--", 

243 "' OR 1=1--" 

244 ] 

245 

246 results.append({ 

247 "vulnerability": "sql_injection", 

248 "file": str(file_path), 

249 "line_number": line_num, 

250 "description": description, 

251 "matched_code": lines[line_num - 1].strip()[:80], 

252 "successful": False, # Safe mode - don't actually exploit 

253 "payloads_tested": test_payloads, 

254 "educational_note": "Vulnerable pattern detected. In real testing, try payloads to confirm.", 

255 "severity": "HIGH" 

256 }) 

257 

258 return results 

259 

260 def _test_xss( 

261 self, 

262 file_path: Path, 

263 content: str, 

264 lines: List[str] 

265 ) -> List[Dict[str, Any]]: 

266 """Test XSS vulnerabilities (educational)""" 

267 results = [] 

268 

269 # Look for unescaped user input 

270 xss_patterns = [ 

271 (r"\.innerHTML\s*=", "Direct innerHTML assignment"), 

272 (r"document\.write\(", "document.write usage"), 

273 (r"\.html\([^)]*\+", "jQuery html() with concatenation"), 

274 ] 

275 

276 for pattern, description in xss_patterns: 

277 matches = re.finditer(pattern, content, re.IGNORECASE) 

278 for match in matches: 

279 line_num = content[:match.start()].count('\n') + 1 

280 

281 test_payloads = [ 

282 "<script>alert('XSS')</script>", 

283 "<img src=x onerror=alert('XSS')>", 

284 "<svg onload=alert('XSS')>" 

285 ] 

286 

287 results.append({ 

288 "vulnerability": "xss", 

289 "file": str(file_path), 

290 "line_number": line_num, 

291 "description": description, 

292 "matched_code": lines[line_num - 1].strip()[:80], 

293 "successful": False, 

294 "payloads_tested": test_payloads, 

295 "educational_note": "Potential XSS vector. Test with payloads in controlled environment.", 

296 "severity": "MEDIUM" 

297 }) 

298 

299 return results 

300 

301 def _test_path_traversal( 

302 self, 

303 file_path: Path, 

304 content: str, 

305 lines: List[str] 

306 ) -> List[Dict[str, Any]]: 

307 """Test path traversal vulnerabilities (educational)""" 

308 results = [] 

309 

310 # Look for file operations with user input 

311 traversal_patterns = [ 

312 (r"open\([^)]*\+[^)]*\)", "File open with concatenation"), 

313 (r"Path\([^)]*\+[^)]*\)", "Path construction with concatenation"), 

314 (r"readFile\([^)]*\+", "readFile with concatenation"), 

315 (r"=\s*[^+]*\+\s*user[_a-zA-Z]*.*\n.*open\(", "File path with user input"), 

316 ] 

317 

318 for pattern, description in traversal_patterns: 

319 matches = re.finditer(pattern, content, re.IGNORECASE) 

320 for match in matches: 

321 line_num = content[:match.start()].count('\n') + 1 

322 

323 test_payloads = [ 

324 "../../../etc/passwd", 

325 "..\\..\\..\\windows\\system32\\config\\sam", 

326 "....//....//....//etc/passwd" 

327 ] 

328 

329 results.append({ 

330 "vulnerability": "path_traversal", 

331 "file": str(file_path), 

332 "line_number": line_num, 

333 "description": description, 

334 "matched_code": lines[line_num - 1].strip()[:80], 

335 "successful": False, 

336 "payloads_tested": test_payloads, 

337 "educational_note": "Potential path traversal. Validate and sanitize file paths.", 

338 "severity": "HIGH" 

339 }) 

340 

341 return results 

342 

343 def _test_command_injection( 

344 self, 

345 file_path: Path, 

346 content: str, 

347 lines: List[str] 

348 ) -> List[Dict[str, Any]]: 

349 """Test command injection vulnerabilities (educational)""" 

350 results = [] 

351 

352 # Look for dangerous functions with user input 

353 cmd_patterns = [ 

354 (r"os\.system\([^)]*\+[^)]*\)", "os.system with concatenation"), 

355 (r"subprocess\.call\([^)]*\+[^)]*\)", "subprocess with concatenation"), 

356 (r"eval\(", "eval() usage"), 

357 (r"exec\(", "exec() usage"), 

358 ] 

359 

360 for pattern, description in cmd_patterns: 

361 matches = re.finditer(pattern, content, re.IGNORECASE) 

362 for match in matches: 

363 line_num = content[:match.start()].count('\n') + 1 

364 

365 test_payloads = [ 

366 "; ls -la", 

367 "| whoami", 

368 "&& cat /etc/passwd" 

369 ] 

370 

371 results.append({ 

372 "vulnerability": "command_injection", 

373 "file": str(file_path), 

374 "line_number": line_num, 

375 "description": description, 

376 "matched_code": lines[line_num - 1].strip()[:80], 

377 "successful": False, 

378 "payloads_tested": test_payloads, 

379 "educational_note": "Command injection risk. Never execute user input directly.", 

380 "severity": "CRITICAL" 

381 }) 

382 

383 return results 

384 

385 async def _exploit_remote(self, params: ExploitParams) -> List[Dict[str, Any]]: 

386 """ 

387 Test exploits on remote target. 

388 

389 Context: Educational remote testing. 

390 """ 

391 results = [] 

392 

393 # Educational notice for remote testing 

394 results.append({ 

395 "vulnerability": "remote_testing", 

396 "description": "Remote exploit testing", 

397 "successful": False, 

398 "educational_note": ( 

399 "Remote exploitation requires proper authorization. " 

400 "This tool provides educational information only. " 

401 "For real testing, use tools like Burp Suite, OWASP ZAP, or Metasploit." 

402 ), 

403 "severity": "INFO" 

404 }) 

405 

406 # Basic checks 

407 if params.target.startswith("http://"): 

408 results.append({ 

409 "vulnerability": "insecure_protocol", 

410 "description": "Target uses HTTP (insecure)", 

411 "successful": True, # This is a confirmed finding 

412 "recommendation": "Use HTTPS instead of HTTP", 

413 "severity": "MEDIUM" 

414 }) 

415 

416 return results 

417 

418 def _is_binary(self, file_path: Path) -> bool: 

419 """Check if file is binary""" 

420 try: 

421 with open(file_path, 'rb') as f: 

422 chunk = f.read(8192) 

423 return b'\x00' in chunk 

424 except Exception: 

425 return False