# not_malware
## Description
Category: Reversing
Points: 150
```To be perfectly frank, I do some malware-y things, but that doesn't mean that I'm actually malware, I promise!
nc rev.chal.csaw.io 5008```
## Analysis
To start with, it's a 64-bit binary that takes a single input via STDIN:
```kali@kali:~/Downloads/csaw$ file not_malwarenot_malware: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=68fd0eecf167f1c5992efffe7855c67a306aee33, for GNU/Linux 3.2.0, strippedkali@kali:~/Downloads/csaw$ ./not_malwareWhat's your credit card number (for safekeeping) ?>> hellokali@kali:~/Downloads/csaw$ echo $?1```
Decompile it with Ghidra. Here's our entry routine:
```cvoid entry(undefined8 param_1,undefined8 param_2,undefined8 param_3)
{  undefined8 in_stack_00000000;  undefined auStack8 [8];   __libc_start_main(FUN_001012bc,in_stack_00000000,&stack0x00000008,&LAB_001018f0,&DAT_00101960,                    param_3,auStack8);  do {                    /* WARNING: Do nothing block with infinite loop */  } while( true );}```
And that invokes `FUN_001012bc`:
```cundefined8 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);}```
Let's break it down. First we call `FUN_001012a6` and get the input from STDIN:
```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);  }```
`FUN_001012a6` is basically an initialization function of sorts:
```cvoid FUN_001012a6(void)
{  FUN_00101782();  FUN_0010186e();  FUN_001018b4();  return;}```
The first one appears to set some variables based on which HV is in use:
```cvoid FUN_00101782(void)
{  int iVar1;  long lVar2;  byte *pbVar3;  byte *pbVar4;  bool bVar5;  bool bVar6;  byte bVar7;  byte local_19 [4];  undefined local_15 [4];  undefined local_11 [4];  undefined local_d;  undefined4 local_c;   bVar7 = 0;  local_c = 0;  local_d = 0;  FUN_0010173a(&local_c,local_19,local_11,local_15);  iVar1 = strncmp((char *)local_19,"VMwareVMware",0xc);  bVar5 = false;  bVar6 = iVar1 == 0;  if (!bVar6) {    lVar2 = 10;    pbVar3 = local_19;    pbVar4 = (byte *)"KVMKVMKVM";    do {      if (lVar2 == 0) break;      lVar2 = lVar2 + -1;      bVar5 = *pbVar3 < *pbVar4;      bVar6 = *pbVar3 == *pbVar4;      pbVar3 = pbVar3 + (ulong)bVar7 * -2 + 1;      pbVar4 = pbVar4 + (ulong)bVar7 * -2 + 1;    } while (bVar6);    if ((!bVar5 && !bVar6) != bVar5) {      iVar1 = strncmp((char *)local_19,"TCGTCGTCGTCG",0xc);      if (iVar1 != 0) {        iVar1 = strncmp((char *)local_19,"Microsoft Hv",0xc);        bVar5 = false;        bVar6 = iVar1 == 0;        if (!bVar6) {          lVar2 = 0xc;          pbVar3 = local_19;          pbVar4 = (byte *)" lrpepyh vr";          do {            if (lVar2 == 0) break;            lVar2 = lVar2 + -1;            bVar5 = *pbVar3 < *pbVar4;            bVar6 = *pbVar3 == *pbVar4;            pbVar3 = pbVar3 + (ulong)bVar7 * -2 + 1;            pbVar4 = pbVar4 + (ulong)bVar7 * -2 + 1;          } while (bVar6);          if ((!bVar5 && !bVar6) != bVar5) {            return;          }        }      }    }  }                    /* WARNING: Subroutine does not return */  exit(1);}```
Let's not follow that rabbit hole just yet.
`FUN_0010186e` throws an error if `LD_PRELOAD` or `/etc/ld.so.preload` are used:
```cvoid FUN_0010186e(void)
{  int iVar1;  char *pcVar2;   pcVar2 = getenv("LD_PRELOAD");  if (pcVar2 != (char *)0x0) {                    /* WARNING: Subroutine does not return */    exit(1);  }  iVar1 = open("/etc/ld.so.preload",0);  if (0 < iVar1) {                    /* WARNING: Subroutine does not return */    exit(1);  }  return;}```
And `FUN_001018b4` calls ptrace to exit if we're trying to debug the binary:
```cvoid FUN_001018b4(void)
{  long lVar1;   lVar1 = ptrace(PTRACE_TRACEME,0,1,0);  if (lVar1 == -1) {                    /* WARNING: Subroutine does not return */    exit(1);  }  return;}```
That `ptrace` call is where it fails if I try to use `strace` for example:
```kali@kali:~/Downloads/csaw$ strace ./not_malwareexecve("./not_malware", ["./not_malware"], 0x7ffc8c717550 /* 44 vars */) = 0brk(NULL)                               = 0x559d8011a000access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3fstat(3, {st_mode=S_IFREG|0644, st_size=118167, ...}) = 0mmap(NULL, 118167, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffa71b72000close(3)                                = 0openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\362\0\0\0\0\0\0"..., 832) = 832fstat(3, {st_mode=S_IFREG|0644, st_size=1321344, ...}) = 0mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffa71b70000mmap(NULL, 1323280, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffa71a2c000mmap(0x7ffa71a3b000, 630784, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf000) = 0x7ffa71a3b000mmap(0x7ffa71ad5000, 626688, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xa9000) = 0x7ffa71ad5000mmap(0x7ffa71b6e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x141000) = 0x7ffa71b6e000close(3)                                = 0openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0n\2\0\0\0\0\0"..., 832) = 832fstat(3, {st_mode=S_IFREG|0755, st_size=1839792, ...}) = 0mmap(NULL, 1852680, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffa71867000mprotect(0x7ffa7188c000, 1662976, PROT_NONE) = 0mmap(0x7ffa7188c000, 1355776, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7ffa7188c000mmap(0x7ffa719d7000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x170000) = 0x7ffa719d7000mmap(0x7ffa71a22000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ba000) = 0x7ffa71a22000mmap(0x7ffa71a28000, 13576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffa71a28000close(3)                                = 0mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffa71864000arch_prctl(ARCH_SET_FS, 0x7ffa71864740) = 0mprotect(0x7ffa71a22000, 12288, PROT_READ) = 0mprotect(0x7ffa71b6e000, 4096, PROT_READ) = 0mprotect(0x559d7f30c000, 4096, PROT_READ) = 0mprotect(0x7ffa71bb9000, 4096, PROT_READ) = 0munmap(0x7ffa71b72000, 118167)          = 0openat(AT_FDCWD, "/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)ptrace(PTRACE_TRACEME)                  = -1 EPERM (Operation not permitted)exit_group(1)                           = ?+++ exited with 1 +++```
We'll have to patch out that `ptrace` call to do any debugging.
```python#!/bin/env python3from pwn import *binary = ELF('./not_malware')context.update(arch='amd64',os='linux')print(binary.path)
addr1 = 0x18d1print(binary.disasm(addr1, 40))binary.asm(addr1,'''nopnopnopnopnop''')patched = binary.path + '_patched'print(patched)print(binary.disasm(addr1, 40))binary.save(patched)os.system('chmod +x ' + patched)```
```kali@kali:~/Downloads/csaw$ ./patch_malware.py [*] '/home/kali/Downloads/csaw/not_malware'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      PIE enabled/home/kali/Downloads/csaw/not_malware    18d1:       e8 0a f8 ff ff          call   0x10e0    18d6:       48 83 f8 ff             cmp    rax, 0xffffffffffffffff    18da:       75 0a                   jne    0x18e6    18dc:       bf 01 00 00 00          mov    edi, 0x1    18e1:       e8 2a f8 ff ff          call   0x1110    18e6:       90                      nop    18e7:       5d                      pop    rbp    18e8:       c3                      ret        18e9:       0f 1f 80 00 00 00 00    nop    DWORD PTR [rax+0x0]    18f0:       f3 0f 1e fa             endbr64     18f4:       41 57                   push   r15    18f6:       4c                      rex.WR    18f7:       8d                      .byte 0x8d    18f8:       3d                      .byte 0x3d/home/kali/Downloads/csaw/not_malware_patched    18d1:       90                      nop    18d2:       90                      nop    18d3:       90                      nop    18d4:       90                      nop    18d5:       90                      nop    18d6:       48 83 f8 ff             cmp    rax, 0xffffffffffffffff    18da:       75 0a                   jne    0x18e6    18dc:       bf 01 00 00 00          mov    edi, 0x1    18e1:       e8 2a f8 ff ff          call   0x1110    18e6:       90                      nop    18e7:       5d                      pop    rbp    18e8:       c3                      ret        18e9:       0f 1f 80 00 00 00 00    nop    DWORD PTR [rax+0x0]    18f0:       f3 0f 1e fa             endbr64     18f4:       41 57                   push   r15    18f6:       4c                      rex.WR    18f7:       8d                      .byte 0x8d    18f8:       3d                      .byte 0x3d```
That's better, now we can trace it and get to the part where it prompts for user input:
```kali@kali:~/Downloads/csaw$ strace ./not_malware_patched execve("./not_malware_patched", ["./not_malware_patched"], 0x7ffeafd53b20 /* 44 vars */) = 0brk(NULL)                               = 0x56309109b000access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3fstat(3, {st_mode=S_IFREG|0644, st_size=118167, ...}) = 0mmap(NULL, 118167, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff2a1f79000close(3)                                = 0openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\362\0\0\0\0\0\0"..., 832) = 832fstat(3, {st_mode=S_IFREG|0644, st_size=1321344, ...}) = 0mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff2a1f77000mmap(NULL, 1323280, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff2a1e33000mmap(0x7ff2a1e42000, 630784, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf000) = 0x7ff2a1e42000mmap(0x7ff2a1edc000, 626688, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xa9000) = 0x7ff2a1edc000mmap(0x7ff2a1f75000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x141000) = 0x7ff2a1f75000close(3)                                = 0openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0n\2\0\0\0\0\0"..., 832) = 832fstat(3, {st_mode=S_IFREG|0755, st_size=1839792, ...}) = 0mmap(NULL, 1852680, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff2a1c6e000mprotect(0x7ff2a1c93000, 1662976, PROT_NONE) = 0mmap(0x7ff2a1c93000, 1355776, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7ff2a1c93000mmap(0x7ff2a1dde000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x170000) = 0x7ff2a1dde000mmap(0x7ff2a1e29000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ba000) = 0x7ff2a1e29000mmap(0x7ff2a1e2f000, 13576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ff2a1e2f000close(3)                                = 0mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff2a1c6b000arch_prctl(ARCH_SET_FS, 0x7ff2a1c6b740) = 0mprotect(0x7ff2a1e29000, 12288, PROT_READ) = 0mprotect(0x7ff2a1f75000, 4096, PROT_READ) = 0mprotect(0x563090829000, 4096, PROT_READ) = 0mprotect(0x7ff2a1fc0000, 4096, PROT_READ) = 0munmap(0x7ff2a1f79000, 118167)          = 0openat(AT_FDCWD, "/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x5), ...}) = 0brk(NULL)                               = 0x56309109b000brk(0x5630910bc000)                     = 0x5630910bc000write(1, "What's your credit card number ("..., 51What's your credit card number (for safekeeping) ?) = 51fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x5), ...}) = 0write(1, ">> ", 3>> )                      = 3read(0, ```
Back to `FUN_001012bc`, after the init routines and prompt for input:
```c  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);  }```
That expects a certain 8 char prefix, followed by `:`. Let's set a breakpoint on that `strncmp` and try some bogus input:
```        001013ca e8 71 fc        CALL       strncmp                                          int strncmp(char * __s1, char *                  ff ff```
```gef➤  rStarting program: /home/kali/Downloads/csaw/not_malware_patchedWhat's your credit card number (for safekeeping) ?>> ^CProgram received signal SIGINT, Interrupt....gef➤  break *0x555555454000 + 0x001013caBreakpoint 1 at 0x5555555553cagef➤  cContinuing.hello```
```[ Legend: Modified register | Code | Heap | Stack | String ]───────────────────────────────────────────────────────────────────────────────────── registers ────$rax   : 0x00007fffffffdcd0  →  0x00000a6f6c6c6568 ("hello\n"?)$rbx   : 0x0               $rcx   : 0x000055555555603b  →  "softbank"$rdx   : 0x8               $rsp   : 0x00007fffffffdc40  →  0x0000000000000000$rbp   : 0x00007fffffffdcf0  →  0x00005555555558f0  →   endbr64 $rsi   : 0x000055555555603b  →  "softbank"$rdi   : 0x00007fffffffdcd0  →  0x00000a6f6c6c6568 ("hello\n"?)$rip   : 0x00005555555553ca  →   call 0x555555555040 <strncmp@plt>$r8    : 0x00007ffff7f49b80  →  0x40671547652b82fe$r9    : 0xffffffff00000000$r10   : 0xfffffffffffff51c$r11   : 0x00007ffff7ead020  →  <powf64+0> sub rsp, 0x18$r12   : 0x0000555555555130  →   endbr64 $r13   : 0x0               $r14   : 0x0               $r15   : 0x0               $eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 ───────────────────────────────────────────────────────────────────────────────────────── stack ────0x00007fffffffdc40│+0x0000: 0x0000000000000000   ← $rsp0x00007fffffffdc48│+0x0008: 0x00000000000000000x00007fffffffdc50│+0x0010: 0x00000000000000000x00007fffffffdc58│+0x0018: 0x00000000000000000x00007fffffffdc60│+0x0020: 0x00000000000000000x00007fffffffdc68│+0x0028: 0x00000000000000000x00007fffffffdc70│+0x0030: 0x00000000000000000x00007fffffffdc78│+0x0038: 0x0000000000000000─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────   0x5555555553bf                  mov    edx, 0x8   0x5555555553c4                  mov    rsi, rcx   0x5555555553c7                  mov    rdi, rax → 0x5555555553ca                  call   0x555555555040 <strncmp@plt>   ↳  0x555555555040 <strncmp@plt+0>  jmp    QWORD PTR [rip+0x2fda]        # 0x555555558020 <[email protected]>      0x555555555046 <strncmp@plt+6>  push   0x1      0x55555555504b <strncmp@plt+11> jmp    0x555555555020      0x555555555050 <__isoc99_fscanf@plt+0> jmp    QWORD PTR [rip+0x2fd2]        # 0x555555558028 <[email protected]>      0x555555555056 <__isoc99_fscanf@plt+6> push   0x2      0x55555555505b <__isoc99_fscanf@plt+11> jmp    0x555555555020─────────────────────────────────────────────────────────────────────────── arguments (guessed) ────strncmp@plt (   $rdi = 0x00007fffffffdcd0 → 0x00000a6f6c6c6568 ("hello\n"?),   $rsi = 0x000055555555603b → "softbank",   $rdx = 0x0000000000000008,   $rcx = 0x000055555555603b → "softbank")─────────────────────────────────────────────────────────────────────────────────────── threads ────[#0] Id 1, Name: "not_malware_pat", stopped 0x5555555553ca in ?? (), reason: BREAKPOINT───────────────────────────────────────────────────────────────────────────────────────── trace ────[#0] 0x5555555553ca → call 0x555555555040 <strncmp@plt>[#1] 0x7ffff7ccccca → __libc_start_main(main=0x5555555552bc, argc=0x1, argv=0x7fffffffdde8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffddd8)[#2] 0x55555555515e → hlt ────────────────────────────────────────────────────────────────────────────────────────────────────gef➤ ```
Check the arguments to `strncmp` above. We're comparing the first 8 chars to `softbank`, which means the first 9 chars of the input string need to be:
```softbank:```
After that, those first 9 chars, there are 3 more expected followed by another `:` char:
```c  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);  }```
Subtracting `0x30` from an ASCII character value can be used to convert a digit into its numerical form. `'0'` (the character) has a hex value of `0x30` and `'4'` has a hex value of `0x34` for example. So `'4' - 0x30` will give you `4`.
After that, there are a couple of while loops doing some basic transformations:
```c  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;  }```
And then we have a long series of char comparisons:
```c  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);  }```
Let's set our next breakpoint on the first of those comparisons, immediately after the while loops:
```  if (local_a8[0] != local_88[0]) {```
```        001014d6 38 c2           CMP        DL,AL        001014d8 74 0a           JZ         LAB_001014e4```
```gef➤  break *0x555555454000 + 0x001014d6Breakpoint 2 at 0x5555555554d6```
Run it again with new input:
```gef➤  rStarting program: /home/kali/Downloads/csaw/not_malware_patched What's your credit card number (for safekeeping) ?>> softbank:AAA:BBBB```
Continue past the first breakpoint and this is what we hit:
```[ Legend: Modified register | Code | Heap | Stack | String ]───────────────────────────────────────────────────────────────────────────────────── registers ────$rax   : 0x0               $rbx   : 0x0               $rcx   : 0x0               $rdx   : 0x42              $rsp   : 0x00007fffffffdc40  →  0x3834373939360000$rbp   : 0x00007fffffffdcf0  →  0x00005555555558f0  →   endbr64 $rsi   : 0x00005555555560c6  →  0x21736b6e61685400$rdi   : 0x00007fffffffd9e0  →  0x00000000fbad8001$rip   : 0x00005555555554d6  →   cmp dl, al$r8    : 0x0               $r9    : 0x00007fffffffdad0  →  0x0000000000000000$r10   : 0x00007fffffffd97f  →  "699748423"$r11   : 0x0               $r12   : 0x0000555555555130  →   endbr64 $r13   : 0x0               $r14   : 0x0               $r15   : 0x0               $eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 ───────────────────────────────────────────────────────────────────────────────────────── stack ────0x00007fffffffdc40│+0x0000: 0x3834373939360000   ← $rsp0x00007fffffffdc48│+0x0008: 0x0000000000333234 ("423"?)0x00007fffffffdc50│+0x0010: 0x0000000a42424242 ("BBBB\n"?)0x00007fffffffdc58│+0x0018: 0x00000000000000000x00007fffffffdc60│+0x0020: 0x00000000400000000x00007fffffffdc68│+0x0028: 0x00000000000000000x00007fffffffdc70│+0x0030: 0x00000000000000000x00007fffffffdc78│+0x0038: 0x0000000000000000─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────   0x5555555554c8                  adc    edi, DWORD PTR [rsi-0x23]   0x5555555554cb                  movzx  edx, BYTE PTR [rbp-0xa0]   0x5555555554d2                  movzx  eax, BYTE PTR [rbp-0x80] → 0x5555555554d6                  cmp    dl, al   0x5555555554d8                  je     0x5555555554e4   0x5555555554da                  mov    edi, 0x1   0x5555555554df                  call   0x555555555110 <exit@plt>   0x5555555554e4                  movzx  edx, BYTE PTR [rbp-0x90]   0x5555555554eb                  movzx  eax, BYTE PTR [rbp-0x70]─────────────────────────────────────────────────────────────────────────────────────── threads ────[#0] Id 1, Name: "not_malware_pat", stopped 0x5555555554d6 in ?? (), reason: BREAKPOINT───────────────────────────────────────────────────────────────────────────────────────── trace ────[#0] 0x5555555554d6 → cmp dl, al[#1] 0x7ffff7ccccca → __libc_start_main(main=0x5555555552bc, argc=0x1, argv=0x7fffffffdde8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffddd8)[#2] 0x55555555515e → hlt ────────────────────────────────────────────────────────────────────────────────────────────────────gef➤  ```
This gives us the value that should come after `softbank:`:
```0x00007fffffffdc48│+0x0008: 0x0000000000333234 ("423"?)```
Run it again with our new input:
```gef➤  rStarting program: /home/kali/Downloads/csaw/not_malware_patched What's your credit card number (for safekeeping) ?>> softbank:423:BBBB```
Continue past the first breakpoint and we get:
```[ Legend: Modified register | Code | Heap | Stack | String ]───────────────────────────────────────────────────────────────────────────────────── registers ────$rax   : 0x38              $rbx   : 0x0               $rcx   : 0x0               $rdx   : 0x42              $rsp   : 0x00007fffffffdc40  →  0x3136373831370000$rbp   : 0x00007fffffffdcf0  →  0x00005555555558f0  →   endbr64 $rsi   : 0x00005555555560c6  →  0x21736b6e61685400$rdi   : 0x00007fffffffd9e0  →  0x00000000fbad8001$rip   : 0x00005555555554d6  →   cmp dl, al$r8    : 0x0               $r9    : 0x00007fffffffdad0  →  0x0000000000000000$r10   : 0x00007fffffffd980  →  "71876166"$r11   : 0x0               $r12   : 0x0000555555555130  →   endbr64 $r13   : 0x0               $r14   : 0x0               $r15   : 0x0               $eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 ───────────────────────────────────────────────────────────────────────────────────────── stack ────0x00007fffffffdc40│+0x0000: 0x3136373831370000   ← $rsp0x00007fffffffdc48│+0x0008: 0x0000000000003636 ("66"?)0x00007fffffffdc50│+0x0010: 0x0000000a42424242 ("BBBB\n"?)0x00007fffffffdc58│+0x0018: 0x00000000000000000x00007fffffffdc60│+0x0020: 0x00000000400000000x00007fffffffdc68│+0x0028: 0x00000000000000000x00007fffffffdc70│+0x0030: "88557665573808687497"0x00007fffffffdc78│+0x0038: "573808687497"─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────   0x5555555554c8                  adc    edi, DWORD PTR [rsi-0x23]   0x5555555554cb                  movzx  edx, BYTE PTR [rbp-0xa0]   0x5555555554d2                  movzx  eax, BYTE PTR [rbp-0x80] → 0x5555555554d6                  cmp    dl, al   0x5555555554d8                  je     0x5555555554e4   0x5555555554da                  mov    edi, 0x1   0x5555555554df                  call   0x555555555110 <exit@plt>   0x5555555554e4                  movzx  edx, BYTE PTR [rbp-0x90]   0x5555555554eb                  movzx  eax, BYTE PTR [rbp-0x70]─────────────────────────────────────────────────────────────────────────────────────── threads ────[#0] Id 1, Name: "not_malware_pat", stopped 0x5555555554d6 in ?? (), reason: BREAKPOINT───────────────────────────────────────────────────────────────────────────────────────── trace ────[#0] 0x5555555554d6 → cmp dl, al[#1] 0x7ffff7ccccca → __libc_start_main(main=0x5555555552bc, argc=0x1, argv=0x7fffffffdde8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffddd8)[#2] 0x55555555515e → hlt ────────────────────────────────────────────────────────────────────────────────────────────────────gef➤  ```
So now we know our input starts with:
```softbank:423:```
And it's now comparing `66` to `BBBB\n`
This string on the stack looks interesting, must be where the `66` comes from:
```0x00007fffffffdc70│+0x0030: "88557665573808687497"```
Right around the time I saw that `88557665573808687497` string above, [datajerk](https://github.com/datajerk) chimed in with:
```so angr solved it, but it does not work, but did help with the formatsoftbank:xxx:xxxxxxxxxxxxxxxxxxxx:end```
`xxxxxxxxxxxxxxxxxxxx` looked like the right length for `88557665573808687497`, so I gave it a shot:
```kali@kali:~/Downloads/csaw$ echo 'softbank:423:88557665573808687497:end' | ./not_malware_patched What's your credit card number (for safekeeping) ?>> Thanks!Segmentation fault```
Nice! That got us through all the remaining checks.
```c  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;  }```
The code above checks for `:end` and gives us the flag if we're successful:
```cvoid FUN_00101229(void)
{  char local_118 [264];  FILE *local_10;   local_10 = fopen("flag.txt","r");  __isoc99_fscanf(local_10,&DAT_00102061,local_118);  puts(local_118);  fclose(local_10);  return;}```
My local run only segfaulted because I don't have a `flag.txt` file.
## Solution
All that's left is to send our input to the remote server to get the flag:
```kali@kali:~/Downloads/csaw$ echo 'softbank:423:88557665573808687497:end' | nc rev.chal.csaw.io 5008What's your credit card number (for safekeeping) ?>> Thanks!flag{th4x_f0r_ur_cr3d1t_c4rd}```
## Addendum
I actually started down the gdb approach to understand the input format so that I could write a well-constrained angr script to solve the problem. Meanwhile, [datajerk](https://github.com/datajerk) started with an angr script and found the format I needed to pair with the strings that I saw on the stack in gdb. Teamwork, FTW!
This is the approach he used to find the input format:
```python#!/usr/bin/env python3
# patch out all the init checks for angrfrom pwn import *binary = ELF('not_malware')binary.write(0x18d1,5*b'\x90')binary.write(0x12aa,5*b'\x90')binary.write(0x12af,5*b'\x90')binary.write(0x12b4,5*b'\x90')binary.save('not_malware_patched')os.chmod('not_malware_patched',0o755)
import angr, time, ioFIND_ADDR=0x401729 # puts("Thanks!");t=time.time()binary = open('./not_malware_patched','rb').read()proj = angr.Project(io.BytesIO(binary),auto_load_libs=False)state = proj.factory.entry_state()simgr = proj.factory.simulation_manager(state)simgr.use_technique(angr.exploration_techniques.DFS())simgr.explore(find=FIND_ADDR)print(simgr.found[0].posix.dumps(0))print(time.time() - t,end="")print(" seconds")```
With no constraints, that gives us:
```kali@kali:~/Downloads/csaw$ ./not_malware_angr.py[*] '/home/kali/Downloads/csaw/not_malware'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      PIE enabled...b'\x02\x02@\x10\x00@\x08\x04:\x00G\xf1:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:end \x10\x10@@\x08\x80@\x01 \x10\x00\x80@\x08\x02 @\x08\x00\x02\x00'73.06609392166138 seconds```
Let's add some constraints, given what we know about the format now.
```python#!/usr/bin/env python3
# patch out all the init checks for angrfrom pwn import *binary = ELF('not_malware')binary.write(0x18d1,5*b'\x90')binary.write(0x12aa,5*b'\x90')binary.write(0x12af,5*b'\x90')binary.write(0x12b4,5*b'\x90')binary.save('not_malware_patched')os.chmod('not_malware_patched',0o755)
import angr, time, io, claripyFIND_ADDR=0x401729 # puts("Thanks!");t=time.time()binary = open('./not_malware_patched','rb').read()proj = angr.Project(io.BytesIO(binary),auto_load_libs=False)
input_len = 37ccard_chars = [claripy.BVS('ccard_%d' % i, 8) for i in range(input_len)]ccard = claripy.Concat(*ccard_chars + [claripy.BVV(b'\n')])state = proj.factory.entry_state(stdin=ccard)
for i, k in enumerate(ccard_chars):    # only printable characters    state.solver.add(k < 0x7f)    state.solver.add(k > 0x20)    if i <= 7:        # first 8 chars are lowercase letters        state.solver.add(k >= 0x61)        state.solver.add(k <= 0x7a)    elif i >= 9 and i <= 11:        # then 3 digits        state.solver.add(k >= 0x30)        state.solver.add(k <= 0x39)    elif i >= 13 and i <= 32:        # then 20 digits        state.solver.add(k >= 0x30)        state.solver.add(k <= 0x39)    elif i >= 34 and i <= 36:        # then 3 lowercase letters        state.solver.add(k >= 0x61)        state.solver.add(k <= 0x7a)
simgr = proj.factory.simulation_manager(state)simgr.use_technique(angr.exploration_techniques.DFS())simgr.explore(find=FIND_ADDR)print(simgr.found[0].posix.dumps(0))print(time.time() - t,end="")print(" seconds")```
That gives us:
```kali@kali:~/Downloads/csaw$ ./not_malware_angr.py ...b'ebdarcpd:002:00000000000000000000:end\n'47.511109828948975 seconds```
At least it's more legible, but still doesn't give us the right answer. And it varies from run to run.
```kali@kali:~/Downloads/csa./not_malware_angr.py ...b'peepbank:006:00000000000000000000:end\n'22.56076955795288 seconds```
That must have something to do with the transformations on the stack. I think you actually have to run this thing to get the correct behavior, not just symbolic execution. I'll be interested to see if someone else comes up with a working angr solution for this challenge.
Even if it's possible to solve with angr, it still seems quicker to step through it with gdb in this case. By the time you've added enough constraints to angr, you probably know enough to solve it on your own.