For the complete documentation index, see llms.txt. This page is also available as Markdown.
ADASREPROASTINGAV-EVASIONGMSAGPPPASSWORD SPRAYINGPOTATOTIMEROASTINGGET-WRITEABLE

Fragments

Challenge Lab (Hard) - by xjr

The following post by 0xb0b is licensed under CC BY 4.0


Scenario

Objective

You have been hired to perform an internal penetration test against the client's Active Directory environment. There is only one host in-scope (the Domain Controller). Your task is to get initial access and then perform lateral movement and privilege escalation until you have fully compromised the domain.

Initial Access

The client has provided you with VPN access to their environment, but no other information.

Summary

Summary

In Fragments, we begin with enumeration of the Domain Controller DC01.fragments.local, exposing DNS 53, Kerberos 88/464, LDAP 389 with GC on 3268/3269, LDAPS 636, signed SMB on 445, RDP 3389, WinRM 5985, and unusually for a DC, a full RPC/NFS stack on 111/2049. Anonymous/guest SMB fails, so we pivot to NFS.

The incidents NFS share leaks two incident reports: one referencing a broken user account, and another naming a suspicious computer, FRG0310$. With no credentials, we run Timeroasting against the DC, cracking one SNTP hash offline via hashcat to authenticate as FRG0310$. The SYSVOL share yields a GPP password from a Group Policy Preferences XML. We pull a GPP password from a Group Policy Preferences XML on SYSVOL, then RID-brute-force the domain users and spray the password across them, giving us access to c.white.

Shell access as c.white via evil-winrm yields the user flag. Browser-credential extraction from c.white's Opera GX profile via hack-browser-data)surfaces a bookmarked password-manager URL with a base64-encoded vault, decoded to reveal creds for j.woods and d.goggins. d.goggins hits STATUS_INVALID_LOGON_HOURS, but j.woods authenticates successfully.

BloodHound shows j.woods has GenericWrite over o.rodrigo, a disabled member of the management group with ReadGMSAPassword over PROD$. We enable the account, set DONT_REQ_PREAUTH, AS-REP roast and crack the hash, then read PROD$'s gMSA password via o.rodrigo's privileges.

PROD$ in turn has write access over d.goggins, letting us clear logonHours via bloodyAD and finally authenticate as d.goggins with the earlier-recovered password. BloodHound enumeration reveals d.goggins can self-add to adminaccs, which holds GenericAll over sharedadmin (a Protected Users member requiring Kerberos auth via getTGT.py). Using sharedadmin, we add c.white to IIS_IUSRS for SeImpersonatePrivilege.

Standard potato exploits (DeadPotato, EsfPotato) are detected or incompatible, so we obfuscate GodPotato into BobTato using InvisibilityCloak, evading Defender. BobTato.exe creates a local admin, bob, granting evil-winrm DC access and the root flag at C:\Users\Administrator\Desktop\root.txt.

Recon

We use rustscan -b 500 -a 10.0.21.34 --top -- -sC -sV -Pn to enumerate all TCP ports on the target machine, piping the discovered results into Nmap, which runs default NSE scripts -sC, service and version detection -sV, and treats the host as online without ICMP echo -Pn.

A batch size of 500 trades speed for stability, the default 1500 balances both, while much larger sizes increase throughput but risk missed responses and instability.

The target 10.1.157.17 is a Domain Controller DC01.fragments.local for the domain fragments.local. DNS is exposed on port 53 and Kerberos on port 88, alongside Kerberos password change on port 464. LDAP is available on port 389, with the Global Catalog on ports 3268 and 3269, and LDAPS is also open on port 636. SMB is exposed via port 445 only, with message signing enabled and required. Unusually for a DC, RPC/NFS services are also exposed, with rpcbind on port 111 and a full NFS stack (nfs, mountd, nlockmgr, status) on port 2049. Remote management and access are available through RDP on port 3389 (leaking NetBIOS domain FRAGMENTS, hostname DC01, and OS build 10.0.26100) and WinRM on port 5985. The host also exposes a .NET Message Framing service on port 9389, RPC over HTTP on ports 593 and 49686, and numerous additional MSRPC endpoints on ports 135, 49664, 49678, 49679, 49682, 49685, 49718, 49742, and 49753.

