ROP - 콜 스택을 제어 하여 리턴 명령어가 실행되기 이전에 프로그램 안에 존재하는 실행 가능한 메모리 영역에서 명령어들을 추출하여 DEP,ASLR같은 기법들을 우회하여 공격할 수 있습니다!


알아야 하는 기본 지식으로는 plt 와 got를 이용하면 got overwriter 와

RTL Chain을 알아야합니다 . 


RTL Chain을 다시 한번 아주 간단하게 알아보고 가겠습니다



RTL Chain이란 그냥 말 그대로 RET를 연속적으로 호출하여 원하는 함수들을 계속 사용해버리는 느낌입니다!


사진을 보시면 RET1~4까지 화살표들이 귀엽게 있습니다.


그중에서 RET2에서 RET3으로 가는 스택안으로 들어가본건데요 

RET2에 POP POP RET가젯을 넣음으로써 POP POP (Parameter1 , 2)에 원하는 실행 함수를 넣고 RET부분에 RET3으로 가게만듬으로써 RET가 계속 일어나게 합니다 .


간단한 예시와 설명을 보면서 진행해보겠습니다 


(*FTZ - LEVEL11을 사용했습니다)



환경변수를 이용한 GOT Overwrite도 다음에 해봐야겠군요!


먼저 ROP를 간단하게 말씀드리자면 버퍼오버플로우 약점이 잇는 함수의 (해당 소스에서는 strcpy) 인자 값만큼 pop하고 ret 구조를 가진 가젯을 찾은 후 공격하기 위한 가젯들의 주소를 하나하나 구해서 페이로드를 짠 뒤 RTL Chain 을 사용해 시스템함수 같은 것을 실행하여 공격합니다 


본 소스로 예제를 보여드리겠습니다 



먼저 gdb분석을 통해 strcpy@plt = 0x804835c를 구했습니다 .



system = 0x4203f2c0 &system 의 주소도 구했습니다 .


strcpy의 안자가 두개기 때문에 pop pop ret 하는 가젯도 구했습니다 .

0x80484ed



여기서 readelf -s 을 사용해서 bss의 주소를 알아야합니다 .


왜 굳이 bss 영역을 사용해야하냐 ? 

bss영역은 한마디로 주소값이 변하지 않습니다 .


bss의 주소는 0804963c 인것을 확인했습니다 .


이제 대강 필요한 정보들을 수집했으니 가젯들을 모아서 

 /bin/sh를 만들어봅시다 .

어렵게 생각하실 필요 없습니다 .

/   ,  b  ,  i  ,  n  , /  , s  , h  문자가 총 몇개지요 ? 

중복되는 문자 / 를 하나 빼면 6개입니다 .

간단합니다 .6개의 주소를 구하면됩니다 .



objdump -s [대상] | grep [원하는 가젯 단어] --color=auto (--color = auto를 사용한 이유는 원하는 가젯 단어에 색깔을 입혀서 구분이 쉽게 만들기 위함입니다.)


자 그러면 저기서 b를 발견했습니다 그래서 b의 주소가 뭐냐?


맨 왼쪽을 보시면 80480f4 라고 써있습니다 . 저 주소는 / 의 주소임을 짐작할 수 있습니다 .


/ = 80480f4 일단 하나를 구했습니다 . 


자 근데 여기서 b의 주소는 /lib/ld ~ 이러한식으로 구성이 되어있는데 

/ = 80480f4 이고 l = 80480f5 i = 80480f6 입니다 


그렇다면? b = 80480f7 입니다 . i 도 구해버렸네요!  i = 80480f6




n = 8048232 



s = 8048240 



h= 8048380


자 모든  친구들이 구해졌습니다! 


이렇게 퍼즐 조각 맞추듯이 가젯들을 모아서 단어를 만들어 공격하는것이 rop입니다.


자 이제 어떻게 페이로드를 짜느냐 ? C라던가 평소와 같이 16진수로 공격하기엔 어어어엄청 복잡하고 길어지더라고요 


그래서 파이썬으로 작성을 해봤습니다



struct 와 os 를 사용한 이유는


struct.pack('<I',number) 부분의 뜻은 리틀엔디안으로 정리한다는 소리입니다.

os.execv(TARGET,("",payload)) 를 사용한 이유는 

execv함수의 원형은 execv(path , argv[]) 입니다 .

path에 지정한 경로명에 있는 파일을 실행하고 argv를 인자로 전달합니다 .

그래서 os.execv(TARGET,("",payload)) 에서 TARGET은 공격할 위치로 만들어주었고 

뒤에 ("",payload) 를 사용한 이유는 (payload) 이렇게만 사용을 하면 파일명에 payload가 들어가서 에러가 난다 



두개 이상이 필요하다고 하면서 에러가 뜨는거같네요 

근데 TARGET 으로 이미 공격할곳을 넣어줬음으로 " "으로 공백으로 전달을 해주고 

argv[1]은 payload를 전달해줌으로써 공격을 합니다! 



이러한식으로 공격이 성립됩니다! 


아직까지는 ROP를 막을 확실한 방법이 없다고 알고 있습니다 .

열심히 공부하고 응용하면 효과적으로 사용할 수 있을거같네요!


'시스템 기법 정리' 카테고리의 다른 글

Fake EBP  (0) 2016.06.02
PLT , GOT [상세히] ,GOT Overwrite  (0) 2016.06.02
Format String Attack (FSB)  (0) 2016.05.30
ASLR를 해제하는 여러가지 방법  (0) 2016.05.29
plt , got , rtl chain , ASLR  (0) 2016.05.28

Fake EBP 란 ? Leave / RET 을 사용하여 ebp를 원하는 위치로 옮긴뒤에 공격자가 원하는 RET를 얻어내는 공격이다!


(*이 기법은 RET까지만 덮어쓸 수 있는 상태이고 RET에 스택과 라이브러리 주소등을 못넣도록 막아놨을때 사용하는 방법이다)

위에도 언급했지만 Leave / RET 이 중요하다!




Leave 와 Ret 의 역할입니다!




오버플로우가 일어나지 않은 상태의 스택입니다 


자 Fake Ebp의 공격방법이 무엇이냐 sfp에 buf+4 부분(쉘코드의 주소)을 덮어줍니다.


*여기서 sfp의 의미란? Sfp는 ebp가 복귀되는 주소를 저장하는 역할입니다!

Sfp에 buf-4값을 넣어줌으로써 ebp가 buf-4로 가게 해줘서 buf에 저장한 쉘코드가 온전하게 실행될 수 있도록 해주는 역할을 합니다


그리고 ret에 ret를 덮어줍니다 . 


그렇게 되면 


