mirror of
https://github.com/mblanke/StrikePackageGPT.git
synced 2026-03-01 14:20:21 -05:00
Add bidirectional command capture - CLI commands now visible in dashboard
Co-authored-by: mblanke <9078342+mblanke@users.noreply.github.com>
This commit is contained in:
@@ -729,6 +729,85 @@ async def clear_scans():
|
||||
return {"status": "cleared", "message": "All scan history cleared"}
|
||||
|
||||
|
||||
# ============== Interactive Command Capture ==============
|
||||
|
||||
@app.get("/commands/captured")
|
||||
async def get_captured_commands(limit: int = 50, since: Optional[str] = None):
|
||||
"""
|
||||
Get commands that were run directly in the Kali container.
|
||||
These are captured via the command logging system in interactive shells.
|
||||
"""
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(
|
||||
f"{KALI_EXECUTOR_URL}/captured_commands",
|
||||
params={"limit": limit, "since": since} if since else {"limit": limit},
|
||||
timeout=10.0
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
return {"commands": [], "error": "Could not retrieve captured commands"}
|
||||
|
||||
captured = response.json()
|
||||
commands = captured.get("commands", [])
|
||||
|
||||
# Import captured commands into scan_results for unified history
|
||||
for cmd in commands:
|
||||
cmd_id = cmd.get("command_id")
|
||||
if cmd_id and cmd_id not in scan_results:
|
||||
scan_results[cmd_id] = {
|
||||
"scan_id": cmd_id,
|
||||
"tool": cmd.get("command", "").split()[0] if cmd.get("command") else "unknown",
|
||||
"target": "interactive",
|
||||
"scan_type": "manual",
|
||||
"command": cmd.get("command"),
|
||||
"status": cmd.get("status", "completed"),
|
||||
"started_at": cmd.get("timestamp"),
|
||||
"completed_at": cmd.get("completed_at"),
|
||||
"result": {
|
||||
"stdout": cmd.get("stdout", ""),
|
||||
"stderr": cmd.get("stderr", ""),
|
||||
"exit_code": cmd.get("exit_code"),
|
||||
"duration": cmd.get("duration")
|
||||
},
|
||||
"source": cmd.get("source", "interactive_shell"),
|
||||
"user": cmd.get("user"),
|
||||
"working_dir": cmd.get("working_dir")
|
||||
}
|
||||
|
||||
# Parse output if available
|
||||
if cmd.get("stdout"):
|
||||
tool = cmd.get("command", "").split()[0]
|
||||
parsed = parse_tool_output(tool, cmd.get("stdout", ""))
|
||||
scan_results[cmd_id]["parsed"] = parsed
|
||||
|
||||
return {
|
||||
"commands": commands,
|
||||
"count": len(commands),
|
||||
"imported_to_history": True,
|
||||
"message": "Captured commands are now visible in scan history"
|
||||
}
|
||||
|
||||
except httpx.ConnectError:
|
||||
return {"commands": [], "error": "Kali executor service not available"}
|
||||
except Exception as e:
|
||||
return {"commands": [], "error": str(e)}
|
||||
|
||||
|
||||
@app.post("/commands/sync")
|
||||
async def sync_captured_commands():
|
||||
"""
|
||||
Sync all captured commands from the Kali container into the unified scan history.
|
||||
This allows commands run directly in the container to appear in the dashboard.
|
||||
"""
|
||||
result = await get_captured_commands(limit=1000)
|
||||
return {
|
||||
"status": "synced",
|
||||
"imported_count": result.get("count", 0),
|
||||
"message": "All captured commands are now visible in dashboard history"
|
||||
}
|
||||
|
||||
|
||||
# ============== Output Parsing ==============
|
||||
|
||||
def parse_tool_output(tool: str, output: str) -> Dict[str, Any]:
|
||||
|
||||
@@ -498,6 +498,88 @@ async def list_installed_tools():
|
||||
return {"installed_tools": installed}
|
||||
|
||||
|
||||
@app.get("/captured_commands")
|
||||
async def get_captured_commands(limit: int = 50, since: Optional[str] = None):
|
||||
"""
|
||||
Get commands that were captured from interactive shell sessions in the Kali container.
|
||||
These are commands run directly by users via docker exec or SSH.
|
||||
"""
|
||||
global kali_container
|
||||
|
||||
if not kali_container:
|
||||
raise HTTPException(status_code=503, detail="Kali container not available")
|
||||
|
||||
try:
|
||||
kali_container.reload()
|
||||
|
||||
# Read command history from the shared volume
|
||||
cmd = ["bash", "-c", "cd /workspace/.command_history && ls -t *.json 2>/dev/null | head -n {}".format(limit)]
|
||||
exit_code, output = kali_container.exec_run(cmd=cmd, demux=True)
|
||||
|
||||
if exit_code != 0 or not output[0]:
|
||||
return {"commands": [], "count": 0}
|
||||
|
||||
# Get list of log files
|
||||
log_files = output[0].decode('utf-8', errors='replace').strip().split('\n')
|
||||
log_files = [f for f in log_files if f.strip()]
|
||||
|
||||
commands = []
|
||||
for log_file in log_files:
|
||||
try:
|
||||
# Read each JSON log file
|
||||
read_cmd = ["cat", f"/workspace/.command_history/{log_file}"]
|
||||
exit_code, output = kali_container.exec_run(cmd=read_cmd, demux=True)
|
||||
|
||||
if exit_code == 0 and output[0]:
|
||||
cmd_data = json.loads(output[0].decode('utf-8', errors='replace'))
|
||||
|
||||
# Filter by timestamp if requested
|
||||
if since:
|
||||
cmd_timestamp = cmd_data.get("timestamp", "")
|
||||
if cmd_timestamp < since:
|
||||
continue
|
||||
|
||||
commands.append(cmd_data)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return {
|
||||
"commands": commands,
|
||||
"count": len(commands),
|
||||
"source": "interactive_shell_capture"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Error reading captured commands: {str(e)}")
|
||||
|
||||
|
||||
@app.delete("/captured_commands/clear")
|
||||
async def clear_captured_commands():
|
||||
"""Clear all captured command history."""
|
||||
global kali_container
|
||||
|
||||
if not kali_container:
|
||||
raise HTTPException(status_code=503, detail="Kali container not available")
|
||||
|
||||
try:
|
||||
kali_container.reload()
|
||||
|
||||
# Clear the command history directory
|
||||
cmd = ["bash", "-c", "rm -f /workspace/.command_history/*.json"]
|
||||
exit_code, _ = kali_container.exec_run(cmd=cmd)
|
||||
|
||||
if exit_code == 0:
|
||||
return {"status": "cleared", "message": "All captured command history cleared"}
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail="Failed to clear history")
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/allowed-commands")
|
||||
async def get_allowed_commands():
|
||||
"""Get list of allowed commands for security validation."""
|
||||
|
||||
@@ -10,18 +10,30 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install additional Python tools
|
||||
# Install additional Python tools and utilities for command logging
|
||||
RUN pip3 install --break-system-packages \
|
||||
requests \
|
||||
beautifulsoup4 \
|
||||
shodan \
|
||||
censys
|
||||
|
||||
# Install jq and uuid-runtime for command logging
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
jq \
|
||||
uuid-runtime \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create workspace directory
|
||||
WORKDIR /workspace
|
||||
|
||||
# Copy entrypoint script
|
||||
# Copy scripts
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
COPY command_logger.sh /usr/local/bin/command_logger.sh
|
||||
COPY capture_wrapper.sh /usr/local/bin/capture
|
||||
RUN chmod +x /entrypoint.sh /usr/local/bin/command_logger.sh /usr/local/bin/capture
|
||||
|
||||
# Create command history directory
|
||||
RUN mkdir -p /workspace/.command_history
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
76
services/kali/capture_wrapper.sh
Normal file
76
services/kali/capture_wrapper.sh
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
# Output Capture Wrapper for Security Tools
|
||||
# Wraps command execution to capture stdout/stderr and save results
|
||||
|
||||
COMMAND_LOG_DIR="${COMMAND_LOG_DIR:-/workspace/.command_history}"
|
||||
mkdir -p "$COMMAND_LOG_DIR"
|
||||
|
||||
# Get command from arguments
|
||||
cmd_string="$@"
|
||||
[[ -z "$cmd_string" ]] && exit 1
|
||||
|
||||
# Generate unique ID
|
||||
cmd_id=$(uuidgen 2>/dev/null || echo "$(date +%s)-$$")
|
||||
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
output_file="$COMMAND_LOG_DIR/${cmd_id}.json"
|
||||
stdout_file="$COMMAND_LOG_DIR/${cmd_id}.stdout"
|
||||
stderr_file="$COMMAND_LOG_DIR/${cmd_id}.stderr"
|
||||
|
||||
# Create initial log entry
|
||||
cat > "$output_file" << EOF
|
||||
{
|
||||
"command_id": "$cmd_id",
|
||||
"command": $(echo "$cmd_string" | jq -Rs .),
|
||||
"timestamp": "$timestamp",
|
||||
"user": "$(whoami)",
|
||||
"working_dir": "$(pwd)",
|
||||
"source": "capture_wrapper",
|
||||
"status": "running"
|
||||
}
|
||||
EOF
|
||||
|
||||
# Execute command and capture output
|
||||
start_time=$(date +%s)
|
||||
set +e
|
||||
eval "$cmd_string" > "$stdout_file" 2> "$stderr_file"
|
||||
exit_code=$?
|
||||
set -e
|
||||
end_time=$(date +%s)
|
||||
duration=$((end_time - start_time))
|
||||
completed_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
# Read captured output
|
||||
stdout_content=$(cat "$stdout_file" 2>/dev/null || echo "")
|
||||
stderr_content=$(cat "$stderr_file" 2>/dev/null || echo "")
|
||||
|
||||
# Update log entry with results
|
||||
cat > "$output_file" << EOF
|
||||
{
|
||||
"command_id": "$cmd_id",
|
||||
"command": $(echo "$cmd_string" | jq -Rs .),
|
||||
"timestamp": "$timestamp",
|
||||
"completed_at": "$completed_at",
|
||||
"user": "$(whoami)",
|
||||
"working_dir": "$(pwd)",
|
||||
"source": "capture_wrapper",
|
||||
"status": "$([ $exit_code -eq 0 ] && echo 'completed' || echo 'failed')",
|
||||
"exit_code": $exit_code,
|
||||
"duration": $duration,
|
||||
"stdout": $(echo "$stdout_content" | jq -Rs .),
|
||||
"stderr": $(echo "$stderr_content" | jq -Rs .)
|
||||
}
|
||||
EOF
|
||||
|
||||
# Clean up temp files
|
||||
rm -f "$stdout_file" "$stderr_file"
|
||||
|
||||
# Output results to terminal
|
||||
cat "$stdout_file" 2>/dev/null || true
|
||||
cat "$stderr_file" >&2 2>/dev/null || true
|
||||
|
||||
echo "" >&2
|
||||
echo "[StrikePackageGPT] Command captured: $cmd_id" >&2
|
||||
echo "[StrikePackageGPT] Exit code: $exit_code | Duration: ${duration}s" >&2
|
||||
echo "[StrikePackageGPT] Results available in dashboard" >&2
|
||||
|
||||
exit $exit_code
|
||||
53
services/kali/command_logger.sh
Normal file
53
services/kali/command_logger.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# Command Logger for StrikePackageGPT
|
||||
# Logs all commands executed in interactive shell sessions
|
||||
# Results are captured and made available to the API
|
||||
|
||||
COMMAND_LOG_DIR="${COMMAND_LOG_DIR:-/workspace/.command_history}"
|
||||
mkdir -p "$COMMAND_LOG_DIR"
|
||||
|
||||
# Function to log command execution
|
||||
log_command() {
|
||||
local cmd="$1"
|
||||
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
local cmd_id=$(uuidgen 2>/dev/null || echo "$(date +%s)-$$")
|
||||
local output_file="$COMMAND_LOG_DIR/${cmd_id}.json"
|
||||
|
||||
# Skip logging for cd, ls, echo, and other basic commands
|
||||
local first_word=$(echo "$cmd" | awk '{print $1}')
|
||||
case "$first_word" in
|
||||
cd|ls|pwd|echo|exit|clear|history|source|alias|\
|
||||
export|unset|env|printenv|which|type|whereis)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Skip empty commands
|
||||
[[ -z "$cmd" ]] && return 0
|
||||
|
||||
# Create log entry with metadata
|
||||
cat > "$output_file" << EOF
|
||||
{
|
||||
"command_id": "$cmd_id",
|
||||
"command": $(echo "$cmd" | jq -Rs .),
|
||||
"timestamp": "$timestamp",
|
||||
"user": "$(whoami)",
|
||||
"working_dir": "$(pwd)",
|
||||
"source": "interactive_shell",
|
||||
"status": "pending"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "[StrikePackageGPT] Command logged: $cmd_id" >&2
|
||||
echo "[StrikePackageGPT] Results will be visible in dashboard" >&2
|
||||
}
|
||||
|
||||
# PROMPT_COMMAND hook to log each command after execution
|
||||
export PROMPT_COMMAND='history -a; if [ -n "$LAST_CMD" ]; then log_command "$LAST_CMD"; fi; LAST_CMD=$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//"); '
|
||||
|
||||
# Also trap DEBUG for more comprehensive logging
|
||||
trap 'LAST_EXEC_CMD="$BASH_COMMAND"' DEBUG
|
||||
|
||||
echo "[StrikePackageGPT] Command logging enabled"
|
||||
echo "[StrikePackageGPT] All security tool commands will be captured and visible in the dashboard"
|
||||
echo ""
|
||||
@@ -1,8 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Enable command logging by default for all bash sessions
|
||||
echo 'source /usr/local/bin/command_logger.sh' >> /root/.bashrc
|
||||
echo 'export COMMAND_LOG_DIR=/workspace/.command_history' >> /root/.bashrc
|
||||
|
||||
# Create convenience aliases for captured execution
|
||||
cat >> /root/.bashrc << 'ALIASES'
|
||||
# Convenience alias to run commands with automatic capture
|
||||
alias run='capture'
|
||||
|
||||
# Helper function to show recent commands
|
||||
recent_commands() {
|
||||
echo "Recent commands logged:"
|
||||
ls -lt /workspace/.command_history/*.json 2>/dev/null | head -10 | while read line; do
|
||||
file=$(echo "$line" | awk '{print $NF}')
|
||||
[ -f "$file" ] && jq -r '"\(.timestamp) - \(.command) [\(.status)]"' "$file" 2>/dev/null
|
||||
done
|
||||
}
|
||||
alias recent='recent_commands'
|
||||
ALIASES
|
||||
|
||||
echo "=================================================="
|
||||
echo " StrikePackageGPT - Kali Container"
|
||||
echo " Security Tools Ready"
|
||||
echo " Security Tools Ready + Command Capture Enabled"
|
||||
echo "=================================================="
|
||||
echo ""
|
||||
echo "Available tools:"
|
||||
@@ -13,6 +33,21 @@ echo " - sqlmap (SQL injection)"
|
||||
echo " - hydra (brute force)"
|
||||
echo " - metasploit (exploitation)"
|
||||
echo " - searchsploit (exploit database)"
|
||||
echo " - aircrack-ng, wifite (wireless)"
|
||||
echo " - john, hashcat (password cracking)"
|
||||
echo " - and 600+ more Kali tools"
|
||||
echo ""
|
||||
echo "🔄 BIDIRECTIONAL CAPTURE ENABLED 🔄"
|
||||
echo ""
|
||||
echo "Commands you run here will be captured and visible in:"
|
||||
echo " • Dashboard history"
|
||||
echo " • API scan results"
|
||||
echo " • Network visualization"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " • Run commands normally: nmap -sV 192.168.1.1"
|
||||
echo " • Use 'capture' prefix for explicit capture: capture nmap -sV 192.168.1.1"
|
||||
echo " • View recent: recent"
|
||||
echo ""
|
||||
echo "Container is ready for security testing."
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user