Rating:
# not_malware Writeup
For more writeups and content follow us on Twitter -- @RagnarSecurity
This challenge was in the category Reverse Engineering for 150 points. This was one of the better challenges worth writing about for CSAW 2020.
To start lets see what happens when we run the binary:
```
WittsEnd2@DESKTOP-1N65HDS:[~/csaw/rev/not_malware]
$ ./not_malware
What's your credit card number (for safekeeping) ?
>> aaaaaaaaaaaaaaaaaaaaaaa
```
It asks for a credit card number, and then exits (or it seems). Lets open this up in Ghidra and see what is actually going on.
```c
undefined8 FUN_001012bc(void)
{
int iVar1;
size_t sVar2;
undefined8 uVar3;
double dVar4;
char local_b6 [10];
undefined4 local_ac;
char local_a8 [32];
char local_88 [4];
char local_84;
char local_83;
char local_82;
char local_81;
char local_80;
char local_7f;
char local_7e;
char local_7d;
char local_7c;
char local_7b;
char local_7a;
char local_79;
char local_78;
char local_77;
char local_76;
char local_75;
char local_68 [8];
char local_60;
char local_5f;
char local_5e;
char local_5d;
char local_5c;
char acStack91 [20];
char local_47;
char acStack70 [30];
char local_28 [8];
int local_20;
int local_1c;
int local_18;
int local_14;
uint local_10;
int local_c;
FUN_001012a6();
printf("What\'s your credit card number (for safekeeping) ?\n>> ");
fgets(local_68,0x3c,stdin);
sVar2 = strlen(local_68);
if (0x3c < sVar2) {
puts("Well this was unnecessary.");
/* WARNING: Subroutine does not return */
exit(1);
}
local_18 = 0x10;
dVar4 = pow(16.00000000,0.50000000);
local_18 = (int)(dVar4 - 1.00000000);
local_c = 0;
while (local_c < 8) {
local_28[local_c] = local_68[local_c];
local_c = local_c + 1;
}
local_28[local_c] = '\0';
iVar1 = strncmp(local_28,"yeetbank" + (long)local_18 * 9,8);
if (iVar1 != 0) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_60 != ':') {
puts("Get out.");
/* WARNING: Subroutine does not return */
exit(1);
}
local_10 = (int)local_5f - 0x30;
local_1c = (int)local_5e + -0x30;
local_20 = (int)local_5d + -0x30;
if (local_5c != ':') {
puts("Get out.");
/* WARNING: Subroutine does not return */
exit(1);
}
local_14 = 0;
while (local_14 < 0x14) {
uVar3 = FUN_00101288((ulong)local_10);
snprintf(local_b6,10,"%ld",uVar3);
local_88[local_14] = local_b6[local_20];
local_10 = local_10 + local_1c;
local_14 = local_14 + 1;
}
local_c = 0;
while (local_c < 0x14) {
local_a8[local_c] = local_68[local_c + 0xd];
local_c = local_c + 1;
}
if (local_a8[0] != local_88[0]) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[16] != local_78) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[11] != local_7d) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[3] != local_88[3]) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[7] != local_81) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[15] != local_79) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[1] != local_88[1]) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[12] != local_7c) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[19] != local_75) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[13] != local_7b) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[14] != local_7a) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[5] != local_83) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[9] != local_7f) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[8] != local_80) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[18] != local_76) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[6] != local_82) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[17] != local_77) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[2] != local_88[2]) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[10] != local_7e) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_a8[4] != local_84) {
/* WARNING: Subroutine does not return */
exit(1);
}
if (local_47 != ':') {
puts("Get out.");
/* WARNING: Subroutine does not return */
exit(1);
}
local_c = 0;
local_ac = 0x646e65;
while( true ) {
if (2 < local_c) {
puts("Thanks!");
FUN_00101229();
return 0;
}
if (*(char *)((long)&local_ac + (long)local_c) != local_68[local_c + 0x22]) break;
local_c = local_c + 1;
}
/* WARNING: Subroutine does not return */
exit(1);
}
```
It looks like it executes some function before asking for our credit card number, then it performs logic to determine a credit card number, and then it prints a flag if a valid credit card. Lets break thsi down further.
Lets look at these lines of code:
```C
while (local_c < 8) {
local_28[local_c] = local_68[local_c];
local_c = local_c + 1;
}
local_28[local_c] = '\0';
iVar1 = strncmp(local_28,"yeetbank" + (long)local_18 * 9,8);
if (iVar1 != 0) {
/* WARNING: Subroutine does not return */
exit(1);
}
```
```asm
LAB_00101379 XREF[1]: 00101394(j)
00101379 8b 45 fc MOV EAX,dword ptr [RBP + local_c]
0010137c 48 98 CDQE
0010137e 0f b6 54 MOVZX EDX,byte ptr [RBP + RAX*0x1 + -0x60]
05 a0
00101383 8b 45 fc MOV EAX,dword ptr [RBP + local_c]
00101386 48 98 CDQE
00101388 88 54 05 e0 MOV byte ptr [RBP + RAX*0x1 + -0x20],DL
0010138c 83 45 fc 01 ADD dword ptr [RBP + local_c],0x1
LAB_00101390 XREF[1]: 00101377(j)
00101390 83 7d fc 07 CMP dword ptr [RBP + local_c],0x7
00101394 7e e3 JLE LAB_00101379
00101396 8b 45 fc MOV EAX,dword ptr [RBP + local_c]
00101399 48 98 CDQE
0010139b c6 44 05 MOV byte ptr [RBP + RAX*0x1 + -0x20],0x0
e0 00
001013a0 8b 45 f0 MOV EAX,dword ptr [RBP + local_18]
001013a3 48 63 d0 MOVSXD RDX,EAX
001013a6 48 89 d0 MOV RAX,RDX
001013a9 48 c1 e0 03 SHL RAX,0x3
001013ad 48 01 c2 ADD RDX,RAX
001013b0 48 8d 05 LEA RAX,[s_yeetbank_00102020] = "yeetbank"
69 0c 00 00
001013b7 48 8d 0c 02 LEA RCX,[RDX + RAX*0x1]
001013bb 48 8d 45 e0 LEA RAX=>local_28,[RBP + -0x20]
001013bf ba 08 00 MOV EDX,0x8
00 00
001013c4 48 89 ce MOV RSI,RCX
001013c7 48 89 c7 MOV RDI,RAX
001013ca e8 71 fc CALL strncmp int strncmp(char * __s1, char *
ff ff
001013cf 85 c0 TEST EAX,EAX
001013d1 74 0a JZ LAB_001013dd
001013d3 bf 01 00 MOV EDI,0x1
00 00
001013d8 e8 33 fd CALL exit void exit(int __status)
ff ff
-- Flow Override: CALL_RETURN (CALL_TERMINATOR)
```
The first thing that it checks is the first eight characters is equal to `iVar1 = strncmp(local_28,"yeetbank" + (long)local_18 * 9,8);` What the heck does this mean? My first instinct was to use GDB to figure this out. I set a breakpoint at the address where main begins; however, I get this output from GDB.
```
[Inferior 1 (process 387) exited with code 01]
```
Huh? Why am I getting an error? How do I defeat this? If we remember from earlier there was this random function being called `FUN_001012a6();`. Lets see what it actually does.
```C
void FUN_001012a6(void)
{
FUN_00101782();
FUN_0010186e();
FUN_001018b4();
return;
}
```
Huh, it calls three other functions. I quickly skimmed the functions and saw something that caught my eye:
```C
void FUN_001018b4(void)
{
long lVar1;
lVar1 = ptrace(PTRACE_TRACEME,0,1,0);
if (lVar1 == -1) {
/* WARNING: Subroutine does not return */
exit(1);
}
return;
}
```
This is a common trick in RE. This is an anti-debugging technique using ptrace. Essentially, GDB uses ptrace (the tracer) in order to perform its breaking and stepping operations; however, this function detects whether another program is trying to trace the current program. If it is, it exits. I highly recommend reading `Programming Linux Anti-Reversing Techniques` by `Jacob Baines` to learn more.
We can remove this function by patching it out. What I did was I patched `FUN_001012a6();`
For this, you need to open a copy of `not_malware in raw binary mode`, go to the address in the assembly where the function is being called, and patch out (by right clicking) all the bytes related to the function call to `NOP (0x90)`.
After patching all of the bytes, you want to click on `file->export` and export it as a binary file. Now you have a patched binary and you can use GDB!
Now that we have GDB, we can set a breakpoint at main and figure out what it is doing! I first got the address of main by setting a break point at `printf`, and annoyingly step through instructions until I returned to the main function (my address in GDB was not exactly the same as in Ghidra). Once I did this, I set a break point at the string comparison to determine the actual values being compared. I saw that it was comparing my input to the value `softbank`. Thus; I know the first 8 characters are should be `softbank`.
From here, it got slightly tricky. After `softbank`, the required values are `:???:????????????????????:???` where `? = ascii character`. Lets break this down:
```C
local_10 = (int)local_5f - 0x30; //:??X:????????????????????:???
local_1c = (int)local_5e + -0x30; //:?X?:????????????????????:???
local_20 = (int)local_5d + -0x30; //:X??:????????????????????:???
if (local_5c != ':') {
puts("Get out.");
/* WARNING: Subroutine does not return */
exit(1);
}
local_14 = 0;
while (local_14 < 0x14) {
uVar3 = FUN_00101288((ulong)local_10);
snprintf(local_b6,10,"%ld",uVar3);
local_88[local_14] = local_b6[local_20];
local_10 = local_10 + local_1c;
local_14 = local_14 + 1;
}
local_c = 0;
while (local_c < 0x14) {
local_a8[local_c] = local_68[local_c + 0xd];
local_c = local_c + 1;
}
```
It looks like the third unknown character (`local_10`) sets the value for `FUN_00101288`. Lets see what this does:
```C
long FUN_00101288(uint param_1)
{
int iVar1;
srand(param_1);
iVar1 = rand();
return (long)iVar1;
}
```
`local_10` sets the seed for rand() and returns the value of `rand()`. As a result, it determined the value of the next 14 unknown characters (which were numbers).
After doing some debugging, we determined that we should use 008. This will set the srand such than it will return 1. without changing the seed again (which is what the value of local_1c does).
Therefore, our result so far was `softbank:008:11111111111111111111:???`.
Last was to determine the last three character.
```C
local_c = 0;
local_ac = 0x646e65;
while( true ) {
if (2 < local_c) {
puts("Thanks!");
FUN_00101229();
return 0;
}
if (*(char *)((long)&local_ac + (long)local_c) != local_68[local_c + 0x22]) break;
local_c = local_c + 1;
}
```
Essentially, what this was doing was comparing the last three characters to the ascii value of 0x646e65 (note: it was reading local_ac in little endian). As a result, our final result was this: `softbank:008:11111111111111111111:end`, and we got the flag! flag{th4x_f0r_ur_cr3d1t_c4rd}