SH1R0_HACKER
stack 1 본문
Stack1.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
modified = 0;
gets(buffer);
if(modified != 0) {
printf("you have changed the 'modified' variable\n");
} else {
printf("Try again?\n");
}
}
코드를 분석해보면 main 함수에 argc와 argv가 전달된다.
사용자로부터 argument를 받아서 실해오디는 루틴을 가지고 있다.
modified를 0x61626364로 바꿔버리면 우리가 원하는 답을 얻을 수 있을 것 같다.
[ 취약점 파악 및 분석 ]
아래는 어셈블리어 해석이다.
0x08048464 <+0>: push ebp
0x08048465 <+1>: mov ebp,esp
0x08048467 <+3>: and esp,0xfffffff0
//새로운 스택 프레임 생성
0x0804846a <+6>: sub esp,0x60
//main 함수가 사용할 스택공간을 0x60만큼 할당
0x0804846d <+9>: cmp DWORD PTR [ebp+0x8],0x1
//[ebp+0x8]과 0x1을 비교한다.
//argc 변수 : ebp+0x8
0x08048471 <+13>: jne 0x8048487 <main+35>
//Jump not Equal 같지 않다면 main+35로 이동한다.
0x08048473 <+15>: mov DWORD PTR [esp+0x4],0x80485a0
// "please specify an argument"
0x0804847b <+23>: mov DWORD PTR [esp],0x1
0x08048482 <+30>: call 0x8048388 <errx@plt>
0x08048487 <+35>: mov DWORD PTR [esp+0x5c],0x0
// [esp+0x5c]에 0을 저장한다.
// modified 변수 : esp+0x5c
0x0804848f <+43>: mov eax,DWORD PTR [ebp+0xc]
// argv[1] : ebp+0xc
0x08048492 <+46>: add eax,0x4
0x08048495 <+49>: mov eax,DWORD PTR [eax]
0x08048497 <+51>: mov DWORD PTR [esp+0x4],eax
0x0804849b <+55>: lea eax,[esp+0x1c]
// buffer 변수 : esp+0x1c
0x0804849f <+59>: mov DWORD PTR [esp],eax
0x080484a2 <+62>: call 0x8048368 <strcpy@plt>
0x080484a7 <+67>: mov eax,DWORD PTR [esp+0x5c]
// eax 레지스터에 modified 변수[esp+0x5c]를 전달한다.
0x080484ab <+71>: cmp eax,0x61626364
0x080484b0 <+76>: jne 0x80484c0 <main+92>
0x080484b2 <+78>: mov DWORD PTR [esp],0x80485bc
// "you have correctly got the variable to the right value"
0x080484b9 <+85>: call 0x8048398 <puts@plt>
0x080484be <+90>: jmp 0x80484d5 <main+113>
0x080484c0 <+92>: mov edx,DWORD PTR [esp+0x5c]
0x080484c4 <+96>: mov eax,0x80485f3
// "Try again, you got 0x%08x\n"
0x080484c9 <+101>: mov DWORD PTR [esp+0x4],edx
0x080484cd <+105>: mov DWORD PTR [esp],eax
0x080484d0 <+108>: call 0x8048378 <printf@plt>
0x080484d5 <+113>: leave
0x080484d6 <+114>: ret
argv의 위치 : ebp+0x8
modified의 위치 : esp+0x5c
buffer의 위치 : esp+0x1c
stack0와 비슷하게 buffer을 가득 채우고 modified를 0x61626364로 수정하면 될 것 같다.
HEX to String을 이용하면
0x61 = a
0x62 = b
0x63 = c
0x64 = d
라는 사실을 알 수 있다.
[ Exploit ]
buffer을 다 채우기 위해 64개의 문자열 A를 생성한다.
그 뒤에 modified 변수에 들어갈 abcd를 입력해준다.
그런데 Try again, you got 0x64636261 이라는 메시지가 보인다.
우리가 넣어야 할 값은 0x61626364인데 거꾸로 입력이 된 모습을 볼 수 있다.
그 이유는 인텔 CPU가 메모리를 읽고 쓰는 바이트 오더가 리틀엔디안 방식이기 때문이다.
바이트오더에는 두 가지 방식이 있다.
리틀 엔디안 (Little Endian) : 메모리 시작 주소가 하위 바이트부터 기록된다. intel 프로세서 계열에서 사용한다.
빅 엔디안 (Big Endian) : 메모리 시작 주소가 상위 바이트부터 기록된다. 주로 UNIX, 시스템인 RISC 프로세서 계열에서 사용한다.
결국 abcd를 그대로 입력하는 것이 아니라 역순으로 입력해야된다.