2 분 소요

문제파악

imgae

이번 문제는 소스코드와 컴파일된 바이너리가 주어지고 netcat을 통한 접속으로 공격을 시도할 수 있는 문제이다. 소스코드를 우선 살펴보자

image

func 함수를 살펴보면 인자로 전달 받은 key(0xdeadbeef)값을 비교하기 전에 gets 함수로 표준입력에서 값을 입력받아 지역변수에 저장하는 것을 볼 수 있다. 조건문을 보면 key값에 분기가 결정되고 특정값(0xcafebabe)일 경우 쉘을 실행하는 것을 볼 수 있다. 정상적인 실행흐름으로는 단순히 “Nah..”를 출력하고 프로그램은 끝나겠지만 이를 수정하여 쉘을 실행하도록 해야한다. 문제 제목에서도 보이듯이 오버플로우를 통해 해결 가능하다. gets 함수는 입력받을 사이즈가 특정되지 않아 오버플로우에 취약하다. 따라서 overflowme 지역변수에서 오버플로우를 일으켜 key값을 특정값(0xcafebabe)으로 수정하면 이 문제는 해결이 가능할 것이다.

이를 실질적으로 수행하기 위해서는 기본적인 call convention과 변수가 스택에 저장되는 방식을 어느정도 알고 있어야 한다.

image

위의 그림은 스택을 표현한 것이다. 어려워 보이지만 봐야할 것은 많지 않다. 스택은 포인터 값이 줄어드는 방향을 커진다는 것, 함수가 호출할 때 함수는 보통 스택에 파라미터를 역순으로 저장하고 반환주소 그리고 ebp를 저장한다는 것, 이 정도만 알면 문제를 해결할 수 있다.

다른 블로그들을 보면 디버거(gdb)를 통해 해결하는 것이 일반적인 거 같지만 멍청하여 ida를 통한 정적 분석으로 어떻게 key값을 변경시킬 지를 분석했다.

image

제일 나를 괴롭혔던 부분은 변수 var_C의 등장이였다. 코드 상으로는 함수의 지역변수는 단 하나이지만 실제 어셈블리 코드에는 추가적으로 var_C라는 변수가 또 생긴다. 스택의 상태를 확인하기 위한 용도로 들어가는 것으로 보인다. ebp를 기준으로 각 변수들이 어디에 저장될 지 계산할 수가 있다. 위에서 설명했듯이 함수의 파라미터는 ebp+8(ebp와 return address 다음)주소에 위치해 있을 것이다. 그리고 gets 함수가 저장할 버퍼는 ebp-2C 위치에 저장되는 것을 알 수 있다. 따라서 key값을 덮어쓰기 위해서는 0x34(52) 바이트 만큼을 더미 데이터로 채운 후 0xcafebabe 값을 넣어주면 된다.

여기서 첫 번째 난관은 char(1 byte) 타입으로 입력을 준 것이 int(4 byte) 타입으로 저장될 때 어떤식으로 저장되는 가를 확인하는 것이다. 검색을 해보니 대부분 little endian 방식을 사용하는 것 같다. 공격 대상이 아닌 일반 컴퓨터로 테스트를 해본 결과 “abcd” -> “0x64636261”로 저장되는 것을 확인할 수 있었다.

두 번째 난관은 ascii 코드를 벗어나는 값을 어떻게 표준 입력에 입력할 수 있는가이다. 가장 간단한 방법은 echo 명령어를 사용하는 방법이다. echo의 -e 옵션은 escape 문자를 사용할 수 있게 된다. 두 번째로는 python과 같은 스크립트를 활용하는 방법이다.

image

종합해보면 echo 또는 스크립트를 통해서 52개의 더미 문자와 “\xbe\xba\xfe\xca”를 붙여서 입력하게 되면 쉘이 실행되도록 흐름을 수정할 수 있을 것이다. 하지만 원하는 플래그를 얻는데는 실패했다. 위의 처럼 Nah..가 실행되지 않은 것을 보면 오버플로우가 된 거 같지만 바로 종료가 된다. 이 부분은 검색을 해보니 eof가 마지막에 전송되면서 shell이 실행된다 하여도 바로 종료한다고 한다. 이를 방지하기 위해서 cat 명령어를 실행한다. cat 명령어는 파일의 내용을 보여주기도 하지만 아무 인자 없이 실행하게 되면 표준 입력 내용을 출력으로 보내주며 지속적으로 입력을 받을 수 있게 된다. 따라서 최종적으로

(python2 -c 'print "A"**52+"\xbe\xba\xfe\xca"'; cat) | nc pwnable.kr 9000

을 통해 공격을 성공하여 쉘을 사용할 수 있었다.

댓글남기기