Excuse the ads! We need some help to keep our site up.
List
Debugging kernel and modules
- 커널을 디버깅하는 방법은 다양하게 존재하며, 여기에서는 VMware를 이용한 디버깅을 설명하겠습니다.
Debug Symbol Packages
- 분석의 편의성을 위해 Debug Symbol를 설치합니다.
Getting -dbgsym.ddeb packages
다음과 같이 저장소 정보를 "/etc/apt/sources.list.d/ddebs.list" 파일에 저장합니다.
echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \ sudo tee -a /etc/apt/sources.list.d/ddebs.list
- Ubuntu 서버에서 Debug Symbol 아카이브 서명 키를 가져옵니다
sudo apt install ubuntu-dbgsym-keyring
Manual install of debug packages
- 해당 시스템의 커널 버전에 맞는 Debug packages를 설치합니다.
sudo apt-get update sudo apt-get install linux-image-$(uname -r)-dbgsym
- 설치된 Debug Symbol 파일을 다음과 같은 경로에 위치합니다.
/usr/lib/debug/boot/vmlinux-$(uname -r)
Disable KASLR
- 다음과 같이 "/etc/default/grub"에 "GRUB_CMDLINE_LINUX_DEFAULT" 필드에 "nokaslr"을 추가하여 KASLR을 비활성화 합니다.
# If you change this file, run 'update-grub' afterwards to update # /boot/grub/grub.cfg. # For full documentation of the options in this file, see: # info -f grub -n 'Simple configuration' GRUB_DEFAULT=0 GRUB_TIMEOUT_STYLE=hidden GRUB_TIMEOUT=0 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet nokaslr" GRUB_CMDLINE_LINUX="find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US" # Uncomment to enable BadRAM filtering, modify to suit your needs # This works with Linux (no patch required) and with any kernel that obtains # the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...) #GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef" # Uncomment to disable graphical terminal (grub-pc only) #GRUB_TERMINAL=console # The resolution used on graphical terminal # note that you can use only modes which your graphic card supports via VBE # you can see them in real GRUB with the command `vbeinfo' #GRUB_GFXMODE=640x480 # Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux #GRUB_DISABLE_LINUX_UUID=true # Uncomment to disable generation of recovery mode menu entries #GRUB_DISABLE_RECOVERY="true" # Uncomment to get a beep at grub start #GRUB_INIT_TUNE="480 440 1"
- 다음과 같이 명령어를 실행하면 변경된 부팅설정을 반영합니다.
sudo update-grub
Debugging Kernel and Modules with VMware
Debugging Preferences to VMware - debugStub
- VMware를 이용하여 커널을 디버깅하기 위해 디버깅 대상 VMware의 *.vmx 파일을 열어 다음과 같은 설정을 추가합니다.
- 디버깅 대상 시스템 환경(32bit, 64bit)에 맞게 해당 내용을 추가합니다.
debugStub.listen.guest32 = "TRUE" debugStub.listen.guest32.remote = "TRUE" debugStub.hideBreakpoints = "FALSE" monitor.debugOnStartGuest32 = "TRUE"
debugStub.listen.guest64 = "TRUE" debugStub.listen.guest64.remote = "TRUE" debugStub.hideBreakpoints = "FALSE" monitor.debugOnStartGuest64 = "TRUE"
Connecting Debugging to the Kernel
- 디버깅 설정이 저장된 VMware를 기동하면 다음과 같은 화면에서 대기하게 됩니다.
- 해당 상태에서 다른 VM에서 Root 권한으로 GDB를 실행시켜 해당 VM에 연결합니다.
- gdb를 연결하기 전에 반드시 해당 시스템의 architecture를 설정해야 합니다.
- 디버거가 정상적으로 연결되면 디버깅 할 VMware는 정지되며, gdb에서 "continue" 명령을 입력하면 운영체제가 정상적으로 부팅됩니다.
- gdb연결시 사용되는 Port는 다음과 같습니다.
- 32bit : 8832
- 64bit : 8864
root@ubuntu:/home/lazenca0x0# gdb -q /usr/lib/debug/boot/vmlinux-4.18.0-12-generic Reading symbols from /usr/lib/debug/boot/vmlinux-4.18.0-12-generic...done. (gdb) set disassembly-flavor intel (gdb) set architecture i386:x86-64:intel The target architecture is assumed to be i386:x86-64:intel (gdb) target remote 192.168.2.44:8864 Remote debugging using 192.168.2.44:8864 0x0000000001000200 in ?? () (gdb) c Continuing.
Kernel Address Display Restriction(KADR)
- 디버깅을 설명하기 전에 "Kernel Address Display Restriction(KADR)"에 대한 이해가 필요합니다.
- 공격자가 커널 취약성을 이용하여 Exploit을 개발하려고 할 때 User Space에서의 Exploit과 같이 필요한 가젯, 커널 함수들의 주소,등의 정보가 필요합니다.
- 커널에서는 KADR에 의해 커널 영역의 주소를 민감한 정보로 처리하고 있으며, 일반 로컬 사용자에게 보이지 않습니다.
- 루트 사용자만 다양한 파일 및 디렉토리를 읽을 수 있도록 설정하였다.
- /boot/vmlinuz*, /boot/System.map*, /sys/kernel/debug/, /proc/slabinfo
- Ubuntu 11.04부터 "/proc/sys/kernel/kptr_rr_rrestrict"는 알려진 커널 주소 출력을 차단하기 위해 "1"로 설정되어 있다.
- 비활성화하기 위해 해당 값을 "0"으로 변경하면 됩니다.
- 다음과 같이 일반 유저로 커널의 모든 심볼 목록을 보관하고 있는 "/proc/kallsyms" 파일을 읽을 경우 해당 심볼의 주소가 "0000000000000000"으로 출력됩니다.
- Root 권한으로 해당 파일을 읽을 경우 각 심볼들의 주소 값이 출력되는 것을 확인 할 수 있습니다.
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ cat /proc/kallsyms |grep escalation 0000000000000000 t chardev_release [escalation] 0000000000000000 t chardev_open [escalation] 0000000000000000 t chardev_write [escalation] 0000000000000000 t chardev_read [escalation] 0000000000000000 t chardev_ioctl [escalation] 0000000000000000 b info [escalation] 0000000000000000 t chardev_init [escalation] 0000000000000000 b chardev_cdev [escalation] 0000000000000000 b chardev_major [escalation] 0000000000000000 b __key.28909 [escalation] 0000000000000000 b chardev_class [escalation] 0000000000000000 t chardev_exit [escalation] 0000000000000000 d __this_module [escalation] 0000000000000000 t cleanup_module [escalation] 0000000000000000 t init_module [escalation] 0000000000000000 d s_chardev_fops [escalation] lazenca0x0@ubuntu:~/Kernel/Module/escalation$
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo cat /proc/kallsyms |grep escalation ffffffffc0301000 t chardev_release [escalation] ffffffffc0301019 t chardev_open [escalation] ffffffffc0301032 t chardev_write [escalation] ffffffffc0301051 t chardev_read [escalation] ffffffffc0301070 t chardev_ioctl [escalation] ffffffffc0303440 b info [escalation] ffffffffc0301154 t chardev_init [escalation] ffffffffc03034e0 b chardev_cdev [escalation] ffffffffc0303548 b chardev_major [escalation] ffffffffc0303440 b __key.28909 [escalation] ffffffffc03034c8 b chardev_class [escalation] ffffffffc03012ac t chardev_exit [escalation] ffffffffc0303100 d __this_module [escalation] ffffffffc03012ac t cleanup_module [escalation] ffffffffc0301154 t init_module [escalation] ffffffffc0303000 d s_chardev_fops [escalation] lazenca0x0@ubuntu:~/Kernel/Module/escalation$
하지만 다음과 같이 "kptr_restrict"의 값을 "0"으로 변경해도 일반 유저 권한으로 커널 심볼들의 주소를 확인 할 수 없습니다.
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo sysctl -w kernel.kptr_restrict=0 kernel.kptr_restrict = 0 lazenca0x0@ubuntu:~/Kernel/Module/escalation$ cat /proc/kallsyms |grep escalation 0000000000000000 t chardev_release [escalation] 0000000000000000 t chardev_open [escalation] 0000000000000000 t chardev_write [escalation] 0000000000000000 t chardev_read [escalation] 0000000000000000 t chardev_ioctl [escalation] 0000000000000000 b info [escalation] 0000000000000000 t chardev_init [escalation] 0000000000000000 b chardev_cdev [escalation] 0000000000000000 b chardev_major [escalation] 0000000000000000 b __key.28909 [escalation] 0000000000000000 b chardev_class [escalation] 0000000000000000 t chardev_exit [escalation] 0000000000000000 d __this_module [escalation] 0000000000000000 t cleanup_module [escalation] 0000000000000000 t init_module [escalation] 0000000000000000 d s_chardev_fops [escalation] lazenca0x0@ubuntu:~/Kernel/Module/escalation$
perf_event_paranoid
- "kptr_restrict"의 값을 "0"으로 변경해도 일반 유저 권한으로 커널 심볼들의 주소를 확인 할 수 없는 이유는 바로 "perf_event_paranoid" 때문입니다.
- "perf_event_paranoid"는 Performance counters(커널의 성능 모니터링)의 액세스를 제한 권한을 관리하고 있습니다.
Value | Description |
---|---|
2 | 사용자 공간 측정만 허용(Linux 4.6 이후 기본값) |
1 | 커널과 사용자 측정을 모두 허용(Linux 4.6 이전 기본값) |
0 | 원시 추적점 샘플이 아닌 CPU별 데이터에 대한 액세스를 허용 |
-1 | 제한 없음 |
- 다음과 같이 "perf_event_paranoid"의 값을 1 또는 0, -1로 변경하면, 일반 유저 권한으로 커널 심볼들의 주소를 확인 할 수 있습니다.
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo sysctl kernel.perf_event_paranoid kernel.perf_event_paranoid = 3 lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo sysctl -w kernel.perf_event_paranoid=0 kernel.perf_event_paranoid = 0 lazenca0x0@ubuntu:~/Kernel/Module/escalation$ cat /proc/kallsyms |grep escalation ffffffffc0a5e000 t chardev_release [escalation] ffffffffc0a5e019 t chardev_open [escalation] ffffffffc0a5e032 t chardev_write [escalation] ffffffffc0a5e051 t chardev_read [escalation] ffffffffc0a5e070 t chardev_ioctl [escalation] ffffffffc0a60440 b info [escalation] ffffffffc0a5e154 t chardev_init [escalation] ffffffffc0a604e0 b chardev_cdev [escalation] ffffffffc0a60548 b chardev_major [escalation] ffffffffc0a60440 b __key.28909 [escalation] ffffffffc0a604c8 b chardev_class [escalation] ffffffffc0a5e2ac t chardev_exit [escalation] ffffffffc0a60100 d __this_module [escalation] ffffffffc0a5e2ac t cleanup_module [escalation] ffffffffc0a5e154 t init_module [escalation] ffffffffc0a60000 d s_chardev_fops [escalation] lazenca0x0@ubuntu:~/Kernel/Module/escalation$
Get section address of module
- 해당 예제에서 사용될 모듈은 04.Creating a kernel module to privilege escalation 에서 사용된 "escalation.ko" 모듈을 사용합니다.
- 우선 디버깅을 위해 해당 모듈을 커널에 등록합니다.
lazenca0x0@ubuntu:~$ cd Kernel/Module/escalation/ lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo insmod escalation.ko lazenca0x0@ubuntu:~/Kernel/Module/escalation$ lsmod |grep escalation escalation 16384 0 lazenca0x0@ubuntu:~/Kernel/Module/escalation$ ls -al /dev/chardev0 crw-r--r-- 1 root root 240, 0 Dec 14 01:10 /dev/chardev0 lazenca0x0@ubuntu:~/Kernel/Module/escalation$
- 앞에서 설명한 "/proc/kallsyms" 파일을 이용하여 디버깅 할 함수들의 주소값을 확인 합니다.
- 그리고 "/sys/module/" 하위에 등록된 모듈들의 ".text", ".bss", ".data" , 등 해당 영역의 시작주소 정보를 얻을 수 있습니다.
- "/sys/module/(모듈 명)/sections/.text.unlikely"
- "/sys/module/(모듈 명)/sections/.bss"
- "/sys/module/(모듈 명)/sections/.data"
- 그리고 "/sys/module/" 하위에 등록된 모듈들의 ".text", ".bss", ".data" , 등 해당 영역의 시작주소 정보를 얻을 수 있습니다.
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo cat /proc/kallsyms |grep escalation ffffffffc0301000 t chardev_release [escalation] ffffffffc0301019 t chardev_open [escalation] ffffffffc0301032 t chardev_write [escalation] ffffffffc0301051 t chardev_read [escalation] ffffffffc0301070 t chardev_ioctl [escalation] ffffffffc0303440 b info [escalation] ffffffffc0301154 t chardev_init [escalation] ffffffffc03034e0 b chardev_cdev [escalation] ffffffffc0303548 b chardev_major [escalation] ffffffffc0303440 b __key.28909 [escalation] ffffffffc03034c8 b chardev_class [escalation] ffffffffc03012ac t chardev_exit [escalation] ffffffffc0303100 d __this_module [escalation] ffffffffc03012ac t cleanup_module [escalation] ffffffffc0301154 t init_module [escalation] ffffffffc0303000 d s_chardev_fops [escalation] lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo cat /sys/module/escalation/sections/.text.unlikely 0xffffffffc0301000 lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo cat /sys/module/escalation/sections/.bss 0xffffffffc0303440 lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo cat /sys/module/escalation/sections/.data 0xffffffffc0303000 lazenca0x0@ubuntu:~/Kernel/Module/escalation$
다음 예제 에서는 "chardev_ioctl" 함수의 시작 주소를 전달하였으며, 해당 함수의 코드를 확인 할 수 있습니다.
(gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. 0xffffffff819f1ac6 in native_safe_halt () at /build/linux-ChQIyb/linux-4.18.0/arch/x86/include/asm/irqflags.h:57 57 /build/linux-ChQIyb/linux-4.18.0/arch/x86/include/asm/irqflags.h: No such file or directory. (gdb) x/10i 0xffffffffc0301070 0xffffffffc0301070: nop DWORD PTR [rax+rax*1+0x0] 0xffffffffc0301075: push rbp 0xffffffffc0301076: mov rdi,0xffffffffc03020e8 0xffffffffc030107d: mov rbp,rsp 0xffffffffc0301080: push r12 0xffffffffc0301082: mov r12,rdx 0xffffffffc0301085: push rbx 0xffffffffc0301086: mov ebx,esi 0xffffffffc0301088: call 0xffffffff810f4e33 <printk> 0xffffffffc030108d: cmp ebx,0x40884702 (gdb)
다음과 같이 objdump를 이용하여 메모리에 저장된 "chardev_ioctl" 함수 코드와 덤프된 코드가 일치하는 것을 확인 할 수 있습니다.
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ objdump -M intel -d escalation.ko escalation.ko: file format elf64-x86-64 Disassembly of section .text.unlikely: 0000000000000000 <chardev_release>: 0: e8 00 00 00 00 callq 5 <chardev_release+0x5> ... 0000000000000019 <chardev_open>: 19: e8 00 00 00 00 callq 1e <chardev_open+0x5> ... 0000000000000032 <chardev_write>: 32: e8 00 00 00 00 callq 37 <chardev_write+0x5> ... 0000000000000051 <chardev_read>: 51: e8 00 00 00 00 callq 56 <chardev_read+0x5> ... 0000000000000070 <chardev_ioctl>: 70: e8 00 00 00 00 call 75 <chardev_ioctl+0x5> 75: 55 push rbp 76: 48 c7 c7 00 00 00 00 mov rdi,0x0 7d: 48 89 e5 mov rbp,rsp 80: 41 54 push r12 82: 49 89 d4 mov r12,rdx 85: 53 push rbx 86: 89 f3 mov ebx,esi 88: e8 00 00 00 00 call 8d <chardev_ioctl+0x1d> 8d: 81 fb 02 47 88 40 cmp ebx,0x40884702 93: 74 36 je cb <chardev_ioctl+0x5b> 95: 81 fb 03 47 88 80 cmp ebx,0x80884703 9b: 74 71 je 10e <chardev_ioctl+0x9e> 9d: 81 fb 00 47 00 00 cmp ebx,0x4700 a3: 0f 85 8e 00 00 00 jne 137 <chardev_ioctl+0xc7> a9: 48 c7 c7 00 00 00 00 mov rdi,0x0 b0: e8 00 00 00 00 call b5 <chardev_ioctl+0x45> b5: 31 ff xor edi,edi b7: e8 00 00 00 00 call bc <chardev_ioctl+0x4c> bc: 48 89 c7 mov rdi,rax bf: e8 00 00 00 00 call c4 <chardev_ioctl+0x54> c4: 31 d2 xor edx,edx c6: e9 81 00 00 00 jmp 14c <chardev_ioctl+0xdc> cb: 48 c7 c7 00 00 00 00 mov rdi,0x0 d2: e8 00 00 00 00 call d7 <chardev_ioctl+0x67> d7: ba 88 00 00 00 mov edx,0x88 dc: 4c 89 e6 mov rsi,r12 df: 48 c7 c7 00 00 00 00 mov rdi,0x0 e6: e8 00 00 00 00 call eb <chardev_ioctl+0x7b> eb: 48 85 c0 test rax,rax ee: 75 55 jne 145 <chardev_ioctl+0xd5> f0: 48 8b 35 00 00 00 00 mov rsi,QWORD PTR [rip+0x0] # f7 <chardev_ioctl+0x87> f7: 48 c7 c2 00 00 00 00 mov rdx,0x0 fe: 48 c7 c7 00 00 00 00 mov rdi,0x0 105: e8 00 00 00 00 call 10a <chardev_ioctl+0x9a> 10a: 31 d2 xor edx,edx 10c: eb 3e jmp 14c <chardev_ioctl+0xdc> 10e: 48 c7 c7 00 00 00 00 mov rdi,0x0 115: e8 00 00 00 00 call 11a <chardev_ioctl+0xaa> 11a: ba 88 00 00 00 mov edx,0x88 11f: 48 c7 c6 00 00 00 00 mov rsi,0x0 126: 4c 89 e7 mov rdi,r12 129: e8 00 00 00 00 call 12e <chardev_ioctl+0xbe> 12e: 31 d2 xor edx,edx 130: 48 85 c0 test rax,rax 133: 75 10 jne 145 <chardev_ioctl+0xd5> 135: eb 15 jmp 14c <chardev_ioctl+0xdc> 137: 89 de mov esi,ebx 139: 48 c7 c7 00 00 00 00 mov rdi,0x0 140: e8 00 00 00 00 call 145 <chardev_ioctl+0xd5> 145: 48 c7 c2 f2 ff ff ff mov rdx,0xfffffffffffffff2 14c: 5b pop rbx 14d: 48 89 d0 mov rax,rdx 150: 41 5c pop r12 152: 5d pop rbp 153: c3 ret 0000000000000154 <init_module>: ... 00000000000002ac <cleanup_module>: ... lazenca0x0@ubuntu:~/Kernel/Module/escalation$
Debugging Modules
- 동적으로 디버깅을 하기 위해 다음과 같이 "chardev_ioctl" 함수의 시작 주소에 Break point를 설정합니다.
(gdb) b *0xffffffffc0301075 Breakpoint 1 at 0xffffffffc0301075 (gdb) c Continuing.
- 모듈을 동작시키키 위해 "Exploit" 프로그램을 실행합니다.
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ ./Exploit
- 다음과 같이 등록한 Breakpoint가 동작하게되며 해당 모듈을 동적으로 분석할 수 있게 됩니다.
- 디버깅 심볼 파일에 의해 모듈이 사용하고 있는 함수명이 출력됩니다.
Breakpoint 1, 0xffffffffc0301075 in ?? () (gdb) i r rip rip 0xffffffffc0301075 0xffffffffc0301075 (gdb) x/22i $rip => 0xffffffffc0301075: push rbp 0xffffffffc0301076: mov rdi,0xffffffffc03020e8 0xffffffffc030107d: mov rbp,rsp 0xffffffffc0301080: push r12 0xffffffffc0301082: mov r12,rdx 0xffffffffc0301085: push rbx 0xffffffffc0301086: mov ebx,esi 0xffffffffc0301088: call 0xffffffff810f4e33 <printk> 0xffffffffc030108d: cmp ebx,0x40884702 0xffffffffc0301093: je 0xffffffffc03010cb 0xffffffffc0301095: cmp ebx,0x80884703 0xffffffffc030109b: je 0xffffffffc030110e 0xffffffffc030109d: cmp ebx,0x4700 0xffffffffc03010a3: jne 0xffffffffc0301137 0xffffffffc03010a9: mov rdi,0xffffffffc03021e1 0xffffffffc03010b0: call 0xffffffff810f4e33 <printk> 0xffffffffc03010b5: xor edi,edi 0xffffffffc03010b7: call 0xffffffff810b5a60 <prepare_kernel_cred> 0xffffffffc03010bc: mov rdi,rax 0xffffffffc03010bf: call 0xffffffff810b56b0 <commit_creds> 0xffffffffc03010c4: xor edx,edx 0xffffffffc03010c6: jmp 0xffffffffc030114c (gdb)
- 해당 모듈에 대한 동적 분석을 조금더 진행해 보겠습니다.
- "cmp ebx,0x40884702" 명령어를 처리하는 0xffffffffc030108d 영역에 Break point를 설정하고 프로그램을 진행합니다.
- 해당 영역에서는 EBX 레지스터에 저장된 값과 0x80884703 값이 같은지 확인합니다.
- EBX 레지스터에는 0x4700 값이 저장되어 있으며, 해당 값은 0xffffffffc030109d 영역의 코드 조건에 만족합니다.
(gdb) b *0xffffffffc030108d Breakpoint 2 at 0xffffffffc030108d (gdb) c Continuing. Breakpoint 2, 0xffffffffc030108d in ?? () (gdb) x/i $rip => 0xffffffffc030108d: cmp ebx,0x40884702 (gdb) i r ebx ebx 0x4700 18176 (gdb) x/10i $rip => 0xffffffffc030108d: cmp ebx,0x40884702 0xffffffffc0301093: je 0xffffffffc03010cb 0xffffffffc0301095: cmp ebx,0x80884703 0xffffffffc030109b: je 0xffffffffc030110e 0xffffffffc030109d: cmp ebx,0x4700 0xffffffffc03010a3: jne 0xffffffffc0301137 0xffffffffc03010a9: mov rdi,0xffffffffc03021e1 0xffffffffc03010b0: call 0xffffffff810f4e33 <printk> 0xffffffffc03010b5: xor edi,edi 0xffffffffc03010b7: call 0xffffffff810b5a60 <prepare_kernel_cred> (gdb)
"cmp ebx,0x4700" 명령어를 처리하는 0xffffffffc030109d 영역에 Break point를 설정하고 프로그램을 진행합니다.
EBX 레지스터의 값이 0x4700 이기 때문에 JNE 명령어를 통과하여 RDI 레지스터에 값을 저장합니다.
RDI 레지스터에 저장되는 값은 0xffffffffc03021e1 이며, 해당 영역에 저장된 값은 "GIVE_ME_ROOT\n" 입니다.
- 즉, printk 함수를 이용하여 출력된 메시지의 인자 값을 전달하는 것입니다.
(gdb) b *0xffffffffc030109d Breakpoint 3 at 0xffffffffc030109d (gdb) c Continuing. Breakpoint 3, 0xffffffffc030109d in ?? () (gdb) x/i $rip => 0xffffffffc030109d: cmp ebx,0x4700 (gdb) i r ebx ebx 0x4700 18176 (gdb) x/10i $rip => 0xffffffffc030109d: cmp ebx,0x4700 0xffffffffc03010a3: jne 0xffffffffc0301137 0xffffffffc03010a9: mov rdi,0xffffffffc03021e1 0xffffffffc03010b0: call 0xffffffff810f4e33 <printk> 0xffffffffc03010b5: xor edi,edi 0xffffffffc03010b7: call 0xffffffff810b5a60 <prepare_kernel_cred> 0xffffffffc03010bc: mov rdi,rax 0xffffffffc03010bf: call 0xffffffff810b56b0 <commit_creds> 0xffffffffc03010c4: xor edx,edx 0xffffffffc03010c6: jmp 0xffffffffc030114c (gdb) si 0xffffffffc03010a3 in ?? () (gdb) si 0xffffffffc03010a9 in ?? () (gdb) x/i $rip => 0xffffffffc03010a9: mov rdi,0xffffffffc03021e1 (gdb) x/s 0xffffffffc03021e1 0xffffffffc03021e1: "GIVE_ME_ROOT\n" (gdb) si 0xffffffffc03010b0 in ?? () (gdb) si printk (fmt=0xffffffffc03021e1 "GIVE_ME_ROOT\n") at /build/linux-ChQIyb/linux-4.18.0/kernel/printk/printk.c:1985 1985 in /build/linux-ChQIyb/linux-4.18.0/kernel/printk/printk.c (gdb)
다음으로 prepare_kernel_cred() 함수 호출 후 리턴되는 값을 확인해 보겠습니다.
prepare_kernel_cred() 함수 호출 후 리턴 값을 RDI 레지스터에 저장하는 코드 영역(0xffffffffc03010bc)에 Break point를 설정합니다.
RAX 레지스터에는 prepare_kernel_cred() 함수 호출 후 리턴 값이 저장되어 있는 주소값이 저장 되어 있습니다.
다음과 같이 RAX 레지스터에 struct cred 구조체를 적용하면 uid, gid, suid, sgid,등의 값이 0으로 설정되어 있습니다.
즉, 이 정보에 의해 Root 권한은 획득하게 되는 것입니다.
(gdb) b *0xffffffffc03010bc Breakpoint 4 at 0xffffffffc03010bc (gdb) c Continuing. Breakpoint 4, 0xffffffffc03010bc in ?? () (gdb) i r rax rax 0xffff880101b3c900 -131937071806208 (gdb) p *(struct cred*)$rax $3 = {usage = {counter = 1}, uid = {val = 0}, gid = {val = 0}, suid = {val = 0}, sgid = { val = 0}, euid = {val = 0}, egid = {val = 0}, fsuid = {val = 0}, fsgid = {val = 0}, securebits = 0, cap_inheritable = {cap = {0, 0}}, cap_permitted = {cap = {4294967295, 63}}, cap_effective = {cap = {4294967295, 63}}, cap_bset = {cap = {4294967295, 63}}, cap_ambient = { cap = {0, 0}}, jit_keyring = 1 '\001', session_keyring = 0x0 <irq_stack_union>, process_keyring = 0x0 <irq_stack_union>, thread_keyring = 0x0 <irq_stack_union>, request_key_auth = 0x0 <irq_stack_union>, security = 0xffff8801390132a8, user = 0xffffffff82453d40 <root_user>, user_ns = 0xffffffff82453de0 <init_user_ns>, group_info = 0xffffffff8245b1a8 <init_groups>, rcu = {next = 0x0 <irq_stack_union>, func = 0x0 <irq_stack_union>}} (gdb) p (*(struct cred*)$rax).uid $3 = {val = 0} (gdb) p (*(struct cred*)$rax).gid $4 = {val = 0} (gdb) p (*(struct cred*)$rax).suid $5 = {val = 0} (gdb) p (*(struct cred*)$rax).sgid $6 = {val = 0} (gdb)
Debugging Preferences to VMware - serial port
- 커널을 디버깅할 때 앞에서 설명한 것 처럼 VMware의 debugStub를 이용할 수 있지만, Serial Port도 이용할 수 있습니다.
- 다음과 같이 각 VMware의 *.vmx 파일에 설정을 추가합니다.
serial0.present = "TRUE" serial0.fileType = "pipe" serial0.fileName = "/private/tmp/com1" serial0.tryNoRxLoss = "FALSE" serial0.pipe.endPoint = "server"
serial0.present = "TRUE" serial0.fileType = "pipe" serial0.fileName = "/private/tmp/com1" serial0.tryNoRxLoss = "FALSE" serial0.pipe.endPoint = "client"
그리고 다음과 같이 "/etc/default/grub"에 "GRUB_CMDLINE_LINUX_DEFAULT" 필드에 "kgdbwait kgdboc/ttyS0,115200"을 추가 합니다.
# If you change this file, run 'update-grub' afterwards to update # /boot/grub/grub.cfg. # For full documentation of the options in this file, see: # info -f grub -n 'Simple configuration' GRUB_DEFAULT=0 GRUB_TIMEOUT_STYLE=hidden GRUB_TIMEOUT=0 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet nokaslr" GRUB_CMDLINE_LINUX="find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US" # Uncomment to enable BadRAM filtering, modify to suit your needs # This works with Linux (no patch required) and with any kernel that obtains # the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...) #GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef" # Uncomment to disable graphical terminal (grub-pc only) #GRUB_TERMINAL=console # The resolution used on graphical terminal # note that you can use only modes which your graphic card supports via VBE # you can see them in real GRUB with the command `vbeinfo' #GRUB_GFXMODE=640x480 # Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux #GRUB_DISABLE_LINUX_UUID=true # Uncomment to disable generation of recovery mode menu entries #GRUB_DISABLE_RECOVERY="true" # Uncomment to get a beep at grub start #GRUB_INIT_TUNE="480 440 1"
- 다음과 같이 명령어를 실행하면 변경된 부팅설정을 반영합니다.
sudo update-grub
- 다음과 같이 IP, Port가 아닌 Serial Port로 연결을 진행합니다.
(gdb) target remote /dev/ttyS0
References
- https://mirrors.edge.kernel.org/pub/linux/kernel/
- https://github.com/surajx/qemu-arm-linux/wiki/Compile-Linux,-BusyBox-for-ARM-and-load-it-using-QEMU
- http://www.berkes.ca/guides/linux_kernel.html
- https://stackoverflow.com/questions/28298220/kernel-module-no-debugging-symbols-found
- http://www.yonch.com/tech/84-debugging-the-linux-kernel-with-qemu-and-eclipse
- http://www.linux-magazine.com/Online/Features/Qemu-and-the-Kernel
- https://www.collabora.com/news-and-blog/blog/2017/03/13/kernel-debugging-with-qemu-overview-tools-available/
- https://vmsplice.net/~stefan/stefanha-kernel-recipes-2015.pdf
- https://events.static.linuxfound.org/sites/events/files/slides/Debugging%20the%20Linux%20Kernel%20with%20GDB.pdf
- https://superuser.com/questions/298826/how-do-i-uncompress-vmlinuz-to-vmlinux
- https://wiki.ubuntu.com/DebuggingProcedures#Kernel.2FDebugging.Kernel_Debugging_Scenarios
- https://sysprogs.com/VisualKernel/tutorials/setup/ubuntu/
- https://github.com/cirosantilli/linux-kernel-module-cheat#linux-kernel-gdb-scripts
- https://wiki.ubuntu.com/Debug%20Symbol%20Packages
- https://www.dcl.hpi.uni-potsdam.de/research/WRK/2011/01/running-wrk-on-mac-os-with-vmware-fusion/index.html
- https://linux.die.net/man/2/perf_event_open
- https://www.anquanke.com/post/id/85837