Failures aren’t the stepping stones to success they’re just stones you’re the one who decides to step them - jones

Analysis

This is a simple reversing challenge, we need to find the correct flag. The challenge binary has two conditions which prints SUCCESS if the correct flag is passed and it prints FAILURE if its wrong. we can simulate this pretty easy with angr and find the flag

Decompilaton

And looking at the decompilation we see a string compare statement which shows the string we’re looking for is 16 characters


undefined4 main(int64_t arg7)
{
    int32_t iVar1;
    undefined auVar2 [16];
    undefined auStack56 [16];
    undefined auStack40 [16];
    
    // [14] -r-x section size 513 named .text
    printf("Flag: ");
    __isoc99_scanf("%15s", auStack56);
    auVar2 = pshufb(auStack56, _SHUFFLE);
    auStack40 = CONCAT412(SUB164(auVar2 >> 0x60, 0) + "ftcg\x02\x06\a\x01\x05\v\t\x0e\x03\x0f\x04\b\n\f\r"._0_4_, 
                          CONCAT48(SUB164(auVar2 >> 0x40, 0) + *(int32_t *)0x4068, 
                                   CONCAT44(SUB164(auVar2 >> 0x20, 0) + *(int32_t *)0x4064, SUB164(auVar2, 0) + _ADD32))
                         ) ^ _XOR;
    iVar1 = strncmp(SUB168(auStack40, 0), auStack56, auStack40, 0x10);
    if ((iVar1 == 0) && (iVar1 = strncmp(auStack40, _EXPECTED_PREFIX, 4), iVar1 == 0)) {
        puts("SUCCESS");
        return 0;
    }
    puts("FAILURE");
    return 1;
}
iVar1 = strncmp(SUB168(auStack40, 0), auStack56, auStack40, 0x10);

0x10 which is 16 in decimal and that has a null byte at end so its considered 16 but the exact size is 15

Now we lookup for the addresses of the failure string and success string

0x00001100      lea     rdi, str.FAILURE ; 0x2018 ; const char *s
0x00001107      mov     r12d, 1
0x0000110d      call    puts       ; sym.imp.puts ; int puts(const char *s)
0x00001112      add     rsp, 0x28
0x00001116      mov     eax, r12d
0x00001119      pop     rbp
0x0000111a      pop     r12
0x0000111c      ret
0x0000111d      lea     rdi, str.SUCCESS ; 0x2010 ; const char *s
0x00001124      call    puts       ; sym.imp.puts ; int puts(const char *s)

FAILURE = 0x00001100

success = 0x0000111d

Solution

#!/usr/bin/env python
import angr
import claripy
import sys

def main(argv):
  path_to_binary = argv[1]
  project = angr.Project(path_to_binary)
  length = 15
  characters = [claripy.BVS('flag{-%d' %i, 8) for i in range(length)]
  input_ = claripy.Concat(*characters + [claripy.BVV(b'\n')])

  state = project.factory.full_init_state(args=["path_to_binary"], stdin=input_)    
  for x in characters:
      state.solver.add(x < 127) ## we add constraints to make the flag readable
      state.solver.add(x > 32)

  simulate = project.factory.simulation_manager(state) # we launch the exploration state for the Success addr and Failure addr
  _success_addr = 0x0010111d
  _failure_addr = 0x00101100
  simulate.explore(find=_success_addr, avoid=_failure_addr)  

  s = []
  for j in simulate.deadended:
      if b"SUCCESS" in j.posix.dumps(1):
          s.append(j)


  valid = s[0].posix.dumps(0)
  print(valid.decode('utf-8'))

if __name__ == '__main__':
  main(sys.argv)

After running our solution script we captured our flag 🚩

angr ➤ python solve.py ./a.out
WARNING | 2020-09-09 08:36:15,359 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000.
WARNING | 2020-09-09 08:36:17,016 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2020-09-09 08:36:17,016 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2020-09-09 08:36:17,016 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state
WARNING | 2020-09-09 08:36:17,016 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2020-09-09 08:36:17,016 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages.
WARNING | 2020-09-09 08:36:17,016 | angr.state_plugins.symbolic_memory | Filling memory at 0x7fffffffffefff8 with 8 unconstrained bytes referenced from 0x59f2cd (__strrchr_sse2+0x25d in libc.so.6 (0x9f2cd))
WARNING | 2020-09-09 08:36:18,712 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffffffffff0000 with 48 unconstrained bytes referenced from 0x58fd40 (strncmp+0x0 in libc.so.6 (0x8fd40))
WARNING | 2020-09-09 08:36:18,724 | angr.state_plugins.symbolic_memory | Filling memory at 0x7ffffffffff0030 with 16 unconstrained bytes referenced from 0x58fd40 (strncmp+0x0 in libc.so.6 (0x8fd40))
CTF{S1MDf0rM3!}