1차 -  Leave = Mov esp , ebp pop ebp 수행 esp에 ebp가 들어갑니다(esp = ebp)

그 뒤에 pop ebp 때문에 ebp가 스택에서 뽑혀서 buf-4로 갑니다  ..

방금 한 pop ebp 과정에서 pop 명령어 때문에 +4가 되므로 buf+4를 sfp에 넣어주는것입니다

그 뒤 ret 이 실행되는데 ret 에는 ret을 넣어놨기 떄문에 ret이 다시 실행됩니다 


2차 - Leave = Mov esp ,ebp pop ebp 를 다시 수행합니다 . 아까와 같이 esp에 ebp가 들어갑니다 .근데 현재 ebp의 위치는 어디죠? 바로 맨 아래입니다

그 뒤에 pop ebp로 ebp는 어딘가로 숑 사라져버리고 pop 명령어를 수행했기 때문에 +4가 됩니다. 그렇다면 esp는 쉘코드를 가리키게 되고 ret 수행으로 eip 명령으로 쉘 코드가 실행됩니다 .


1차 Leave = Mov esp , ebp pop ebp 에 대한 그림입니다 



Leave Mov esp , ebp 에서 buf 어딘가에 존재하던 esp가 Mov esp ebp 라는 명령어에 의해서 Sfp(&Buf-4)를 가리키고 있던 ebp와 같은 곳에 존재하게됩니다 .

그 뒤에 pop ebp 명령어가 실행이 되면서 sfp에 들어있던 buf-4로 ebp는 이동하게 되고 esp는 Leave/Ret을 가리키고 있는 상태가 됩니다 



pop eip 명령에 의해서 leave - ret의 주소를 가지게 되었고 jmp eip를 함으로써 

다시 한번 leave-ret을 진행합니다



2차 leave - ret이 진행됐습니다. 

leave mov esp , ebp가 진행됨으로써 아까 buf-4부분을 ebp가 가리키고 있기때문에 esp도 마찬가지로 똑같은 위치에 있게됩니다. 그리고 pop ebp를 수행함으로써 esp는 shellcode를 가리키게되고 ebp는 신경안쓰셔도됩니다


그 후에 ret 이 진행이 됩니다 현재 esp가 Shellcode를 가리키고 있는 상태기 때문에 eip 도 Shellcode를 가리키게 되고 pop eip를 함으로써 eip의 &Shellcode가 들어가게됩니다. 

그 후에 jmp eip로 쉘코드가 실행이됩니다 .


LOB - ZOMIBE_ASSASSIN 은 FAKE EBP를 통해서 푸는 문제입니다! 직접 해보고 싶으신 분은 참고 하시길 바랄게요!


'시스템 기법 정리' 카테고리의 다른 글

ROP  (0) 2016.06.06
PLT , GOT [상세히] ,GOT Overwrite  (0) 2016.06.02
Format String Attack (FSB)  (0) 2016.05.30
ASLR를 해제하는 여러가지 방법  (0) 2016.05.29
plt , got , rtl chain , ASLR  (0) 2016.05.28

PLT : 외부 프로시저를 연결해주는 테이블 , PLT를 통해 다른 라이브러리에 있는 프로시저를 호출해 사용할 수 있다.
[어떤 프로시저(함수들)이 있는지 나열되어있는 테이블]

 

GOT : PLT가 참조하는 테이블 프로시저들의 주소가 들어있다.

[실제 함수들의 주소가 들어있는 테이블]

 

PLT 와 GOT의 대략적인 개념을 말씀드리겠습니다.

 

함수를 호출하면(PLT를 호출하면) GOT로 점프하는데 GOT에는 함수의 실제 주소가 쓰여있습니다

첫 번째 호출에는 GOT는 함수의 주소를 가지고 있지 않으나 "어떠한 과정"을 거쳐서 주소를 알아내게 되고 

두 번째 호출 부터는 첫 번째 호출 때 알아낸 주소로 바로 점프합니다 .

 

어떠한 과정이란? 대략 과정만 말씀드리자면 

 

 

Static Link 와 Dynamic Link

PLT와 GOT에 대해서 조금 더 깊게 들어가 보기 위해서는 링커에 관해서 알아야합니다 

 

소스파일을 실행파일로 만들어 주기 위해서는 컴파일이라는 과정이 필요합니다.

