C언어에서는 출력할 때 %d, %s, %c 등 과 같은 형식 지정자를 사용하는데 여기서는 이를 사용하지 않아 FSB(포맷 스트링 버그)가 발생한다.
FSB
위와 같이 문자열만 입력하는 것이 아니라 %x와 같은 형식 지정자가 일반 문자열에 포함되어 있으면 문자열을 출력할 뿐만 아니라 서식문자가 명령어처럼 실행되어 문자열로 입력하지 않은 부분이 출력되는 것을 볼 수 있다.
다음과 같이 스택이 구성되어 있을 때 print(bleh)가 실행되면 스택의 &bleh에서 바로 bleh의 내용을 가지고 오게 된다. 처음에는 AAAA, AAAA가 출력되지만 %x를 만나면 스택에서 &bleh 위의 값을 차례대로 읽어서 출력하게 된다. 이렇게 계속 출력하다보면 실제 blef 위치까지 이동해서 bleh 값까지 아스키 값으로 출력된다.
특히 형식 지정자 중 %n 은 현재 스택에 해당하는 변수 혹은 주소에 바로 이전에 출력한 값의 크기를 저장한다. 따라서 %n을 적절하게 사용하면 ret에 우리가 원하는 값을 덧씌울 수 있다.
우선 실행되어야 하는 쉘 코드를 환경변수에 저장해서 쉘 코드 주소를 얻어보자
쉘 코드의 주소는 0xbffffba9
이제 ret 주소를 알아내보자
ret 주소를 얻으려 gdb를 실행했지만 main함수가 없다는 메시지가 출력되었다.
해결방법을 찾아내기 위해 구글링한 결과 FSB 취약점 공격을 할 때는 보통 .dtors 영역을 사용한다고 한다.
.dtors 세그먼트는 gcc 컴파일 시에 자동으로 생성되며 main 함수 종료 후에 실행된다.
따라서 ret에 쉘 코드 주소를 넣는 대신 .dtors에 쉘 코드 주소를 덧 씌워보자.
위와 같이 .dtors의 주소는 0x08049594 이다.
우리는 여기에 +4 를 한 0x08049598 에 쉘 코드 주소 0xbffffba9 를 넣어줄 것이다.
(아직까지 의문인 부분은 왜 .dtors 세그먼트 주소에 4바이트를 더한 위치에 쉘 코드를 저장할까?)
이제 payload를 작성해보자
기본적인 흐름은 위와 같다
여기서 주의해야 하는 부분은 .dtors 주소에 쉘 코드 주소를 넣을 때 2바이트씩 나누어 입력해야하는 부분이다.
(x86에서는 일정 길이 이상의 숫자는 읽어들이지 못한다.)
왼쪽 그림처럼 .dtors에는 0xfba9가 .dtors+2에는 0xbfff가 들어가도록 %c를 구성해야 한다.
또한 %c 바이트 수를 계산해야하는데, 이 과정에서 %x에서 출력하는 바이트 수를 정확하게 지정해야 오류없이 전달할 수 있다.