Flare On 2014 - Challenge 5 Writeup

Everybody, protect your keystrokes!

Detect it easy revealed that the file for the fifth challenge is a C/C++ library (DLL) file:

42369e0cd50870aa7f551fb1d6b09fa4.png

I started analyzing the file using cutter and the Ghidra decompiler.

In the beginning, the "malware" copies itself to C:\Windows\System32\svchost.dll.

This is happening in fcn.1000a680() whichgets called by fcn.1000b090() (which is called in entry0).

620d8c6001580bd1c1522c7dbdd40a88.png

And then creates a Registry entry:

d5e7d4456d7c85b5491216cadc91e63a.png

This behavior is pretty common for malicious software; after digging deeper I found a call to GetAsyncKeyState which can be used for keylogging.

The method called fcn.1000a4c0() shown in the second screenshot calls a method which then calls the keylogging method:

2bfb2152dda4354d9499816953152880.png

The keylogging function appears to be big but follows a rather simple structure. First, there are a lot of if-statements checking which key was pressed by the user.

 0iVar1 = (*_GetAsyncKeyState)((int32_t)(int16_t)vKey);
 1 if (iVar1 == -0x7fff) {
 2     if ((int16_t)vKey == 0x27) {
 3  uVar2 = fcn.100093b0();
 4  return uVar2;
 5     }
 6     if ((int16_t)vKey == 0x28) {
 7  uVar2 = fcn.100093c0();
 8  return uVar2;
 9     }
10     if ((int16_t)vKey == 0x29) {
11  uVar2 = fcn.100093d0();
12  return uVar2;
13     }
14     if ((int16_t)vKey == 0x2a) {
15  uVar2 = fcn.100093e0();
16  return uVar2;
17     }
18[...]

I began renaming the functions and also checked why ones simply "did nothing" (or in this reset the progress):

d13722a36e0690ee2a1e5a28590b1fde.png

The functions marked with "USELESS" lead to a "reset function" being called:

 0void reset_progress(void)
 1{
 2    *(undefined4 *)0x10017000 = 1;
 3    *(undefined4 *)0x10019460 = 0;
 4    *(undefined4 *)0x10019464 = 0;
 5    *(undefined4 *)0x10019468 = 0;
 6    *(undefined4 *)0x1001946c = 0;
 7    *(undefined4 *)0x10019470 = 0;
 8    *(undefined4 *)0x10019474 = 0;
 9    *(undefined4 *)0x10019478 = 0;
10    *(undefined4 *)0x1001947c = 0;
11    *(undefined4 *)0x10019480 = 0;
12    *(undefined4 *)0x10019484 = 0;
13    *(undefined4 *)0x10019488 = 0;
14    *(undefined4 *)0x1001948c = 0;
15[...]

Reversing The Logging

I picked a random function, that was not marked as useless. It doesn't matter with which one you start - I picked letter_I() for now:

0undefined4 letter_I(void)
1{
2    if (*(int32_t *)0x1001946c < 1) {
3        reset_progress();
4    } else {
5        *(int32_t *)0x1001946c = 0;
6        *(undefined4 *)0x10019470 = 1;
7    }
8    return 0x100142c4;
9}

Next, I used the Cross-Reference function to check where 0x1001946c is set to 1:

8b7515f3171be942549d34d62833af85.png

letter_G() does that, so I moved on to that function:

 0undefined4 letter_G(void)
 1{
 2    if (*(int32_t *)0x10019464 < 1) {
 3        if (*(int32_t *)0x10019468 < 1) {
 4            if (*(int32_t *)0x10019474 < 1) {
 5                reset_progress();
 6            } else {
 7                *(int32_t *)0x10019474 = 0;
 8                *(undefined4 *)0x10019478 = 1;
 9            }
10        } else {
11            *(int32_t *)0x10019468 = 0;
12            *(undefined4 *)0x1001946c = 1;
13        }
14    } else {
15        *(int32_t *)0x10019464 = 0;
16        *(int32_t *)0x10019468 = 1;
17    }
18    return 0x100142bc;
19}

Since I want 0x1001946c set to 1 we have to move the code execution to the second else-block. To do so 0x10019468 has to be 1.

From now on I repeated the progress => cross-referencing the global variable in the if-check and looking for the function that sets it to 1.

Eventually, I arrived at letter_L() where 0x10017000 is checked for being less than 1. Said variable gets set to 1 by the reset function so I assumed that this is the starting point.

letter_L() can set two global variables to 1 depending on the results of the nested if-statements.

Since the outer check will fail I continue to cross-reference 0x10019460 and looked for a cmp instruction:

808355a72ea6a7cfff9ede43bd76ad61.png

In the number_0 function 0x10019460 will be set to 0 and 0x10019464 to 1, so I continued by cross-referencing the latter.

Repeating that technique I eventually arrived at letter_M() which "terminates" the logging sequence:

0undefined4 letter_M(void)
1{
2    if (0 < *(int32_t *)0x100194fc) {
3        reset_progress();
4        fcn.10001240();
5    }
6    return 0x100142d4;
7}

Reversing the algorithm lead to the following string:

l0gging dot ur dot 5tr0ke5 at flare dash on dot com

Which can be "translated" to:

[email protected]


Challenge 4