Soda workflow
The first we have to do is decompile the jar
file. A great tool for that is Java decompiler jd-gui
:
1
jd-gui soda.jar
Now that we can see the source code, it’s time to go over it and run the program a couple of times to get an idea of what’s happening. After some code analysis, here’s a typical workflow of the soda machine.
First, we purchase
a drink and, unfortunately for us, it always gets stuck
.
Now, we can punch
, kick
, shake
or shatter
the vending machine, but this will not get us anywhere.
To help free our soda, we have to get rid of soda.Drink.DrinkStatus.STUCK
status. We can do this by calling a tap function:
1
2
3
4
5
6
public void tap() {
for (byte b = 0; b < 12; b++) {
if ((this.drinks[b]).status == soda.Drink.DrinkStatus.STUCK && (this.drinks[b]).stuck > 0)
(this.drinks[b]).stuck--;
}
}
How many times do we have to tap? 3 times - we can get that info from class Drink
property stuck
:
1
2
3
4
5
6
7
8
class Drink {
float cost = (float)(Math.random() * 6.0D);
DrinkStatus status = (Math.random() > 0.75D) ? DrinkStatus.EMPTY : DrinkStatus.READY;
int stuck = 3;
// more code ...
Okay, now that the drink has loosen up, we can try to reach
for it and knock it down. However:
There are people around and we have to wait
. How long?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (paramArrayOfString[0].equalsIgnoreCase("wait")) {
int i = 0;
try {
i = Integer.parseInt(paramArrayOfString[1]);
} catch (Exception exception) {
System.out.println(">> Not sure what you mean");
return;
}
pause(i);
if (i >= 10) {
bystanders = false;
System.out.println(">> ...Looks like nobody's around...");
} else {
bystanders = true;
System.out.println(">> People are walking down the street.");
}
return;
At least 10 seconds.
Now we can try to reach
for our drink:
Now that it had fallen, we can grab
it:
And … we don’t get the flag.
retrieve() my drink!
There is an interesting thing going on in the retrieve()
function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void retrieve() {
byte b = -1;
float f = -1.0F;
for (byte b1 = 0; b1 < 12; b1++) {
if ((this.drinks[b1]).status != soda.Drink.DrinkStatus.EMPTY &&
(this.drinks[b1]).cost > f) {
b = b1;
f = (this.drinks[b1]).cost;
}
}
if ((this.drinks[b]).status == soda.Drink.DrinkStatus.DROPPED) {
soda.printFlag();
} else {
System.out.println(">> No flags in here... was the prophecy a lie...?");
}
}
When we use grab
command, retrieve()
function is called. And contrary to what we would probably expect, it does not really retrieve the drink we ask for. Instead, it will iterate over all of the drinks, and choose the most expensive
, not empty
drink there is. Then, it will check if this
drink is equal to the one we decided to purchase
at the very beginning and then heroically rescue - only then will it give us the flag.
So basically what we have to do to get he flag is buy the most expensive drink there is. This can be tricky because all soda prices are random and can sometimes exceed 5$ budget we have. No problem - we just keep reconnecting to the server until the most expensive (and not empty) drink is under 5$. In the example below, soda number 4
meets the requirements for most expensive
and not empty
:
Then, it is the matter of repeating the whole procedure and we get the flag: