alienrecon 3.0.0__py3-none-any.whl

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 (93) hide show
  1. alienrecon/__init__.py +1 -0
  2. alienrecon/__main__.py +5 -0
  3. alienrecon/agent/__init__.py +0 -0
  4. alienrecon/agent/assistant.py +88 -0
  5. alienrecon/agent/brain.py +317 -0
  6. alienrecon/agent/instructor.py +592 -0
  7. alienrecon/api.py +188 -0
  8. alienrecon/cli.py +323 -0
  9. alienrecon/core/__init__.py +0 -0
  10. alienrecon/core/exceptions.py +44 -0
  11. alienrecon/core/flag_celebrator.py +126 -0
  12. alienrecon/core/input_validator.py +431 -0
  13. alienrecon/curriculum/__init__.py +0 -0
  14. alienrecon/curriculum/profile.py +178 -0
  15. alienrecon/curriculum/rooms.py +229 -0
  16. alienrecon/data/README.md +80 -0
  17. alienrecon/data/ctf_info/htb_lame.yaml +23 -0
  18. alienrecon/data/ctf_info/test_box.yaml +11 -0
  19. alienrecon/data/ctf_info/thm_basic_pentesting.yaml +24 -0
  20. alienrecon/data/prompts/mcp_system_prompt.txt +253 -0
  21. alienrecon/data/rooms/index.yaml +134 -0
  22. alienrecon/data/rooms/thm-activerecon.yaml +322 -0
  23. alienrecon/data/rooms/thm-authenticationbypass.yaml +395 -0
  24. alienrecon/data/rooms/thm-boilerctf.yaml +706 -0
  25. alienrecon/data/rooms/thm-breachingad.yaml +955 -0
  26. alienrecon/data/rooms/thm-bruteit.yaml +454 -0
  27. alienrecon/data/rooms/thm-burpsuitebasics.yaml +371 -0
  28. alienrecon/data/rooms/thm-burpsuiteextensions.yaml +129 -0
  29. alienrecon/data/rooms/thm-burpsuiteintruder.yaml +421 -0
  30. alienrecon/data/rooms/thm-burpsuiteom.yaml +314 -0
  31. alienrecon/data/rooms/thm-burpsuiterepeater.yaml +289 -0
  32. alienrecon/data/rooms/thm-careersincyber.yaml +347 -0
  33. alienrecon/data/rooms/thm-chillhack.yaml +876 -0
  34. alienrecon/data/rooms/thm-contentdiscovery.yaml +539 -0
  35. alienrecon/data/rooms/thm-cyborg.yaml +485 -0
  36. alienrecon/data/rooms/thm-defensivesecurityintro.yaml +380 -0
  37. alienrecon/data/rooms/thm-exploitingavulnerabilityv2.yaml +349 -0
  38. alienrecon/data/rooms/thm-fileinc.yaml +410 -0
  39. alienrecon/data/rooms/thm-fowsniff.yaml +640 -0
  40. alienrecon/data/rooms/thm-h4cked.yaml +708 -0
  41. alienrecon/data/rooms/thm-hackpark.yaml +783 -0
  42. alienrecon/data/rooms/thm-idor.yaml +300 -0
  43. alienrecon/data/rooms/thm-internal.yaml +506 -0
  44. alienrecon/data/rooms/thm-introtoc2.yaml +680 -0
  45. alienrecon/data/rooms/thm-introtoshells.yaml +829 -0
  46. alienrecon/data/rooms/thm-lazyadmin.yaml +451 -0
  47. alienrecon/data/rooms/thm-linprivesc.yaml +985 -0
  48. alienrecon/data/rooms/thm-linuxprivesc.yaml +1347 -0
  49. alienrecon/data/rooms/thm-metasploitexploitation.yaml +460 -0
  50. alienrecon/data/rooms/thm-metasploitintro.yaml +517 -0
  51. alienrecon/data/rooms/thm-meterpreter.yaml +539 -0
  52. alienrecon/data/rooms/thm-netsecchallenge.yaml +286 -0
  53. alienrecon/data/rooms/thm-nmap01.yaml +636 -0
  54. alienrecon/data/rooms/thm-nmap02.yaml +569 -0
  55. alienrecon/data/rooms/thm-nmap03.yaml +623 -0
  56. alienrecon/data/rooms/thm-nmap04.yaml +609 -0
  57. alienrecon/data/rooms/thm-offensivesecurityintro.yaml +314 -0
  58. alienrecon/data/rooms/thm-oscommandinjection.yaml +239 -0
  59. alienrecon/data/rooms/thm-passiverecon.yaml +260 -0
  60. alienrecon/data/rooms/thm-pentestingfundamentals.yaml +373 -0
  61. alienrecon/data/rooms/thm-principlesofsecurity.yaml +389 -0
  62. alienrecon/data/rooms/thm-protocolsandservers.yaml +367 -0
  63. alienrecon/data/rooms/thm-protocolsandservers2.yaml +443 -0
  64. alienrecon/data/rooms/thm-raceconditionsattacks.yaml +260 -0
  65. alienrecon/data/rooms/thm-relevant.yaml +642 -0
  66. alienrecon/data/rooms/thm-solar.yaml +866 -0
  67. alienrecon/data/rooms/thm-sqlinjectionlm.yaml +957 -0
  68. alienrecon/data/rooms/thm-ssrfqi.yaml +283 -0
  69. alienrecon/data/rooms/thm-subdomainenumeration.yaml +327 -0
  70. alienrecon/data/rooms/thm-ultratech.yaml +572 -0
  71. alienrecon/data/rooms/thm-vulnerabilities101.yaml +325 -0
  72. alienrecon/data/rooms/thm-vulnerabilitycapstone.yaml +305 -0
  73. alienrecon/data/rooms/thm-walkinganapplication.yaml +487 -0
  74. alienrecon/data/rooms/thm-windowsprivesc20.yaml +741 -0
  75. alienrecon/data/rooms/thm-wreath.yaml +1966 -0
  76. alienrecon/data/rooms/thm-xss.yaml +415 -0
  77. alienrecon/data/rooms/thm-yearoftherabbit.yaml +687 -0
  78. alienrecon/data/templates/ctf_notes_template.md +118 -0
  79. alienrecon/display/__init__.py +0 -0
  80. alienrecon/display/ui.py +140 -0
  81. alienrecon/prompts/system_prompt.txt +246 -0
  82. alienrecon/prompts/terminal_simulation_prompt.txt +46 -0
  83. alienrecon/prompts/welcome_message.txt +7 -0
  84. alienrecon/prompts/welcome_message_with_target.txt +7 -0
  85. alienrecon/tools/__init__.py +0 -0
  86. alienrecon/tools/checker.py +93 -0
  87. alienrecon/tools/vpn.py +166 -0
  88. alienrecon/wordlists/dns-fast-clean.txt +58 -0
  89. alienrecon/wordlists/dns-fast.txt +58 -0
  90. alienrecon-3.0.0.dist-info/METADATA +187 -0
  91. alienrecon-3.0.0.dist-info/RECORD +93 -0
  92. alienrecon-3.0.0.dist-info/WHEEL +4 -0
  93. alienrecon-3.0.0.dist-info/entry_points.txt +3 -0
