brunch

You can make anything
by writing

C.S.Lewis

by Younggi Seo Apr 29. 2018

Section2: malicious RCE attack

재밌으면 그냥 하고 미칠 정도로 파고들어서 끝장낸다(Overflowed)

세계의 모든 인터넷 웹 페이지들의 56.43%(두 번째가 독일어로 7.72%)가 영어로 이루어져 있다는 통계를 봤다(Lobachev, 2008). 그중 한국어는 1.52%밖에 안되니 사실 IT 분야에서 최신 경향의 소통이 이루어지는 소스의 99%는 영어와 기타 언어로 유통되는 셈이고 다시 한국어로 번역하여 출판되면 그제야 한국의 IT 업종에서 그 시류를 쫓는 형국이다. 우리가 IT 분야의 선두주자가 되려면 먼저 IT에 쓰이는 영어 표현 자체를 익숙하게 받아들이는 의식 구조를 가장 빨리 만드는 게 필요하다고 생각한다.



어차피 IT 분야뿐만 아니라 학문의 최전선 연구분야를 보고하는 논문에서도 영어가 56.43% 이상을 차지하면 영어라는 외국어의 모양새에만 혈안이 되어있는 한국의 세태를 비판할 시간도 아깝다. 그냥 그 학문을 연구하는 것 자체에 모국어, 외국어 따질 거 없이 빠지면 그 학문에서의 주류 언어는 시간이 흐르면 터득할 수밖에 없다. 그 분야가 자신이 좋아하는 분야라면 언어 자체에 얽매이지 않고 말의 쓰임새에 주목하므로 알아듣는 거, 읽는 거, 쓰는 것을 마치 언어교육이 먼저 전제되어야 한다는 한국식 영어 교육에서 벗어나는 것은 시간문제다.



잠깐 해킹 개념을 파헤치기 전에 옆길로 샜는데, 언어교육에 있어 강조하고 싶은 점은 외국 유학을 다녀온 사람이라고 유학한 국가의 언어 구사를 다 잘한다는 게 아니라는 말이다. 한국에서 국문학을 전공했다고 한국어를 방송국 아나운서처럼 용법에 맞게 구사할 수 있다고 말할 수 없듯이 거꾸로 특정 외국어 평가 점수 하나가 그 사람이 전문분야에서도 해당 외국어의 활용새가 높을 거라고 예단시키는 사회적 편견은 일찍 감치 타파하여야 한다.




컴퓨터의 초기 시스템 구조 중 특히 레지스터(CPU)가 이용하는 메인 메모리의 논리적 프레임(Heap and Stack Frame) 취약점을 이용한 '버퍼 오버플로우'라는 버그는 왜 발생했을까?
     


이 공격이 성공하면 자신이 만든 악성코드를 실행시킬 수 있게 되고 결국 어떤 식으로든 소기의 목적(관리자 권한을 탈취하려는 시스템에서 돈이 되는 정보를 획득)을 달성할 수 있게 된다. 아래의 한 대학의 강좌 캡처를 참조하면 메모리 구조에서 Stack 부분과 Dynamic Data 부분 사이가 바로 여분의 공간인 버퍼(임시적인 메모리 공간)이다. 그리고 여기에 데이터가 넘치면 오버 플로우이다. 합쳐서 'Buffer Overflow'이다.


Cited from University of Washington


CPU 내부의 레지스터가 자신에게 저장시키는 프로그램 처리의 기본단위(Procedure)의 주소(Label)를 통해 오고 가는 프로그램의 데이터를 연산 및 제어할 수 있도록 메인 메모리(DRAM 혹은 자주 쓰이는 데이터인 경우는 SRAM에 캐시 형태로 저장되어)를 이용할 수 있도록 지시한다. 레지스터(총 16~80bit에 불과하고 현재는 32bit나 64bit)는 자신에게 저장한 주소(Label)를 참조해서 메인 메모리에 연산한 값을 출력 곧 저장(Write)시키거나 복잡한 연산인 경우(Windows 시스템은 다중 프로세스를 구동하는 환경이므로) 다시 부르기(Read) 위해 값을 호출(Call)하는 과정을 반복한다.  