SMB

We try to authenticate anonymously and via guest against SMB using NetExec, but without success. The account is disabled. Nevertheless, we generate a hosts file entry with the following command:

We add the following entry to our /etc/hosts file.

NFS

We'll start by focusing on the unusual discovery of an NFS share on the domain controller. It contains an incidents folder.

We'll mount this and retrieve the files.

Incident IR_20260122_ACC reveals that a user account is no longer functioning properly following a change. The account may have had permissions revoked, been added to a specific group, disabled, or been deleted. Let's see if this will still be useful information.

Incident IR_20260202_FRG0310 is more interesting. It reveals that a security alert was triggered: a computer with the hostname FRG0310 exhibited suspicious behavior and several unsuccessful authentication attempts were observed. At least we have a computer name now: FRG0310$

Access as FRG0310$

Since we do not have any credentials yet we try every technique listed from the orange cyber defense mindmap. With the classics like blind kerberoasting, we get nothing. But we have some success with Timeroasting.

Timeroasting is an attack technique that abuses Microsoft's proprietary NTP extension to extract password-equivalent hashes for computer and trust accounts from domain controllers without requiring authentication. These hashes can subsequently be cracked offline.

Domain-joined computers synchronize their system clocks using NTP, with domain controllers acting as authoritative time sources. To address NTP's lack of authentication, Microsoft implemented a custom extension that cryptographically authenticates NTP responses using computer account credentials.

When a computer requests time synchronization, it includes its computer account's RID (Relative Identifier) in the NTP request. The domain controller responds with a Message Authentication Code (MAC) computed using the computer account's NTLM hash as the key. This design allows unauthenticated clients to request salted password hashes for any computer account in the domain by specifying different RID values.

A nice article about Timeroasting can be found here:

Via Timeroasting we are able to retrieve three SNTP hashes that can we try to crack using Hashcat mode 31300.

Unfortunately my version installed was too old and did not support the mode.

For convinience I switched to my Windows host wich had hashcat v7.1.2 already present. We are able to crack the hash for the user with the RID 1103.

Since we only have one valid machine account name from the incident reports we try to authenticate as FRG0310$ and are successful.

We try to enumerate the shares, but on first sight we do not find anything interesting.

But maybe the SYSVOL share contains something useful.

Access as c.white

Besides scripts the SYSVOL share could also contain passwords in the policies:

And we acually find something that looks like a password in the description of the following policy.

We could also make use of the gpp_password module of NetExec to look for passwords automatically.

Next, we enumerate some users via a RID brute force.

We create a wordlist of users...

... and perform a password spray via NetExec. We got access as c.white.

BloodHound Enumeration

Since we now have a machine account and a user we run our first BloodHound enumeration.

The machine account FRG0310 has no special privileges.

We see that c.white is member of the remotemgmt group, which is member of the remote management users group which would allow us to get a shell using evil-winrm. For now we do not see any other interesting group memberships or privileges. So we continue trying to get an interactive shell.

Shell as c.white

We can connect as c.white via evil-winrm and find the users flag at C:\Users\c.white\Desktop\user.txt.

We enumerate the target machine but do not find any services, scheduled tasks or special permissions to abuse.

Access as j.woods

We continue to enumerate the target and see that Opera GX is installed.

We try to dump the browsers secrets using the tool we used for the scenario NovaForge, but without success. We do not find any credentials.

Next, we research a bit and find another tool that is capable of extracting and decrypting the browsers data. There may be more.

We upload the tool...

..and run it with the -h parameter to see how to use it.

We use it as follows.

The -b flag specifies the target browser, -d specifies the output directory, and -p specifies the profile path.

