- ASLR
Address Space Layout Randomization(ASLR)은 라이브러리, 힙, 스택 영역 등의 주소를 바이너리가 실행될 때마다 랜덤하게 바꿔 RTL과 같이 정해진 주소를 이용한 공격을 막기 위한 보호 기법이다.
Ubuntu 16.04에서는 /proc/sys/kernel/randomize_va_space 파일의 값을 확인하면 서버의 ASLR 설정 여부를 알 수 있다. 설정 파일의 값으로는 0, 1, 2가 있고, 각 값은 아래와 같은 의미를 갖는다.
0 : ASLR을 적용하지 않음
1 : 스택, 힙 메모리를 랜덤화
2 : 스택, 힙, 라이브러리 메모리를 랜덤화
만약 해당 파일의 값이 2가 아니라면, 루트 권한으로 다음 명령어를 실행하여 ASLR 보호 기법을 적용할 수 있다.
# cat /proc/sys/kernel/randomize_va_space
0
# echo 2 > /proc/sys/kernel/randomize_va_space
# cat /proc/sys/kernel/randomize_va_space
2
#
다음은 example3.c 예제이다.
//example3.c
//gcc -o example3 example3.c -m32
#include <stdio.h>
#include <stdlib.h>
int main(void){
char * buf = (char *)calloc(1, 4096);
FILE * fp = 0;
size_t sz = 0;
fp = fopen("/proc/self/maps", "r");
sz = fread(buf, 1, 4096, fp);
fwrite(buf, 1, sz, stdout);
}
example3.c는 프로세스 자신의 메모리 맵을 읽어 출력해주는 코드이다. 서버에 ASLR이 켜져있을 때, 라이브러리, 힙, 스택 영역의 주소가 랜덤하게 바뀐다. 라이브러리 주소가 계속 바뀌기 때문에 스택 버퍼 오버플로우 취약점을 공격할 때 정적 주소를 이용한 공격을 사용할 수 없다. 하지만 바이너리 코드 영역의 주소는 변하지 않는다. 이를 이용해 ASLR 보호 기법을 우회하여 익스플로잇할 수 있다.
아래는 예제는 example4.c이다.
//gcc -o example4 example4.c -fno-stack-protector -mpreferred-stack-boundary=2 -m32
#include <stdio.h>
int main(void){
char buf[32] = {};
puts("Hello World!");
puts("Hello ASLR!");
scanf("%s", buf);
return 0;
}
scanf 함수로 입력 길이의 제한 없이 배열 buf에 데이터를 입력 받으므로 스택 버퍼 오버플로우 취약점이 발생한다. 최종 목표는 /bin/sh 바이너리를 실행하는 것이다. 우선 바이너리에 NX bit가 적용되어 있는지 확인한다.
확인결과 스택 메모리의 권한이 RW로 되어있다. NX bit가 적용되어 있는 것을 확인했고, ASLR 보호 기법도 적용되어 있기 때문에 이전과 같은 공격 방식은 통하지 않는다.
NX bit가 걸려 있으므로, 프로그램이 비정상 종료하지 않기 위해서는 ret을 실행 권한이 있는 코드 영역으로 덮어야 한다. 그리고 ASLR 보호 기법으로 라이브러리, 힙, 스택 메모리 주소는 랜덤으로 변하지만 바이너리의 코드나 데이터 영역들의 주소는 변하지 않는다. 이점을 유의하며 다음 PLT와 GOT 설명을 보도록 하자.
- PLT & GOT Section
Procedure Linkage Table(PLT)는 외부 라이브러리 함수를 사용할 수 있도록 주소를 연결해주는 역할을 하는 테이블이다.
Global Offset Table(GOT)는 PLT에서 호출하는 resolve 함수를 통해 구한 라이브러리 함수의 절대 주소가 저장되어 있는 테이블이다.
ASLR이 적용되어 있는 환경에서, 동적으로 라이브러리를 링크하여 실행되는 바이너리(Dynamically linked binary)는 바이너리가 실행될 때마다 라이브러리가 매핑되는 메모리의 주소가 변한다. 때문에 Dynamically linked binary의 경우 바이너리가 실행되기 전까지 라이브러리 함수의 주소를 알 수 없지만, 정적 주소를 통해 해당 함수의 PLT와 GOT 영역에 접근하면 함수의 주소를 찾을 수 있다.
디버깅을 통해 puts 함수를 호출했을 때 PLT에서 어떤 일을 하는지 확인해봤다.
puts@plt+0에서는 0x804a00c 메모리를 참조하여 저장되어있는 값으로 점프한다. 해당 메모리에는 puts@plt+6의 주소가 저장되어 있다. puts@plt+6에서는 스택에 0을 push한 후 0x8048310 함수로 점프한다. 이후에는 0x804a008 주소에 저장되어 있는 0xf7fedfe0 함수로 점프한다.
링커 라이브러리인 ld-linux.so.2 메모리에 있는 0xf7fedfe0 함수가 리턴하는 시점에 브레이크포인트를 설정해 스택 메모리를 확인해 봤다. puts 함수로 점프하는 것으로 보아, 0xf7fedfe0 함수는 호출된 라이브러리 함수의 주소를 알아내는 함수라는 것을 알 수 있다.
이렇게 특정 함수의 PLT를 호출하면 함수의 실제 주소를 호출하는 것과 같은 효과를 볼 수 있다. PLT의 주소는 고정되어 있기 때문에 서버에 ASLR 보호 기법이 적용되어 있어도 PLT로 점프하면 RTL과 비슷한 공격이 가능하다.
함수가 호출될 때 GOT에 저장된 주소로 점프하기 때문에 GOT에 저장된 값을 바꾸면 원하는 주소로 점프할 수 있다. example4 바이너리의 main 함수에 브레이크포인트를 걸고 실행한 후 puts 함수의 GOT인 0x804a00c 메모리의 값을 0xdeadbeef로 바꾸어 보겠다.
puts가 호출될 때 puts@got에 저장된 값으로 점프해 eip 레지스터의 값이 0xdeadbeef로 바뀌게 된 것을 확인했다. 이제 PLT에 존재하는 함수들, 즉 프로그램에서 한 번 이상 사용하는 라이브러리 함수들은 고정된 주소를 통해 호출할 수 있다는 것을 알게 되었다.
PLT의 주소를 알아내어 ret 영역에 덮어씌우고, GOT을 system 함수의 주소로 바꾸면 systme("/bin/sh")을 실행할 수 있을 것이다.
'Study > System Hacking' 카테고리의 다른 글
[LOB] troll -> vampire (0) | 2021.02.12 |
---|---|
[lazenca] Protection Tech / ASLR & Canaries (0) | 2021.02.12 |
[LOB] orge -> troll (0) | 2021.02.09 |
[LOB] darkelf -> orge (0) | 2021.02.09 |
[LOB] wolfman -> darkelf (0) | 2021.02.09 |