개발자가 작성한 프로그램의 소소코드를 컴파일러(Compiler, 번역기)에 의해 CPU가 해석할 수 있도록 기계어(Native Code)로 번역된 뒤에는 레지스터와 메인 메모리 사이에서 결괏값을 내기 위한 인자(Argument)를 투입하고 결괏값을 반환하기 위해 쓰인 레지스터의 주소(RETurn address: Label)를 받는다. 이때 쓰이는 가상의 논리적 구조인 다이어그램(캡처 화면)에서 'Stack'과 'Dynamic Data(Heap)'라는 프레임*이 있고, 이 사이 공간(아랫부분의 힙 메모리가 요구한 메모리 할당량)인 'Buffer'가 동영상 강좌에 의하면, 'overfilled', 'running into each other(스택과 힙)', potentially overwriting(덮어쓰기)가 가능하므로 소스 코드에서 "too much other data at one time"이 벌어지면 이 버퍼의 범위를 벗어나서(out of memory) 오작동을 일으킨다.



'오작동'의 내용을 알려면 레지스터의 종류를 알아야 한다. 레지스터는 CPU가 접근할 수 있는 메모리 중에서 가장 빠르게 동작하는 메모리로 CPU가 여러 가지 연산 및 제어 등의 처리를 하는 동안 필요한 임시적인 데이터들을 보관하는 데 사용된다. 반면에 메인 메모리는 바이트 단위로 매겨진 번지 혹은 주소를 이용해서 접근할 위치를 구분하는데, 레지스터의 경우는 번지의 개념이 없고 모두 '고유한 이름(Label)'이 부여(이를테면 0x8048b69 16진수)되어 있다.



데이터 저장과 실행을 위해 메인 메모리에서 대표적으로 작동하는 레지스터로는 ESP(32bit 컴퓨터인 경우, 'Extended'의 E가 접두어이며, 64bit인 경우는 앞에 'R'이 붙음), EIP, EAX가 있다. ESP(Extended Stack Pointer)는 하나의 스택 프레임(메인 메모리의 단위, 아래 동영상 커버 페이지에서 사각형 한 개에 해당)의 끝 지점 주소를 저장한다. EIP(Extended Instruction Pointer)라는 명령어 포인터 레지스터는 다음에 실행해야 할 명령어의 주소(RETurn Address)를 EAX 레지스터에 저장한다. 데이터의 논리 연산을 수행하고 함수(Routine)의 반환값을 저장하는 용도가 EAX(Extended Accumulator, 확장된 연산) 레지스터이다.



https://youtu.be/XbZQ-EonR_I


