반응형


원문 주소 : http://geundi.tistory.com/120


아직 퍼와도 된다는 허락은 못맡았지만.. 댓글을 달아놨습니다..

안된다면.. 내려야죠 (+__)ㅋ

해당 메모리를 계산할 수 있을 거라고 생각을 못했었는데....

물론 오래된 버전 내용이라 지금은 그냥 공부용이지만.. 한번 정도 보시는 것도 좋을 듯 합니다.


시스템 해킹기법으로 널리 알려진 해킹기법중의 하나인 버퍼오버플로우를 이용하는데에 있어 함수의 리턴주소가 저장되어 있는 곳을 정확히 알아야 합니다.

함수의 리턴어드레스가 저장되어 있는 곳을 약간이나마 추측할 수 있는 방법을 써보고자 합니다.

다 들 아시는 부분이겠지만 gcc 2.96이상 버전으로 컴파일을 하게 되면, 버퍼오버플로우를 하는데 ret의 위치까지의 거리를 계산하는데 어려움이 있습니다. 메모리에 버퍼를 위한 공간이 할당되는데 각 버퍼 다음에 그 크기가 일정하지 않는 공간이 잡히기(이하 쓰레기) 때문입니다.

그런데 이 쓰레기의 크기가 정해지는 것에 있어서 다소 규칙성을 발견하 여 작성한 글(글 마지막에 참고문서 링크)이 있어 그 글을 기초로 하여  정리를 해보려 합니다.


1. 버퍼가 하나인 경우 

main()
{
 buffer1[ ];

}

이러한 소스를 가진 프로그램이 수행되면 스택은 다음과 같을 것입니다.


                 /-------------------------/  메모리의 높은 숫자의 주소
                 |         ret             |      
                 /-----------------/
                 |         sfb            |
                 /-----------------/
                 |                         |
                 |      쓰레기1        |
                 |                         |
                 /-----------------/
                 |                         |
                 |      쓰레기2        |
                 |                         |
                 /-----------------/
                 |                         |
                 |       buffer1        |
                 |                         |
                 /-------------------------/   메모리의  낮은 숫자의 주소


쓰레기1buffer1의 크기9바이트 이상인 경우에만 8바이트로써 일정하게 생깁니다. 9바이트 미만인 경우에는 생기지 않거나 생기는 그 크기가 뷸규칙 합니다.

쓰레기2 buffer1의 크기9바이트 이상인 경우 "buffer1의 시작주소 와 쓰레기1의 시작 주소간의 거리는 16배수로 단위로 되는 규칙성"이 있습니다.

9 <=  buffer1  <=16  :  buffer1시작주소와 쓰레기1의 시작주소의 차이는 16
16 <  buffer1  <=32   :   32
32 <  buffer1  <=48   :   48
48 <  buffer1  <=64   :   64
~
~


이러한 나름의 규칙성을 가지고 buffer1이 9바이트 이상의 크기로 선언이 되었다면 다음과 같이 ret까지의 거리를 계산할 수 있습니다.

(buffer1 시작주소와 에서 쓰레기1 시작주소의 거리) + (쓰레기1 8바이트) + (sfb 4바이트)
= buffer1의 시작주소에서 ret시작 주소까지의 거리

따라서 버퍼오버플로우를 할 때 쉘코드주소가 들어가야 할 자리를 정확히 잡아주는데 큰 도움이 될 것입니다.


2. 버퍼가 2개인 경우

버퍼가 2개인 경우에는 버퍼 2개의 크기를 모두 고려해야합니다.

main()
{
 buffer1[ ];
   buffer2[ ];
}

