itsmrop/Kali-Linux
0
1import os2import sys3import json4import subprocess5import shlex6import threading7import time8from flask import Flask, request, jsonify, render_template9from flask_socketio import SocketIO, emit10from flask_cors import CORS11import pexpect12 13app = Flask(__name__)14app.config['SECRET_KEY'] = os.urandom(24)15 16CORS(app, resources={17 r"/api/*": {18 "origins": ["*"]19 }20})21 22# FIX: Force threading mode (eventlet fails in this container)23socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading')24 25SPACE_NAME = "itsmrop/Kali-Linux"26ALLOWED_TOOLS = {27 'ls': 'List directory', 'pwd': 'Print working directory', 'cat': 'View files',28 'echo': 'Echo text', 'whoami': 'Current user', 'id': 'User identity',29 'uname': 'System info', 'ps': 'Process status', 'grep': 'Search text',30 'awk': 'Text processing', 'sed': 'Stream editor', 'curl': 'Transfer data',31 'wget': 'Downloader', 'nmap': 'Network scanner', 'hashcat': 'Password cracker',32 'hashid': 'Hash identifier', 'hydra': 'Login cracker', 'dirb': 'Web scanner',33 'gobuster': 'Directory buster', 'sqlmap': 'SQL injection', 'john': 'John the Ripper',34 'nc': 'Netcat', 'ping': 'Network test', 'traceroute': 'Route trace',35 'netstat': 'Network stats', 'ifconfig': 'Network interfaces', 'tcpdump': 'Packet capture',36 'aircrack-ng': 'Wireless tools', 'nikto': 'Web scanner'37}38 39class CommandRunner:40 def __init__(self):41 self.processes = {}42 self.process_counter = 043 44 def execute_streaming(self, command, session_id):45 self.process_counter += 146 proc_id = self.process_counter47 48 try:49 interactive_keywords = ['hydra', 'sqlmap', 'john', 'msfconsole', 'burp', 'nc -l', 'ncat']50 is_interactive = any(k in command for k in interactive_keywords)51 52 if is_interactive:53 self._run_interactive(command, session_id, proc_id)54 else:55 self._run_standard(command, session_id, proc_id)56 except Exception as e:57 socketio.emit('command_error', {58 'proc_id': proc_id, 'error': str(e), 'session_id': session_id59 })60 61 def _run_standard(self, command, session_id, proc_id):62 try:63 process = subprocess.Popen(64 shlex.split(command),65 stdout=subprocess.PIPE,66 stderr=subprocess.STDOUT,67 text=True,68 bufsize=1,69 cwd='/app',70 env={**os.environ, 'TERM': 'xterm-256color'}71 )72 self.processes[proc_id] = process73 74 for line in iter(process.stdout.readline, ''):75 if line:76 socketio.emit('command_output', {77 'proc_id': proc_id, 'data': line, 'session_id': session_id78 })79 socketio.sleep(0.01)80 81 process.stdout.close()82 return_code = process.wait()83 84 if proc_id in self.processes:85 del self.processes[proc_id]86 87 socketio.emit('command_complete', {88 'proc_id': proc_id, 'returncode': return_code, 'session_id': session_id89 })90 except Exception as e:91 socketio.emit('command_error', {92 'proc_id': proc_id, 'error': str(e), 'session_id': session_id93 })94 95 def _run_interactive(self, command, session_id, proc_id):96 try:97 child = pexpect.spawn(command, encoding='utf-8', timeout=None, cwd='/app')98 child.setwinsize(24, 80)99 self.processes[proc_id] = child100 101 # FIX: Raw strings for regex patterns102 patterns = [103 pexpect.EOF, 104 pexpect.TIMEOUT,105 r'[Pp]assword:\s*',106 r'[Uu]sername:\s*',107 r'[Ee]nter\s+.*:\s*',108 r'[Yy]es/[Nn]o',109 r'[Cc]ontinue\?',110 r'.*\$\s*',111 r'.*#\s*',112 r'\n'113 ]114 115 while True:116 try:117 idx = child.expect(patterns, timeout=0.1)118 119 if idx == 0:120 break121 elif idx == 1:122 socketio.sleep(0.05)123 continue124 elif idx in [2, 3, 4, 5, 6, 7, 8]:125 output = child.before126 if output:127 socketio.emit('command_output', {128 'proc_id': proc_id, 'data': output, 'session_id': session_id129 })130 socketio.emit('command_prompt', {131 'proc_id': proc_id,132 'prompt': child.after,133 'type': 'input_required',134 'session_id': session_id135 })136 break137 elif idx == 9:138 if child.before:139 socketio.emit('command_output', {140 'proc_id': proc_id, 'data': child.before + '\n', 'session_id': session_id141 })142 except:143 break144 145 if child.isalive():146 child.close()147 148 if proc_id in self.processes:149 del self.processes[proc_id]150 151 socketio.emit('command_complete', {152 'proc_id': proc_id,153 'returncode': child.exitstatus if child.exitstatus else 0,154 'session_id': session_id155 })156 except Exception as e:157 socketio.emit('command_error', {158 'proc_id': proc_id, 'error': str(e), 'session_id': session_id159 })160 161runner = CommandRunner()162 163@app.route('/')164def index():165 return render_template('index.html', space_name=SPACE_NAME)166 167@app.route('/api/execute', methods=['POST'])168def execute_sync():169 data = request.json or {}170 command = data.get('command', '').strip()171 timeout = data.get('timeout', 60)172 173 if not command:174 return jsonify({'error': 'Empty command'}), 400175 176 base_cmd = command.split()[0]177 if base_cmd not in ALLOWED_TOOLS and not data.get('force'):178 return jsonify({179 'error': f'Command not allowed: {base_cmd}',180 'allowed': list(ALLOWED_TOOLS.keys())[:20]181 }), 403182 183 try:184 process = subprocess.run(185 shlex.split(command),186 capture_output=True,187 text=True,188 timeout=timeout,189 cwd='/app'190 )191 return jsonify({192 'stdout': process.stdout,193 'stderr': process.stderr,194 'returncode': process.returncode,195 'command': command,196 'space': SPACE_NAME197 })198 except subprocess.TimeoutExpired:199 return jsonify({'error': 'Timeout'}), 408200 except Exception as e:201 return jsonify({'error': str(e)}), 500202 203@app.route('/api/tools')204def list_tools():205 available = {}206 for tool, desc in ALLOWED_TOOLS.items():207 if tool in ['ls', 'pwd', 'cat', 'echo', 'whoami']:208 continue209 try:210 subprocess.run(['which', tool], capture_output=True, check=True)211 available[tool] = desc212 except:213 pass214 return jsonify({215 'space': SPACE_NAME,216 'tools': available,217 'basic_commands': ['ls', 'pwd', 'cat', 'echo', 'whoami', 'grep', 'curl']218 })219 220@app.route('/api/system')221def system_info():222 try:223 uname = subprocess.run(['uname', '-a'], capture_output=True, text=True).stdout.strip()224 return jsonify({225 'space': SPACE_NAME,226 'uname': uname,227 'workspace': '/app',228 'user': 'root'229 })230 except Exception as e:231 return jsonify({'error': str(e)}), 500232 233@socketio.on('connect')234def handle_connect():235 emit('connected', {'status': 'Connected', 'space': SPACE_NAME})236 237@socketio.on('execute_command')238def handle_execute(data):239 command = data.get('command', '').strip()240 session_id = data.get('session_id', 'default')241 if not command:242 return243 244 thread = threading.Thread(target=runner.execute_streaming, args=(command, session_id))245 thread.daemon = True246 thread.start()247 248@socketio.on('send_input')249def handle_input(data):250 proc_id = data.get('proc_id')251 user_input = data.get('input', '')252 if proc_id in runner.processes:253 process = runner.processes[proc_id]254 if isinstance(process, pexpect.spawn):255 process.sendline(user_input)256 257@socketio.on('kill_command')258def handle_kill(data):259 proc_id = data.get('proc_id')260 if proc_id in runner.processes:261 process = runner.processes[proc_id]262 try:263 if isinstance(process, pexpect.spawn):264 process.close(force=True)265 else:266 process.kill()267 del runner.processes[proc_id]268 emit('command_killed', {'proc_id': proc_id})269 except Exception as e:270 emit('error', {'error': str(e)})271 272if __name__ == '__main__':273 os.makedirs('/app/workspace', exist_ok=True)274 print(f"[*] Starting {SPACE_NAME} on port 7860")275 socketio.run(app, host='0.0.0.0', port=7860, debug=False, allow_unsafe_werkzeug=True)