본문 바로가기

3. Pwnable (포너블)

[2024.11.09]PWN PWN 해 5주차 활동

5주차는 드림핵의 System Hacking 로드맵에서 Bypass PIE & RELRO, Out of bounds, Format String Bug 강의를 공부했다.

https://dreamhack.io/lecture/roadmaps/2

 

System Hacking

시스템 해킹을 공부하기 위한 로드맵입니다.

dreamhack.io


 

Bypass PIE & RELRO

 

ELF : 실행 파일(Executable), 공유 오브젝트(Shared Object, SO)

실행 파일 : addr

공유 오브젝트 : libc.so

 

Position-Independent Code(PIC) : 메모리의 어느 주소에 적재되어도 코드의 의미가 훼손되지 않는 코드, 공유 오브젝트로 재배치(Relocation)가 가능하도록 설계.

 

Position-Independent Executable(PIE) : 무작위 주소에 매핑돼도 실행 가능한 실행 파일, ASLR 도입되기 전에는 실행 파일 무작위 주소에 매핑할 필요 X

 

PIE on ASLR : PIE는 재배치가 가능 -> ASLR이 적용된 시스템에서는 실행 파일도 무작위 주소에 적재 = ASLR이 적용되지 않은 시스템에서는 PIE가 적용된 바이너리더라도 무작위 주소에 적재되지 않는다.

 

PIE 우회

 

코드 베이스 구하기 : ASLR 환경에서 PIE가 적용된 바이너리는 실행될 때마다 다른 주소에 적재된다. 코드 영역의 가젯을 사용하거나, 데이터 영역에 접근하려면 바이널가 적재된 주소를 알아야하며, 코드 베이스를 구하려면 코드 영역의 임의 주소를 읽고, 그 주소에서 offset을 빼주어야 한다.

 

Partial Overwrite : ASLR의 특성 상, 코드 영역의 주소도 하위 12비트 값은 항상 같기 때문에, 코드 가젯의 주소가 반환 주소와 하위 한 바이트만 다르다면, 이 값만 덮어서 원하는 코드를 실행시킬 수 있다.

 

RELRO

 

Partial RELRO : RELRO를 부분적으로 적용, .init_array.fini_array에 대한 권한 부여가 제거된 경우 -> 두 부분을 나누어 쓰려면 공격을 수행하기가 어렵다.

 

.got : 읽기 전용

.got.plt : 읽기/쓰기 가능

 

Full RELRO : 가장 넓은 곳에 RELRO를 적용, .init_array, 능력이 있는 경우 -> .fini_array에도 .got권한이 제거

 

.got : 읽기 전용

.got.plt : 읽기 전용

 

RELRO 검사

: gccFull RELRO를 기본적으로 적용, PIE를 떠나면 Partial RELRO를 적용

 

Out of bounds

 

Memory Corruption

 

- 배열 : 요소(Element)들로 이루어짐

- 인덱스(Index) : 각 요소의 위치

- 배열의 길이 (Length) : 요소의 개수와 요소 자료형의 크기를 곱한 값

- 배열 각 요소의 주소 : 배열의 주소, 요소의 인덱스, 요소 자료형의 크기를 이용하여 계산

 

Out of Bounds : 요소를 참조할 때, 인덱스 값이 음수이거나 배열의 길이를 벗어날 때 발생, 개발자가 인덱스의 범위에 대한 검사를 명시적으로 프로그래밍하지 않으면, 프로세스는 앞서 배운 식을 따라 요소의 주소를 계산할 뿐, 계산한 주소가 배열의 범위 안에 있는지 검사하지 않는다.

 

OOB로 임의 주소 읽기 : 배열과 변수가 같은 세그먼트에 할당되어 있다면, 둘 사이의 오프셋은 항상 일정하므로 디버깅을 통해 쉽게 알아낼 수 있다. 만약 같은 세그먼트가 아니라면, 다른 취약점을 통해 두 변수의 주소를 구하고, 차이를 계산해야 한다.

- 전제 조건 : 읽으려는 변수와 배열의 오프셋을 알아야 한다.

 

OOB로 임의 주소 쓰기 : 해당 변수에 값을 직접 쓰는 부분이 없더라도 OOB 취약점이 있다면 디버거로 주소 확인 후 인데스 참조하여 변수 조작 가능

 

 

Format String Bug

 

포맷 스트링

 

포맷 스트링 구성 : %[parameter][flags][width][.precision][length][specifier]

 

형식 지정자(specifier) : 인자를 어떻게 사용할지 지정

 

 

Width : 최소 너비를 지정, 치환되는 문자열이 이 값보다 짧을 경우, 공백 문자(' ')를 문자열 앞에 패딩

 

Length : 출력하고자 하는 변수의 크기를 지정하며, d, n 등의 형식 지정자 앞에 쓰인다.

 

 

Parameter : 참조할 인자의 인덱스를 지정

 

포맷 스트링 버그(Format String Bug, FSB) : 포맷 스트링 함수를 잘못 사용하여 발생하는 버그. 포맷 스트링을 사용자가 직접 입력할 수 있을 때, 공격자는 레지스터와 스택을 읽을 수 있고, 임의 주소 읽기 및 쓰기를 할 수 있다.

 

- 임의 주소 읽기 : 스택에 어떤 메모리의 주소값이 적혀있다면, 해당 주소에 적혀있는 값을 파라미터 값을 활용해 읽어올 수 있다.

 

- 임의 주소 쓰기 : 포맷 스트링에 임의의 주소를 넣고, %[n]$n의 형식 지정자를 사용하면 그 주소에 데이터를 쓸 수 있다.