아래는 간단한 컴파일 과정입니다(*출처 http://blog.eairship.kr/2)

자 여기서 만약에 작성한 소스 안에 printf 함수를 호출하는 코드가 존재하고 

include<stdio.h>와 같은 헤더 파일에는 printf의 선언이 잇다고 가정합니다

이런식으로 컴파일을 통해 오브젝트 파일이 생성됩니다 . 

하지만 오브젝트 파일만으로는 실행이 가능하지 않습니다 그 이유는 printf의 구현 코드를 모르기 때문입니다 . printf를 호출 했을 때 어떠한 코드를 실행해야 하는지 우리가 작성한 코드만 가지고서는 아직 아무것도 알 수 없습니다

 

오브젝트 파일을 실행 가능하게 만들기 위해서는 printf의 실행 코드를 찾아서 오브젝트 파일과 연결 시켜야 합니다.

printf의 실행 코드는 printf의 구현 코드를 컴파일한 오브젝트 파일로 연결 해야합니다.

이런 오브젝트 파일들이 모여있는 곳을 라이브러리(Library)라고 합니다.

 

이렇게 라이브러리와 같이 필요한 오브젝트 파일들을 연결시키는 과정을 링킹이라고 합니다 

 

이렇게 링킹을 하는 방법은 Static과  Dynamic 방식이 존재합니다 

Static Link 방식을 통한 실행파일 생성입니다 . 

 

Static Link 방식은 파일 생성시 라이브러리 내용을 포함한 실행 파일을 만듭니다

 

Static Link로 파일을 만들던 도중에

 

/usr/bin/ld : cannot find -lc 라는 에러가 나왔습니다.

 

collect2 : ld returned 1 exit status 라는 오류 같은 경우에는 lob golem 편에서도 언급을 햇었는데요 라이브러리가 정의가 되어있지 않아서 발생한 오류였습니다.

 

그렇다면 이번에도 마찬가지로 라이브러리가 문제란것을 짐작할 수 있습니다.

 

--------------------------------틀린 방법입니다 참고만 하세요 --------------------------

 

-l 뒤에 있는 문자가 검색되지 않은 라이브러리입니다! 

그렇다면 해당되는 라이브러리를 검색해보겠습니다

 

 

yum list | grep *문제되는 라이브러리명*lib (여기서는 lc 이므로 clib) 을 검색해줬더니 두 친구들이 검색되네요!

 

 

이러한 식으로 설치를 진행하겠습니다!  

 

--------------------------------------------------------------------------------------------------

 

 

해당 명령어를 사용함으로써  

 

성공하였습니다 -static 컴파일이 됐군요! 

 

Static 옵션을 사용하면 Statick Link 방식으로 컴파일이 됩니다.

 

실행 파일 안에 모든 코드가 포함 되어서 라이브러리 연동 과정이 따로 필요 없으며 한 번 생성한 파일에 대해서는 필요한 라이브러리를 따로 관리하지 않아도 되기 때문에 간편합니다. 

 

하지만 단점으로써는 파일 크기가 커지고 동일한 라이브러리를 사용하더라도 해당 라이브러리를 사용하는 모든 프로그램들은 라이브러리의 내용을 메모리에 매핑 시켜야 된다는 것이 있습니다.(매핑 - 연결 시킨다는 느낌으로 아시면 될거같습니다 )



† Dynamic linking

Dynamic Link 방식은 공유라이브러리를 사용합니다.
라이브러리를 하나의 메모리 공간에 매핑한뒤에 여러 프로그램에서 공유하여 사용합니다.

실행파일 안에 라이브러리 코드를 포함하지 않아도 되므로 Static Link 방식을 사용해 컴파일 했을때보다 파일 크기가 훨씬 작아지고 적은 메모리를 차지하며 라이브러리를 따로 업데이트 할 수도 있는것이 장점입니다 .

하지만 단점으로썬 실행파일이 라이브러리에 의존해야 하기 때문에 라이브러리가 없으면 실행할 수 없다는 점 입니다.



아무런 옵션을 주지 않은채 gcc 컴파일을 한다면 자동으로 Dynamic Link방식으로 컴파일이 됩니다.


이제 여기서 공부하려고 했던 PLT 와 GOT를 Dynamic Link 컴파일에서 사용합니다.


그 이유는 Static Link 방식으로 컴파일 시 라이브러리가 프로그램 내부에 존재하기 때문에 함수의 주소를 알아오는 과정이 따로 필요하지 않지만 Dynamic Link 방식으로 컴파일을 하면 라이브러리가 프로그램 외부에 존재하기 때문에 함수의 주소를 알아오는 과정이 필요합니다 .


Dynamic Link 방식으로 프로그램이 컴파일 된다면 함수를 호출 할 때 PLT를 먼저 첫번째로 참조합니다. 

그 뒤에 PLT 에서 GOT로 점프를 하는데 GOT에 라이브러리에 존재 하는 실제 함수의 주소가 쓰여있어서 이 함수를 호출하게 됩니다 . 


이것은 첫 번째 호출이고 첫 번째 호출에는 GOT에 실제 함수의 주소가 쓰여있지 않으나 두 번째 호출부터는 GOT에 실제 함수의 주소가 쓰여있습니다.


그래서 첫 번째 호출에는 Linker가 dl_resolve라는 함수를 사용해서 필요한 함수의 주소를 알아오고 GOT에 그 주소를 써준 후 해당 함수를 호출하게됩니다.


Dynamic link 방식으로 컴파일 된 프로그램입니다 .


시작하기 전의 printf 의 주소를 봤더니 printf@plt 라는 이름과 함께 0x80482f4를 가르키고 있습니다 .

하지만 시작 한 후에 printf의 주소를 다시 보니 plt라는 이름이 사라진 후 다른 주소로 변한 것을 볼 수 있습니다. 


Static link 방식으로 컴파일 된 프로그램입니다 

프로그램 시작과 시작 전의 주소가 같은 것을 확인 할 수 있습니다 .


자 근데 Dynamic link 방식으로 만들어진 프로그램에서는 GOT에 실제주소가 존재하는것을 알게됬습니다. 

근데 이 GOT 주소를 해커가 원하는 흐름으로 갈 수 있는 함수의 주소로 바꿔버린다면? 쉘을 실행시키는거도 가능하겠지요 ? 

이러한 기법이 GOT_Overwrite 입니다 . 


간단한 예제를 보여드리겠습니다!



printf 라는 함수가 있지요 ? 이 printf를 system으로 바꿈으로써 쉘을 실행시켜볼겁니다! ( 쉘 대신 youngzzang을 넣었엇지만 결과를 보여드릴수가 없어서 바꿧습니다-_-)




먼저 컴파일을 할 때 혹시나 있을 보호기법을 생각해서

gcc -o young young.c -z execstack -fno-stack-protector -m32 로 컴파일을 해서 

nx bit 보호 기법을 해제해줬습니다 [ young 대신에 파일명을 넣어주세요 ] 


readelf 명령어로 간단히 plt가 어떠한 주소에 있나 파악한 후에 

printf 의 0x80482f4를 타고 들어간 후에 printf@plt 에서 jmp 하는 *0x08049650부분을 system 함수의 주소로 set 해주어서 실행시켰습니다.

set &printf@got=&system 

그랬더니 쉘이 실행된것을 볼 수 있엇습니다!


참고 bpsecblog.wordpress.com/2016/03/09/about_got_plt_2/

      shayete.tistory.com/

      항상 두 블로그에서 많은것을 배우며 작성중입니다! 


'시스템 기법 정리' 카테고리의 다른 글

ROP  (0) 2016.06.06
Fake EBP  (0) 2016.06.02
Format String Attack (FSB)  (0) 2016.05.30
ASLR를 해제하는 여러가지 방법  (0) 2016.05.29
plt , got , rtl chain , ASLR  (0) 2016.05.28

Format String 이란 

int num = 5;

printf("ABC%d", num); 같은 형태의 코드가 있을 때 %d가 바로 포맷 스트링입니다

보통 프로그래머들이 코딩을 할 때 저런식으로 입력하지않고 편리성을 위해서 

fgets(num , 10 , stdin);

printf(num)

이러한 식으로 입력을 하는 경우가 있는데 만약 저기 num안에 문자열을 넣으면 문자열로 정상적으로 인식하지만 서식문자를 넣었을 경우에는 서식문자로 인식합니다 .

그 후에 넣어진 서식문자를 만나면 메모리의 다음 "4바이트"를 그 서식자의 기능대로 출력하게 됩니다 . 

주로 알려지고 쓰이는 서식문자들의 역할입니다 

%d : 정수형 10진수 상수                   %lf : 실수형 상수(double)
%f : 실수형상수 (float)                      %c :  문자 값 (char)
%s : 문자열                                    %u : 양의 정수 (10진수)
%o: 양의 정수 (8진수)                       %x : 양의 정수 (16진수)
%n:*int(쓰인 총 바이트 수)                 %hn : %n의 반인 2바이트 단위 

자 여기서 서식문자를 만나면 메모리의 다음 "4바이트"를 그 서식자의 기능대로 출력하게 된다고 말씀드렸는데요 과연 여기서 다음 "4 바이트"가 무엇을 의미하는 것일까요 ?

(* 본 소스는 FTZ-LEVEL20의 소스를 예제 삼아서 진행하고 있습니다 .
문제의 풀이를 보기 싫으신분은 다른 글을 참고해주세요!)


fgets 함수를 통해서 printf(bleh)으로 바로 호출하네요

printf로 바로 출력을하면 포맷스트링어택이 가능하게 됩니다 ! 

자 그럼 여기서 아까 말씀드린 다음 4바이트가 뭐인지 설명해드리겠습니다 .

AAAA라는 문자열을 친 후에 %x(양의 정수)라는 서식문자를 사용했습니다!

분명히 %x를 입력했을텐데 지금 나오는 숫자들은 무슨 숫자들일까요 ?

4f를 10진수로 바꾸면? 79입니다 

4212ecc0 은 무슨 뜻일까요 ? 

stdin이라고 뜨는군요! 그렇다면 지금 fgets(bleh,79,stdin) 에서 79와 stdin이 나왔네요?

4207a750 은 무엇을 의미할까요 ? 

strrchr이 나왔네요 ? 의미는 임의의 문자열이 시작되는 위치를 구합니다 .

그리고 마지막으로 41414141은 제가 입력한 A의 아스키코드입니다 

그렇다면 메모리 구조는 

| printf | | size of (bleh) | | stdin | | strrchr | | bleh | | dummy | | sfp | | ret |

이렇게 되잇겠네요!

고로 다음 "4바이트" 가 의미하는것은 | ~ | 공간을 하나씩 출력한다고 보시면 되겠습니다   

자 그럼 이 서식문자를 통해서 공격을 해야되는데요 ! 

여기서 가장 중요한 서식문자 %n을 알아보도록 하겠습니다! 

%n이란 ? %n이라는 서식문자가 나오기 전까지 출력된 값들을 전부 세서 그 다음 메모리주소값에 그 숫자만큼 대입해버리는 서식문자입니다

만약에 AAAA %1111x %x %x %n 를 입력햇을 경우에는 현재 %n의 위치가 원래 41414141이 있던곳입니다 .

고로 AAAA(4) + 공백(1) + %1111x(1111)+ 공백(1) + %x(1) + 공백 (1) + %x(1) + 공백(1) = 4 + 1 + 1111 + 1 + 1 + 1 + 1 = 1120 이므로  0x41414141 = 0x460(1120) 이 됩니다 . 

방금 위에서 설명한거로 알 수 있는것은 %1111x 를 넣었더니 1111만큼 주소값이 증가한것을 확인 할 수 있습니다 .
그렇다면 원하는 흐름의 주소를 알아낸다음에 그 만큼 주소 값을 올려서 원하는 메모리 주소에 덮어 쓸 수 있다면(ex - ret )? 공격이 가능하겠습니다!

먼저 쉘코드를 실행 시키는게 관점이겠습니다 . 본 글에서는 전에 풀이했던 FTZ LEVEL20의 풀이를 가져와서 다시 한번 설명하겠습니다  

먼저 에그쉘을 이용해서 쉘코드를 구했습니다 .

그 다음엔 ret 주소를 구합니다  > 08049598 

자 근데 0xffffffff를 32bit 시스템에서는 받아들일 수 없습니다! 
0xffffff를 10진수로 표현하면 43억 가까이 되기 때문이지요! 그래서 먼저 쉘코드의 주소를 2바이트씩 나눠서 입력하는 방법을 선택해야 합니다 .
bfff / f2cb 이렇게 나누어서 10진수로 바꾼 후 공격하는 것이지요 

근데 쉘 코드를 ret에 넣어줘야 합니다. 그럼 ret에는 2바이트씩 나눠져서 입력을 하기 떄문에 
f2cb 는 08049598 bfff 는 0804959a 에 입력을 해줘야겠습니다! 
(리틀 엔디안 방식을 이용해야 하기 떄문에 낮은자리를 먼저 입력합니다 )

자! 그럼 페이로드를 대충 구성해보죠! 

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%f2cb의 10진수%n%bfff의 10진수%n

이렇게 되겠습니다! 

자 여기서 왜 AAAA를 써줘야 하느냐 ? %n 의 위치와 ret의 위치를 맞춰주기 위해서입니다!

서식문자는 4바이트를 참고한다고 햇지요 ? 쉘코드를 각자 나눠서 입력한 서식문자가 AAAA 에 들어가게되고 %n에는 쉘코드의 주소를 넣어놧으니 ret에 들어가도록 하기 위해서입니다! 

%8x를 굳이 쓴 이유가 무엇이냐 ?

| printf | | size of (bleh) | | stdin | | strrchr | | bleh | | dummy | | sfp | | ret |

아까 위에서 이러한 메모리구조를 말씀 드렸습니다 

%8x 를 쓴 이유는 저 메모리의 각 부분들이 각자 8자리만큼 차지하기 때문이고 
%8x를 3번 사용해준 이유는 bleh 부문으로 넘어가게 해서 값들이 정상적으로 ret에 들어가게 하기 위해서입니다 .

자 여기서 이제 f2cb 와 bfff를 10진수로 나타내면 f2cb = 62115 bfff = 49151 이겠습니다.

근데 %n은 여태까지 입력된 자리를 모두 계산한다고 하였지요 ?
AAAA(4)+ ret(4) + AAAA(4) + ret(4) + 8 + 8 + 8 = 40이 되겠습니다

그렇다면 62125 라는 숫자가 되기 위해서는 62125 - 40 = 62085 라는 숫자를 %c에 넣어줘야지 여태까지 나온 숫자값들을 모두 합산해서 62125가 되겠네요!

어라 근데 bfff = 49151 이네요 ? 여태까지 모두 나온 숫자들을 빼줘버리면 음수가 되고 그렇다고 가만히 내비두자니 ret의 주소에 접근할 수가 없습니다.

이럴때 방법은 bfff 앞에 1을 붙이면 1bfff:114687이 됩니다. 

여기서 62125 만큼 빼주면 52562입니다 . 고로 페이로드는 

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%62085c%n%52562c%n 이렇게 되겠습니다 .  이렇게 각 쉘코드가 ret의 주소에 들어가게 되고

권한을 취득할 수 있게됩니다 

'시스템 기법 정리' 카테고리의 다른 글

Fake EBP  (0) 2016.06.02
PLT , GOT [상세히] ,GOT Overwrite  (0) 2016.06.02
ASLR를 해제하는 여러가지 방법  (0) 2016.05.29
plt , got , rtl chain , ASLR  (0) 2016.05.28
버퍼 오버플로우 - 2 RTL (개념 정리, 기초)  (0) 2016.05.27


(rtl은 내가 만든 연습용 소스이다!)

ldd명령어( 공유 라이브러리의 주소를 출력(?) ) 을 이용해 주소를 확인해보니 
여기저기로 톡톡 날아다니는것을 알 수 있다 

바로 ASLR 기법이 걸린 상태이다 .

이제 ASLR 기법을 해제하는 방법을 소개하고자 한다 .

첫번째는 /proc/sys/kernel/randomize_va_space 를 조작하는 방법이다

randomize_va_space 는 0 , 1 , 2 의 값을 가진다

0값은 randomize를 해제하는 옵션이고 1 , 2 는 실행하게 하는 옵션이라고 생각하면 된다 그렇다면 randomize_va_space를 훔쳐보도록 하자!

2로 값이 설정되있다 randomize가 실행되고 있다

그렇다면 이 값을 조절해보자! 

그런데 그냥 실행하면 퍼미션 디나이드가 뜬다 

randomize 값을 조절 하려면 root의 권한이 필요하다

echo 0 > /proc/sys/kernel/randomize_va_space 라고 입력해준후에 

randomize의 값을 확인해보니 0으로 변한것을 알 수 있습니다 . 

라이브러리의 주소가 더 이상 변하지않는 것을 확인할 수 있습니다!


두 번째로 소개드릴 방법은 setarch xxxxxx -R 입니다

[xxxxxx 에는 각 리눅스의 버전 ex) linux64 liunx32 i386 등등, 을 적어주시면 됩니다

addr-no-randomize 라고 적혀있네요! 

이 명령어를 실행하면 쉘이 뜨는데요 그 쉘 안에서는 aslr이 풀려있는 상태입니다!

이 명령어는 딱히 권한이 필요 없으므로그나마 이 방법이 가장 좋다고 생각할 수 있습니다! 

쉘이 열렸고 주소 값이 변하지 않는 것을 확인할 수 있습니다 


세번째로 소개 해드릴 방법은 

ulimit -s unlimited 입니다! 

코어덤프와 상당히 유사하지요 ? 스택을 꽈악 채워버려서 주소값이 움직이지 못하도록 합니다! 

다만 32비트에서만 사용이 가능하고 64비트에서는 사용을 못합니다!

plt - 어떠한 함수들이 있는지 나열되어 있는 테이블
사용자가 만든 함수는plt를 참조할 필요가 없지만 외부 라이브러리에서 가져다가 사용할 경우에는 plt를 참조하게 됩니다

got - 함수들의 주소를 담고 있는 테이블
라이브러리에서 함수를 호출할 때 plt가 got를 참조합니다 .

gdb로 까봣을때 함수 뒤에 plt가 들어가있으면 외부 라이브러리에서 가져온 함수

함수 뒤에 plt가 존재하지않는다면 사용자가 만든 함수 

[gdb로 plt가 붙은 함수가 jmp 하는곳을 x/i 주소 명령어를 통해 확인해보면 got가 있는곳으로 jmp하는 것을 알 수 있다

got에는 함수의 실제 주소를 가리키는것이 아니라 plt+6의 주소를 가리킨다 
이 이유를 이해하기 위해서는 아래 과정을 알아야 한다.

함수를 실행하기 전 

plt address -> got address (jmp plt +6) -> jmp global_offset_table -> _dl_runtime_resolve -> _dl_fixup -> _dl_lookup_symbol_x -> jmp function address

    함수_got -----------------------------------> 함수 실제 주소 


함수를 실행한 후 

plt address -> got address -> jmp function address

이 부분은 https://bpsecblog.wordpress.com/2016/03/09/about_got_plt_2/< 참고 해주세요 

rtl 기법으로는 

DEP와 NX bit가 적용된 스택을 우회하기 위해서 주로 사용됩니다

DEP - 데이터 실행 방지로 스택이나 힙에서의 쉘 코드 실행을 막아주는 보호기법입니다!

NX bit - NX 특성으로 지정된 모든 메모리 구역은 데이터 저장을 위해서만 사용되고 프로세서 명령어가 그 곳에 상주하지 않음으로써 실행되지 않도록 만들어 준다 .
(only 데이터 저장 , 실행x )

RTL chain 

ret로 특정한 함수를 호출하면 (인자 값이 하나인 함수일 경우에) 스태ㅐㄱ의 맨위가 pop ret 주소가 리턴어드레스가 되고 parameter 1이 인자값이된다 그리고 또 ret에 특정 함수를 넣으면 .,..반복 

/-----------/
|   ret1   |
/-----------/
| pop ret |
/-----------/
|  인자1  |
/-----------/
|   ret2   |
/-----------/ 반복 --------

이럴 경우 buf +sfp +ret+"dummy"+parameter 에서 dummy 부분에 exit를 써주는게 안전하다! 


ASLR 이란 ? 
동적 라이브러리의 주소를 프로그램 실행마다 랜덤하게 매핑한다 .
주소값이 프로그램 실행마다 매번 바뀌면 RTL을 위해 System() 함수의 주소를 구하고 싶어도 구하기가 하늘의 별따기입니다 

그러면 이 문제를 어떻게 해결하나 ? 
코어 덤프 할때 쓰던 명령어와 비슷한 명령어를 사용해줍니다
ulimit -s unlimited
ulimit      = 시스템 제한
-s          = 최대 스택 크기 출력
unlimited = 크기에 제한을 두지 않는다.

스택이 더 이상 증가하지 못 할 정도로 증가시켜서 랜덤으로 매핑되게 하는 ASLR기법을 방지합니다

코드영역에는 스택, 힙과는 다르게 고정된 값이 박혀있습니다 

코드영역 0x8048000을 열어본 후 쉽게 아스키코드로 쉽게 파악할 수 있는 값
ex) A - 41을 system 의 인자로 사용합니다 

cp /bin/sh ./A 이러한식으로 /bin/sh를 ./A라는 이름을 가진 실행파일에 복사해서 쉘이 실행되도록 만든 후 export PATH=.:$PATH 를 이용해서 환경변수에 현재 위치를 등록합니다 

그 후 

buf + sfp + &system + dummy + 0x41가 들어있는 주소 를 넣으면 

system("A")가 실행되고 쉘이 실행됩니다 

저도 공부중이라서 뒤죽박죽 정리했습니다 

안녕하세요! 저번에는 단순히 버퍼 오버 플로우가 어떤 원리로 일어나는 것인가 ? 
오버 플로우를 통해서 어떻게 공격을 할 수 있는가? 를 알아봤습니다!

이번에는 좀 더 업그레이드 되고 세련된 방법을 소개해드리겠습니다!
바로 Retrun To Library입니다!

이러한 구조로 되어있습니다!
버퍼 오버플로우 - 1 에서 SFP , RET의 역할과 오버플로우를 통하여 RET에 닿으면 어찌되는가 ? 를 설명해드렸는데 혹시 기억이 안나시거나 개념이 햇갈리시면 다시 봐주시길 바랍니다 .

먼저 RTL이란 ? Ret 주소에 libc라는 공유라이브러리 함수 주소로 덮어서 
system 함수를 호출하도록 유도하여 공격하는겁니다!

예제를 통해 RTL이란 무엇인가? 를 설명해드리겠습니다!


( 본 소스는 해커스쿨 FTZ-LEVEL12입니다 . 풀이를 알게 될 수도 있으니 스스로의 힘으로 LEVEL12를 풀어보시려는 분은 다른 좋은 글들을 읽어주시면 되겠습니다! )
setreuid가 걸려있어서 따로 만들어 줄 필요는 없어보입니다!
그리고 gets 함수를 사용하네요 
gets 함수의 취약점은 엔터를 입력받을 때 까지 모두 버퍼에 담아버립니다
즉 256을 넘길 수 있으니 버퍼 오버플로우가 일어나겠군요 ? 
그렇다면 이러한 취약점을 통해서 RTL로 공격해보겠습니다 

버퍼는 일단 256만큼 주어졌습니다!

자 ! 여기서 이제 RTL을 위해 알아야 하는 주소들은 무엇들이 있을까요 ?

먼저 System 주소를 파악해야합니다!
(이유  : System 함수란? 운영체제의 명령어나 외부의 각종 실행 파일을 실행하기 위해서 사용합니다! 또한 쉘을 실행시켜야 하기 때문에 시스템 함수로 /bin/sh을 호출해야합니다!)



system 의 주소는 이런식으로 gdb분석에서 얻을 수 있는 방법이 있습니다!

system의 주소를 휙득했으면 /bin/sh의 주소도 얻어야겠지요 ? 

/bin/sh의 주소는 저러한식으로 gdb에서 읽어 낼 수 없고 따로 코드를 써서 얻어내셔야합니다 

이러한 소스를 이용해서 /bin/sh의 주소를 구하고 나면 페이로드를 어떻게 짜야할까요 ?
해당 level12의 소스는 256이 주어졌습니다 [실제로 푸실떄는 더미값을 계산하셔야합니다.]
buf[256]+sfp[4]+ret[4] 이러한식으로 스택이 구성되잇겠습니다.

buf[256]+sfp[4]+system주소[4]+dummy[4]+/bin/sh[4] 이런식으로 짤 수 잇겠습니다.

dummy를 넣어주는 이유는 system 함수나 execl 함수는 ebp+8 위치의 인자를 인식하기 때문에 dummy+4를 넣어서 /bin/sh를 불러옵니다!
(system주소 다음에 리턴어드레스를 쓰레기값으로 채우기 위한 용도라고도 합니다)

이것이 RTL의 기본 원리입니다 

응용과 좀 더 자세하게는 다음에 찾아뵙곘습니다


'시스템 기법 정리' 카테고리의 다른 글

Format String Attack (FSB)  (0) 2016.05.30
ASLR를 해제하는 여러가지 방법  (0) 2016.05.29
plt , got , rtl chain , ASLR  (0) 2016.05.28
버퍼 오버플로우 - 1  (1) 2016.05.25
버퍼 오버 플로우 역사 및 정리  (0) 2016.05.24

*초보자가 작성한 글이므로 완벽하지 않을 수 도 있습니다 
 부족한 점이 있다면 지적해주시고 고쳐주시면 감사하겠습니다*
----------------------------------------------------------------------------------------------

0xb000000(낮은 주소)                                             0xbffffff(높은 주소)

낮은주소                                                                    높은주소 
 Stack         Heap             Bss                Data                 Text

* 스택은 거꾸로 자랍니다! 변수를 선언하면 선언할수록 메모리의 높은주소 에서 메모리의 낮은 주소로 향합니다! 이 부분은 직접 gdb로 분석 해보시다 보면 이해가 가실겁니다! *

이러한 식으로 메모리 구조가 이루어져있고 이번에 포스팅할 게시글은 
Stack 을 이용한 버퍼 오버플로우를 소개하겠습니다

[ 버퍼 오버플로우 - 버퍼를 초과시켜서 공격자가 원하는 대로 프로그램의 흐름을 만들어 작동시키는 것 입니다!]

Stack은 프로그래밍을 할때 함수 내부에서 선언하는 변수들이 들어가게 됩니다
또 한 스택은 먼저 들어간것은 가장 나중에 뺄 수 있습니다! 
버스기사 님들의 동전을 생각하면 되겠네요 동전을 넣어가면 점점 쌓이게 되고
그 동전을 빼려고 하면 위에서 부터 뺄수 잇겠지요 ?
(너무 진지하게 아래서 나오잖아요! 라곤 생각하지 맙시다! 그냥 저렇게 생각하시라구요!)
아니면 설거지를 하고 접시를 차곡차곡쌓는다 할때 먼저 설거지가 끝난 순서대로 접시를 쌓아 올리겠죠 ? 그 접시를 사용하려면 위에 접시부터 빼야지 아래 접시부터 빼서 쓰지는 않잖아요! 그런겁니다!

스택의 이러한 입출 방식을 기억하고 있으셔야지 좀 더 이해가 쉬우실겁니다!

자 이제 버퍼 오버플로우에서 버퍼란 ? 
데이터를 일시적으로 저장하는 메모리의 영역을 뜻합니다!
또한 스택 안에서 생성이 됩니다! 
또한 버퍼 오버플로우는 C , C++ , 어셈블리어 에서만 주로 발생을 하지
Java , Perl , Python 같은 언어들은 메모리를 자체적으로 관리하기 때문에 버퍼 오버플로우가 발생하지 않는다고 합니다! C#의 경우는 보호 기능을 제공하구요 
자 이제 여기서 버퍼 오버플로우가 어느 상황에서 발생하느냐!

[본 소스는 해커스쿨에서 제공하는 FTZ LEVEL 9 입니다]

버퍼 오버 플로우를 간단하게 이해하고 사용해보기 위하여 만들어진 소스네요!
buf , buf2에 각각 10의 크기가 할당 되어 있습니다!

[*주의* 데이터 영역의 전역 변수는 " 메모리의 낮은 주소 " 에서 " 메모리의 높은 주소 " 로 자라납니다
스택 영역은 " 메모리의 높은 주소 " 에서 "메모리의 낮은 주소" 로 자라납니다!
이번 글에서 다룰 영역은 --스택-- 영역입니다!"

자 그렇다면 아주 간단하게 스택을 만들어보죠 
/---------------------------------------/
|                                      |

|           buf 2  [10]              |

|           buf  [10]                | 

|                                     |

/-----------------------------------------/

이러한 식으로 스택이 들어간다는 소리입니다!

만약에 여기서 1,2,3,4,5  = buf 2 A,B,C,D,E = buf  이렇게 들어간다면 어떻게 될까요 ? 
/---------------------------/ 메모리 높은 주소
|           ret             |
/---------------------------/
|           sfb             |
/---------------------------/
|             5             |
|             4             |
|             3             |
|             2             |
|             1             |
/---------------------------/
|             E             |
|             D             |
|             C             |
|             B             |
|             A             |
/---------------------------/ 메모리 낮은 주소 

높은 주소에서 낮은 주소로 자란다는 것이 어떤것인지 이해 하셨을거라 생각하겠습니다!
자 다시 한번 저 소스의 스택을 보죠!

/---------------------------/ 메모리 높은 주소
|           ret             |
/---------------------------/
|           sfb             |
/---------------------------/
|          buf2 [10]       |
/---------------------------/
|          buf [10]        |
/---------------------------/

ret와 sfb는 각각 4바이트를 가지고있습니다!

근데 소스를 보면

stdin으로 40만큼의 크기를 buf에 넣어버리네요!

근데 지금 buf에는 10의 크기가 할당 되어 있습니다 

여기서 만약 10을 넘는 크기를 넣어주면 어떻게 될까요 ?

좀 더 편히 설명하기 위해서 buf 에는 5의 크기가 할당 되어 있고 5를 넘는 크기를 넣어 준다고 가정 하겠습니다! (단순히 크기만 10에서 5로 바뀌어서 설명하는겁니다!)

buf 에다가 8를 넣어보겠습니다!

/---------------------------/ 메모리 높은 주소
|           ret[4]          |
/---------------------------/
|           sfb[4]          |
/---------------------------/
|             5             |
|             4             |
|             H             |     buf2[5]
|             G             |
|             F             |
/---------------------------/
|             E             |
|             D             |
|             C             |     buf[5]
|             B             |
|             A             |
/---------------------------/ 메모리 낮은 주소

buf 에서만 입력한 영어들이 buf2 까지 올라갔네요! 입력된 것을 모두 저장하기 위해서 buf 에만 할당한 스택을 넘어서 buf2 라는 다음 메모리 영역까지 사용을 해버렸습니다.

근데 위에 sfb 와 ret 라는 친구들이 보이지요 ? 
만약 buf[5] 와 buf2 [5]의 합 크기 10 을 넘어선 값을 입력하게 된다면..?
sfp와 ret 까지 침범하게 될겁니다 .

자 여기서 sfp와 ret가 무엇이냐 ?

sfp - 함수 프롤로그에서 push ebp 에 의해서 저장된 ebp에 값입니다!
[함수에서 지역변수들이 사용될 때 EBP가 기준으로 사용이 됩니다.
그런데 함수를 하나만 사용하는것이 아니잖아요 ? 다른 함수로 넘어갔다가 다시 돌아올 때 함수가 시작햇을 당시 처음의 EBP 주소로 이동하기 위해서 저장해두는 값입니다!] 

ret - 함수가 임무를 수행하고 끝마친뒤 다음에 실행되어야할 " 명령이 위치한 메모리의 주소" 를 뜻합니다 
만약 근데 버퍼 오버플로우가 일어나서 ret 부분에 공격자가 원하는 공격이 위치한 메모리의 주소로 덮어씌어버린다면? 
공격자가 원하는 공격을 하도록 흐름이 바뀌겟지요 ?  


[gcc = GNU에서 만든 C컴파일러 이고 전처리기 , 컴파일러 ,어셈블러, 링커를 호출해주는 역할을 한다]

gcc 2.96 이상 버전으로 업그레이드 되면서 버퍼의 각 변수뒤에 dummy값이 생기게 되었습니다 

아까 와 같은 스택을 gcc2.96 이상에서 컴파일 하게 된다면

/---------------------------/ 메모리 높은 주소
|           ret[4]          |
/---------------------------/
|           sfb[4]          |
/---------------------------/
|             ?             |
|             ?             |
|             ?             |     dummy[?]
|             ?             |
|             ?             |
/---------------------------/
|             5             |
|             4             |
|             3             |     buf2[5]
|             2             |
|             1             |
/---------------------------/

|             ?             |
|             ?             |
|             ?             |     dummy[?]
|             ?             |
|             ?             |
/---------------------------/
|             E             |
|             D             |
|             C             |     buf[5]
|             B             |
|             A             |
/---------------------------/ 메모리 낮은 주소


이러한 식으로 버퍼의 각 변수 뒤에 dummy값이 형성이 됩니다 
그리고 이러한 dummy값 때문에 오버플로우를 일으켜서 ret에 덮어 씌우는 작업이
좀 더 어려워졌습니다

자 이제 여기서 dummy 값을 정확히 파악하기 위해서 gdb를 이용해 분석을 합니다.
[어셈블리와 gdb 사용법에 대해서 공부를 해야합니다 ]

gdb를 분석한 뒤 dummy 값을 찾아낸 뒤에 자신이 공격을 할 주소의 자신이 원하는 흐름대로 프로그램을 이끌어갈 주소를 넣으면 
이것이 바로 버퍼 오버플로우 공격입니다 
여기까지가 스택을 이용한 버퍼 오버플로우의 기본이였습니다 . 감사합니다 

버퍼 오버플로우가 일어날 수 있는 취약한 함수들 

Strcpy() =  정해진 곳에 지정된 내용을 복사해서 넣고 그 길이를 체크하지 않는다.

ex) strcpy( ( str ,argv[1]);
100이라는 배열을 선언햇지만 200을 집어 넣어도 무작정 집어넣게됨 

Strcat , sprintf , vsprintf ,gets , fsacnf , scanf, sprintf , sscanf, vfscanf, vsprintf,
vscanf , vsscanf , streadd, strecpy , strtrns 도 마찬가지로 이러한 취약점들이 존재함

Stack Overflow = 위에 게시해놓은 함수들에 의해서 문자열이 복사될 때 버퍼를 넘치게 하여 임의의 코드 (setuid가 담긴 쉘 코드라던가 시스템 함수라던가) 가 담긴 곳으로 ret에 주소를 넣어서 임의의 코드를 실행할 수 있도록 만들어서 공격한다 .



Return Into Libe  (RTL) 

함수의 ret를 덮어서 공유 라이브러리에 존재 하고있는 system() 함수를 가리키게 한뒤에 실행에 필요한 매개 변수를 전달하여 공격한다 
(메모리에 적재된 공유 라이브러리는 스택에 존재하는 것이 아니므로
Non-executable Stack을 우회하는 것이 가능하다)

RET를 메모리에 적재된 System()함수를 가리키게 하고 환경변수에 저장한 "\bin\sh"의 주소를 ebp+x(레드헷 6.2 기준 4 레드헷 8기준 8) 에 위치시킨뒤 공격한다 
|\x90*4|&System() | dummy4 or &exit() | &\bin\sh | \x90 | SFP | RET (레드헷6.2기준)
으로 페이로드를 짜서 공격하면 된다 .

 

Defeating Non-executable Stack 패치

리눅스에서 공유 라이브러리는PLT와 GOT를 이용한다 .
PLT - 프로그램이 호출하는 모든 함수를 나열하고 있는 테이블
GOT - 프로그램 실행 후 libc.so 내의 실제 함수 주소가 저장되는 곳
libc.so(표준 C라이브러리의 공유 객체 파일)
PTL는 실제 호출될 함수를 나타내는 값을 _dl_runime_resolve 함수의 인자로 넘기고 
_dl_runtime_resolve 함수는 전달된 인자 값을 사용하여 호출된 함수의 실제 주소를 구한 후 GOT에 저장한 뒤 호출된 함수로 점프한다.
이 후에 동일한 함수가 다시 호출되면 동적 링커는 GOT에 저장되어 있는 호출된 함수의 실제 주소로 바로 점프하게 된다.
공격하는 사람은 취약한 함수를 사용해서 RET를 strcpy()[취약한 함수] 함수의 PLT 주소로 변경한다.
PLT 주소에는 Null String이 포함되어 있지 않으므로 취약한 함수가 실행되는데 
이 함수가 환경변수에 저장된 shellcode를 데이터 세그먼트 영역에 복사한다 .
strcpy() 함수가 완료된 후 RET가 복사된 데이터 세그먼트 영역의 shellcode를 가리키면서 shellcode가 실행되게 한다 .


나 - strcpy()를 두 번 호출하여 GOT를 덮어쓰기
두 번째부터의 동일한 함수 호출 시에는 GOT에 저장된 주소로 점프하게 되므로 첫 번째 strcpy()실행 시 GOT에 system()의 주소가 쓰여지게 한다 .
그리고 두 번째로 strcpy()가 호출될 때는 GOT에 저장된 system()의 주소로 점프하게 되고 환경변수에 저장된 인자 값을 실행하게 된다 .

Stack Guard

버퍼 오버 플로우 방어 기술  
RET 앞에 Canary 값을 주입하고 에필로그 (leave , ret)시에 Canary 값이 변조되었는지의 여부를 확인하여 버퍼 오버플로우 공격을 탐지한다.
버퍼부터 RET까지 덮어쓰는 공격은 Canary 값을 덮어쓸 수 밖에 없으므로 버퍼 오버플로우가 발생했다는것을 알 수 있다.

Canary는 프로그램 시작 시간에 crt0 라이브러리를 이용하여 무작위로 생성된다
Null Canary , Terminator Canary , XOR Random Canary 등으로 변경되었다.
Null Canary , Terminator Canary는 Canary로 사용될 값에 Null 문자나 Terminator 문자가 들어가게 하여 strcpy() 같은 문자열 복사 함수가 실행될 때 해당 문자에 의해 실행이 종료되어 버리도록 한다. (터미네이터 = 문자열의 맨 마지막을 가리킨다
C 언어에서는 터미네이터로서 널(null) 문자가 사용됩니다)

XOR Random Canary
함수가 호출될 때 RET와 Random Canary를 XOR한 값을 메모리에 저장한 후 RET 앞에 위치시킨다(Canary) 이 후에 함수가 return될 때 메모리에 저장되어있던 Canary와 스택 상의 RET 값을 XOR 연산한 후 이 값을 스택상의 XOR이 된 Canary 값과 비교하여서 일치해야지 실행이된다.

Window 버퍼 오버플로우

소스가 공개되어 있지 않아서 리눅스와는 달리 비교적 안전하다고 생각되었던 Windows를 공격하는 방법은 리눅스에서의 버퍼 오버플로우 공격과 원리는 동일 하였고 단지 shell을 실행하기 위해서는 필요한API(응용 프로그램에서 사용할 수 있도록 운영 체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스를 뜻함)를 가지고 있는 몇몇 dll 파일을 적재하는 코드가 포함되어야 한다는 차이가 있다.

Random Stack 

프로그램이 실행될 때마다 스택이 서로 다른 주소에 위치하도록 하여 버퍼의 주소를 알아내기 힘들게 한 것 스택을 비고정적으로 위치시키기 위해 /dev/urandom , alloca()를 써야 한다고 제안을 하였고 RTL같은 공격으로부터 방어를 하기 위해서 공유 라이브러리의 주소 또한 무작위로 할당할 것을 권하였다 .

Heap Overflow

Heap 메모리에서 오버플로우를 해서 공격한다 

HEAP 영역은 malloc()에 의해 할당되고 Heap 영역 외에도 static 지시자에 의해 할당되는 bss 영역에서도 동일하게 버퍼 오버플로우가 일어날 수 있다 
setjmp() 함수를 이용해 현재의 명령어 , 스택 포인터 , 다른 레지스터를 jmp_buf에 저장한 후 longjmp() 함수로 setjmp()에 의해 저장된 jmpbuf를 복구한 다음 jmpbuf를 덮어씀으로써 longjmp() 함수가 호출될 때 shellcode를 실행할 수 있다.

strlcpy() and strlcat()

strncpy() , strncat() 은 자동으로 Destination의 문자열을 Null String으로 마치게 해주지만 [ char *strncpy(char *dest, char *src , size_t maxlen) =  문자열의 정해진 길이만큼 src에서 dest로 복사한다 ] src 문자열의 길이가 dest 문자열의 길이보다 클 때 오버폴르오가 일어날 수 있다 


Frame Pointer Overwrite (SFP)

SFP의 1바이트 조작만으로 공격이 가능한 기법  SFP의 마지막 1바이트를 덮어써서 eip가 버퍼에 주입된 shellcode를 가리키는 주소가 되도록 조작한다.
leave의 명령어는 mov ebp,esp | pop ebp 이렇게 두 개로 구성이 되있다.
leave 명령어에 의해 ret이 실행되기 전 1바이트가 조작된 ebp(SFP)의 값이 esp에 들어가게 된다.  pop ebp를 수행하면서 esp가 4바이트가 증가하게 되고 eip(현재 실행되고 있는 프로그램의 실행코드가 저장된 메모리의 주소)
즉 eip는 esp+4에 저장된 값을RET으로 인식하여 버퍼에 주입된 shellcode를 실행하게 된다 


+ Recent posts