동영상 강좌 내용을 따라오면 %eip 레지스터(왜 앞에 %를 붙여서 레지스터명을 표현하는지는 구글링 중)에 의해 프로그램의 첫 Procedure(절차)가 시작한다. 그리고 %esp(스택 포인터) 레지스터에 메인 메모리의 Address(번지)인 0x108을 기록(레지스터에 임시저장)하고 그 주소에 해당하는 위쪽 메인 메모리 자리(할당한 힙 영역에 스택이 동시에 동작함)에 123이라는 인자값(Argument)을 저장했다. 이 인자값이 스택**화 되어 하나의 Procedure(실행 중인 프로그램의 Function)에 투입(push)한다. EIP 레지스터( 레지스터의 주소명('0x8048553'이라는 16진수)을 먼저 기록해 놓고(함수를 실행하고 돌아가기 위한 본래의 메인 메모리의 번지를 저장해놓고), %eax에서 호출된 함수의 주소 ‘0x8048b90’가 저장되어있는 %eip 레지스터에 컴파일러에 의해 계산된 %eip 레지스터 레이블('0x8048553')을 밀어 넣는다(push and replaced. = ‘displacement’ ).



이 지점나중에 버퍼오버플로우 공격을 실제로 수행할 때 필요한 CPU 아키텍처의 컴파일러 과정이다. 아키텍처 종류에 따라 다르지만 인텔의 IA(Intel Architecture)-32 같은 경우는 리틀 엔디안(Little Endian)***이라는 계산과정을 거쳐서 복귀 주소를 저장하는 레지스터의 레이블을 생성하는데, 이 레이블을 리틀 엔디안 방식으로 리눅스에 기본 내장된 펄(Perl)이라는 언어를 이용하여 공격 명령어의 주소로 바꿔버리면 프로시저 복귀 주소 자리를 해커가 원하는 리턴 주소로 변경할 수 있기 때문이다.



다시 말해 레지스터의 오른편에 'call'의 레지스터 주소에서 callee의 수행이 끝나면 복귀 주소('0x8048553')를 맨 하단의 %eip 자리에 다시 덮어쓰는('replaced memory') 과정을 동영상 막바지의 세 번째 스택 프레임 구조에서 설명해주는데, 이때 덮어쓰는 레지스터 값(RETurn Address)이 컴파일러에 의해 정확하게 계산된 재배치 정보(인텔 아키텍처의 경우 리틀 엔디안으로 재배치된 레이블)가 아니라 해커의 악성코드가 주입된 주소라면 프로그램의 'Next address after call' '악성코드의 Procedure' 바뀔  있다. 그러면 해커가 의도한 시스템 권한 탈취나 바이러스가 심어져 있는 악성코드(Malware code)가 실행될 수 있고, 이것을 '버퍼 오버플로 공격'이라고 부른다.




Figure 13-3. The processor registers and runtime memory layout

cited from etutorials.org

  

또 다른 사이트에서 캡처한 'Stack Layout'이다. 여기서는 'EBP(Extended Base Pointer)'라는 레지스터의 종류가 한 개 더 보인다. 그리고 '레이아웃'이라는 의미가 어떤 계층을 펼치다는 의미로 CPU가 하나의 프로그램을 실행할 때(Processing) 펼쳐지는 구조의 순서는 일반적인 동작 순서(스택이 아래에서 LIFO 순의 처리절차)를 따르나(Convention) 레이아웃은 가상의 논리적인 조감도의 한 예이다.



스택 프레임을 통해 ‘버퍼 오버 플로'라는 버그의 유형을 살펴보았고, 같은 원리이지만 다르게 응용한 형식 지정 문자열(Format String) 공격에 대해서도 그림에서 언급한 사이트의 레퍼런스를 통해 알 수 있다.


Section 7에서 리눅스 시스템에서 버퍼 오버플로우 공격을 실제로 진행한 과정을 확인할 수 있다.




* 프레임 : 보조기억장치(HDD)에서 '실행 중인 프로그램(Process)'이 메인 메모리(SRAM일 경우 가상 메모리 영역)로 한 번에 가져올 수 있는(Swap-in) 페이지의 묶음 단위도 '프레임(Frame)'임.


** Stack의 본래 의미는 '건초더미'라는 뜻으로 이 건초를 밑에서 차례로 쌓는다는 의미에서 stack이라는 동사적 뉘앙스를 사용함. 그리고 논문도 하나의 stack이라고도 호칭함.


*** 리틀 엔디안을 구하는 방식은 동영상 강좌와 같이 4 byte의 메모리 데이터(32bit인 경우 8자리의 16진수 word)에서 2자리씩 뒤로 보내서 거꾸로 쓰면 된다. 일례로 '0xf4830408'이라는 데이터라면 "\x08\x04\x83\xf4"로 입력해야지 컴파일 시 '0xf4830408'로 뒤바뀌어 연산이 된다. 반면 '빅 엔디안'은 가장 높은 자리가 가장 먼저 저장되어야 함.



Cited

1) Lobachev, S. (n.d.). Top languages in global information production. Retrieved from https://journal.lib.uoguelph.ca/index.php/perj/article/view/826/1358#. WuVA6 oiFPgk


2) 13.4 Classic Buffer-Overflow Vulnerabilities. (n.d.). Retrieved from http://etutorials.org/Networking/network security assessment/Chapter 13. Application-Level Risks/13.4 Classic Buffer-Overflow Vulnerabilities/

매거진의 이전글 Section1: malicious RCE attack
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari