Tags: web 

Rating: 4.0

# pwgen – Web (332 pts)

## Challenge Description
> Password policies aren't always great. That's why we generate passwords for our users based on a strong master password!

Service: [http://52.59.124.14:5003](http://52.59.124.14:5003)
Hint: “For pwgen you get the source by appending `/?source`.”

---

## Recon & Source Code

First, I checked the source endpoint:

```http
GET /?source HTTP/1.1
Host: 52.59.124.14:5003
```

This revealed the PHP code:

```php
1000 or $shuffle_count < 1) {
echo "Bad shuffle count!";
die();
}

srand(0x1337); // fixed seed

for($i = 0; $i < $shuffle_count; $i++) {
$password = str_shuffle($FLAG);
}

if(isset($password)) {
echo "Your password is: '$password'";
}
?>
```

### Key Observations
- The flag is loaded from `flag.php`.
- Passwords are generated by repeatedly shuffling the flag string.
- The PRNG is seeded **once with `srand(0x1337)`**.
- Because of the static seed, shuffles are **deterministic**.

So, every “password” is just the flag’s characters permuted the same way each time → **if we can invert the shuffle, we recover the flag.**

---

## Exploitation

### Step 1: Grab a Shuffled Flag
Using Burp Repeater or curl:

```bash
curl -s 'http://52.59.124.14:5003/?nthpw=777'
```

Response:

```
Your password is: '568cN6ES45r5f5T4_Rf_:S885FTD_6e16eS3C72b8pH454_Fc4HS30871A4LL__3-4/88_4fdbBfaV07}34p1OWG0a6H1TLb1d63NiONH_TWEe3_{d3U83n6e29_13D264'
```

This is the **flag characters shuffled**.

---

### Step 2: Understand PHP’s `str_shuffle`
`str_shuffle` uses **Fisher–Yates shuffle** with `mt_rand`.
Since the seed is fixed, the permutation sequence can be reconstructed.

---

### Step 3: Reconstruct the Shuffle
I wrote a PHP one-liner that:
1. Grabs the shuffled string from curl.
2. Replays the exact PRNG sequence.
3. Builds the permutation.
4. Inverts it to recover the original flag.

```bash
N=777
SHUF="$(curl -s 'http://52.59.124.14:5003/?nthpw='$N | sed -nE "s/.*Your password is: '([^']+)'.*/?/p")"

php -r '
$s=$argv[1]; $n=(int)$argv[2];
mt_srand(0x1337);
$L=strlen($s);
for($k=1;$k<$n;$k++){ for($i=$L-1;$i>0;$i--) mt_rand(0,$i); }
$idx=range(0,$L-1);
for($i=$L-1;$i>0;$i--){ $j=mt_rand(0,$i); $t=$idx[$i]; $idx[$i]=$idx[$j]; $idx[$j]=$t; }
$inv=array_fill(0,$L,0);
foreach($idx as $k=>$src){ $inv[$src]=$k; }
$out="";
for($pos=0;$pos<$L;$pos++){ $out.=$s[$inv[$pos]]; }
echo $out, PHP_EOL;
' -- "$SHUF" "$N"
```

---

## Final Flag ?

Running the above recovered:

```
ENO{N3V3r_SHUFFLE_W1TH_STAT1C_S333D_OR_B4D_TH1NGS_WiLL_H4pp3n:-/_0d68ea85d88ba14eb6238776845542cf6fe560936f128404e8c14bd5544636f7}
```

---

## Lessons Learned
- Using `srand()` with a static seed is dangerous — it makes “random” outputs predictable.
- `str_shuffle` is not cryptographically secure.
- Always use strong, unpredictable randomness (e.g. `random_bytes()`) for secrets like passwords/flags.

---