PoliCTF 2015 - John's Pastry Shop

The Problem:

“Among his hobbies, John likes baking cakes to eat during the warm afternoons in Milan. He is damn good at this such that, a couple of months ago, he decided to open a pastry shop on his own. The shop was an immediate success and John needed to bake just so many cakes that he decided to outsource the production of his famous NewYorkCheeseCake to another external and trusted pastry shop, the Shamano's (see shamanoPastryShop.pem). John provided Shamano's with the original basic recipe of his Cake (see Cake.java) and, after his customization, Shamano returns to John a cake container holding the NewYorkCheeseCake (see ShamanoCakeContainerEncoded.jar). Notice that Shamano has to follow John's directions carefully and that is why he always have to encode properly his cake containers so that John can verify all of them accordingly to a fixed decoding process (see extract of source code in Decode.java). John always tries his best for verifying the quality and genuineness of the incoming NewYorkCheeseCake but, you know, to busy people, like he is, it may sometimes happen to forget to check something... You can find the shop at pastry.polictf.it:80”

Yikes. Let's dive in!

We're given three files to download: Cake.java, Decrypt.java, ShamanoCakeContainerEncoded.jar

Cake.java:
public abstract class Cake {

    protected boolean shouldBeAddedTheSpecialIngredient;
    protected List<String> ingredientsList;

    // Zero constructor
    protected Cake() {
        shouldBeAddedTheSpecialIngredient = false;
        ingredientsList = new LinkedList<>();
    }

    // To be implemented in the classes that extends this one.
    // by filling up the ingredientsList with all the ingredients
    // of the extending Cake.
    public abstract void addIngredientsToCake();

    public @NotNull List<String> getIngredients() {
        return ingredientsList;
    }
}

Simple class definition for cake – didn't spend any time here. In hindsight, the bool for the 'special ingredient' should have been noticed. Alas, slower alternative solution incoming.

ShamanoCakeContainerEncoded.jar:

Tried to extract this file (jars are just colourful zips), but no luck as apparently it's encoded somehow.

Decode.java
/*
*  John Decoding System:
*
*  Dear Shamano,
*  This is how I will check whether you have properly prepared my cake containers.
*  Please take into account these guidelines carefully before sending back the cake containers!
*  If your cake container won't comply to the current rules, I'll reject them immediately!
*
*  Best,
*  John, the Pastry Master. 
* 
*/ 

// ..Extract from the decoding system..

// These are the special bytes for the encoding/decoding.
private static final byte INIT_BYTE = (byte) 0x17;
private static final byte ESCAPE_BYTE = (byte) 0x18;
private static final byte EXIT_BYTE = (byte) 0x19;

// These are helper flags.
private static boolean isValidData = false;
private static boolean isEscapingMode = false;
private static boolean isSequenceClosed = false;


// Decoder behavior for the input cake containers:
// ouputStream holds a FileOutputStream, which writes the 
// decoded version of the file..
int read;

while ((read = System.in.read()) != -1 && !isSequenceClosed) {

    if ((byte) read == INIT_BYTE && !isEscapingMode)
        isValidData = true;
        else {

            if ((byte) read == EXIT_BYTE && !isEscapingMode) {
            isValidData = false;
                        isSequenceClosed = true;
                }
                else {

                    if (!isEscapingMode && (byte) read == ESCAPE_BYTE)
                        isEscapingMode = true;
                        else {

                            if (isEscapingMode && !isValidData)
                                    isEscapingMode = false;
                                else {

                                    if (isValidData) {
                                        isEscapingMode = false;
                                        outputStream.write((byte) read);
                                    }
                                }
                        }

                    }
                }
        }

}

Looks like the decoding algorithm, not too fancy. INIT_BYTE, ESCAPE_BYTE and EXIT_BYTE. Read through it a few times, and it looks like the decoder simply reads a file, waits for the INIT_BYTE (0x17), then spews out the data until EXIT_BYTE (0x19). Legit 0x17 and 0x19 bytes in between these are escaped with 0x18.

Next step, decode the encoded jar file. For this I just wrapped the Decode.java code into a real Java class, modified it to read the encoded jar and output the file.

