googleCTF Beginner Reversing
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!}