La CTF 2023 - stuff (setvbuf)
Jason keeps bullying me for using Fedora so here’s a binary compiled on Fedora.
nc lac.tf 31182
- [7 solves / 497 points]
새로운 것을 많이 배운 문제이다.
Analysis
분석하기에 앞서 patchelf
를 이용하여 열심히 패치를 진행했다.
1 | [jir4vvit@arch stuff]$ ldd stuff-p |
이제 IDA를 이용해서 코드를 분석해보자.
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
두 가지의 기능이 있다. heap 주소를 출력해주는 기능과 fread
함수를 이용해서 char ptr[12]
에 0x20만큼 쓸 수 있다. 여기에서 overflow를 발생시킬 수 있고 ret를 덮어 rip를 조절할 수 있다.
여기서 중요한 점은 setvbuf
를 이용하여 stdin
을 사용할 때 버퍼링을 이용하기 때문에 힙에 내가 입력한 값들이 저장된다. 이것은 힙 주소에 페이로드를 저장시킬 수 있다는 것을 의미한다.
Solve
scanf
로 숫자 데이터를 하나 보낼 때, 뒤에 다른 값들을 덧붙이면 이 데이터들은 힙 버퍼에 저장되고, 다음 입력 때 사용자의 키보드로부터 입력을 받지 않고 이 힙 버퍼에 저장된 값을 바로 입력으로 넣어줄 수 있다.
주어진 기능을 이용해 힙 주소를 leak한다.
2번 기능을 이용하기 위해 2를 서버로 전송하는데, 뒤에 ROP 페이로드를 덧붙여서 한 번에 전송한다. 참고로 이 ROP 페이로드는 2번 기능의
fread
함수가 종료된 뒤, main이 끝나고leave ret
을 한 번 더 수행하여 rbp와 rsp를 heap주소로 만들어준다. 그 heap 주소는 ROP 페이로드가 저장된 곳이고fread
가젯을 배치하여 바로 한 번 더 실행하게 한다. 이 때, 두 번째 페이로드를 입력으로 받는데, 이것의 역할은printf
함수를 실행하도록 하는 것이다.fread
함수가 종료되고printf();
가 실행되는데 NULL pointer를 출력하게 되면 아무것도 출력하지 않는다. 중요한 것은printf
함수가 종료되고rsi
에libc
주소가 들어가는 것이다. 그 다음0x4011f6
주소가 실행되는데 이는rsi
레지스터를 출력하므로 최종적으로libc
주소를 출력할 수 있다.1
2
3.text:00000000004011F6 mov edi, offset format ; "here's your leak: %p\n"
.text:00000000004011FB mov eax, 0
.text:0000000000401200 call _printf이 작업이 끝나면 main의
while(1)
루프 내부이므로 main의 기능을 추가적으로 더 실행할 수 있다.화면에 leak된 libc 주소를 가져와 libc base를 구한다.
원가젯을 이용하여 쉘을 획득한다. 2번 메뉴를 이용하여 rip를 조작할 수 있다. 조건은 아래와 같으므로 레지스터 상황을 살펴보고 적당한 가젯을 찾아 값을 바꿔준다.
1
2
3
4
50x4d1a0 posix_spawn(rsp+0xc, "/bin/sh", 0, rbx, rsp+0x50, environ)
constraints:
rsp & 0xf == 0
rcx == NULL
rbx == NULL || (u16)[rbx] == NULL
Exploit Code
1 | from pwn import * |