시스템/시스템 보안

Buffer Overflow-2

비니화이팅 2018. 1. 29. 00:05

[소스코드 1]

- Buffer Overflow공격을 진행하기 전에 우선 아래의 소스코드를 분석해본다.

- Callee인 Func함수에서의 스택 구조는 아래와 같다.

 0012FB68

 BUF[4] 공간

 =>

 Test

 0012FB6C

 EBP

 =>

  sta

 0012FB70

 RET

 =>

 ck B

 0012FB74

 

 

 uffe

 0012FB78

 

 

 r Ov

 0012FB7C

 

 

 er F

 0012FB80

    low

 0012FB84

   0000

 0012FB88

   0000
 ...   ...

- memcpy함수를 실행하면 EBP는 물론 RET까지 attack배열의 문자열로 덮어 버린다. 즉, memcpy함수는 Buffer Overflow에 취약한 함수임을 알 수 있다.


[소스코드 2]

- 그렇다면 RET값을 임의의 값으로 변경할 수 있지 않을까? 라는 의문이 든다. AAAA라는 값으로 RET값을 변경시켜 보자.

 0012FB68

 BUF[4] 공간

 =>

 Test

 0012FB6C

 EBP

 =>

  sta

 0012FB70

 RET

 =>

 ck B

 0012FB74

 

 

 uffe

 0012FB78

 

 

 r Ov

 0012FB7C

 

 

 er F

 0012FB80

    low

 0012FB84

   0000

 0012FB88

   0000
 ...  

 ...

- 위의 표를 살펴보면 RET값에  "ck b"문자열이 들어가져 있는 것을 볼 수 있는데, 이것을 "AAAA"로 바꾸면 RET값을 "AAAA"로 바꿀 수 있을 것이다. 즉, attack문자열에서 8번 인덱스에 AAAA를 복사하면 원하는 대로 들어갈 것이다.

- 소스코드를 수정하고 확인해보면 스택구조는 아래와 같다.

 0012FB68

 BUF[4] 공간

 =>

 Test

 0012FB6C

 EBP

 =>

  sta

 0012FB70

 RET

 =>

 AAAA

 0012FB74

 

 

 uffe

 0012FB78

 

 

 r Ov

 0012FB7C

 

 

 er F

 0012FB80

    low

 0012FB84

   0000

 0012FB88

   0000
 ...  

 ...


[소스코드 3]

- RET도 내가 원하는 임의의 값으로 변경해봤다. 그러면 RET에 내가 작성한 코드의 시작 주소를 넣어 실행시킬 수 있도록 하려면 어떻게 해야 할까? 일단 "BBBBCCCCDDDD"가 내가 작성한 쉘코드라고 가정하고 스택에 삽입한다.

- 스택 공간은 아래와 같이 구성되어 있을 것이다.

 0012FB68

 BUF[4] 공간

 =>

 Test

 0012FB6C

 EBP

 =>

  sta

 0012FB70

 RET

 =>

 AAAA

 0012FB74

 

 

 BBBB

 0012FB78

 

 

 CCCC

 0012FB7C

 

 

 DDDD

 0012FB80

    low

 0012FB84

   0000

 0012FB88

   0000
 ...  

 ...



[소스코드 4]

 - "BBBBCCCCDDDD"라는 쉘코드가 스택에 삽입 되었으니 이번에는 쉘코드를 실행시키기 위해서 RET값을 쉘코드의 주소로 바꿔줘야 한다.

쉘코드의 주소는 표에서 나타나있듯이 0012FB74이다. 따라서 RET에 0012FB74를 넣어주면 되는데 리틀 엔디안 방식이기 때문에 역순으로 넣어주면 된다.

[소스코드 5]

- [소스코드 4]에서 쉘코드의 주소를 직접 넣어주었다. 하지만 다른 코드에서도 주소가 같을 것이라는 보장이 없다. 어디서나 RET에 쉘코드의 주소를 삽입하도록 할 수는 없을까? 

RET할때 ESP값으로 JMP한다면 어떤 코드에서든지 쉘 코드를 실행시킬 수 있을 것이다. 즉, RET값을 JMP ESP가 실행되도록 바꿔줘야한다.


- 그렇다면 JMP ESP를 쓰고 있는 주소를 확인해보자. 일단 현재의 프로그램에서는 JMP ESP를 쓰고 있지 않으므로 모듈에서도 확인해주기 위해 alt+e키를 눌러준다. 그러면 아래와 같이 창이 뜨는데 

여기서 찾아볼 모듈의 이름은 선택한 후 엔터를 눌러 들어간 후 JMP ESP가 있는지 찾아본다.


- 그러면 ntdll모듈에서 아래와 같이 JMP ESP주소를 확인할 수 있을 것이다.


- 알아낸 JMP ESP주소를 RET값에다가 넣어준다. 그러면 JMP ESP명령어가 있는 곳으로 RET할 것이고 JMP ESP 명령어가 실행되어 쉘코드가 있는 곳으로 JMP할 것이다.


[소스코드 6]

- 아래는 stdcall방식이지만 [소스코드 5]와 같은 결과가 나온다. 쉘코드가 들어가는 위치를 바꾸어 주었다.



- 이제 쉘코드가 실행되도록 RET를 변조해봤으니 쉘코드를 직접 작성해본다.

  아래와 같이 새로운 프로젝트를 만든다.



- 프로젝트를 만든 후 FileView를 클릭한다. 작업은 shellcode.cpp에서 할 것이다.



[소스코드 7]

- 아래와 같은 코드를 작성하고 실행해본다.


- 그러면 아래와 같이 cmd창이 뜬다.


- 해당 코드의 실행파일을 ollydbg로 연다.

 아래와 같이 main함수의 패턴을 찾아서 main함수로 따라들어간다.


- 블록 지정한 부분을 복사한다.


- 아까 작성한 소스코드를 주석처리하고 아래와 같이 복사한 어셈블리어를 붙여넣고 밑줄친 부분처럼 적당히 수정해준다. (참고로 EBP-8이 아닌 EBP-C라면 C앞에 0x를 붙여 16진수인 것을 표현해야 한다. 다른 것도 마찬가지이다.)


- 그리고 다시 실행해보면 아까와 같이 cmd창이 뜬다.

 

- 다시 이 코드의 실행파일을 ollydbg로 열어준다.

  아까와 마찬가지로 main함수로 들어가서 아래의 블록지정한 부분을 복사하는데 복사할 때는 마우스 오른쪽버튼 Binary-Binary copy로 복사한다.


- 복사한 값을 메모장에 붙여넣으면 아래와 같이 넣고 공백 부분을 \x로 채워준다.


- [소스코드 5]의 "BBBBCCCCDDDD"을 지우고 만든 쉘코드를 넣어준다.


[소스코드 8]

- [소스코드 7]에서 작성한 코드를 실행해보면 cmd창은 정상적으로 뜨지만 main함수가 정상적으로 종료되지 않아서 아래와 같은 창이 뜬다.


- 이 창을 뜨지 않도록 하려면 [소스코드 5]에 ExitProcess(0);을 추가해준다. 

- 이번에도 [소스코드 7 ]과 같은 방식으로 쉘코드를 만들면 된다.