Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Enumeration:
Writing:
--rbcd source Operation to write or remove RBCD. Also used to pass in the source computer account used
for the attack.
--altsecid value Operation to write the altSecurityIdentities attribute value, writes by default unless "
--remove" is specified
--spn value Operation to write the servicePrincipalName attribute value, writes by default unless "
--remove" is specified
--asrep Operation to write the DONT_REQ_PREAUTH (0x400000) userAccountControl flag on a target
Expand Down
8 changes: 8 additions & 0 deletions src/adws.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import datetime
import logging
import socket
import re
from base64 import b64decode
from enum import IntFlag
from typing import Self, Type
Expand Down Expand Up @@ -426,6 +427,13 @@ def _handle_str_to_xml(self, xmlstr: str) -> ElementTree.Element | None:
soap message return by the server
"""

# handling of unescaped XML special characters in
# responses to ensure smooth parsing
m = re.search(r'<ad:value[^>]*>(X509:<[^>]+>(.*?))</ad:value>', xmlstr)
if m:
unescaped = m.groups()[0]
xmlstr = xmlstr.replace(unescaped, '<![CDATA[%s]]>' % unescaped)

if ":Fault>" and ":Reason>" not in xmlstr:
return ElementTree.fromstring(xmlstr)

Expand Down
43 changes: 35 additions & 8 deletions src/soa.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,41 +85,44 @@ def getAccountDN(
return dn


def set_spn(
def set_array_attribute(
target: str,
attr: str,
value: str,
username: str,
ip: str,
domain: str,
auth: NTLMAuth,
remove: bool = False,
):
"""Set a value in servicePrincipalName. Appends value to the
attribute rather than replacing.
"""Set a value in AD attribute that has isSingleValued=False
(e.g. servicePrincipalName, altSecurityIdentities).
Appends value to the attribute rather than replacing.

Args:
target (str): target samAccountName
value (str): value to append to the targets servicePrincipalName
attr (str): name of attribute to modify
value (str): value to append to the target's attribute
username (str): user to authenticate as
ip (str): the ip of the domain controller
auth (NTLMAuth): authentication method
remove (bool): Whether to remove the value
"""

dn = getAccountDN(target=target,username=username,ip=ip,domain=domain,auth=auth)
dn = getAccountDN(target=target, username=username, ip=ip, domain=domain, auth=auth)

put_client = ADWSConnect.put_client(ip, domain, username, auth)

put_client.put(
object_ref=dn,
operation="add" if not remove else "delete",
attribute="addata:servicePrincipalName",
attribute=f"addata:{attr}",
data_type="string",
value=value,
)

print(
f"[+] servicePrincipalName {value} {'removed' if remove else 'written'} successfully on {target}!"
f"[+] {attr} {value} {'removed' if remove else 'written'} successfully on {target}!"
)

def set_asrep(
Expand Down Expand Up @@ -412,6 +415,12 @@ def run_cli():
metavar="source",
help="Operation to write or remove RBCD. Also used to pass in the source computer account used for the attack.",
)
writing.add_argument(
"--altsecid",
action="store",
metavar="value",
help='Operation to write the altSecurityIdentities attribute value, writes by default unless "--remove" is specified',
)
writing.add_argument(
"--spn",
action="store",
Expand Down Expand Up @@ -503,17 +512,35 @@ def run_cli():
auth=auth,
remove=options.remove,
)
elif options.altsecid != None:
if not options.account:
logging.critical(
'Please specify an account with "--account"'
)
raise SystemExit()

set_array_attribute(
ip=remoteName,
domain=domain,
target=options.account,
attr='altSecurityIdentities',
value=options.altsecid,
username=username,
auth=auth,
remove=options.remove
)
elif options.spn != None:
if not options.account:
logging.critical(
'Please specify an account with "--account"'
)
raise SystemExit()

set_spn(
set_array_attribute(
ip=remoteName,
domain=domain,
target=options.account,
attr='servicePrincipalName',
value=options.spn,
username=username,
auth=auth,
Expand Down
4 changes: 2 additions & 2 deletions src/soap_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@
<da:Change Operation="{operation}">
<da:AttributeType>{attribute}</da:AttributeType>
<da:AttributeValue>
<ad:value xsi:type="xsd:{data_type}">{value}</ad:value>
<ad:value xsi:type="xsd:{data_type}"><![CDATA[{value}]]></ad:value>
</da:AttributeValue>
</da:Change>
</da:ModifyRequest>
</s:Body>
</s:Envelope>"""
</s:Envelope>"""