# LACTF 2023

## the-secret-of-java-island

> **The Secret of Java Island** is a 2024 point-and-click graphic adventure
> game developed and published by LA CTF Games. It takes place in a fictional
> version of Indonesia during the age of hacking. The player assumes the role
> of Benson Liu, a young man who dreams of becoming a hacker, and explores
> fictional flags while solving puzzles.  
>  
> Author: aplet123  
>  
> [`game.jar`](https://raw.githubusercontent.com/D13David/ctf-
> writeups/main/lactf24/rev/secret_of_java/game.jar)

Tags: _rev_

## Solution  
For this challenge we get a java binary. This can be reversed quite
confortable with [`Java Decompiler`](https://java-decompiler.github.io/)
offline or [`Java Decompilers`](http://www.javadecompilers.com/) online.

The game is fairly small. There is only some text displayed and two buttons
that trigger different interactions. Every screen of the game is represented
with a `game-state` and we can draw the state machine quite easily from it.
The function `transitionState` takes an int value that is either `0` (left
button) or `1` (right button). It tracks the click history for each transition
and then just transitions to the next state, depending on the state machine
logic.

```java  
private static void transitionState(int var0) {  
history.add(var0);  
prevState = state;  
switch(state) {  
case 0:  
if (var0 == 0) {  
state = 1;  
} else {  
state = 2;  
}  
break;  
case 1:  
if (hasGlove) {  
System.exit(0);  
} else if (var0 == 0) {  
state = 4;  
} else {  
state = 0;  
}  
break;  
case 2:  
if (var0 == 0) {  
state = 3;  
} else {  
state = 0;  
}  
break;  
case 3:  
if (!hasGlove) {  
System.exit(0);  
} else {  
state = 5;  
}  
break;  
case 4:  
if (var0 == 0) {  
exploit = exploit + "d";  
story.setText("You clobbered the DOM. That was exploit #" + exploit.length() +
".");  
} else {  
exploit = exploit + "p";  
story.setText("You polluted the prototype. That was exploit #" +
exploit.length() + ".");  
}

if (exploit.length() == 8) {  
try {  
MessageDigest var1 = MessageDigest.getInstance("SHA-256");  
if (!Arrays.equals(var1.digest(exploit.getBytes("UTF-8")), new byte[]{69, 70,
-81, -117, -10, 109, 15, 29, 19, 113, 61, -123, -39, 82, -11, -34, 104, -98,
-111, 9, 43, 35, -19, 22, 52, -55, -124, -45, -72, -23, 96, -77})) {  
state = 7;  
} else {  
state = 6;  
}

updateGame();  
} catch (Exception var2) {  
throw new RuntimeException(var2);  
}  
}

return;  
case 5:  
case 7:  
System.exit(0);  
break;  
case 6:  
if (var0 == 0) {  
state = 0;  
} else {  
state = 1;  
}  
}

updateGame();  
}  
```

There are two interesting bits. First there is a `hasGlove` variable, that is
influencing the transitioning in some cases. Also in state `4` the user builds
an `exploit`, this is, a string of 8 charachters containing a certain
permutation of `d` (DOM clobbering) and `p` (prototype pollution). The state
only transitions to either state `6` or `7` when the exploit is fully entered.
We can simply bruteforce the correct exploit and see that `dpddpdpp` fulfills
the `sha256` validation.

![](https://raw.githubusercontent.com/D13David/ctf-
writeups/main/lactf24/rev/secret_of_java/state_machine.svg)

The final piece is `updateGame` that updates the story text depending on the
state we are in. The interesting state is state `5` that calls to
`chall.lac.tf:31151` that should give back the flag. Sadly only calling the
endpoint is not enough as the server expects us to send the correct click
history.

```bash  
$ nc chall.lac.tf 31151

<html>The flag is written in messy handwriting but you can barely make it out.
It says: STOP CHEATING. Contact an admin if you haven't done anything out of
the ordinary.</html>  
```

```java  
private static void updateGame() {  
switch(state) {  
case 0:  
if (prevState == 2) {  
story.setText("<html>You want nothing to do with that lever and back away from
it. The computer and lever still sit menacingly in the room.</html>");  
} else if (prevState == 1) {  
story.setText("<html>It's important to limit your screen time, or at least
that's what your doctor told you. You back away from the computer.
Surprisigly, the computer immediately turned off again after stepping away
from it. The computer and lever still sit menacingly in the room.</html>");  
} else if (prevState == 6) {  
story.setText("<html>You run away from the hissing vent, but nothing seems to
have happened. The computer and lever still sit menacingly in the
room.</html>");  
} else {  
story.setText("<html>You wake up in a musty room with no lighting except a
squeaky lamp hanging from the center of the ceiling. You remember nothing
other than the fact that your name is Benson Liu and that you're on a quest to
become the greatest hacker in the world. You look around the room and see a
decrepit Dell workstation in the corner and a rusty lever on the opposite
wall.</html>");  
}

button1.setText("Inspect the computer");  
button2.setText("Inspect the lever");  
break;  
case 1:  
if (!hasGlove) {  
story.setText("<html>You walk up to the computer and reach for the power
button. Before your hand even reaches the power button, the computer springs
to life with a concerningly loud hum. The screen says \"WELCOME bliutech\" on
it with a green button that says \"LOG IN\".</html>");  
button1.setText("Click the button");  
button2.setText("Back away");  
} else {  
story.setText("<html>While you're next to the computer, a distinct smell in
the air makes you come to a realization: the hissing sound from the vent was
from the release of toxic gas. Unfortunately, you realized this too late as
you become light-headed before passing out. Game over.</html>");  
button1.setText("I understand");  
button2.setText("I understand");  
}  
break;  
case 2:  
story.setText("<html>As you walk closer to the lever you realize that it's
completely covered in spider webs.</html>");  
button1.setText("Pull the lever");  
button2.setText("Back away");  
break;  
case 3:  
if (!hasGlove) {  
story.setText("<html>You reach for the lever, plunging your hand into the
thick veil of spider webs. While trying to pull the lever, you feel a sharp
pain on your arm before your vision fades to black. Game over.</html>");  
button1.setText("I understand");  
button2.setText("I understand");  
} else {  
story.setText("<html>You reach for the lever, plunging your gloved hand into
the thick veil of spider webs. The lever makes a loud creaking sound as you
press it down, powering a large floodlight that lights up the entire room.
When you look at your hand in the newfound light, you see several large
spiders climbing on your glove. Startled, you shake the glove off and run to
the other corner of the room, where you see a flag that must've been there the
entire time.</html>");  
button1.setText("Read the flag");  
button2.setText("Read the flag");  
}  
break;  
case 4:  
story.setText("<html>As soon as you click the button, a pair of handcuffs
locks you to the table. There's no backing out now. The computer loads a
website belong to X Enterprises: a multinational corporation that hates
puppies! You have to hack them otherwise puppies all over the world will face
the wrath of X Enterprises!</html>");  
button1.setText("Clobber the DOM");  
button2.setText("Pollute the prototype");  
break;  
case 5:  
try {  
Socket var0 = new Socket("chall.lac.tf", 31151);  
String var1 = "";

int var3;  
for(Iterator var2 = history.iterator(); var2.hasNext(); var1 = var1 + var3) {  
var3 = (Integer)var2.next();  
}

var0.getOutputStream().write((var1 + "\n").getBytes("UTF-8"));  
Scanner var5 = new Scanner(var0.getInputStream());  
String var6 = var5.nextLine();  
story.setText(var6);  
var5.close();  
var0.close();  
} catch (Exception var4) {  
System.err.println(var4.getMessage());  
story.setText("<html>The flag is garbled and unreadable. Contact an admin if
you haven't done anything out of the ordinary.</html>");  
}

button1.setText("Leave");  
button2.setText("Leave");  
break;  
case 6:  
story.setText("<html>As you submit the last exploit, X Enterprise's website
goes down. You did it! You're officially the best and coolest hacker in the
world. Your hand is released and a yellow kitchen glove is dropped from the
ceiling and you put it on. As the computer shuts off and its loud humming
comes to a halt, you can hear something quieter in the background: a soft
hissing sound coming from the vent next to the computer.</html>");  
hasGlove = true;  
button1.setText("Run away");  
button2.setText("Investigate");  
break;  
case 7:  
story.setText("<html>Your hacking was ineffective, and the firewall in front
of X Enterprises has detected malicious activity and IP banned you. You are
not the hacker that you wanted to be, and promptly collapse to the ground with
grief. Game over.</html>");  
button1.setText("I understand");  
button2.setText("I understand");  
}

}  
```

By writing down the state transitions and knowing we want to end in state `5`
we can find easily the following path

```  
State 0 (Inspect the computer)  
State 1 (Click the button)  
State 4 (Exploit "dpddpdpp")  
State 6 (Run away)  
State 0 (Inspect the lever)  
State 2 (Pull the lever)  
State 3 (Read the flag)  
State 5  
```

By doing this the click history is 00010010110100. We can now send it via `nc`
or just click through the game. Either way, we get the flag.

```bash  
$ echo "00010010110100" | nc chall.lac.tf 31151  
<html>The flag is written in ornate gold lettering:
lactf{the_graphics_got_a_lot_worse_from_what_i_remembered}</html>  
```

Flag `lactf{the_graphics_got_a_lot_worse_from_what_i_remembered}`

Original writeup (https://github.com/D13David/ctf-
writeups/blob/main/lactf24/rev/secret_of_java/README.md).