쓰기 권한이 불필요한 데이터 세그먼트에서 쓰기 권한을 제거합니다.
RELRO는 범위에 따라 부분적으로 적용하는 Partial RELRO와 가장 넓은 영역에 적용하는 Full RELRO가 있습니다.
// Name: relro.c
// Compile: gcc -o prelro relro.c -no-pie -fno-PIE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
FILE *fp;
char ch;
fp = fopen("/proc/self/maps", "r");
while (1) {
ch = fgetc(fp);
if (ch == EOF) break;
putchar(ch);
}
return 0;
}
kali@kali ~/dreamhack/Bypass_PIE_RELRO/RELRO gcc -o prelro relro.c -no-pie -fno-PIE
kali@kali ~/dreamhack/Bypass_PIE_RELRO/RELRO checksec prelro
[*] '/home/kali/dreamhack/Bypass_PIE_RELRO/RELRO/prelro'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
kali@kali ~/dreamhack/Bypass_PIE_RELRO/RELRO ./prelro
00400000-00401000 r--p 00000000 08:01 2633797 /home/kali/dreamhack/Bypass_PIE_RELRO/RELRO/prelro
00401000-00402000 r-xp 00001000 08:01 2633797 /home/kali/dreamhack/Bypass_PIE_RELRO/RELRO/prelro
00402000-00403000 r--p 00002000 08:01 2633797 /home/kali/dreamhack/Bypass_PIE_RELRO/RELRO/prelro
00403000-00404000 r--p 00002000 08:01 2633797 /home/kali/dreamhack/Bypass_PIE_RELRO/RELRO/prelro
00404000-00405000 rw-p 00003000 08:01 2633797 /home/kali/dreamhack/Bypass_PIE_RELRO/RELRO/prelro
01e6f000-01e90000 rw-p 00000000 00:00 0 [heap]
7f7f8ed69000-7f7f8ed6b000 rw-p 00000000 00:00 0
7f7f8ed6b000-7f7f8ed91000 r--p 00000000 08:01 1835719 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f7f8ed91000-7f7f8eee9000 r-xp 00026000 08:01 1835719 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f7f8eee9000-7f7f8ef35000 r--p 0017e000 08:01 1835719 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f7f8ef35000-7f7f8ef36000 ---p 001ca000 08:01 1835719 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f7f8ef36000-7f7f8ef39000 r--p 001ca000 08:01 1835719 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f7f8ef39000-7f7f8ef3c000 rw-p 001cd000 08:01 1835719 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f7f8ef3c000-7f7f8ef47000 rw-p 00000000 00:00 0
7f7f8ef5c000-7f7f8ef5d000 r--p 00000000 08:01 1835021 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7f7f8ef5d000-7f7f8ef81000 r-xp 00001000 08:01 1835021 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7f7f8ef81000-7f7f8ef8b000 r--p 00025000 08:01 1835021 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7f7f8ef8b000-7f7f8ef8d000 r--p 0002e000 08:01 1835021 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7f7f8ef8d000-7f7f8ef8f000 rw-p 00030000 08:01 1835021 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7ffc85840000-7ffc85861000 rw-p 00000000 00:00 0 [stack]
7ffc859d9000-7ffc859dd000 r--p 00000000 00:00 0 [vvar]
7ffc859dd000-7ffc859df000 r-xp 00000000 00:00 0 [vdso]
쓰기 권한이 할당되어 있는 주소를 찾아보면 0x404000-0x405000에 쓰기 권한이 할당되어 있습니다.
kali@kali ~/dreamhack/Bypass_PIE_RELRO/RELRO objdump -h ./prelro
./prelro: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
22 .got.plt 00000030 0000000000404000 0000000000404000 00003000 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .data 00000010 0000000000404030 0000000000404030 00003030 2**3
CONTENTS, ALLOC, LOAD, DATA
24 .bss 00000008 0000000000404040 0000000000404040 00003040 2**0
ALLOC
이 주소에 해당하는 영역을 봐보면 .got.plt와 .data와 .bss 영역이 있습니다.
여기서 .got.plt 영역과 .got 영역의 차이점을 봐보면
.got 영역의 경우 프로그램이 실행되는 시점에 바인딩되는 변수가 위치합니다. 그래서 바이너리가 실행 중일 때는 더 이상 바인딩을 하지 않기 때문에 쓰기 권한을 부여하지 않습니다.
하지만 .got.plt 영역은 실행 중에 바인딩되는 변수가 위치합니다. 그래서 쓰기 권한이 남아 있습니다.
Partial RELRO가 적용된 바이너리에서 대부분의 함수들의 GOT 엔트리는 .got.plt에 저장됩니다.
kali@kali ~/dreamhack/Bypass_PIE_RELRO/RELRO gcc -o frelro relro.c -z now
kali@kali ~/dreamhack/Bypass_PIE_RELRO/RELRO checksec frelro
[*] '/home/kali/dreamhack/Bypass_PIE_RELRO/RELRO/frelro'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Full RELRO가 적용되면 모든 라이브러리 함수들의 주소가 바이너리 로딩 시점에 바인딩됩니다. 그래서 GOT에는 쓰기 권한이 없고 data와 bss에만 쓰기 권한이 있습니다.
Parital RELRO의 경우 .got.plt 영역에 대한 쓰기 권한이 존재해서 GOT Overwrite를 할 수 있습니다.
Full RELRO의 경우 .got 영역에 대한 쓰기 권한이 없어서 GOT Overwrite를 할 수 없습니다.
하지만 hook 함수의 경우 Full RELRO 방어기법이 걸려 있어도 주소를 덮어쓸 수 있습니다.
대표적인 hook 함수인 malloc hook이나 free hook을 이용해서 Hook Overwrite를 하면 Full RELRO 상황에서도 프로그램의 실행 흐름을 조작할 수 있습니다.