Add mysql fingerprinting and improve struct parsing

This commit is contained in:
Daniel Goldberg 2017-09-25 12:01:48 +03:00
parent cb7bb56588
commit 10c9648854
5 changed files with 111 additions and 3 deletions

View File

@ -3,7 +3,7 @@ import sys
from network.range import FixedRange, RelativeRange, ClassCRange
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter,\
SambaCryExploiter
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger
from abc import ABCMeta
from itertools import product
import uuid
@ -140,7 +140,7 @@ class Configuration(object):
max_iterations = 1
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
SSHExploiter, ShellShockExploiter, SambaCryExploiter # Linux
]

View File

@ -39,7 +39,8 @@
"SSHFinger",
"PingScanner",
"HTTPFinger",
"SMBFinger"
"SMBFinger",
"MySQLFinger"
],
"max_iterations": 3,
"monkey_log_path_windows": "%temp%\\~df1563.tmp",

View File

@ -23,5 +23,6 @@ from tcp_scanner import TcpScanner
from smbfinger import SMBFinger
from sshfinger import SSHFinger
from httpfinger import HTTPFinger
from mysqlfinger import MySQLFinger
from info import local_ips
from info import get_free_tcp_port

View File

@ -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

View File

@ -1,6 +1,7 @@
import socket
import select
import logging
import struct
DEFAULT_TIMEOUT = 10
BANNER_READ = 1024
@ -8,6 +9,33 @@ BANNER_READ = 1024
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):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)