Neko


조금 너무 간결한 개념설명(정리)

RedHat 6.2 기준 스택 버퍼오버플로우에 대한 글 입니다.

모든 지적 다 감사히 받습니다


정말 개념만 설명하고 싶었습니다. 가장 기본적인 것들만 적어놓았으니 이런 개념이구나 참고만 하시기 바랍니다 :)


[1] Buffer Overflow (BOF)

#include<stdio.h>

main(int argc, char *argv[])

{

char buffer[40];

strcpy(buffer, argv[1]);

printf("%s\n", buffer);

}


이런 코드가 있을 때 메모리의 상황은

(낮은 주소)[buffer(40 bytes)] [(Saved)Frame Pointer(4 bytes)] [Return Address(RET) (4bytes)](높은 주소)

이렇게 되게 된다.  (스택이 자라는 방향: 낮은주소 ← 높은주소, 스택은 LIFO(Last-In-First-Out))


*)SFP(Saved Frame Pointer)란?- 처음에 함수 프롤로그(push %ebp)에 의해 저장된 EBP의 값이다 (함수에서 지역변수들이 사용될 때 그 기준으로 EBP가 쓰이는데 타 함수로 넘어갈 경우 다시 돌아왔을 때 ebp를 복귀시켜야 하니 저장해두는 값. 함수가 시작했을때 당시 최초의 EBP 주소를 가리키고 있다)

*)RET(Return Address)란?- EIP(Extended Instruction Pointer)가 가리킬 곳을 가르쳐주는 주소값. EIP는 프로그램이 끝난 후 돌아갈 주소를 가지고 있다.


위의 소스에서 strcpy때문에 버퍼오버플로우가 생기게 되는데, 만약에 버퍼(유저입력으로) 48바이트를 넣는다면 유저의 입력이 버퍼인 40바이트 뿐만아니라 버퍼 뒤에 있는 SFP와 RET까지 덮어버려 프로그램의 실행을 바꾸기 때문이다. RET는 EIP, 즉 함수 실행이 끝난 뒤 어디로 EIP가 가야 할 지 가르쳐주는 역할을 가지고 있기 때문에 만약에 사용자가 RET를 덮어쓴다면 사용자가 입력한 주소로 프로그램의 실행이 계속 되게 된다. 이를 이용해 버퍼에 쉘코드를 넣고 RET를 버퍼의 시작 주소값 또는 NOPSLED 중 하나의 주소값으로 설정한다면 EIP는 버퍼로 가 쉘코드를 그대로 실행하게 된다.


예시:

Payload= "\x90"x20bytes, "Shellcode"x24bytes, "Buffer Address"x4bytes


*)NOPSLED란?- NOP(No OPeration)는 (Intel x86 CPU기준으로)"\x90"을 뜻하는데, 이 명령은 아무 동작도 하지 않고 다음 명령으로 넘어가라는 뜻이다. 만약에 NOP가 900개 있고 그 후에 쉘코드가 있다면 ESP(Extended Stack Pointer- 실행하는 명령을 가르키는 포인터)는 NOPSLED(미끄럼틀같이) 아무 명령도 실행하지 않고 계속 진행하다가 마지막에 있는 쉘코드를 실행하게 된다.

*)Little Endian이란?- 엔디안은 메모리에 수를 저장하는 방식인데, 차이점은 빅 엔디안과 리틀 엔디안에 12345678을 넣게 된다면, 빅 엔디안(이 방식을 쓰는 컴퓨터는 별로 없다)은 12 34 56 78로 메모리에 적재하는 반면 리틀 엔디안은 뒤에서부터 78 56 34 12 이렇게 두바이트씩 끊어 거꾸로 넣게 된다. 고로 리턴 어스레스 같은 경우에도 페이로드에 거꾸로 뒤집어 준 후 넣어야 한다. \x~~\x~~\xff\xbf <- 이런 식

설명은 <http://itguru.tistory.com/71>을 참고했습니다



[2] Return-to-Libc (RTL)