alienrecon/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "3.0.0"
alienrecon/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Main entry point for alienrecon when run as a module."""
2
+ from alienrecon.cli import main
3
+
4
+ if __name__ == "__main__":
5
+ main()
File without changes
@@ -0,0 +1,88 @@
1
+ """Free mode assistant — student picks target, agent assists."""
2
+
3
+ import logging
4
+ from typing import Optional
5
+
6
+ from rich.console import Console
7
+ from rich.panel import Panel
8
+
9
+ from ..curriculum.profile import StudentProfile
10
+
11
+ logger = logging.getLogger(__name__)
12
+ console = Console()
13
+
14
+
15
+ class Assistant:
16
+ """Assistant for free-form reconnaissance."""
17
+
18
+ def __init__(self, profile: StudentProfile, dry_run: bool = False):
19
+ self.profile = profile
20
+ self.dry_run = dry_run
21
+
22
+ def start(self, target: str):
23
+ """Start a free-form recon session."""
24
+ from ..core.input_validator import InputValidator
25
+
26
+ # Validate target
27
+ try:
28
+ validated = InputValidator.validate_target(target)
29
+ except Exception as e:
30
+ console.print(f"[red]Invalid target: {e}[/red]")
31
+ return
32
+
33
+ console.print(Panel(
34
+ f"[green]Target:[/green] {validated}\n"
35
+ f"[dim]Free mode — you lead, I assist.[/dim]\n\n"
36
+ f"Type commands to run, or ask me questions.\n"
37
+ f"Type [cyan]help[/cyan] for options, [cyan]exit[/cyan] to quit.",
38
+ title="Free Recon",
39
+ border_style="cyan",
40
+ ))
41
+
42
+ # Interactive loop
43
+ while True:
44
+ try:
45
+ cmd = console.input("\n[green] alienrecon> [/green]")
46
+ except (KeyboardInterrupt, EOFError):
47
+ console.print("\n[yellow]Session ended.[/yellow]")
48
+ break
49
+
50
+ cmd = cmd.strip()
51
+ if not cmd:
52
+ continue
53
+
54
+ if cmd.lower() in ("exit", "quit", "q"):
55
+ console.print("[yellow]Session ended.[/yellow]")
56
+ break
57
+
58
+ if cmd.lower() == "help":
59
+ self._show_help()
60
+ continue
61
+
62
+ # For now, basic command pass-through
63
+ # TODO: Add smart suggestions and explanations
64
+ if self.dry_run:
65
+ console.print(f"[dim][dry-run] Would execute: {cmd}[/dim]")
66
+ else:
67
+ import subprocess
68
+ try:
69
+ result = subprocess.run(
70
+ cmd, shell=True, capture_output=True, text=True, timeout=300
71
+ )
72
+ output = result.stdout + result.stderr
73
+ if output.strip():
74
+ console.print(Panel(output.strip()[:5000], title="Output", border_style="dim"))
75
+ except subprocess.TimeoutExpired:
76
+ console.print("[red]Command timed out.[/red]")
77
+ except Exception as e:
78
+ console.print(f"[red]Error: {e}[/red]")
79
+
80
+ def _show_help(self):
81
+ console.print(Panel(
82
+ "[cyan]help[/cyan] — Show this help\n"
83
+ "[cyan]exit[/cyan] — End session\n"
84
+ "[cyan]<command>[/cyan] — Run a shell command\n\n"
85
+ "[dim]Tip: Try nmap, gobuster, nikto, wpscan, etc.[/dim]",
86
+ title="Commands",
87
+ border_style="cyan",
88
+ ))
@@ -0,0 +1,317 @@
1
+ """Claude-powered brain for the AlienRecon instructor.
2
+
3
+ All Claude API calls go through the AlienRecon API proxy.
4
+ Users do not need their own Anthropic API key.
5
+ """
6
+
7
+ import logging
8
+ from typing import Optional
9
+
10
+ from .. import api
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def is_available() -> bool:
16
+ """Check if the user is logged in and can use the AI instructor."""
17
+ return api.is_logged_in()
18
+
19
+
20
+ # ── Instructor tools ─────────────────────────────────────────────────────
21
+
22
+ INSTRUCTOR_TOOLS = [
23
+ {
24
+ "name": "run_command",
25
+ "description": "Execute a shell command on the student's machine against the target. Use this when the student is ready to run a scan, exploit, or other tool. The output will be returned to you for discussion.",
26
+ "input_schema": {
27
+ "type": "object",
28
+ "properties": {
29
+ "command": {
30
+ "type": "string",
31
+ "description": "The shell command to execute. Use {target} as placeholder for the target IP."
32
+ },
33
+ "explanation": {
34
+ "type": "string",
35
+ "description": "Brief explanation of what this command does (shown to student before execution)."
36
+ }
37
+ },
38
+ "required": ["command"]
39
+ }
40
+ },
41
+ {
42
+ "name": "show_hint",
43
+ "description": "Give the student a progressive hint. Use level 1 for nudges, level 2 for more direct guidance, level 3 for nearly the answer. Only use when the student is stuck.",
44
+ "input_schema": {
45
+ "type": "object",
46
+ "properties": {
47
+ "hint": {
48
+ "type": "string",
49
+ "description": "The hint text."
50
+ },
51
+ "level": {
52
+ "type": "integer",
53
+ "description": "Hint level: 1=nudge, 2=direct, 3=nearly answer",
54
+ "enum": [1, 2, 3]
55
+ }
56
+ },
57
+ "required": ["hint", "level"]
58
+ }
59
+ },
60
+ {
61
+ "name": "check_answer",
62
+ "description": "Check if the student's answer to a platform question is correct. Use when the student provides an answer to a TryHackMe question.",
63
+ "input_schema": {
64
+ "type": "object",
65
+ "properties": {
66
+ "question_id": {
67
+ "type": "string",
68
+ "description": "The question ID from the YAML (e.g. t1-q1, q1)."
69
+ },
70
+ "student_answer": {
71
+ "type": "string",
72
+ "description": "The student's answer to check."
73
+ }
74
+ },
75
+ "required": ["question_id", "student_answer"]
76
+ }
77
+ },
78
+ {
79
+ "name": "advance_step",
80
+ "description": "Signal that the student has understood the current step and is ready to move on. Only call this when the student has demonstrated understanding of the key concept — not just after running a command.",
81
+ "input_schema": {
82
+ "type": "object",
83
+ "properties": {
84
+ "summary": {
85
+ "type": "string",
86
+ "description": "Brief summary of what was learned in this step."
87
+ }
88
+ },
89
+ "required": ["summary"]
90
+ }
91
+ },
92
+ ]
93
+
94
+ # ── System prompt ────────────────────────────────────────────────────────
95
+
96
+ INSTRUCTOR_SYSTEM = """You are AlienRecon, a cybersecurity mentor walking a student through a CTF room.
97
+
98
+ You are having a CONVERSATION, not reading a script. You are the senior operator — the student is shadowing you.
99
+
100
+ ## How You Teach
101
+
102
+ 1. ORIENT: Explain what we need to accomplish, ask the student how they'd approach it
103
+ 2. GUIDE: Based on their response, guide them toward the right tool/approach
104
+ 3. EXECUTE: When ready, use the run_command tool to run the appropriate command
105
+ 4. ANALYZE: After seeing output, ask the student what they notice before explaining
106
+ 5. CONNECT: Help them understand why this matters and what it means for our next move
107
+ 6. ADVANCE: When they understand the key concept, use advance_step to move on
108
+
109
+ ## Rules
110
+
111
+ - Keep responses to 2-4 sentences. This is a conversation, not a lecture.
112
+ - Ask questions BEFORE showing commands. Make the student think.
113
+ - After command output, ask what they notice before you explain.
114
+ - Never give flags directly — let them find flags in output.
115
+ - Only call advance_step when understanding is demonstrated, not just after running a command.
116
+ - If the student is stuck, use show_hint with progressive levels before giving answers.
117
+ - If they suggest a different approach, evaluate it honestly.
118
+ - Match their energy — if they clearly know nmap, don't explain what port scanning is.
119
+
120
+ ## Context
121
+
122
+ Room: {room_name} ({difficulty})
123
+ Brief: {room_brief}
124
+ Phase: {phase_name} — {phase_objective}
125
+
126
+ ## Your Cheat Sheet (DO NOT dump this to the student)
127
+
128
+ {step_knowledge}"""
129
+
130
+
131
+ SYSTEM_PROMPT = """\
132
+ You are AlienRecon, a cybersecurity mentor walking a student through a CTF room.
133
+
134
+ You are the senior operator. The student is shadowing you. You show your work, \
135
+ explain your thinking, and answer questions. You are NOT a quiz master — you're \
136
+ a mentor working alongside them.
137
+
138
+ Style:
139
+ - Direct, clear, no fluff — like a real hacker mentoring a junior
140
+ - Explain the WHY, not just the WHAT
141
+ - Keep responses to 2-4 sentences unless explaining a concept in depth
142
+ - Reference real tools and real-world context
143
+ - Celebrate when they notice something good
144
+
145
+ Room brief: {room_brief}
146
+ Phase: {phase_name} — {phase_objective}
147
+ Step: {step_id}
148
+
149
+ {step_context}
150
+
151
+ Rules:
152
+ - NEVER give flags directly — let them find flags in command output
153
+ - If they suggest a different approach, evaluate it honestly
154
+ - When analyzing output, focus on what matters for THIS room, not generic observations
155
+ - If output looks wrong/empty, help troubleshoot specifically"""
156
+
157
+
158
+ def _handle_api_error(result: dict) -> None:
159
+ """Log API errors for debugging."""
160
+ if result.get("error"):
161
+ logger.warning(f"API error ({result.get(status)}): {result.get(detail)}")
162
+
163
+
164
+ def ask_claude(
165
+ student_input: str,
166
+ room_name: str = "",
167
+ room_brief: str = "",
168
+ room_slug: str = "",
169
+ phase_name: str = "",
170
+ phase_objective: str = "",
171
+ step_id: str = "",
172
+ step_instruction: str = "",
173
+ look_for: Optional[list[str]] = None,
174
+ key_takeaway: str = "",
175
+ if_fails: str = "",
176
+ skill_level: str = "beginner",
177
+ hints_used: int = 0,
178
+ conversation_history: Optional[list[dict]] = None,
179
+ ) -> Optional[str]:
180
+ """Legacy v1 interface — used for intro, debrief, non-step contexts."""
181
+ if not is_available():
182
+ return None
183
+
184
+ step_parts = []
185
+ if look_for:
186
+ step_parts.append("What to look for in output:\n" + "\n".join(f"- {item}" for item in look_for))
187
+ if key_takeaway:
188
+ step_parts.append(f"Key takeaway for student: {key_takeaway}")
189
+ if if_fails:
190
+ step_parts.append(f"If this step fails: {if_fails}")
191
+ step_context = "\n\n".join(step_parts)
192
+
193
+ system = SYSTEM_PROMPT.format(
194
+ room_brief=room_brief or f"Room: {room_name}" if room_name else "Free mode",
195
+ phase_name=phase_name or "N/A",
196
+ phase_objective=phase_objective or "N/A",
197
+ step_id=step_id or "N/A",
198
+ step_context=step_context or "No additional context for this step.",
199
+ )
200
+
201
+ messages = []
202
+ if conversation_history:
203
+ for entry in conversation_history[-20:]:
204
+ messages.append(entry)
205
+ messages.append({"role": "user", "content": student_input})
206
+
207
+ result = api.chat_sync(
208
+ messages=messages,
209
+ system=system,
210
+ room_slug=room_slug,
211
+ )
212
+
213
+ if result.get("error"):
214
+ _handle_api_error(result)
215
+ return None
216
+
217
+ # Parse Anthropic response format
218
+ try:
219
+ content = result.get("content", [])
220
+ for block in content:
221
+ if block.get("type") == "text":
222
+ return block.get("text")
223
+ except Exception as e:
224
+ logger.warning(f"Failed to parse response: {e}")
225
+
226
+ return None
227
+
228
+
229
+ def teach_step(
230
+ message: str,
231
+ step_knowledge: dict,
232
+ phase_name: str = "",
233
+ phase_objective: str = "",
234
+ room_name: str = "",
235
+ room_brief: str = "",
236
+ room_slug: str = "",
237
+ difficulty: str = "",
238
+ conversation_history: Optional[list[dict]] = None,
239
+ ):
240
+ """Conversation-driven Claude call with tool_use.
241
+
242
+ Returns:
243
+ tuple: (text_response: str, tool_calls: list[dict])
244
+ """
245
+ if not is_available():
246
+ return None, []
247
+
248
+ # Build step knowledge text
249
+ parts = []
250
+ if step_knowledge.get("step_id"):
251
+ parts.append(f"Step: {step_knowledge[step_id]}")
252
+ if step_knowledge.get("narration"):
253
+ parts.append(f"What to teach: {step_knowledge[narration]}")
254
+ if step_knowledge.get("command"):
255
+ parts.append(f"Command to run: {step_knowledge[command]}")
256
+ if step_knowledge.get("expected_output"):
257
+ out = step_knowledge["expected_output"]
258
+ if len(out) > 1500:
259
+ out = out[:1500] + "..."
260
+ parts.append(f"Expected output:\n{out}")
261
+ if step_knowledge.get("explanation"):
262
+ parts.append(f"Technical explanation: {step_knowledge[explanation]}")
263
+ if step_knowledge.get("look_for"):
264
+ parts.append("Key observations:\n" + "\n".join(f" - {i}" for i in step_knowledge["look_for"]))
265
+ if step_knowledge.get("key_takeaway"):
266
+ parts.append(f"Key takeaway: {step_knowledge[key_takeaway]}")
267
+ if step_knowledge.get("if_fails"):
268
+ parts.append(f"If command fails: {step_knowledge[if_fails]}")
269
+ if step_knowledge.get("conversation"):
270
+ parts.append(f"Discussion prompt: {step_knowledge[conversation]}")
271
+ if step_knowledge.get("questions"):
272
+ for q in step_knowledge["questions"]:
273
+ parts.append(f"Question: {q.get(text, )} (id: {q.get(id, )}, answer: {q.get(answer, N/A)})")
274
+
275
+ system = INSTRUCTOR_SYSTEM.format(
276
+ room_name=room_name or "Unknown",
277
+ difficulty=difficulty or "unknown",
278
+ room_brief=room_brief or "No brief.",
279
+ phase_name=phase_name or "N/A",
280
+ phase_objective=phase_objective or "N/A",
281
+ step_knowledge="\n".join(parts) if parts else "No additional context.",
282
+ )
283
+
284
+ messages = []
285
+ if conversation_history:
286
+ messages.extend(conversation_history[-30:])
287
+ messages.append({"role": "user", "content": message})
288
+
289
+ result = api.chat_sync(
290
+ messages=messages,
291
+ system=system,
292
+ tools=INSTRUCTOR_TOOLS,
293
+ room_slug=room_slug,
294
+ )
295
+
296
+ if result.get("error"):
297
+ _handle_api_error(result)
298
+ return None, []
299
+
300
+ # Parse Anthropic response format
301
+ text_parts = []
302
+ tool_calls = []
303
+ try:
304
+ content = result.get("content", [])
305
+ for block in content:
306
+ if block.get("type") == "text":
307
+ text_parts.append(block.get("text", ""))
308
+ elif block.get("type") == "tool_use":
309
+ tool_calls.append({
310
+ "id": block.get("id"),
311
+ "name": block.get("name"),
312
+ "input": block.get("input", {}),
313
+ })
314
+ except Exception as e:
315
+ logger.warning(f"Failed to parse response: {e}")
316
+
317
+ return "\n".join(text_parts) if text_parts else None, tool_calls