TUCTF LuckyCharms. Exploiting Simple Java Deserialization Vulnerability.
15 May 2016This task was about exploiting (perhaps) the simplest form of Java Deserialization vulnerabilities. First, off 146.148.10.175:1033
(if the host is still up) was a web application running on Tomcat. Looking at its HTML sources we see a friendly comment suggesting to look up http://146.148.10.175:1033/LuckyCharms?look=LuckyCharms.java
<html>
<body>
Frosted Lucky Charms,
<br>
They're magically delicious!
<br>
<img src="https://upload.wikimedia.org/wikipedia/en/f/ff/Lucky-Charms-Cereal-Box-Small.jpg">
<!-- <a href="/?look=LuckyCharms.java"></a> -->
</body>
</html>
which in turn spat out the Java code of this very app:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.nio.file.Path;
import java.nio.file.Paths;
abstract class OSFile implements Serializable
{
String file = "";
abstract String getFileName();
}
class WindowsFile extends OSFile
{
public String getFileName()
{
//Windows filenames are case-insensitive
return file.toLowerCase();
}
}
class UnixFile extends OSFile
{
public String getFileName()
{
//Unix filenames are case-sensitive, don't change
return file;
}
}
public class LuckyCharms extends HttpServlet
{
public void init() throws ServletException {}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
OSFile osfile = null;
try
{
osfile = (OSFile) new ObjectInputStream(request.getInputStream()).readObject();
}
catch (Exception e)
{
//Oops, let me help you out there
osfile = new WindowsFile();
if (request.getParameter("look") == null)
{
osfile.file = "charms.html";
}
else
{
osfile.file = request.getParameter("look");
}
}
String f = osfile.getFileName().replace("/","").replace("\\","");
if (f.contains("flag"))
{
//bad hacker!
out.println("You'll Never Get Me Lucky Charms!");
return;
}
try
{
Path path = Paths.get(getServletContext().getRealPath(f.toLowerCase()));
String content = new String(java.nio.file.Files.readAllBytes(path));
out.println(content);
}
catch (Exception e)
{
out.println("Nothing to see here");
}
}
public void destroy() {}
}
Automatically my sight fell on (OSFile) new ObjectInputStream(request.getInputStream()).readObject();
which is the point of entry for the exploit. This is simply how to process serialized Java objects - read off the network (in this case), shove in to ObjectInputStream
, read as object using readObject()
and cast to whatever object one needs (OSFile
).
getInputStream()
is servlet framework’s method to fetch the body of an HTTP request. Thus our payload should be a serialized object. What does the payload consist of? Looking at the sources it becomes clear that we need to make sure osfile
variable referencing our crafted object has the file
property pointing to the flag file. That way the app will read the file specified by osfile.file
. The one thing that’s left is to bypass the tiny filter if (f.contains("flag")) ...
which is not too
hard to do provided the path to the flag is case-insensitive. Quickly checking that the servlet is running on a Windows machine by calling http://146.148.10.175:1033/LuckyCharms?look=luckycharms.java makes it clear that the filter is incomplete and will let something like flaG
go through.
Putting it all together:
- Construct our OSFile object with a desired path to the flag
- Serialize it
- Send over the result as body of an HTTP request to the servlet
- Profit
Here’s Java code that constructs the desired object and saves the binary to disk. Compile, run javac Main.java && java Main
and send over the binary cd /tmp && curl -X POST --data-binary @serialized.bin 'http://146.148.10.175:1033/LuckyCharms'
to get the flag: TUCTF{a_cup_of_joe_keeps_the_hackers_away}
(whatever that means).
Read the manual if unsure, post comment(s) if unclear.