Linux Kernel Exploit Development with VMware - Lab1::Debugging Environment
목차
Debugging Environment
우선 디버깅 환경을 살펴보니 MasterVM
와 TestVM
(1,2)으로 구성이 되어 있다. MasterVM
은 TestVM
을 디버깅하기 위해 필요한 패키지들이 설치되어 있는 VM이고, TestVM
은 Debug/Explot 대상이 될 VM이다.
먼저 TestVM1의 .vmx
파일 수정을 통해 VMware debugStub을 활성화시킨다. 아래 내용을 .vmx
파일에 추가한다.
1
2
debugStub.listen.guest64 = "TRUE"
debugStub.listen.guest64.remote = "TRUE"
TestVM1이 64bit VM이기 때문에 *.guest64
옵션으로 설정해준다. (32bit VM을 디버깅하려면 *.guest32
옵션으로 설정해주면 된다.) 이후 해당 VM에서 gdbserver 0.0.0.0:8864
listen 상태가 되기 때문에 해당 VM의 ip와 8864번 포트를 통해 remote debugging이 가능해진다.
이제 아래 값을 .vmx
파일에 추가해 TestVM1에 추가적인 시리얼 포트를 생성한다.
1
2
3
serial1.present = "TRUE"
serial1.fileType = "file"
serial1.fileName = "/tmp/testvm1"
저장 후 TestVM1을 부팅한다.
부팅이 완료되었으면, GRUB config
(/boot/grub/grub.cfg)를 수정해야 한다.
Before
1
2
3
4
5
6
7
8
9
10
11
99 menuentry 'Ubuntu, with Linux 3.5.0-23-generic (stock)' --class ubuntu --class gnu-linux --class gnu --class os {
100 recordfail
101 gfxmode $linux_gfx_mode
102 insmod gzio
103 insmod part_msdos
104 insmod ext2
105 set root='(hd0,msdos1)'
106 search --no-floppy --fs-uuid --set=root cba0f2b2-4d3a-412e-abd0-84f430c8c448
107 linux /vmlinuz-3.5.0-23-generic root=/dev/mapper/ubuntu-root ro
108 initrd /initrd.img-3.5.0-23-generic
109 }
After
1
2
3
4
5
6
7
8
9
10
11
12
99 menuentry 'Ubuntu, with Linux 3.5.0-23-generic (stock)' --class ubuntu --class gnu-linux --class gnu --class os {
100 recordfail
101 gfxmode $linux_gfx_mode
102 insmod gzio
103 insmod part_msdos
104 insmod ext2
105 set root='(hd0,msdos1)'
106 search --no-floppy --fs-uuid --set=root cba0f2b2-4d3a-412e-abd0-84f430c8c448
107 linux /vmlinuz-3.5.0-23-generic root=/dev/mapper/ubuntu-root ro console=ttyS1,115200n8 nosm
ep nosmap
108 initrd /initrd.img-3.5.0-23-generic
109 }
저장 후 VM을 재시작한다. cat /proc/cpuinfo | grep -E 'sm[e|a]p
명령을 통해 smep
과 smap
옵션이 비활성화 되었는지 확인한다.
Debugger Attach
MasterVM, TestVM의 네트워크를 NAT로 설정한 후 IP Address를 확인한다.
MasterVM: 192.168.94.175
TestVM: 192.168.94.174
이때, Host의 IP는 192.168.94.1
이므로, 이 IP로 Attach하면 된다. 이후 MasterVM에서 gdb를 통해 TestVM1에 attach한다.
1
2
3
4
5
6
7
8
root@master:~# gdb -q kernels/vmlinux-3.5.0-23-generic
Reading symbols from kernels/vmlinux-3.5.0-23-generic...done.
(gdb) target remote 192.168.94.1:8864
Remote debugging using 192.168.94.1:8864
native_safe_halt () at /build/buildd/linux-lts-quantal-3.5.0/arch/x86/include/asm/irqflags.h:50
warning: Source file is more recent than executable.
50 }
(gdb)
Debug Program
아래 프로그램을 TestVM에서 컴파일한다.
1
2
3
4
5
6
7
#include <stdio.h>
int main() {
printf("test\n");
return 0;
}
1
2
3
4
5
6
7
8
test@ubuntu:~/exercises/syscall_bp$ gcc -o trigger trigger.c
test@ubuntu:~/exercises/syscall_bp$ ls
trigger trigger.c
test@ubuntu:~/exercises/syscall_bp$ file trigger
trigger: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), forGNU/Linux 2.6.24, BuildID[sha1]=0xb765e4c5a119d7f5dbab570bdee3cd30c2cba8c6, not stripped
test@ubuntu:~/exercises/syscall_bp$ ./trigger
test
test@ubuntu:~/exercises/syscall_bp$
이제 해당 프로그램의 printf()
함수 내에서 호출하는 sys_write()
함수에 breakpoint를 걸고, 출력 값을 test
에서 1337
로 변경해 보자.
우선, breakpoint를 sys_write에 걸어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(gdb) b * sys_write
Breakpoint 1 at 0xffffffff81187e10: file /build/buildd/linux-lts-quantal-3.5.0/fs/read_write.c, line 479.
(gdb) c
Continuing.
Breakpoint 1, sys_write (fd=10, buf=0x7f0e8c209640 ".", count=1)
at /build/buildd/linux-lts-quantal-3.5.0/fs/read_write.c:479
warning: Source file is more recent than executable.
479 {
(gdb) c
Continuing.
Breakpoint 1, sys_write (fd=2,
buf=0x1f2f008 ".]0;test@ubuntu: ~/exercises/syscall_bp\atest@ubuntu:~/exercises/syscall_bp$ ", '\337' <repeats 123 times>, <incomplete sequence \337>..., count=1)
at /build/buildd/linux-lts-quantal-3.5.0/fs/read_write.c:479
479 {
(gdb) c
Continuing.
Breakpoint 1, sys_write (fd=3,
buf=0x7f0e8c1f27c0 "oE\322I\227\002产\347\004h\260\353\272\025\f3\225O=*x\221̴`\233\263\241U]z\250\275\234TF\360\021y\333\327\064H\231\035\320\b@?\367\243\344-\363\321F\a\016e5\025Ѿ\355\262\220\226\363*\366\202V\243\226\371!\276\226Z\003c\203\201J\212\030\311\317G\347\366\001\317\060\262\362q$Ⓨ՛\377Ub\276\177\063\336\371\350\233[\301\177\r\321Mk-\362\t\004]{\213w:\375\365\030j\215pf1\375x\347\022h>\004\367\337P\332\352\212\361'g4\376_sw\177\371\342\201%#\023\035R\362ϧf;\205", count=64)
at /build/buildd/linux-lts-quantal-3.5.0/fs/read_write.c:479
479 {
(gdb)
하지만 위의 출력처럼 fd
의 값이 2,3,10 등인 sys_write 함수 호출까지 잡히기 때문에 printf()
로 출력되는 문자열(stdout
)만 보기 위해 conditional breakpoint를 잡아준다.
1
2
3
4
(gdb) b * sys_write if (fd==1)
Breakpoint 2 at 0xffffffff81187e10: file /build/buildd/linux-lts-quantal-3.5.0/fs/read_write.c, line 479.
(gdb) c
Continuing.
이후 ./trigger
명령을 통해 breakpoint가 printf 지점에 정확하게 잡히는지 확인해본다.
testVM
1
test@ubuntu:~/exercises/syscall_bp$ ./trigger
MasterVM
1
2
3
4
5
6
7
(gdb) c
Continuing.
Breakpoint 2, sys_write (fd=1, buf=0x7f80d1e5c000 "test\n", count=5)
at /build/buildd/linux-lts-quantal-3.5.0/fs/read_write.c:479
479 {
(gdb)
우리가 찾던 "test\n"
문자열이 정확하게 잡혔다. 이제 rsi
레지스터가 가르키는 주소의 값을 변경하여 1337
을 출력하도록 변조해보자.
MasterVM
1
2
3
4
5
6
7
8
9
10
11
(gdb) c
Continuing.
Breakpoint 2, sys_write (fd=1, buf=0x7f1f7e831000 "test\n", count=5)
at /build/buildd/linux-lts-quantal-3.5.0/fs/read_write.c:479
479 {
(gdb) set {char [6]} 0x7f1f7e831000 = "1337\n"
(gdb) x/s 0x7f1f7e831000
0x7f1f7e831000: "1337\n"
(gdb) c
Continuing.
TestVM
1
2
3
test@ubuntu:~/exercises/syscall_bp$ ./trigger
1337
test@ubuntu:~/exercises/syscall_bp$
1337을 성공적으로 출력했다. 이제 다음 Lab으로 넘어가자!