Privilege Escalation
From a low-privilege database account to DBA, and from one database host to the next: credential harvesting, definer's-rights abuse, and linked-server pivoting.
Overview
An injection frequently lands you as a low-privilege account — enough to read application tables but not to write files or execute commands. Privilege escalation bridges that gap, turning a limited foothold into DBA-level control, and often into a foothold on other systems.
Three broad routes:
- Harvest credentials stored in or reachable from the database, and reuse them.
- Abuse elevated code paths — stored procedures and functions that run with more privilege than the caller.
- Pivot to other database instances via stored connection credentials.
Assess your starting privilege first, because it determines which routes are even worth attempting.
Assess Current Privilege
SELECT CURRENT_USER();
SHOW GRANTS;
SELECT * FROM information_schema.user_privileges;
-- The high-value flags
SELECT super_priv, file_priv FROM mysql.user WHERE user=SUBSTRING_INDEX(CURRENT_USER(),'@',1);
-- Can you read the credential table? (needs privilege on mysql.user)
SELECT user, host, authentication_string FROM mysql.user;Credential Harvesting and Reuse
The database and its neighbours are full of credentials. Even without escalating within the database, harvested credentials often unlock more elsewhere.
Database account hashes. If you can read the credential tables above, crack the hashes offline (hashcat has modes for MySQL, PostgreSQL SCRAM, MSSQL, and Oracle). Database passwords are notoriously reused for OS accounts and other services.
Application secrets in data. Applications store API keys, OAuth tokens, and integration credentials in ordinary tables. Grep the schema for columns named token, secret, key, password, credential.
Connection strings in reachable files. If you have file read, configuration files hold the credentials the application itself uses — frequently a more privileged database account than the one the injection runs as. Reconnecting with those is a direct escalation.
Password reuse across accounts. A hash cracked for one database user often works for the DBA account too.
Abusing Elevated Code Paths
Databases let code run with privileges other than the caller's. That is the intended design; it is also an escalation primitive when such code is injectable or invokable.
MSSQL — EXECUTE AS and TRUSTWORTHY. A stored procedure signed or marked to execute as a more privileged principal runs your injected SQL at that level. A database with TRUSTWORTHY ON owned by a sysadmin-mapped login is a classic path: create a procedure WITH EXECUTE AS OWNER and you inherit sa.
PostgreSQL — SECURITY DEFINER functions. A function declared SECURITY DEFINER runs as its owner. If such a function (owned by a superuser) builds dynamic SQL from its arguments, injecting into it executes as superuser. This is the PostgreSQL analogue of definer's-rights abuse.
Oracle — AUTHID DEFINER procedures. The default for PL/SQL procedures. One owned by SYS that concatenates its input into dynamic SQL is the canonical Oracle privilege escalation — historically a steady stream of these shipped in Oracle's own built-in packages. Inject a call to a function that grants you DBA.
Definer's-rights escalation
-- A SECURITY DEFINER function owned by postgres that builds
-- dynamic SQL from its argument:
CREATE FUNCTION lookup(name text) RETURNS SETOF users
SECURITY DEFINER
LANGUAGE plpgsql AS $$
BEGIN
RETURN QUERY EXECUTE
'SELECT * FROM users WHERE name = ''' || name || '''';
END $$;
-- Injecting into `name` executes as postgres (superuser):
-- name = x'; ALTER ROLE attacker SUPERUSER; --
-- After that, you have COPY TO PROGRAM -> RCE. See /guide/rce.Lateral Movement via Linked Servers
Database instances are often connected to each other, and each connection stores credentials — frequently for a more privileged account than the one you compromised. A single injection into a peripheral, low-privilege database can therefore become a foothold across an estate.
MSSQL linked servers are the most common case. Links can be bidirectional and chained, and a low-privilege account on one instance may map to sa on another.
-- Enumerate
SELECT srvname FROM master..sysservers;
EXEC sp_linkedservers;
-- Execute on the linked server with its stored credentials
EXEC ('SELECT IS_SRVROLEMEMBER(''sysadmin'')') AT [REMOTE];
SELECT * FROM OPENQUERY([REMOTE], 'SELECT @@version');
-- Chain through multiple hops
EXEC ('EXEC (''xp_cmdshell ''''whoami'''''') AT [HOP2]') AT [HOP1];
PostgreSQL offers the same via dblink and postgres_fdw; Oracle via database links (SELECT … FROM table@dblink). In every case the stored credential is the prize — dump it or use it in place.
From DBA to the Operating System
DBA within the database is usually one short step from the OS, because DBA implies the file and execution primitives from RCE and File Read and Write:
- MSSQL sysadmin →
xp_cmdshellas the service account. - PostgreSQL superuser →
COPY TO PROGRAMas thepostgresuser. - MySQL with
FILE→ webshell or UDF. - Oracle DBA → Java stored procedure or
DBMS_SCHEDULER.
Once on the host, the database service account is often over-privileged there too — SQL Server running as LocalSystem, or a postgres user with sudo rights — which extends the escalation into the operating system itself. The database was only the entry point.
Operational Care
Privilege escalation is high-impact and leaves durable changes.
- Every grant persists. A
GRANT DBA, an addedsysadminmember, or a new superuser role survives the engagement. Record each and revoke it, or hand the client a precise cleanup list. - Do not move laterally beyond scope. A linked server may reach systems that are not in the engagement. Confirm before pivoting; another organisation's database is not covered by your authorization.
- Harvested credentials are sensitive. Cracked hashes and connection strings belong in the report's protected appendix, handled per the engagement's data-handling rules, not left in scratch files.
- Restore altered configuration.
TRUSTWORTHY, re-enabled procedures, and created objects all need reverting. - Report the chain, not just the endpoint. The value to the client is the path — low-priv injection here mapped to DBA there mapped to the OS — because that is what they need to break.
Prevention
Parameterise to remove the entry point. Then constrain what any single compromise can reach:
- Least privilege, genuinely. The application account should hold only the DML it needs on its own schema — no
FILE, noSUPERUSER, nosysadmin, noCREATE JOB. This caps every route on this page. - Segment credentials. Do not reuse database passwords for OS accounts or other services. Assume any stored credential will be read.
- Encrypt secrets at rest and keep them out of database tables and world-readable config; use a secrets manager the application reads at runtime.
- Audit definer's-rights code. Every
SECURITY DEFINERfunction,AUTHID DEFINERprocedure, andEXECUTE ASpath that builds dynamic SQL is an escalation primitive. Parameterise inside them and prefer invoker's rights. - Remove
TRUSTWORTHYon MSSQL databases that do not require it, and avoid sa-mapped database owners. - Restrict and audit linked servers / dblinks. Remove unused ones; ensure links do not carry elevated credentials; monitor cross-instance queries.
- Patch promptly. Oracle in particular ships definer's-rights escalations in built-in packages; the quarterly CPU exists for this.
Related
The routes from an injected query to a shell on the database host, and which engines hand it to you directly.
Turning a database query into filesystem access: reading configuration and credentials, and writing a webshell into a served directory.
Verbose errors, universal stacked-query support, and xp_cmdshell — the friendliest engine to attack and the one where escalation is most direct.
Mandatory FROM clauses, no LIMIT, no stacked queries, and uppercase identifiers — the engine where the most standard payloads fail for syntactic reasons.
Appending an entirely separate statement after the original. Where supported it turns a read-only injection into arbitrary write access — and often into code execution.
Parameterisation removes the bug; these layers limit the blast radius when a parameterisation is missed. Least privilege, allowlists, monitoring, and what each is actually worth.