Intro

Case study is a new section to study specific cases. In the world of cybersecurity, there are two important concepts: 1) attack vector, 2) attack path. We are only going to lightly cover this topic, because I intend to do a more detailed blog on this topic in the future.

  • An attack vector is a step an attack can take to advance a bit further.
  • An attack path is a list of attack vectors that chains up to a successful exploit. In penetration testing, it’s important to learn about the attack vectors and master them so that in a real practice, you can quickly realise what vectors are involved and how to utilise them to form an attack path.

Background

Very often, when you register an account at a website, it may require you to perform a verification step to activate the account. There are many ways to perform the verification, such as requiring a phone number to receive a code, asking to provide an email and link on a verification link. In the essense, the backend server created an arbitrarily generated string (i.e the code) and asking the user to receive it via an overt channel. Yet it is crucial to ensure that the generation of the code cannot be predicted.

Case Study

In this case study, we are going to discuss a case of predictable verification code and how it may be exploited.

Let’s start by looking at a piece of code:

function gen_code() {
    $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
    srand(time());
    $code = "";
    for ($i = 0; $i < 32; $i++) {
        $code = $code . $chars[rand(0, strlen($chars) - 1)];
    }
    return $code;
}

Do you see the issue here? before using the rand() function, there is a call to srand() that nailed down the seed of the random number generator, i.e using the timestamp at the time of running this piece of code. Therefore, the seemingly random string can be predicted.

To calculate the generated code, all we need to do is to get the time of when the code was executed, this can usually be found by looking at the server’s response time. However, to ensure we are not missing out any opportunities, it’s better to generate a range of codes with a range of +/- timestamp to get things covered thoroughly. For example, consider the following code:

$ref_time = date("U",strtotime('server_response_time'));
for ($t = $ref_time - 500; $t <= $ref_time + 500; $t++){
    echo gen_code($t)."\n";
}

This way, we can obtain a list of possible verification codes and the real one can be bruteforced to find out.

Remediation

It is important to make sure the verification code is generated in a way that is not end-user predictable.

For example

$random_hash = md5(uniqid(rand(), true));

Alternative methods

$random_hash = md5(openssl_random_pseudo_bytes(32));
$random_hash = md5(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
$random_hash = bin2hex(random_bytes(32)); // PHP7