replacer.id는 0으로 초기화되어있고, 바로 입력할 수 있는 부분이 없기 때문에 겉으로 보기엔 replace_tag_v1 함수만 계속 실행된다. 하지만 replace_tag_v2에서 FSB가 발생하기 때문에 저 함수를 실행시켜야하는데 저 함수를 실행시키려면 replacer.id를 1로 세팅해야한다. 하지만 이걸 어떻게 세팅해야 할까? main 함수에서 흐름을 바꿀 취약점이 보이지 않는다.
replace_tag_v1 함수는 무조건 실행되니까 이 함수에서 취약점을 찾아서 replace_tag_v2 함수를 실행시켜 FSB를 트리거하는 방향으로 생각해야 한다.
replace_tag_v1 함수만 떼어놓고 분석해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
voidreplace_tag_v1(char *message, char from, char to) { size_t start_tag_index = -1; for (size_t i = 0; i < MESSAGE_LEN - 2; i++) { if (message[i] == '<' && message[i + 1] == from && message[i + 2] == '>') { start_tag_index = i; break; } } if (start_tag_index == -1) return;
MESSAGE_LEN은 0x20으로 고정되어 있다. 참고로 meesage, from, to 모두 입력값이다. 첫 번째 반복문에서 start_tag_index는 <[from]>이 나오는, 즉 <의 인덱스를 의미한다. 반복문을 볼 떄는 항상 반복문 조건의 인덱스 범위가 반복문 내부에서 사용하는 인덱스 범위를 넘어가지 않는지 항상 주시를 해야한다.
두 번째 반복문에서는 start_tag_index+3, 즉 <[from]> 다음 인덱스부터 사용한다. 문제는 인덱스가 MESSAGE_LEN-1 일 때 message[i+2]에 접근한다. OOB가 발생한다. 사실 이거보다 더 중요한건 message[end_tag_index + 2] = to; 이 구문이다. 접근함에도 모자라 값을 쓰고 있다! 정확한 디버깅을 통해 저 부분이 replacer.id라면 1로 세팅해줄 수 있다.
Solve
FSB를 트리거할 수 있으면 익스플로잇은 간단하다. pie base와 libc base를 구한 다음에 printf@got를 system으로 덮고 입력을 조절할 수 있으므로 /bin/sh\x00를 보내면 끝이다!