|
||
---|---|---|
.. | ||
README.md | ||
decode.py | ||
src.zip |
README.md
Object Oriented Programming
Pwn/Rev 413 points 88 solves
There's this small up and coming language called java I want to tell you about
provided file: <src.zip>
writeup
we are given a zip file with java source. there's a Main.java and a bunch of 2-character source files, with classes that contain a bunch of 2-character methods that return 2-character strings. examining Main, we see it uses reflection to call into these classes and methods to map the input string to the output.
String userInput = getUserInputMethodFromScannerAndInvokeAndReturnOutput(scanner);
if (userInput.length() != SIXTEEN)
System.exit(0);
if (executeCodeThatDoesSomethingThatYouProbablyNeedToFigureOut(userInput).equals(scanner.getClass().getPackageName().replace(".", ""))) {
invokePrintLineMethodForOutputPrintStream(outputPrintStream, printLineMethod, "Nice. Flag: rgbCTF{" + userInput + "}");
} else {
invokePrintLineMethodForOutputPrintStream(outputPrintStream, printLineMethod, "Try again.");
}
this code shows us the user input must be 16 chars exactly, and then a transformation function is
called (with the parameter javautil
, the package of Scanner without periods).
public static String executeCodeThatDoesSomethingThatYouProbablyNeedToFigureOut(String stringToExecuteAforementionedCodeOn) throws Exception {
String encryptedString = reallyBasicQuoteUnquoteEncryptionFunctionThatWillOnlyTakeTimeToFigureOutIfYouKeepReadingTheseRidiculouslyLongMethodNames(stringToExecuteAforementionedCodeOn);
String returnValueOfThisFunction = new String();
String[] chunksOfEncryptedStringOfLengthFour = splitStringIntoChunksOfLength(encryptedString, FOUR);
for (String chunkOfEncryptedStringOfLengthFour : chunksOfEncryptedStringOfLengthFour) {
String[] chunksOfChunkOfEncryptedStringOfLengthFourOfLengthTwo = splitStringIntoChunksOfLength(chunkOfEncryptedStringOfLengthFour, TWO);
String firstChunkOfChunkOfEncryptedStringOfLengthFourOfLengthTwo = chunksOfChunkOfEncryptedStringOfLengthFourOfLengthTwo[0];
String secondChunkOfChunkOfEncryptedStringOfLengthFourOfLengthTwo = chunksOfChunkOfEncryptedStringOfLengthFourOfLengthTwo[1];
Class<?> classAndExtraCharactersSoItsNotAKeyword = Class.forName(firstChunkOfChunkOfEncryptedStringOfLengthFourOfLengthTwo);
Object object = classAndExtraCharactersSoItsNotAKeyword.getConstructors()[ZERO].newInstance();
for (int loopArbitraryCounterIterator = 0; loopArbitraryCounterIterator < THREE; loopArbitraryCounterIterator++) {
Method method = classAndExtraCharactersSoItsNotAKeyword.getMethod(secondChunkOfChunkOfEncryptedStringOfLengthFourOfLengthTwo);
secondChunkOfChunkOfEncryptedStringOfLengthFourOfLengthTwo = (String)method.invoke(object);
}
returnValueOfThisFunction = new String(returnValueOfThisFunction + secondChunkOfChunkOfEncryptedStringOfLengthFourOfLengthTwo);
}
return returnValueOfThisFunction;
}
looking at the transformation function, we find it calls a method that XORs all the characters in
the input with a key, which turns out to be 2
(found by modifying the code to print it out). then,
it splits the input into chunks of 4, uses the first 2 chars as a class name, and iterates 3 times
to transform the second 2 chars by calling the method in the class. the result is concatenated
together. this can be solved with a simple script to parse the java files to map the
transformations, then reversing the transformation by doing the opposite of this operation.
first, parse the java. it's very regular so this isn't too hard
mapping = {}
for file in pathlib.Path("src").iterdir():
if "Main" in file.name:
continue
name = file.name.replace(".java", "")
with file.open("r") as f:
data = f.read()
thisfile = {}
mname = None
for line in data.split("\n"):
if mname is None:
if "public String" in line:
mname = line.strip().split(" ")[2].replace("()", "")
else:
val = line.strip().split(" ")[1].replace('"', "").replace(";", "")
thisfile[mname] = val
mname = None
mapping[name] = thisfile
next, go through backwards to find which class has 3 methods that can be chained to produce the required output. (implemented this with horrible CTF spaghetti code, please don't tell my fundies professor :)
start = "javautil"
def rfind(what):
for file,data in mapping.items():
for k,v in data.items():
if v == what and k in data.values():
for k2, v2 in data.items():
if v2 == k and k2 in data.values():
for k3, v3 in data.items():
if v3 == k2:
return file + k3
data = rfind("ja") + rfind("va") + rfind("ut") + rfind("il")
finally compute XOR for the flag
data = bytearray(data.encode())
for i in range(len(data)):
data[i] ^= 2
print(data)