이러한 소스를 가진 프로그램이 수행되면 스택은 다음과 같을 것입니다.


                 /-------------------------/  메모리의 높은 숫자의 주소
                 |         ret             |      
                 /-----------------/
                 |         sfb            |
                 /-----------------/
                 |                         |
                 |      쓰레기1        | 
                 |                         |
                 /-----------------/
                 |                         |
                 |      쓰레기2        |
                 |                         |
                 /-----------------/
                 |                         |
                 |       buffer1        |
                 |                         |
                 /-----------------/
                 |                         |
                 |      쓰레기3        | 
                 |                         |
                 /-----------------/
                 |                         |
                 |       buffer2        |
                 |                         |
                 /-------------------------/   메모리의  낮은 숫자의 주소



우리는 이제 쓰레기3의 크기를 구하여야 하는데,
쓰레기3의 크기를 구하는데 있어서는 buffer2의 시작주소와 buffer1의 시작주소의 거리를 계산하는 방식으로 이루어 집니다.

buffer2와 buffer1의 거리에 대한 규칙성은 다음 그림을 참고하시면 될것 같습니다.
(그 림에서는 제가 여태 설명해오던 buffer1과 buffer2가 바뀌어져 있으니 참고하시기 바랍니다)

사용자 삽입 이미지
<그림출처 : http://hackerleon.cybersoldier.net/images/gcc296.jpg>

그림에 약간의 보충 설명을 덧붙이도록 하겠습니다.

버퍼가 2개인 경우에는 각각의 버퍼의 크기가 9이상이면 buffer2에서 buffer1까지의 거리에 일정한 규칙성을 가지게 됩니다.(그림을 보시면 아시겠지만 반드시 2개의 버퍼 각각의 크기가 모두 9이상이어야 합니다, 둘중에 하나라도 9바이트 미만이면 규칙성은 없습니다.)

(buffer1 의 크기가 9이상이고, buffer2의 크기도 9이상인 경우)
9 <=  buffer1 <= 16  : buffer1와 buffer2의 거리는 16
16 < buffer1  <= 32    :  32
32 < buffer1  <= 48   :   48
48 < buffer1  <= 64   :   64
~
~

이제 우리는 buffer2에서 buffer1까지의 거리를 알 수 있습니다. 그리고 buffer1 에서 ret까지의 거리는 앞서서 설명했던 버퍼가 하나인 경우와 같으므로, buffer2에서 ret까지의 거리를 구할 수 있게 되었습니다.

따라서 buffer2에서 ret까지의 거리는 다음과 같을 것입니다.

(buffer2에서 buffer1까지의 거리) + (buffer1에서 쓰레기1까지의 거리) + (쓰레기1 8바이트) + (sfb 4바이트)


3. 변수에 메모리를 할당하는 단위가 바뀐 것인가?

저는 여태 리눅스는 변수에 메모리를 할당할 때에 1워드 즉, 4바이트를 단위로 한다고 알고 있었습니다.

그런데 어떤 글을 보니 이러한 해석이 있더군요.

 gcc 2.96이상 버전에서는 8바이트 이하의 변수에는 1워드 단위로 할당을 하지만, 8바 이트를 초과하는 즉, 9바이트 이상의 변수에는 4워드(16바이트)단위로 할당을 한다는 것입니다.

만약 이러한 해석에 기초를 둔다면 각 버퍼가 9바이트 이상인 경우에 발견되었던 16바이트 단위의 그 규칙성에 대한 설명이 가능해집니다. 그런데 앞에서 제가 쓰레기라고 우겼던 것들은 쓰레기가 아닌게 되겠습니다. 당연히 변수에 할당된 메모리의 공간이라고 생각되기 때문입니다.

그러나 버퍼의 크기가 8바이트 이하인 경우에는 이 해석이 맞지 않는것 같습니다. 위의 그림에서 8바이트 이하인 경우에 두 버퍼간의 거리를 보시면 알 수 있는 부분입니다.

이러한 규칙성을 어떻게 받아들여야할지는 좀더 공부를 해보거나 자기가 편한대로 해야겠습니다.

또한 이러한 규칙성은 gcc버전에 따라 다를수도 있으니 이점 염두해주시기 바랍니다.

수고하셨습니다.

반응형

+ Recent posts