- 리눅스 커널 디버깅은 일반적인 ELF 바이너리 디버깅과 크게 다르지 않다.
- 다만 커널은 운영체제의 핵심적인 기능을 수행하기 때문에, 커널에 브레이크 포인트를 걸고 디버깅하면 커널이 브포에 도달한 순간 운영체제가 정지하게 된다는 차이점이 존재한다.
- 따라서 디버깅의 대상이되는 커널과 디버거를 분리하여 디버깅을 진행해야 한다.
커널 모듈이란?
- 커널은 한 번 컴파일 되고, 운영체제로 기능하기 시작하면 이를 수정하기 쉽지 않다.
- 그런데 커널의 권한으로 실행되야 하는 많은 장치 드라이버의 경우 빈번하게 개발되고 패치되고 있으며, 커널에 심각한 보안상의 문제가 발생하면 이를 즉각적으로 패치하여야 한다.
- 따라서 리눅스 개발진은 부족한 확장성을 개선하기 위해 커널에 필요한 기능을 탈부착할 수 있도록 적재 가능한 커널 모듈(Loadable Kernel Module, LKM)이라는 기술을 도입했다.
- 이 규격을 따른 모듈을 커널 모듈이라고 줄여서 부른다.
- 커널 모듈의 경우 커널에 부착되면 커널과 같은 메모리 공간을 사용하기 때문에, 커널 모듈에서 취약점이 발생하면 커널 공격으로 이어질 수 있다.
커널 디버깅 방법
- 커널을 디버깅하기 위해 사용하는 디버거는 크게 다음 세가지로 분류한다.
- 커널 디버거
- 직렬(serial)포트나 네트워크 등을 통해 커널에 직접 디버깅 명령을 내린다. 이러한 명령은 커널 내부에서 처리한다. 커널과 디버거가 같은 메모리에 위치하기 때문에 커널이 불안정하거나 디버거 메모리가 변조될 수 있으면 사용이 어렵다.
- 하드웨어 디버거
- 하드웨어 점검에 사용되는 JTAG등을 통해 회로의 상태를 직접 조회하고 변경한다. 이 방법을 사용하기 위해서는 별도의 디버깅 장비가 필요하다.
- 가상 머신 디버거
- 커널을 하드웨어 바로 위에서 구동하는 대신 가상 머신을 활용하는 방식이다. 별도의 디버깅 장비가 필요하지 않다. 이런 방식으로 가상 머신 상태를 외부에서 조사하는 것을 VMI(Virtual Machine Introspection)이라고 부른다.
커널 심볼 활용하기
디버그 심볼
- 심볼이란 컴파일러가 소스코드를 컴파일하는 과정에서 식별한 정보이다.
- 예를 들어, int foo(int a, int b)라는 함수가 있을 때, 컴파일러는 foo라는 함수가 있다라는 정보를 코드로부터 식별한다.
- 이러한 정보가 컴파일된 프로그램에 남아 있다면 프로그램을 역분석하기 쉬워진다.
- 많은 경우 심볼은 프로그램 실행에 필요하지 않기 때문에, 개발자들은 용량을 줄이거나, 역분석을 어렵게 만들기 위해 심볼을 제거한다.
- 디버그 심볼(Debug Symbol)은 컴파일러가 해당 심볼 정보를 소스코드의 어느 부분에서 식별했는지등의 정보까지 담고 있다.
- 따라서 gdb등의 디버거는 디버깅을 할 때, 디버그 심볼의 정보를 활용하여 함수의 소스코드를 보여주는 기능도 존재한다.
gdb로 커널 심볼 읽기
- Dreamhack에서 제공해주는 파일에서 linux-5.4.63-1+drhlke 디렉토리로 이동하게 되면 커널 소스와 vmlinux라는 파일이 존재한다.
- 이는 커널을 컴파일할 때 생성되는 ELF인데, 커널의 심볼 및 디버그 심볼이 포함되어 있다.
- 이 파일을 gdb로 열면 디버그 심볼을 읽을 수 있다.
커널 디버깅 스크립트 활용하기
- 리눅스 커널 소스코드에는 디버깅에 유용한 파이썬 스크립트가 포함되어 있다.
- 이 스크립트는 linux-5.4.63-1+drhlke/scripts/gdb/vmlinux-gdb.py 에 위치한다.
- 이 스크립트를 사용하기 위해서는 먼저 gdb에서 해당 스크립트 적재를 허용해야 한다.
- 홈 디렉토리에 .gdbinit파일이 존재하지 않으면 생성 후 다음과 같은 줄을 추가한다.
add-auto-load-safe-path <vmlinux-gdb.py 경로>
명령 | 설명 |
lx-symbols [경로...] | 리눅스 커널(vmlinux)과 모듈(.ko)의 심볼을 불러들인다. |
lx-dmesg | 커널 로그 버퍼(kmsg) 내용을 출력한다. |
lx-lsmod | 현재 적재된 커널 모듈의 목록을 출력한다. |
lx-ps | 프로세스 및 스레드 목록을 출력한다. |
lx-cpus | 커널이 인식하는 CPU코어들의 관리 상태를 출력한다. |
lx-list-check <list_head> | 주어진 list_head 연결 리스트 구조체의 일관성을 검사한다. unlink 관련 취약점을 디버깅할 때 유용하다. |
디버거 부착
- 가상 머신 내의 커널을 디버깅하려면 QEMU의 원격 디버깅 기능을 사용해야 한다.
- 원격 디버깅은 네트워크 통신을 통해 가상 머신이나 다른 컴퓨터를 디버깅하는 것을 의미하는데, QEMU는 게스트를 원격 디버깅할 수 있도록 관련된 기능을 제공한다.
- -S옵션을 통해 커널을 정지 시키고 -gdb옵션을 통해 디버깅 포트를 연다.
- gdb로 심볼이 존재하는 vmlinux파일을 연 뒤, 원격 디버깅 포트에 접근한다.
init_module()
- init_module()은 모듈이 커널에 적재될 때, 가장 먼저 실행되는 모듈 함수이다.
- init_module()의 코드는 testmod/testmod.c에 존재한다.
int init_module(void) {
int i;
int sum = 0;
printk("Hello, world!\n");
printk("my_count = %d\n", my_count);
for (i = 1; i <= my_count; i++) {
printk("%d\n", i);
sum += i;
}
printk("Sum is: %d\n", sum);
return 0;
}
init_module 진입
- lx-symbols명령어로 testmod/를 심볼경로에 추가하여 testmod.ko의 심볼 정보를 읽고, testmod.c:init_module에 breakpoint를 설정한다.
- 커널에 아직 이 모듈을 적재하지 않아서 경고가 발생하지만 곧 적재 할 것이므로 y를 누른다.
- 그 후 continue명령어로 커널을 계속 실행 시킨다.
- 부팅이 완료 되면 게스트에서 testmod.ko 커널 모듈을 적재시킬 수 있다.
- insmod는 모듈을 적재하는 커맨드로 insert module의 약자이다.
Hello, world!
- 커널 모듈이 로드되면서 init_module의 breakpoint에 도달하여 멈추게 된다.
- until명령어는 바이너리의 소스 코드와 심볼이 존재할 때, 특정 줄까지 바이너리를 실행시킬 수 있는 명령어이다.
- until 15로 15번째 줄까지 커널을 실행 시킨다.
- 그럼 위와 같이 게스트에 Hello, world!가 출력되는 것을 확인 가능하다.
set variable
- 이번에는 my_count 변수의 값을 조회하고 원하는 값으로 변경해보자.
- set은 변수의 값을 설정하는 명령어이며, finish는 함수의 끝까지 실행하는 명령어이다.
- 현재는 심볼과 코드가 모두 gdb에 등록되어 있기 때문에, 코드에 존재하는 변수의 이름을 그대로 사용할 수 있다.
- 위와 같이 my_count를 16으로 변경하면 게스트에 1부터 16까지 세는 메시지와 합계가 출력된다.
cleanup_module()
- 이번에는 모듈을 내릴 때 실행되는 함수인 cleanup_module()을 디버깅해보자.
- cleanup_module()의 소스코드는 다음과 같다.
...
void cleanup_module(void) { printk("Goodbye, cruel world...\n"); }
...
- init_module과 마찬가지로 breakpoint를 걸고, 커널을 계속 실행시킨다.
- 게스트에서 모듈을 내리면 중단점에 도달하게 된다.
- rmmod는 remove module의 약자로, 커널에서 모듈을 탈착할 때 사용하는 명령어이다.
- 디버거를 확인하면 cleanup_module()에 도달한 것을 확인할 수 있다.
- continue명령으로 모듈을 탈착시키면 Goodbye, cruel world...문자열이 게스트에서 출력되는 것을 볼 수 있다.
디버거 탈착
- quit명령을 사용하여 디버거를 탈착가능하다.
- QEMU의 경우 poweroff 명령어를 사용하여 종료할 수 있다.
'Pwn > Kernel Security' 카테고리의 다른 글
[Pwn] Dreamhack - 6.Mitigation: KASLR (0) | 2024.01.07 |
---|---|
[Pwn] Dreamhack - 5.Exploit Tech: prepare & commit (1) | 2024.01.07 |
[Pwn] Dreamhack - 4.Background: Tasks (0) | 2024.01.06 |
[Pwn] Dreamhack - 2.Tool: QEMU (0) | 2024.01.05 |
[Pwn] Dreamhack - 1.Introduction: Linux Kernel Exploit (1) | 2024.01.05 |