The Vulnerability

Đoạn code sau sử dụng Random không có seed và dùng kết quả sinh ra để tạo password reset token:

var num = new Random().Next(min, max);
var token = make_password_reset_token(num);
save_reset_token_to_db(user, token);
return issue_password_reset(user.email, token);

Và nó có thể bị khai thác.

How the C# PRNG Works

Đây là hàm Random của C# khi không có seed:

public Random()
	: this(Environment.TickCount) {
}

Về cơ bản, nó sẽ là TickCount mà cụ thể hơn là milliseconds kể từ lúc máy tính được bật.

The Exploit

Documentation của Microsoft có nhắc đến 2 random number có thể giống nhau nếu nó được tạo ra từ 2 lời gọi constructor liền kề nhau:

Cite

In .NET Framework, the default seed value is derived from the system clock, which has finite resolution. As a result, different Random objects that are created in close succession by a call to the parameterless constructor have identical default seed values and, therefore, produce identical sets of random numbers.

Nếu chúng ta gửi 2 request trong cửa sổ thời gian 1ms, 2 lần gọi hàm Random liên tiếp sẽ sử dụng cùng seed và do đó mà có cùng kết quả đầu ra. Để tấn công, ta có thể gửi 2 request để reset email với của victim và của attacker sử dụng kỹ thuật Single Packet Attack1 (là kỹ thuật đặt nhiều gói tin HTTP 2.0 vào một gói tin TCP), vốn được hỗ trợ bởi Burp Suite.

Mục tiêu của chúng ta là đảm bảo round-trip time còn dưới 1ms. Nếu endpoint dùng để reset password bị lock thì ta có thể chờ và tấn công lại.

Wrapping Up

Để tránh bị khai thác, có thể cân nhắc sử dụng RandomNumberGenerator thay vì Random.

Resources

Footnotes

  1. Xem thêm Race Conditions