Lord of BOF_9_Vampire

핵심적인 알고리즘은 다음과 같다.

  • main함수 인자를 받음
  • 스택의 환경변수 내용을 없앰
  • argv[1]의 48번째 바이트가 \xbf가 아니면 끝냄
  • argv[1]의 길이가 48보다 크면 끝냄
  • buffer에 argv[1] 내용을 strcpy
  • main함수 stackframe의 buffer 내용을 memset으로 0으로 설정
  • main함수 인자를 각각의 strlen만큼 0으로 초기화

조건

  • return address는 스택 주소이어야 함
  • argv 길이 제한
  • argv 내용은 지워질 것(각자 strlen만큼) (이용 불가)
  • 환경변수 이용 불가

조작 가능

  • EBP backup
  • return address

bof를 이용하여 argc를 조작해볼까 했는데, save argc에 옮긴 후 이용하기 때문에 소용이 없다. 또한 길이 제한에 의해 불가능하다. saved argc 역시 스택에 있지만, buffer보다 낮은 주소에 있어 조작할 수 없다.

saved argc에 음수가 저장되게 해볼까 생각했지만, 그러기 위해서는 argv의 개수가 2^31개 이상이어야 하는데, 그러면 메모리가 먼저 터진다. 같은 맥락으로 strlen의 결과가 음수가 나오도록 할 수도 없다.

argv에 NULL 문자를 넣고 그 뒤에 셸 코드를 넣으면 어떨까? 안 된다. NULL문자 이후는 끊겨서 나온다. 유니코드를 사용하여 strlen의 길이가 다르게 하면? LOB에 유니코드는 안 먹을 것 같다.

심볼릭 링크 이름을 /bin/sh로 짓고 RTL하는 건 어떨까? 일단 RTL을 하려면 48글자보다 많은 공간을 필요로 한다. return address 뒤쪽에 함수 인자를 넣어야 하기 때문이다. 그리고 argv[0]도 지워지기 때문에 실행 경로 역시 써먹을 수 없다.

시도

여기서 새로운 접근 방법으로, so injection을 이용해보았다. 먼저 셸을 실행하는 라이브러리를 제작한다.

그리고 LD_PRELOAD 환경변수를 이용하여 so injection을 한다. 그러면 constructor에 의해 라이브러리가 올라오고, 실행될 것 같았다. 하지만, 실제로 셸이 실행은 되었지만, 권한 상승은 하지 못 했다. 라이브러리 코드를 실행할 때 권한은 vampire로, vampire 권한을 가진 셸이 실행되는 것이 아닐까 한다.

참고

사실 잘 풀리지 않아 다른 사람들의 풀이를 참고했다. argv[0]에는 실행 경로의 문자열 주소가 들어간다. 위 프로그램에서는 이 문자열은 조작하지 않는다. 따라서 실행 경로는 지워지지 않으며, 이용할 수 있는 영역이 된다.

아이디어는 다음과 같다.

  • 프로그램의 이름을 셸 코드로 지정(단, 여기엔 /문자(아스키코드 0x2F)가 들어가서는 안 됨 - 경로로 해석되기 때문)
  • return address를 조작하여 경로에 적힌 셸코드로 이동

셸코드에 /문자가 없도록 보정해주었다.

파일 이름을 바꿔야 하는데, 일반적인 키보드 입력으로는 입력할 수 없는 글자들이 있으므로, 이름을 바꾸기 위한 파이썬 스크립트를 제작한다.

프로그램 이름 문자열이 들어가는 정확한 주소를 알아내기 위해 core파일을 생성한다. 복사한 실행 파일의 이름을 셸코드로 바꾼 후, 크래시를 내어 core 파일을 생성한다.

프로그램 로직에 유의하여 크래시를 내었다.

메모리 검색을 이용하여 문자열을 검색하면 스택의 가장 꼭대기에서 경로 문자열을 찾을 수 있다.

./를 생략하고, 실제 셸 코드가 시작하는 주소를 찾으면 0xbfffffcb가 되겠다.

return address를 셸코드로 하는 exploit 코드를 작성했다. 이제 링크파일을 제작하고, 프로그램을 실행한다.

skeleton의 비밀번호는 shellcoder이다.


tags: writeup, pwnable, buffer overflow, stack overflow, memory corruption, elf file, linux, c lang, x86asm