Success! Inside the jar:

  • NewYorkCheeseCake.class
  • Java META-INF stuff

Decompiled Jar

Cool, so Shamano just extends the Cake class and adds the required ingredients.

(Server responses approximated using poor memory, sorry!)

nc pastry.polictf.it 80 
> Hello, welcome to the Cake upload service. Please send your Cake now.

cat decoded.jar | nc pastry.polictf.it 80
> This doesn't seem to be encoded correctly, go away plz!

cat ShamanoCakeContainerEncoded.jar | nc pastry.polictf.it 80
> Thanks for the Cake :D. Here are the ingredients:
  Cream Cheese
  Biscuits
  Sugar
  Isinglass

Righto, so the server is happy to run this Java code and give us the results. Let's try to modify the code a little and see if we're right.

Modified cake code

Compile the code, then do the old switcharoo with the NewYorkCheeseCake.Class in the ShamanoCakeContainerDecrypted archive.

cat modifiedCake.jar | nc pastry.polictf.it 80
>This doesn't seem to be encoded correctly, go away plz!

Oh yeah, the encoding! The encoding is a case of sticking 0x17 at the front, 0x19 at the end and escaping all other 0x17, 0x18 and 0x19 bytes with 0x18. No need for a smart console one-liner for this, I won't need to repeat it 20 times, right? wrong... At least I had a HexEdit macro.

Encoding with HexEdit

cat modifiedCakeEncoded.jar | nc pastry.polictf.it 80
>The file hash is incorrect, be off with you.

Oh dear. Where is the hash stored? Only other things in the archive are...

Files in META-INF

So it's got this SHA1 hash in the manifest. Can I just change it to the actual hash of my files? Which files?

Google search for Hmm...

Google search for

Apparently there's a thing called keytool which comes with the JDK, which allows you to create an RSA public key for signing your jars.

keytool -genkey -alias theCakeMaster -keystore myFancyNewKeystore -sigalg RSA

Then you can use jarsigner (also in the JDK) to sign a jar with your key.

jarsigner -keystore myFancyNewKeystore -sigalg RSA modifiedCakeEncoded.jar  theCakeMaster

Cool, so we have a signed jar now!

cat modifiedCakeEncodedSigned.jar | nc pastry.polictf.it 80
>This Cake wasn't signed by Shamano. Skedaddle, pronto!

Grr. How is that information stored in the jar? Presumably they don't check the public key, otherwise this isn't gonna be easy. Perhaps it's just the alias I chose in keytool?

keytool -genkey -alias Shamano -keystore myFancyNewKeystore -sigalg RSA
jarsigner -keystore myFancyNewKeystore -sigalg RSA modifiedCakeEncoded.jar Shamano

cat modifiedCakeEncodedSignedByShamano.jar | nc pastry.polictf.it 80
>This Cake wasn't signed by Shamano. Skedaddle, pronto!

There's something else afoot. Looking at the Shamano RSA file, it has some other metadata involved, like Italy, Milano, Shimano Inc Etc.

Maybe these are the values entered into the keytool when creating my keys? But which is which? By comparing my CakeMaster.RSA file with the Shimano.RSA file, it's easy to map field to field.

keytool -genkey -alias Shamano -keystore myFancyNewKeystore -sigalg RSA
Country: Italy
Company: Shamano Inc.
... etc

cat modifiedCakeEncodedSignedByShamanoReally.jar | nc pastry.polictf.it 80
    >Thanks for the Cake :D. Here are the ingredients:
    Cream Cheese
    Biscuits
    Sugar
    Isinglass
    I'm in ur base!

Woohoo! Successfully modified the Cake! Now to exploit...

Couldn't find anything quick and easy which worked on my machine, so changed tack. The flag must be in a file nearby, right?

cat outputAllFiles.jar | nc pastry.polictf.it 80

No obvious flag file present anywhere nearby, but the JohnPastryShop.jar file is there. Perhaps that holds the secrets. Obviously, that's a jar file so we can't just print it directly to terminal (didn't find that out the long way, honest). Gotta unzip it first!

Compile, sign, encode...

  cat unzipAndPrint.jar | nc pastry.polictf.it 80 | grep flag
  >flag{something_something_pastry_something!}

Result!