brunch

You can make anything
by writing

C.S.Lewis

by Younggi Seo Jun 01. 2018

Section 7-2: RCE testing

우분투 리눅스 10.04.4 LTS에서 버퍼오퍼플로 취약점 점검

이번 섹션을 마지막으로 버퍼오버플로우 버그를 일으키는 실습을 정리하면서 어떻게 레지스터와 스택이라는 메모리 공간을 통해 공격자의 코드를 실행시킬 수 있는지 확인해보겠다. 화이트 해커가 공격을 할 수 있는 것은 그러한 공격을 단순히 따라 하는데 그치지 않고 같은 원리의 다른 공격을 유추할 수 있는 능력까지 키우는 게 목적이라는 게 본인의 생각이다.


예상할 수 있는 공격뿐만 아니라 예상치 못한 공격에 대한 대응을 할 수 있는 것이 보안이 공격을 앞설 수 있는 유일한 방법이다.



앞선 섹션에서 디버그 단에서 메모리 상태를 확인하는 명령을 치면 아래와 같은 화면이 나온다(명령어 x/16xw $rsp).

콜론 좌측 메모리는 '레지스터'의 레이블, 우측 메모리 '스택'은 그 레이블에 저장되는 메모리 데이터의 번지수.


32비트로 치면 ESP(Extended Stack Pointer)라는 메모리 끝단인 0x7ffffffffe2c0부터 0x7fffffffe2f0까지 4개의 레지스터 레이블에 해당하는 메모리 데이터를 보여준다. 앞서서 run 'AAAA' 명령어를 입력한 후 이미 설정되어 있는 첫 번째 중단점까지의 메모리 내용이다. 첫 번째 중단점은 main() 함수 내부의 function() 함수를 호출하기 전의 행인 15행이었고 이 중단점까지의 메모리 내용은 곧 main() 함수가 사용한 스택 메모리 번지값들이다.


이번에는 명령어 'x/1xw $rbp' 명령어로 다음과 같은 메모리 내용을 확인해보자.

하단의 $rbp로 확인한 메모리의 시작점인 RBP 레지스터부터 메모리 데이터 1개를 출력해서 끝단인 RSP 레지스터까지의 범위를 확인가능.


위의 결과를 통해 %ESP 레지스터에 해당하는 메모리 데이터는 아래 네 개의 번지와 같다.

main() 함수의 스택 메모리 데이터.


한번 더 continue 명령을 입력해서 다음 중단점(10행)인 function() 함수가 호출되어 함수 내부의 strcpy 함수가 실행되기 직전까지의 메모리 데이터를 출력한다. 그래서 다시 'x/16xw $rsp' 명령과 'x/1xw $rbp' 명령으로 %ESP와 %EBP의 레지스터를 출력해서 다음 function() 함수 부분의 메모리 데이터를 확인해본다.

function() 함수의 메모리 데이터 확인 후 main() 함수의 디스에셈블 명령을 실행한 화면.
function() 함수의 %ESP 스택 메모리 데이터.


function() 함수의 %ESP 레지스터 구간은 위의 출력 화면과 같으며 그 위의 화면에서 'disass(embly) main' 명령을 통해 main() 함수의 어셈블리 과정을 확인할 수 있다. 여기서 알아야 할 포인트function() 함수(화면에서 <function1> )를 호출하는 지점에서 다음 main() 함수로 jump 해야 할 주소(RETurn addr.)가 '0x00...04005b7'이라는 것이다.


스택 메모리의 논리적 흐름을 앞선 섹션들에서 말했듯이 caller 함수인 main() 함수에서 function() 함수를 호출하면 function() 함수 내부의 루틴을 실행하기 전에, 먼저 루틴 수행 후 복귀해야 할 주소(RETurn addr.)를 %edi(extended destination index: 목적지 인덱스) 레지스터로 push 한다. 그래서 'callq 0x400574 <function1>'이라는 어셈블리 호출 명령 다음에 바로 복귀 주소인 메모리 번지가 나와야 하고 그 번지를 로딩하는 레지스터는 %edi다.

 

노란색 마킹한 부분은 필자가 'AAAA'가 들어가는 메모리 번지로 착오한 부분이므로 공격을 위한 전개 내용과 관계없다.


한 번 더 continue 명령을 입력하면 다음번 중단점(11행)까지 실행되면서 strcpy 함수가 실행되고 난 이후의 function() 함수의 바뀐 메모리 데이터를 확인할 수 있다. 위의 그림에서 '0x004005b7' 메모리 데이터의 복귀 주소가 그대로 있다는 것을 알 수 있고, '0x41414141' 부분(본래 0xf7a69aa8 메모리 데이터 '주소값'에 덮어씀)이 문자열 'AAAA'가 메모리에 기록된 부분이라는 것도 판단할 수 있다. A는 16진수로 ‘41’이므로 바뀐 부분을 눈으로 찾으며 되고, 뒤따라나오는 본래의 '0x00007fff'이었던 메모리 부분이 '0x00007f00'으로 바뀐 것으로 보아 'AAAA' 글자 뒤에 공백 1 byte(개행문자 : Carrige Return Line Feed)가 뒤따라와서 ‘00’으로 바뀌었다는 것도 확인할 수 있다.



이제 gdb 명령 단에서 처음과 달리 다음과 같은 명령으로 시작해본다.

run $(perl -e 'print "A" x 30')이라는 명령은 펄(perl)이라는 스크립트 언어를 이용해서 실행하는 구문으로 A라는 문자를 30번 출력해라는 명령이다. 이후에 continue 명령을 계속 입력하면서 명령어 실행 후 중간에 끊기지 않고 계속 실행할 수 있게 하기 위해 펄을 이용한다.


function() 함수의 $rbp 레지스터 0x7f~fe290에 0xffffe2b0이 로딩되기 전의 메모리 데이터 부분에서 확인가능한 RETurn 주소의 번지 0x004005b7

다음 continue 명령을 통해 function() 함수의 메모리 구조까지 다시 살펴보고 앞서 복귀 주소로 확인한 부분을 위의 출력 화면을 통해 다시 확인할 수 있다(RET으로 표시한 부분).


이제 흥미로운 부분이 나온다. 한번 더 continue 명령을 입력하면 이전과 달리 ‘41’로 시작하는 메모리 데이터 부분이 훨씬 더 많이 발생한 것을 확인할 수 있다. 정확히 '41'이 30개 더 메모리 데이터들을 덮어썼다는 것을 알 수 있다. 이제 공격을 위한 소스 코드의 주소를 몇 번째 이후에 입력해야 할지 정확히 계산해야 할 시점이다.  

본래의 복귀주소 자리인 RET 부분까지 덮어써서 복귀주소의 내용까지도 41414141로 바뀐 것이 확인가능하다.


Section 7-3: RCE testing 에서 이어짐.


매거진의 이전글 Section 7-1: RCE testing
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari