Flare-On 6 CTF WriteUp (Part 2)

This is the second part of the Flare-On 6 CTF WriteUp series.

2 - Overlong

The challenge reads

The secret of this next challenge is cleverly hidden. However, with the right approach, finding the solution will not take an overlong amount of time.

We have a PE file named Overlong.exe. Running, a MessageBox pops up.

Figure 1: Just a MessageBox

It displays "I never broke the encoding". The binary doesn't exhibit any other behavior. Let's debug in x64dbg.

Figure 2: Debugging in x64dbg 

Analyzing the code at the entrypoint, we can see a call to sub_1231160 followed by a call to MessageBoxA@16. MessageBoxA is a WinAPI function which displays a modal dialog box on the screen. Hence the MessageBox we saw must be the result of calling this function.

As per the docs, the function takes 4 parameters. The second parameter LPCTSTR lpText contains a pointer to the string to be displayed. We set a breakpoint on the call and allow it to run as in Figure 2. Once the breakpoint hits, let's have a look at the function arguments window in x64dbg at the right.

Figure 3: The arguments passed to MessageBoxA

Let's try to decode the arguments

HWND hWnd: NULL
LPCTSTR lpText: 0x001AFAAC (Pointer to "I never broke the encoding: "
LPCTSTR lpCaption:0x01233000 (Pointer to "Output")
UINT uType: 0 (MB_OK)

This explains the MessageBox call but what about the other function which was called before this. Reading assembly can often be time consuming. Let's load it in Ghidra which is an open source reverse engineering tool. Among other features, it sports a decompiler which converts the assembly listing to pseudo C, speeding up our analysis.

Figure 4: The binary in Ghidra

The decompiled code for the function at the entrypoint looks like Figure 4. local_88 is a array of 128 bytes located on the stack. This is passed to the function FUN_00401160 as the first argument. The second argument &DAT_00402008 is a pointer within the data section. The last argument is the integer 0x1c.

Navigating to 402008 we see a bunch of bytes.

Figure 5: The bytes at 402008

It looks that this is an encrypted piece of data which the function is going to decrypt with the output written to the local_88 array. We don't know yet the purpose of the third argument 0x1c, it may be the key.

Figure 6: The decompiled code listing of FUN_00401160

This function in turn calls FUN_00401000. However, it doesn't look like that the third parameter (0x1c) is the key as it is used as the loop upper bound. Interestingly, the number of characters in the string "I never broke the encoding: " is also 0x1c or in 28 decimal. Could it mean that it refers to the number of characters in the final output? Let's check FUN_00401000 before deciding.

Figure 7: The decompiled code of FUN_00401000

This looks to be the function performing the actual decryption. However, it just takes two parameters - input and output. It does not take a key. This suggests that 0x1c is not the key but rather the length of the output. Let's run the program in x64dbg and try increasing the passed value (0x1c).

Figure 8: Changing the parameters in x64dbg

This is easy to do. Set a breakpoint at the call instruction. When the breakpoint hits, double-click on 0x1c on the stack window to open the Modify dialog. From here, we can set any value. Lets put 38 and run.

Figure 9: A part of the flag?

This time we get some piece of additional text at the end which looks to be a part of the flag. Our assumption that 0x1c is the length of the output is right. From Ghidra, we know that the maximum length of the output is 128 which is the length of the output array. Lets re-run the program changing the parameter to 128.

Figure 10: The flag!

And that's the flag.

Flag I_a_M_t_h_e_e_n_C_o_D_i_n_g@flare-on.com