Libc란 공유 라이브러리를 뜻한다. 이 공유 라이브러리는 프로그램이 호출될 때 사용되는 함수를 포함하고 있으며 프로그램이 언제든지 함수를 호출할 때 사용된다.

아까와 같은 코드가 있다면, (위에서 다시 가져와보자)

#include<stdio.h>

main(int argc, char *argv[])

{

char buffer[40];

strcpy(buffer, argv[1]);

printf("%s\n", buffer);

}

물론 아까처럼 버퍼오버플로우를 사용하여 공격할 수도 있다. 하지만 이번에는 Libc를 사용해 공격해볼 것이다.

쉘을 따려면 system("/bin/sh");를 실행시키면 된다

프로그램을 gdb로 메인에 브레이크포인트(b main)를 건 후, 실행시킨 후 브레이크포인트에서 p system을 하면 system함수의 주소가 나온다.

RET를 이 시스템 함수의 주소로 하고, 그 후 4바이트 뒤에 &"/bin/sh" (/bin/sh의 주소) 를 넣으면 system이 ebp+8부터 인자를 참조하기 때문에 system("/bin/sh"); 를 실행하게 된다.

ebp+4에는(system함수 주소 뒤, /bin/sh주소 앞) 더미를 넣거나, 깔끔하게 끝내고 싶다면 exit의 주소를 넣으면 된다. 이곳은 system실행 후 돌아갈 ret을 저장하는 곳이기 때문이다.

예시:

Payload= "\x90"x44bytes, "system address"x4bytes, "exit address"x4bytes, "/bin/sh address"x4bytes



[3] RTL Chaining (Chaining RTL Calls)

RTL Chaining, 이건 위의 RTL과 비슷한 개념이다.

만약에 위와 같은 코드에 strcpy를 두번, system을 한번 이렇게 연속으로 실행하고 싶다고 가정해보자.(걍 머리에 떠오르는 함수,...)

그렇다면 이것들을 어떻게 실행시켜야 할까?

strcpy의 인자는 2개(char *dest, const char *source)이다. 인자가 하나라면 (function) (function) (argv) (argv) 이렇게 끼워서 두 번 실행시킬 수 있겠지만, 늘 함수 주소 뒤의 4바이트를 인자로 참조하기 때문에 인자가 2개인 strcpy는 1번 이상 사용할 수 없다.

&strcpy &strcpy(x) &dest &source <...어떻게 전달하지?>


RTL Chaining을 사용하면 된다!!(??뜬금 느낌표)

우선 개념도를 적어놓겠다.

&strcpy &poppopret &dest &source &strcpy &poppopret &dest &source &system &exit &/bin/sh

pop- Stack에서 데이터를 빼는 것 말고도 pop는 esp+4를 하게 되기 때문에 다음 인스트럭션을 가르키게 된다. 이 경우에는 가젯이 pop pop ret이기 때문에 strcpy를 실행하고 인자를 두 개 전달한 뒤, pop pop ret을 이용해 두 인자를 건너뛰고 그 뒤에 있는 두 번째 strcpy의 주소를 ret하여 다시 실행하게 되는 것이다.

*)가젯(Gadget)이란?- 프로그램 내부의(함수에) 기계어로 된 ret로 끝나는 코드의 조각으로써 필요한 가젯을 찾아 공격자의 마음대로 사용하면 된다

만약에 건너뛰어야 하는 인자가 3개라면, pop pop pop ret, 하나라면 pop ret을 찾아 사용하면 되는 것이다.


[4] SFP overwrite (One-byte Overflow)


[5] Fake EBP (FEBP)

<기말공부다하고돌아오겟슴니다>


'~ 2015 > 컴퓨터공부' 카테고리의 다른 글

Bufferoverflow 기법 정리  (0) 2015.09.22
핸드레이  (0) 2015.09.05
strace, 제가 한번 사용해 보겠습니다.  (0) 2014.07.22
Buffer Overflow  (3) 2014.05.18
Frame Pointer Overwrite/One Byte Overflow  (5) 2014.04.06
MISCCCCCCCCCC!  (0) 2014.03.11
Posted by 에버토끼