FTZ WriteUp

Level12

 

자 이번 문제부터 본격적인 BOF문제이다

 

소스코드를 보니 버퍼는 256바이트이며 gets함수를 사용한다

 

gets함수도 strcpy와 같이 길이를 재지 않고 복사를한다

 

아 우선 BOF에 대해 설명하자면

 

BOF Buffer OverFlow의 약자로 버퍼가 넘치는 데이터를 집어 넣어 쉘을 얻는 공격기법중 하나이다

 

시스템 해킹 공격기법중의 기초라고도 할 수 있으며 BOF의 공격기법 또한 다양하다

 

우리는 가장 기초적인 BOF공격기법과 환경변수를 이용한 공격기법, 그리고 RTL이라는 공격기법을 통해 쉘을 얻을 것이다

 

아 참고로 기본적인 RET SFP이런것들은 알고 있다고 가정하고 설명하겠다

 

우선 스택을 파악해보자

 

 

우선 스택에 0x108, 264가 할당이 됐다

버퍼가 256바이트인데 264가 할당이 됐다, 더미가 있다는 뜻이다

더미가 버퍼 앞에있는지 뒤에있는지 알 수 없다 우선 값을 넣어보고 알아보자

우선 저기 빨간 부분은 버퍼다 총 256바이트이다 그리고 아까 말했듯이 264바이트가 할당되었는데 264-256 8이다 8바이트가 더미로 잡혀있으며 노란색이다

그리고 초록색은 SFP이다 그리고 그 다음 파란색은 RET이다

그리고 남색은 argc이다. 보라색은 argv이다 이 두 개는 main함수의 매개변수들이다

그리고 마지막 흰색은 env 환경변수들이다

자 스택을 그려보면

이렇게 된다

우리는 Buffer(256) + Dummy(8) + SFP(4) 에다가 쉘코드와 NOP를 집어넣고 RET에 쉘코드 시작주소를 넣을것이다

 

아 여기서 NOP을 집어넣는데 NOP이란 아무 일도 하지 않는다는 뜻이란 명령어 같은 것이다 이것을 많이 깔아서 쉘코드에 쉽게 다가가는 것을 NOP슬라이딩이라고 한다.

 

스크립트 언어로 쉘코드와 NOP 268바이트를 덮고 RET에 쉘코드 시작주소를 넣을 것이다

페이로드는 NOP(100)+shellcode(25)+NOP(143)+RET이렇게 된다

 

공격해보자

 

세그멘테이션 폴트가 뜬다

레드햇 9.0부터는 메모리 보호기법중 하나인 ASLR이 적용이 된다 그래서 이런방식으로 뚫기란 매우 힘들다

 

그럼 환경변수를 이용한 공격을 해보자

먼저 쉘코드를 집어넣을 환경변수를 만들자

그리고 이 환경변수가 시스템 어디에 있는지 알려줄 간단한 소스를 코딩한다

 

사용법은 다음과 같다

 

./getenvaddr 환경변수이름 프로그램경로

우리가 사용할 환경변수이름은 SHELLCODE이고

프로그램경로는 attackme이니 이렇게 사용하면 된다

 

자 그럼 이제 쉘코드가 있는 곳도 알았고 RET직전까지 아무거나 덮어쓰고 RET에 저 주소를 입력하면 RET jmp eip명령어로 인해 쉘코드가있는곳으로 점프할 것이고 쉘코드를 실행할것이다

 

이번 공격에는 NOP같은게 쓰이지 않는다 왜냐하면 이미 쉘코드의 주소를 정확히 알기 때문이다

공격해보자

공격이 성공했다

그리고 다른 공격방법 하나를 더 소개하려 한다

RTL, Return To Libc라는 공격인데 리눅스에 존재하는 공유 라이브러리 개념을 이용한 기법이다

예를 들자면 printf 함수를 사용할 때 printf를 포함한 내장 라이브러리인 libc libc 영역에 적재되게 된다

이때 printf 함수만 libc 영역에 적재되는 것이 아니라 libcso에 모든 함수가 이 영역에 적재되게 된다