We spot a bookmark that points to a URL resolving to a password manager: passwordmgmt.fragments.local, with the vault database base64-encoded in the vaultDATA GET parameter.

We pass the base64-encoded data to CyberChef to decode it and identify two sets of credentials. One for j.woods and one for d.goggins.

We're trying to authenticate using the set of credentials of d.goggins, but we're getting a timeout via SMB. When we check via LDAP, we see that the logon hours don't match.

We receive the error message STATUS_INVALID_LOGON_HOURS.

The flag STATUS_INVALID_LOGON_HOURS means the account authenticated fine but hit AD's logonHours attribute restriction. The current time falls outside the allowed window. This has nothing to do with the password, so no need to touch that.

The logonHours is a 21-byte bitmask (168 bits = 7 days × 24 hours). If it's NULL/unset, logon is unrestricted by default.

As long as we don't have write permissions over this account, we can't overwrite it and gain access to it.

But we can successfully authenticate ourselves as j.woods.

Access as o.rodrigo

The user j.woods has no special permissions or group memberships in our BloodHound dataset.

As a last resort we enumerate the writable objects to identify an alternative path that is not covered in the BloodHound data. The user j.woods has write premissions overthe user o.rodrigo, which means we can modify that user's attributes.

We'll take a closer look in Bloodhound to assess whether this user and the uncovered permissions can help us expand our privileges.

The user o.rodrigo is a member of the management group, which has ReadGMSA permissions over PROD$. This means that if we can compromise o.ordrigo, we may be able to elevate our privileges further as $PROD. But we also notice that this user is not enabled and a direct path to compromise o.rodrigo is also not depicted in the data.

The idea is to first enable the account and then allow AS-REP roasting by setting DONT_REQ_PREAUTH using the write permission we possess. This allows us to perform an AS-REP roast and eventually gather the credentials of o.rodrigo.

First, we enable the account...

... and set the DONT_REQ_PREAUTH attribute.

We use NetExec for AS-REP Roasting and are able to extract the blob o.rodrigo.

We try to crack the blob using hashcat and retrieve the password.

We test the credentials using NetExec and are able to authenticate.

Access as PROD$

e recall the relationship discovered in BloodHound that o.rodrigo has ReadGMSAPassword permissions over PROD$. However, there is nothing more beyond that in BloodHound. Let's see what we can gather after getting access as PROD$ through the ReadGMSAPassword permission, and check whether the service account possesses any writeable permissions.

We read the managed password of the gMSA account PROD$ by querying its msDS-ManagedPassword attribute using our authorized access via o.rodrigo.

This abuse stands out a bit from other abuse cases. It can be carried out when controlling an object that has enough permissions listed in the target gMSA account's msDS-GroupMSAMembership attribute's DACL. Usually, these objects are principals that were configured to be explictly allowed to use the gMSA account.

The attacker can then read the gMSA (group managed service accounts) password of the account if those requirements are met.

We retrive the NT hash of PROD$.

We test the hash using NetExec and are able to authenticate.

Access as d.goggins

We check the writable objects of PROD$ and see that we have write permissions over d.goggins, which allows us to set the logon hours to gain access as that account.

We had previously recovered the credentials for user d.goggings from the leaked password vault in the bookmarks of Opera GX, but we received the error message STATUS_INVALID_LOGON_HOURS and were previously unable to override it.

The Write permission lets us clear it outright with bloodyAD's set object module. We authenticate with the hash of PROD$.

We try to authenticate as d.goggins using NetExec again, and we authenticate successfully.

BloodHound Enumeratio II

With access as d.goggins, we gain further opportunities to move laterally and expand our privileges. The user d.goggins is a member of the soc group, which has the permission AddSelf to add itself to the adminaccs group.

The adminaccs group has GenericAll permission over the sharedadmin account, which allows us to take over that account by changing the user's password. However, this user is also a member of the Protected Users group, which would deny NTLM authentication.

And the sharedadmin account is member of the account operators group which has a bunch of GenericAll permissions over some users and groups. Shortest path from Owned objects

