해설
ssh로 접속해 passcode.c를 확인해보면 다음과 같다.

프로그램은 크게 welcome 함수와 login 함수로 이루어져 있다. welcome 함수에서는 100바이트의 입력을 받고 login 함수에서는 passcode1과 passcode2를 받아서 조건에 따라 flag 값을 출력해주는 것 같다. 자세히보니 passcode1과 passcode2를 입력 받는 scanf 부분에 에러가 있다. scanf("%d", &passcode1)로 되어 있어야 의도한 대로 프로그램이 동작하겠지만, c 코딩을 처음 배울 때 흔히 하는 실수로 scanf 함수로 입력을 받을 때 &연산자를 빼먹었다.
따라서 login 함수의 scanf는 passcode1, passcode2에 담겨진 값을 주소로 보고, 이 가리키는 주소에 4바이트 정수 입력을 받게 된다. 이 부분을 조작하면 원하는 주소에 원하는 값을 써 넣을 수 있을 것만 같다.

gdb로 확인해보니 passcode1의 위치는 $ebp-0x10, passcode2의 위치는 $ebp-0xc 였다. 여기까지만 봤을 때는 두 개의 주소를 조작할 수 있으니 8바이트만큼을 조작할 수 있을 것처럼 보였다.

welcome 함수를 보면, 오프셋 ebp-0x70부터 입력을 받는 것을 알 수 있고, 0x70은 112바이트이다. 그런데 코드를 다시 살펴보니 welcome 함수에서 문자열을 입력 받을 때 포맷 스트링 %100s를 사용했다. 이 포맷 스트링은 100바이트 까지만 입력을 받을 수 있도록 제한해주는 역할을 하기 때문에, 실제로는 112바이트를 전부 덮어 쓸 수 있는 것이 아니라 ebp-0x10 까지만 덮어 쓸 수 있고, 따라서 passcode1 부분까지만 조작 할 수 있다.
여기까지 와서 무엇을 할 수 있나 정리해보면 결국 임의의 4바이트 주소에 임의의 4바이트 값을 넣을 수 있게 되었다. 4바이트로 무엇을 할 수 있을까 생각해보면 GOT overwriting이라는 좋은 녀석이 있다.

GOT overwriting을 하기 위해 exit 함수를 덮어쓰기로 했다. 사실 어떤 함수를 덮어도 상관 없다. 실행 흐름 상에 존재하는 함수를 아무거나 덮어도 될 것 같다. 0x804a018에 있는 4바이트를 system("/bin/cat flag")가 실행되는 라인인 0x80485e3으로 덮어 쓰면, 실행 흐름 상 exit 함수가 call 될 때 0x80485e3으로 점프하게 된다.
따라서 다음과 같은 입력을 통해 flag 값을 얻을 수 있다. 참고로 입력을 할 때 0x80485e3은 정수로 입력해줘야 한다.

낙서
이 문제를 pwntools의 process 모듈을 이용해 코딩하려 했는데, 왜 그런지 모르겠지만 process로 passcode 바이너리를 실행하면 recv를 제대로 받을 수 없고 페이로드를 sendline해도 제대로 먹지 않는 문제가 있었다. 다음과 같이 process로 passcode를 열고 중간에 pause()를 끼워 넣으면 정상적으로 작동하는데,
https://www.akashtrehan.com/writeups/pwnablekr_todders_bottle/ 에서 힌트를 얻었다. 아직까지도 왜 pause()를 넣어야 하는지는 이해하지 못했다..

'pwn' 카테고리의 다른 글
| pwnable.kr - leg (0) | 2020.03.04 |
|---|---|
| pwnable.kr - input (0) | 2019.08.26 |
| pwnable.kr - random (0) | 2019.08.16 |
| pwnable.kr - flag (0) | 2019.08.13 |
| pwnable.kr - bof (0) | 2019.08.12 |