Загрузить файлы в «WEBSHELL»
gotty.py - WEB шелл с разрешенным списком команд gotty(unsafe).py - WEB шелл без фильтрации команд (допускает любые команды оболочки ЛИНУКС BASH)
This commit is contained in:
512
WEBSHELL/gotty(unsafe).py
Normal file
512
WEBSHELL/gotty(unsafe).py
Normal file
@@ -0,0 +1,512 @@
|
|||||||
|
from flask import Flask, render_template, request, jsonify, session
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.secret_key = os.urandom(24) # Случайный секретный ключ
|
||||||
|
|
||||||
|
|
||||||
|
class CommandSession:
|
||||||
|
def __init__(self):
|
||||||
|
self.sessions = {}
|
||||||
|
|
||||||
|
def create_session(self):
|
||||||
|
session_id = str(uuid.uuid4())
|
||||||
|
self.sessions[session_id] = {
|
||||||
|
'created_at': datetime.now(),
|
||||||
|
'working_dir': os.getcwd(),
|
||||||
|
'history': []
|
||||||
|
}
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
def execute_command(self, session_id, command):
|
||||||
|
if session_id not in self.sessions:
|
||||||
|
return "Сессия не найдена"
|
||||||
|
|
||||||
|
session_data = self.sessions[session_id]
|
||||||
|
|
||||||
|
# Добавляем команду в историю
|
||||||
|
session_data['history'].append({
|
||||||
|
'command': command,
|
||||||
|
'timestamp': datetime.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Выполняем команду в текущей директории сессии
|
||||||
|
process = subprocess.Popen(
|
||||||
|
command,
|
||||||
|
shell=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
cwd=session_data['working_dir'],
|
||||||
|
executable='/bin/bash' if os.name != 'nt' else None
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ждем завершения с таймаутом
|
||||||
|
try:
|
||||||
|
stdout, stderr = process.communicate(timeout=60)
|
||||||
|
return_code = process.returncode
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
process.kill()
|
||||||
|
stdout, stderr = process.communicate()
|
||||||
|
return f"Ошибка: команда выполнялась слишком долго и была остановлена\n\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}"
|
||||||
|
|
||||||
|
# Формируем вывод
|
||||||
|
output = ""
|
||||||
|
if stdout:
|
||||||
|
output += f"STDOUT:\n{stdout}\n"
|
||||||
|
if stderr:
|
||||||
|
output += f"STDERR:\n{stderr}\n"
|
||||||
|
if return_code != 0:
|
||||||
|
output += f"Код возврата: {return_code}"
|
||||||
|
|
||||||
|
return output.strip()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"Ошибка выполнения: {str(e)}"
|
||||||
|
|
||||||
|
def change_directory(self, session_id, new_dir):
|
||||||
|
if session_id not in self.sessions:
|
||||||
|
return False, "Сессия не найдена"
|
||||||
|
|
||||||
|
try:
|
||||||
|
if new_dir == "~":
|
||||||
|
new_dir = os.path.expanduser("~")
|
||||||
|
elif new_dir == "-":
|
||||||
|
# Возврат к предыдущей директории (можно реализовать при необходимости)
|
||||||
|
new_dir = os.getcwd()
|
||||||
|
|
||||||
|
new_dir = os.path.abspath(new_dir)
|
||||||
|
|
||||||
|
if os.path.exists(new_dir) and os.path.isdir(new_dir):
|
||||||
|
os.chdir(new_dir)
|
||||||
|
self.sessions[session_id]['working_dir'] = new_dir
|
||||||
|
return True, f"Директория изменена на: {new_dir}"
|
||||||
|
else:
|
||||||
|
return False, f"Директория не существует: {new_dir}"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return False, f"Ошибка смены директории: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
|
command_manager = CommandSession()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
if 'session_id' not in session:
|
||||||
|
session['session_id'] = command_manager.create_session()
|
||||||
|
return render_template('index.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/execute', methods=['POST'])
|
||||||
|
def execute_command():
|
||||||
|
if 'session_id' not in session:
|
||||||
|
return jsonify({'error': 'Сессия не найдена'})
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
command = data.get('command', '').strip()
|
||||||
|
|
||||||
|
if not command:
|
||||||
|
return jsonify({'error': 'Пустая команда'})
|
||||||
|
|
||||||
|
# Обработка команды cd
|
||||||
|
if command.startswith('cd '):
|
||||||
|
new_dir = command[3:].strip()
|
||||||
|
success, message = command_manager.change_directory(session['session_id'], new_dir)
|
||||||
|
return jsonify({
|
||||||
|
'output': message,
|
||||||
|
'command': command,
|
||||||
|
'timestamp': datetime.now().strftime('%H:%M:%S'),
|
||||||
|
'current_dir': command_manager.sessions[session['session_id']]['working_dir']
|
||||||
|
})
|
||||||
|
|
||||||
|
# Выполняем команду
|
||||||
|
output = command_manager.execute_command(session['session_id'], command)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'output': output,
|
||||||
|
'command': command,
|
||||||
|
'timestamp': datetime.now().strftime('%H:%M:%S'),
|
||||||
|
'current_dir': command_manager.sessions[session['session_id']]['working_dir']
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/sessions', methods=['GET'])
|
||||||
|
def list_sessions():
|
||||||
|
return jsonify({
|
||||||
|
'active_sessions': len(command_manager.sessions),
|
||||||
|
'current_session': session.get('session_id'),
|
||||||
|
'current_dir': command_manager.sessions.get(session.get('session_id', ''), {}).get('working_dir', '')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/history', methods=['GET'])
|
||||||
|
def command_history():
|
||||||
|
if 'session_id' not in session:
|
||||||
|
return jsonify({'error': 'Сессия не найдена'})
|
||||||
|
|
||||||
|
session_data = command_manager.sessions.get(session['session_id'])
|
||||||
|
if not session_data:
|
||||||
|
return jsonify({'error': 'Данные сессии не найдены'})
|
||||||
|
|
||||||
|
history = session_data.get('history', [])[-10:] # Последние 10 команд
|
||||||
|
formatted_history = [
|
||||||
|
{
|
||||||
|
'command': item['command'],
|
||||||
|
'timestamp': item['timestamp'].strftime('%H:%M:%S')
|
||||||
|
}
|
||||||
|
for item in history
|
||||||
|
]
|
||||||
|
|
||||||
|
return jsonify({'history': formatted_history})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/clear-session', methods=['POST'])
|
||||||
|
def clear_session():
|
||||||
|
session_id = session.get('session_id')
|
||||||
|
if session_id in command_manager.sessions:
|
||||||
|
del command_manager.sessions[session_id]
|
||||||
|
session.pop('session_id', None)
|
||||||
|
return jsonify({'message': 'Сессия очищена'})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/system-info', methods=['GET'])
|
||||||
|
def system_info():
|
||||||
|
"""Базовая информация о системе"""
|
||||||
|
try:
|
||||||
|
# Информация о системе
|
||||||
|
system_info = {
|
||||||
|
'platform': os.uname().sysname if hasattr(os, 'uname') else os.name,
|
||||||
|
'current_user': os.getenv('USER', 'Unknown'),
|
||||||
|
'cpu_count': os.cpu_count(),
|
||||||
|
'current_directory': os.getcwd()
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify(system_info)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': f'Не удалось получить информацию о системе: {str(e)}'})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Создаем папку для шаблонов если её нет
|
||||||
|
os.makedirs('templates', exist_ok=True)
|
||||||
|
|
||||||
|
# Создаем HTML шаблон
|
||||||
|
with open('templates/index.html', 'w', encoding='utf-8') as f:
|
||||||
|
f.write('''<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Web Shell - Полный доступ</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg-color: #1e1e1e;
|
||||||
|
--text-color: #00ff00;
|
||||||
|
--error-color: #ff4444;
|
||||||
|
--warning-color: #ffaa00;
|
||||||
|
--info-color: #4488ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
border-bottom: 1px solid #333;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
background-color: var(--warning-color);
|
||||||
|
color: #000;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 10px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal {
|
||||||
|
background-color: #000;
|
||||||
|
border: 1px solid #333;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 20px;
|
||||||
|
height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #000;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt {
|
||||||
|
margin-right: 10px;
|
||||||
|
color: var(--info-color);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-input {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
outline: none;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timestamp {
|
||||||
|
color: #888;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: var(--error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
color: var(--info-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-command {
|
||||||
|
color: #ffff00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #333;
|
||||||
|
color: var(--text-color);
|
||||||
|
border: 1px solid #555;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<h1>Web Shell - Полный доступ к системе</h1>
|
||||||
|
<div class="warning">
|
||||||
|
⚠️ ВНИМАНИЕ: Полный доступ к оболочке системы! Будьте осторожны с командами.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<button onclick="clearTerminal()">Очистить терминал</button>
|
||||||
|
<button onclick="clearSession()">Новая сессия</button>
|
||||||
|
<button onclick="showHistory()">История команд</button>
|
||||||
|
<button onclick="showSystemInfo()">Информация о системе</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="terminal" id="terminal">
|
||||||
|
<div class="output info">
|
||||||
|
Добро пожаловать в Web Shell с полным доступом!<br>
|
||||||
|
Доступны все команды системы. Таймаут выполнения: 60 секунд.<br>
|
||||||
|
Текущая директория: <span id="current-dir"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="command-line">
|
||||||
|
<span class="prompt" id="prompt">$</span>
|
||||||
|
<input type="text" id="command-input" placeholder="Введите любую команду системы...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="status-bar">
|
||||||
|
<span id="session-info">Загрузка...</span>
|
||||||
|
<span id="command-counter">Команд выполнено: 0</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const terminal = document.getElementById('terminal');
|
||||||
|
const commandInput = document.getElementById('command-input');
|
||||||
|
const sessionInfo = document.getElementById('session-info');
|
||||||
|
const commandCounter = document.getElementById('command-counter');
|
||||||
|
const currentDirElement = document.getElementById('current-dir');
|
||||||
|
const promptElement = document.getElementById('prompt');
|
||||||
|
|
||||||
|
let commandCount = 0;
|
||||||
|
|
||||||
|
// Фокус на поле ввода
|
||||||
|
commandInput.focus();
|
||||||
|
|
||||||
|
// Загружаем начальную информацию
|
||||||
|
loadSessionInfo();
|
||||||
|
loadSystemInfo();
|
||||||
|
|
||||||
|
// Обработка отправки команды
|
||||||
|
commandInput.addEventListener('keypress', function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
const command = commandInput.value.trim();
|
||||||
|
if (command) {
|
||||||
|
executeCommand(command);
|
||||||
|
commandInput.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function executeCommand(command) {
|
||||||
|
// Показываем команду в терминале
|
||||||
|
addOutput('$ ' + command, 'user-command');
|
||||||
|
|
||||||
|
// Отправляем на сервер
|
||||||
|
fetch('/execute', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({command: command})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
addOutput(data.error, 'error');
|
||||||
|
} else {
|
||||||
|
addOutput(data.output, 'command-output');
|
||||||
|
// Обновляем текущую директорию
|
||||||
|
if (data.current_dir) {
|
||||||
|
currentDirElement.textContent = data.current_dir;
|
||||||
|
promptElement.textContent = `[${data.current_dir.split('/').pop() || data.current_dir}] $`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commandCount++;
|
||||||
|
commandCounter.textContent = `Команд выполнено: ${commandCount}`;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
addOutput('Ошибка сети: ' + error, 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addOutput(text, className) {
|
||||||
|
const output = document.createElement('div');
|
||||||
|
output.className = `output ${className}`;
|
||||||
|
output.textContent = text;
|
||||||
|
terminal.appendChild(output);
|
||||||
|
terminal.scrollTop = terminal.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearTerminal() {
|
||||||
|
terminal.innerHTML = '<div class="output info">Терминал очищен</div>';
|
||||||
|
loadSystemInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSession() {
|
||||||
|
if (confirm('Вы уверены, что хотите начать новую сессию?')) {
|
||||||
|
fetch('/clear-session', {method: 'POST'})
|
||||||
|
.then(() => {
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showHistory() {
|
||||||
|
fetch('/history')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
addOutput(data.error, 'error');
|
||||||
|
} else {
|
||||||
|
let historyText = 'Последние команды:\\n';
|
||||||
|
data.history.forEach((item, index) => {
|
||||||
|
historyText += `${index + 1}. [${item.timestamp}] ${item.command}\\n`;
|
||||||
|
});
|
||||||
|
addOutput(historyText, 'info');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSystemInfo() {
|
||||||
|
fetch('/system-info')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
addOutput(data.error, 'error');
|
||||||
|
} else {
|
||||||
|
let infoText = 'Информация о системе:\\n';
|
||||||
|
for (const [key, value] of Object.entries(data)) {
|
||||||
|
infoText += `${key}: ${value}\\n`;
|
||||||
|
}
|
||||||
|
addOutput(infoText, 'info');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSessionInfo() {
|
||||||
|
fetch('/sessions')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
sessionInfo.textContent = `Активных сессий: ${data.active_sessions}`;
|
||||||
|
if (data.current_dir) {
|
||||||
|
currentDirElement.textContent = data.current_dir;
|
||||||
|
promptElement.textContent = `[${data.current_dir.split('/').pop() || data.current_dir}] $`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSystemInfo() {
|
||||||
|
// Загружаем базовую информацию о системе при старте
|
||||||
|
showSystemInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Автодополнение по Tab
|
||||||
|
commandInput.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Tab') {
|
||||||
|
e.preventDefault();
|
||||||
|
// Базовая реализация автодополнения
|
||||||
|
const currentInput = commandInput.value;
|
||||||
|
// Можно расширить для реального автодополнения
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>''')
|
||||||
|
|
||||||
|
print("Запуск Web Shell с полным доступом...")
|
||||||
|
print("⚠️ ПРЕДУПРЕЖДЕНИЕ: Этот сервер предоставляет полный доступ к системе!")
|
||||||
|
print("🚫 Не используйте в production без дополнительных мер безопасности!")
|
||||||
|
print("🌐 Сервер запущен: http://0.0.0.0:5000")
|
||||||
|
|
||||||
|
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||||
273
WEBSHELL/gotty.py
Normal file
273
WEBSHELL/gotty.py
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
from flask import Flask, render_template, request, jsonify, session
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.secret_key = 'your-secret-key-here' # Измените на случайный ключ
|
||||||
|
|
||||||
|
# Безопасные команды (можно расширить)
|
||||||
|
SAFE_COMMANDS = ['ls', 'pwd', 'whoami', 'date', 'uptime', 'uname', 'df', 'free']
|
||||||
|
|
||||||
|
|
||||||
|
class CommandSession:
|
||||||
|
def __init__(self):
|
||||||
|
self.sessions = {}
|
||||||
|
|
||||||
|
def create_session(self):
|
||||||
|
session_id = str(uuid.uuid4())
|
||||||
|
self.sessions[session_id] = {
|
||||||
|
'created_at': datetime.now(),
|
||||||
|
'working_dir': os.getcwd()
|
||||||
|
}
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
def execute_command(self, session_id, command):
|
||||||
|
if session_id not in self.sessions:
|
||||||
|
return "Сессия не найдена"
|
||||||
|
|
||||||
|
session_data = self.sessions[session_id]
|
||||||
|
|
||||||
|
# Безопасность: проверяем команду
|
||||||
|
cmd_parts = command.strip().split()
|
||||||
|
if not cmd_parts:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
base_command = cmd_parts[0]
|
||||||
|
|
||||||
|
# Ограничиваем доступные команды для безопасности
|
||||||
|
if base_command not in SAFE_COMMANDS:
|
||||||
|
return f"Команда '{base_command}' не разрешена для выполнения"
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Выполняем команду в текущей директории сессии
|
||||||
|
result = subprocess.run(
|
||||||
|
command,
|
||||||
|
shell=True,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
cwd=session_data['working_dir'],
|
||||||
|
timeout=30 # Таймаут 30 секунд
|
||||||
|
)
|
||||||
|
|
||||||
|
output = result.stdout
|
||||||
|
if result.stderr:
|
||||||
|
output += f"\nОшибка: {result.stderr}"
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return "Ошибка: команда выполнялась слишком долго"
|
||||||
|
except Exception as e:
|
||||||
|
return f"Ошибка выполнения: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
|
command_manager = CommandSession()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
if 'session_id' not in session:
|
||||||
|
session['session_id'] = command_manager.create_session()
|
||||||
|
return render_template('index.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/execute', methods=['POST'])
|
||||||
|
def execute_command():
|
||||||
|
if 'session_id' not in session:
|
||||||
|
return jsonify({'error': 'Сессия не найдена'})
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
command = data.get('command', '').strip()
|
||||||
|
|
||||||
|
if not command:
|
||||||
|
return jsonify({'error': 'Пустая команда'})
|
||||||
|
|
||||||
|
# Выполняем команду
|
||||||
|
output = command_manager.execute_command(session['session_id'], command)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'output': output,
|
||||||
|
'command': command,
|
||||||
|
'timestamp': datetime.now().strftime('%H:%M:%S')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/sessions', methods=['GET'])
|
||||||
|
def list_sessions():
|
||||||
|
return jsonify({
|
||||||
|
'active_sessions': len(command_manager.sessions),
|
||||||
|
'current_session': session.get('session_id')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/clear-session', methods=['POST'])
|
||||||
|
def clear_session():
|
||||||
|
session_id = session.get('session_id')
|
||||||
|
if session_id in command_manager.sessions:
|
||||||
|
del command_manager.sessions[session_id]
|
||||||
|
session.pop('session_id', None)
|
||||||
|
return jsonify({'message': 'Сессия очищена'})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Создаем папку для шаблонов если её нет
|
||||||
|
os.makedirs('templates', exist_ok=True)
|
||||||
|
|
||||||
|
# Создаем HTML шаблон
|
||||||
|
with open('templates/index.html', 'w', encoding='utf-8') as f:
|
||||||
|
f.write('''<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Web Shell</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
color: #00ff00;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.terminal {
|
||||||
|
background-color: #000;
|
||||||
|
border: 1px solid #333;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 20px;
|
||||||
|
height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.output {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.command-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.prompt {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
#command-input {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #00ff00;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
outline: none;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.timestamp {
|
||||||
|
color: #888;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #ff4444;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
color: #4488ff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Web Shell Interface</h1>
|
||||||
|
<div class="info">
|
||||||
|
Разрешенные команды: {{ safe_commands|join(', ') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="terminal" id="terminal">
|
||||||
|
<div class="output info">
|
||||||
|
Добро пожаловать в Web Shell!<br>
|
||||||
|
Введите команду ниже. Доступные команды: {{ safe_commands|join(', ') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="command-line">
|
||||||
|
<span class="prompt">$</span>
|
||||||
|
<input type="text" id="command-input" placeholder="Введите команду...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 20px;">
|
||||||
|
<button onclick="clearTerminal()">Очистить терминал</button>
|
||||||
|
<button onclick="clearSession()">Новая сессия</button>
|
||||||
|
<span id="session-info"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const terminal = document.getElementById('terminal');
|
||||||
|
const commandInput = document.getElementById('command-input');
|
||||||
|
const sessionInfo = document.getElementById('session-info');
|
||||||
|
|
||||||
|
// Фокус на поле ввода
|
||||||
|
commandInput.focus();
|
||||||
|
|
||||||
|
// Обработка отправки команды
|
||||||
|
commandInput.addEventListener('keypress', function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
const command = commandInput.value.trim();
|
||||||
|
if (command) {
|
||||||
|
executeCommand(command);
|
||||||
|
commandInput.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function executeCommand(command) {
|
||||||
|
// Показываем команду в терминале
|
||||||
|
addOutput('$ ' + command, 'user-command');
|
||||||
|
|
||||||
|
// Отправляем на сервер
|
||||||
|
fetch('/execute', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({command: command})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
addOutput(data.error, 'error');
|
||||||
|
} else {
|
||||||
|
addOutput(data.output, 'command-output');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
addOutput('Ошибка: ' + error, 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addOutput(text, className) {
|
||||||
|
const output = document.createElement('div');
|
||||||
|
output.className = `output ${className}`;
|
||||||
|
output.textContent = text;
|
||||||
|
terminal.appendChild(output);
|
||||||
|
terminal.scrollTop = terminal.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearTerminal() {
|
||||||
|
terminal.innerHTML = '<div class="output info">Терминал очищен</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSession() {
|
||||||
|
fetch('/clear-session', {method: 'POST'})
|
||||||
|
.then(() => {
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем информацию о сессии
|
||||||
|
fetch('/sessions')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
sessionInfo.textContent = `Активных сессий: ${data.active_sessions}`;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>''')
|
||||||
|
|
||||||
|
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||||
Reference in New Issue
Block a user