따라서 함수를 사용하지 않더라도 모든 함수가 올라오고 이 함수들을 자유롭게 사용할 수 있게 된다

이러한 원리를 이용해 RET에 내장함수의 주소로 변경해 공격하는 기법을 RTL이라 한다

 

우선 공격하기 전에 ret명령으로 실행하는 함수와 함수의 인자 사용에 대해 알아보려 한다

ret명령은 현재 esp가 가리키는 곳에서 값을 꺼내 eip로 설정하는데 ret에 함수의 주소가 있다면 함수를 실행하게 된다

그럼 call명령어에 의한 함수 호출과 ret명령어에 의한 함수 호출의 차이는 무엇일까?

 

call명령어의 경우 ret를 스택에 저장하고 함수를 호출한다

호출한 함수가 끝나고 함수내에서 ret를 실행하면 esp+4한부분부터 실행흐름을 계속하게 된다

 

그럼 공격을 해보자

페이로드는 다음과 같다

쓰레기값(268)+system함수주소(4)+exit함수주소(4)+/bin/sh가있는주소(4) 이렇게 집어넣을 것이다

 

아 그리고 저렇게 system 함수 뒤에 다른함수도 넣는 까닭은 system(“/bin/sh”)이것을 실행하기 위함인데 exit가 들어가는 이유는 system함수의 리턴주소에 exit함수를 넣은 것이다.

 

자 이제 각 함수들의 주소들을 알아보자

이제 /bin/sh의 주소를 알아보기 위해 다음을 코딩한다

 

자 이제 모든 주소를 알았다 공격해보자

Level12를 클리어했다

Phantom@TeamH4C

댓글

댓글 본문
  1. 질문!
    친절한 답변 감사드립니다.

    그러나 저의 부족함으로 인해 질문을 더 드리는 점 양해부탁드립니다.

    1)argv[0]은 getenvaddr의 경로이고 argv[2]는 attackme의 경로로 이해했습니다. 제가 이해한 것이 맞다면 두 경로는 '다른'데 어떤 이유로 빼는지 잘 모르겠습니다. 혹시 구체적으로 설명해주실 수 있을까요?

    2-1)만약 제가 이해한 방향이 틀려 argv[0]도 attackme의 경로이고 argv[2]도 attackme의 경로일 경우, 두 개의 차가 0이라는 건 쉽게 이해할 수 있습니다. 그러나 두 경로가 달라 차가 0이 아닌 경우 *2 연산은 어떤 의미를 가지는 건가요?

    2-2)만약 제가 이해한 방향이 옳아 argv[0]은 getenvaddr의 경로이고 argv[2]는 attackme의 경로일 경우, 두 경로의 차에 *2를 하는 연산은 포인터 주소를 "어떻게" 이동시키는 것인지 쉽게 이해가 가지 않습니다. 혹시 조금 더 알려주실 수 있을까요?

    읽어주셔서 감사합니다.
  2. ph4nt0m
    argv[0]은 프로그램의 경로입니다. argv[2]도 경로이구요. 이 경로들이 프로그램 실행시엔 ./attackme 처럼 상대경로로 입력해도 프로그램 내부적으론 /home/levelxx/attackme 이렇게 절대경로로 인식하게 됩니다

    따라서 strlen함수의 결과도 달라지겠죠 그것을 조정하는거라 보면 됩니다.

    두번째는 포인터를 연산한 값만큼 주소를 이동시키는거라 보시면될것같습니다
    대화보기
    • 질문!
      좋은 자료 참고해서 잘 보고 있습니다.

      "환경변수가 시스템 어디에 있는지 알려줄 간단한 소스"에 대한 질문인데요.
      ptr += (strlen(argv[0]) - strlen(argv[2]))*2; 는 어떤 의미에서 사용된 건가요?
      덧붙여, 어떻게 포인터 자료형에 정수 자료형이 더해지는지도 여쭙고 싶습니다.

      읽어주셔서 감사합니다.