At first glance, there doesn't seem to be a way to become a Domain Admin here. At least, the query Shortest path from Owned objects or Shortest path to Domain Admins doesn't reveal this to us.

However, two groups would allow us to do so. First, there's the DNSADMINS group. Using this path, you could try the following: Members of the DNSADMINS group are able to modify the DNS service, causing it to load an arbitrary DLL, but this seems to be patched:

Or alternatively, we could leverage the Account Operators Replicating Trust Attack (AORTA). AORTA is a technique in which members of the Account Operators group abuse Incoming Forest Trust Builders together with DNSAdmins privileges to create a forest trust with TGT delegation enabled, allowing the compromise of the domain. This was uncovered by ahos6 and actuaL during QA and remains an additional task for the reader and myself, since I hadn't gotten far enough with it yet.

The other interesting group is the IIS_IUSRS group. Members of the IIS_IUSRS group have the SeImpersonatePrivilege. Since members of this group hold that privilege, we can escalate our privileges using a potato-style exploit, such as JuicyPotato or GodPotato.

Access as sharedadmin

We follow the identified attack path around d.goggins and attempt to escalate to the sharedadmin account, then add a user who is a member of the Remote Management Users group - in this case, c.white - to the IIS_IUSRS group in order to escalate using SeImpersonatePrivilege.

We use bloodyAD to add d.goggins to the adminaccs group, adding ourselves to the administrative accounts group using the credentials we already compromised.

We make use of the GenericAll permission over the sharedamin account and change the users password.

Since the user is a member of the Protected Users group, they can't authenticate to services with a password directly, but we can request a ticket and then authenticate using that.

Shell as bob (Local Administrator)

Now that we have access as sharedadmin, we add c.white to the IIS_IUSRS group.

We get a new session using evil-winrm and see that we have now the SeImpersonatePrivilege.

We upload a potato exploit in this case DeadPotato which we used for another scenario and proofed quiet powerfull...

... but it gets detected by Windows Defender.

So we need to bypass Windows Defender now.

The first attempt was to compile EsfPotato on the machine; this worked, it did not get detected, but the exploit didn't work on this current version of Windows.

The next attempt was to establish a connection to Sliver C2 via a session and, from there, load the executable directly using execute-assembly. This wasn't detected at first, but the process was aborted during execution by Windows and didn't work.

Alternatively, we could have tried other C2s like Adaptix, but in this case, we're taking the somewhat cumbersome yet still quick route of obfuscation.

However, I needed a Windows VM to compile the project.

Lainkusanagi from our community has published a great article on Medium about the obfuscation of a potato exploit, in which he obfuscated the project of GodPotato by using a few scripts. We follow this blog.

For this we need the tool InvisibilityCloak...

... and the source of the GodPotato exploit.

We open the project file GodPotato.csproj in Visual Studio and save it as GodPotato.sln. We might need to migrate the project to .NET 4.8.

Next, all comments needs to be removed from the project like depicted in the blog so the InvisibilityCloak script does not fail.

After having saved the .sln file and removed the comment we close visual studio.

Finally, we use the InvisibilityCloak Python script to rename the project and modify strings according to our chosen method.

We run the InvisibilityCloak script against the GodPotato directory, renaming the project to BobTato and applying the reverse obfuscation method.

We notice that the .csproj file had its output type set to Library instead of Exe. This needs to be corrected by BobTato.csproj to ensure the project compiled as an executable.

Next, we open the project in visual studio and compile the project in visual studio and end up with our very own BobTato potato exploit.

We upload the BobTato exploit and run a whoami command. We do not receive the output, but we see that it does not get detected.

Next, we execute BobTato.exe to add a local user account,...

... and then make it a local administrator.

We are not able to authenticate as bob a local administrator to the domain controller and are able to retrieve the root flag at C:\Users\Administrator\Desktop\root.txt.

As a post-compromise action, we use NetExec with the newly created bob account to dump the SAM database over SMB.

Last updated