Reverse Engineering

Challenge
Link

JavaCPScript (210 pts)

FlagChecker (430 pts)πŸ₯‡

No Math Rev (480 pts)

JavaCPScript (210 pts)

Description

Mutex in JavaCPScript

Solution

The challenge written javascript, basically it is a flag validator challenge but it utilize threading and some recursive way to validate the flag. In this case i tried to find the code that will validate wether our input is correct or not. Looking at Util.js i found interesting code

function Solver(a, b, c) {
    function S1(d) {
        function S2(e, f, g) {
            if ((e == null) || (f == null))
                return c(g)
            else {
                function S3(h, i) {
                    function S4(j, k) {
                         return d(i, k, (g) && ((h == null) ? true : (h == j.data)))
                    }
                    return f(S4)
                }
                return e(S3)
            }
        }
        return S2
    }
    return TaskManager(S1)(a, b, true)
}

Trying to dump used variable in line 9 i found interesting variable which is g. I notice that g will validate the value from backward and it will return True if our input is correct for each character. The total return from Solver function is 32 which is same like in the modulus value in callReadFileSync function. So the next step is i just bruteforce for each character by running the challenge and check the g variable. Here is the solver i used

  • Add line 10 to printout g variable

Run the solver and it will get the full flag at the end

Flag: DEAD{CPS7yl3_4nd_3vn7_10op_h3ll}

FlagChecker (430 pts)

Description

A simple flag checker

Solution

Given ELF 64 bit, open it using IDA. Looking at available strings i found interesting string that related to pyinstaller.

Based on above information i tried to extract the pyc file using pyinstxtractorarrow-up-right.

Lets use pycdc to deocmpile main.pyc file

Although it was not fully decompile the pyc but we still can understand the flow.

  • Some variable are initialize as global variable

  • import checker library

  • check the value of chk variable

  • run checker.check

  • print value of t xored with static value (array)

If we try to do the xor with above information we will get the output below

In this step my assumption is t variable will be overwritten with some value. Also our input will be processed by some function because t variable and our input (FLAG variable) are global variable. The executed code after receiving our input are importing check library and run check.check, so lets check the library.

From image above we can see that there is no checker.pyc and the only file named checker is shared object file. It is possible to load shared object file in python, so lets try to decompile it using IDA. After opened it in IDA, search function with name checker because the called function is checker. Modify some function and variable based to make it easier to understand

  • PyObject_GetAttrString_OBF will do string deobfuscation/decode for the second argument

  • Our input will be stored in input variable and the size should be 32

  • input[31] should 125 or "}"

  • The first 5 bytes of input (index 0 - 4) should be "DEAD{"

So if our input contains DEAD{...} and it has length 32 it will change the chk variable to 4919 which is same like 0x1337. PyObject_GetAttrString_OBF just do negation of the second argument, below is the implementation in python

The function will be added in flight through function PyModule_AddFunctions. The second argument will pointing to check function. Double click off_6500 and take a look on off_6500+8, there will be function address.

Static analyze the code and take some notes

Now we know that we need to satisfy the comparison. Because the function that process our input is sub_1DBC lets take a look on it.

The algorithm doesnt directly shown up, lets take a look on PROCESSED_INPUT variable. PROCESSED_INPUT variable has so many cross references, go to one of the cross reference we will see function like below.

From code above we can see that our input will processed by rotate left instruction, looking at another function there will be some arithmetic or logic operator also. My approach during the competition is to set breakpoint on all available operator and then trace the operation. Set breakpoint on all available operator using idapython

After that setup debugger for .so files

  • guest

    • run linux_server64 on linux as root (1)

    • run python3.10 main.pyc (4)

    • trigger breakpoint by input value (7)

  • host

    • Select debugger (2)

      • Remote linux debugger

    • process options (3)

      • set hostname to IP Address of guest

    • attach to process (5)

      • select python3 main.pyc

    • continue the process (6)

    • click same on debugger warning popup (8)

After that we will see image like below

run automation below to trace the instruction

Generate output for 2 different input then use script below to parse the output

Use the comment and uncomment versoin of op = op.replace(key, hex(data2[key])) then analyze it. In this step my objective is to recover the instruction and put it as python code, below is the reconstructed version.

Looking at all operations above i notice that all of them are reversible, so lets put it backward and reverse the operation.

For below part i reverse it manually

  • First part

  • Last part

Put all the code together and run it

Flag: DEAD{run_pybyt3c0d3_w1th_C_4P1!}

Another solution

I'll try to analyze code on PyCode_New

TBU

No Math Rev (480 pts)

Description

Enough of this mathematics, how about trying some actual reverse engineering?

Solution

TBU

Last updated