forked from p15670423/monkey
Add mysql fingerprinting and improve struct parsing
This commit is contained in:
parent
cb7bb56588
commit
10c9648854
|
@ -3,7 +3,7 @@ import sys
|
||||||
from network.range import FixedRange, RelativeRange, ClassCRange
|
from network.range import FixedRange, RelativeRange, ClassCRange
|
||||||
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter,\
|
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter,\
|
||||||
SambaCryExploiter
|
SambaCryExploiter
|
||||||
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger
|
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger
|
||||||
from abc import ABCMeta
|
from abc import ABCMeta
|
||||||
from itertools import product
|
from itertools import product
|
||||||
import uuid
|
import uuid
|
||||||
|
@ -140,7 +140,7 @@ class Configuration(object):
|
||||||
max_iterations = 1
|
max_iterations = 1
|
||||||
|
|
||||||
scanner_class = TcpScanner
|
scanner_class = TcpScanner
|
||||||
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger]
|
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger]
|
||||||
exploiter_classes = [SmbExploiter, WmiExploiter, RdpExploiter, Ms08_067_Exploiter, # Windows exploits
|
exploiter_classes = [SmbExploiter, WmiExploiter, RdpExploiter, Ms08_067_Exploiter, # Windows exploits
|
||||||
SSHExploiter, ShellShockExploiter, SambaCryExploiter # Linux
|
SSHExploiter, ShellShockExploiter, SambaCryExploiter # Linux
|
||||||
]
|
]
|
||||||
|
|
|
@ -39,7 +39,8 @@
|
||||||
"SSHFinger",
|
"SSHFinger",
|
||||||
"PingScanner",
|
"PingScanner",
|
||||||
"HTTPFinger",
|
"HTTPFinger",
|
||||||
"SMBFinger"
|
"SMBFinger",
|
||||||
|
"MySQLFinger"
|
||||||
],
|
],
|
||||||
"max_iterations": 3,
|
"max_iterations": 3,
|
||||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||||
|
|
|
@ -23,5 +23,6 @@ from tcp_scanner import TcpScanner
|
||||||
from smbfinger import SMBFinger
|
from smbfinger import SMBFinger
|
||||||
from sshfinger import SSHFinger
|
from sshfinger import SSHFinger
|
||||||
from httpfinger import HTTPFinger
|
from httpfinger import HTTPFinger
|
||||||
|
from mysqlfinger import MySQLFinger
|
||||||
from info import local_ips
|
from info import local_ips
|
||||||
from info import get_free_tcp_port
|
from info import get_free_tcp_port
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
import socket
|
||||||
|
import logging
|
||||||
|
from network import HostFinger
|
||||||
|
from .tools import struct_unpack_tracker, struct_unpack_tracker_string
|
||||||
|
from model.host import VictimHost
|
||||||
|
|
||||||
|
MYSQL_PORT = 3306
|
||||||
|
SQL_SERVICE = 'mysqld-3306'
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MySQLFinger(HostFinger):
|
||||||
|
"""
|
||||||
|
Fingerprints mysql databases, only on port 3306
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._config = __import__('config').WormConfiguration
|
||||||
|
|
||||||
|
def get_host_fingerprint(self, host):
|
||||||
|
"""
|
||||||
|
Returns mySQLd data using the host header
|
||||||
|
:param host:
|
||||||
|
:return: Success/failure, data is saved in the host struct
|
||||||
|
"""
|
||||||
|
assert isinstance(host, VictimHost)
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.settimeout(0.5)
|
||||||
|
|
||||||
|
try:
|
||||||
|
s.connect((host.ip_addr, MYSQL_PORT))
|
||||||
|
header = s.recv(4) # max header size?
|
||||||
|
|
||||||
|
tmp, curpos = struct_unpack_tracker(header, 0, "I")
|
||||||
|
tmp = tmp[0]
|
||||||
|
response_length = tmp & 0xff
|
||||||
|
data = s.recv(response_length)
|
||||||
|
# now we can start parsing
|
||||||
|
protocol, curpos = struct_unpack_tracker(data, 0, "B")
|
||||||
|
protocol = protocol[0]
|
||||||
|
|
||||||
|
if protocol == 0xFF:
|
||||||
|
# error code, bug out
|
||||||
|
LOG.debug("Mysql server returned error")
|
||||||
|
return False
|
||||||
|
|
||||||
|
version, curpos = struct_unpack_tracker_string(data, curpos) # special coded to solve string parsing
|
||||||
|
version = version[0]
|
||||||
|
host.services[SQL_SERVICE]['version'] = version
|
||||||
|
version = version.split('-')[0].split('.')
|
||||||
|
host.services[SQL_SERVICE]['major_version'] = version[0]
|
||||||
|
host.services[SQL_SERVICE]['minor_version'] = version[1]
|
||||||
|
host.services[SQL_SERVICE]['build_version'] = version[2]
|
||||||
|
thread_id, curpos = struct_unpack_tracker(data, curpos, "<I") # ignore thread id
|
||||||
|
|
||||||
|
if protocol == 10:
|
||||||
|
# new protocol
|
||||||
|
self._parse_protocol_10(curpos, data, host)
|
||||||
|
return True
|
||||||
|
if protocol == 9:
|
||||||
|
return True
|
||||||
|
s.close()
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
LOG.debug("Error getting mysql fingerprint: %s", err)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _parse_protocol_10(self, curpos, data, host):
|
||||||
|
salt, curpos = struct_unpack_tracker(data, curpos, "s8B")
|
||||||
|
capabilities, curpos = struct_unpack_tracker(data, curpos, "<H")
|
||||||
|
host.services[SQL_SERVICE]['capabilities'] = capabilities[0]
|
||||||
|
charset, curpos = struct_unpack_tracker(data, curpos, "B")
|
||||||
|
status, curpos = struct_unpack_tracker(data, curpos, "<H")
|
||||||
|
extcapabilities, curpos = struct_unpack_tracker(data, curpos, "<H")
|
||||||
|
host.services[SQL_SERVICE]['extcapabilities'] = extcapabilities[0]
|
||||||
|
# there's more data but it doesn't matter
|
|
@ -1,6 +1,7 @@
|
||||||
import socket
|
import socket
|
||||||
import select
|
import select
|
||||||
import logging
|
import logging
|
||||||
|
import struct
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = 10
|
DEFAULT_TIMEOUT = 10
|
||||||
BANNER_READ = 1024
|
BANNER_READ = 1024
|
||||||
|
@ -8,6 +9,33 @@ BANNER_READ = 1024
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def struct_unpack_tracker(data, index, fmt):
|
||||||
|
"""
|
||||||
|
Unpacks a struct from the specified index according to specified format.
|
||||||
|
Returns the data and the next index
|
||||||
|
:param data: Buffer
|
||||||
|
:param index: Position index
|
||||||
|
:param fmt: Struct format
|
||||||
|
:return: (Data, new index)
|
||||||
|
"""
|
||||||
|
unpacked = struct.unpack_from(fmt, data, index)
|
||||||
|
return unpacked, struct.calcsize(fmt)
|
||||||
|
|
||||||
|
|
||||||
|
def struct_unpack_tracker_string(data, index):
|
||||||
|
"""
|
||||||
|
Unpacks a null terminated string from the specified index
|
||||||
|
Returns the data and the next index
|
||||||
|
:param data: Buffer
|
||||||
|
:param index: Position index
|
||||||
|
:return: (Data, new index)
|
||||||
|
"""
|
||||||
|
ascii_len = data[index:].find('\0')
|
||||||
|
fmt = "%ds" % ascii_len
|
||||||
|
unpacked = struct.unpack_from(fmt, data, index)
|
||||||
|
return unpacked, struct.calcsize(fmt)
|
||||||
|
|
||||||
|
|
||||||
def check_port_tcp(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
|
def check_port_tcp(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.settimeout(timeout)
|
sock.settimeout(timeout)
|
||||||
|
|
Loading…
Reference in New Issue