angr 多次读取相同的变量

逆向工程 愤怒
2021-07-07 13:26:15

我正在编写一个脚本来解决一个要求多个字符串的二进制文件,如以下 c 代码,

我的问题我不能转储“second_pass”,我只能得到“first_pass”。可能是由于使用相同的变量来读取输入。任何帮助请

#include <stdio.h>

int main()
{
    int i, lose = 1;
    char first[20]="first_pass", input[20];
    scanf("%s", input);
    for(i=0; i < strlen(first); i++)
    {
        if(first[i] != input[i])
            lose = 0;
    }

    if(lose)
        printf("gg");
    else
        printf("fail");


    char second[20]="second_pass";
    scanf("%s", input);
    for(i=0; i < strlen(second); i++)
    {
        //printf("%c = %c\n", second[i], input[i]);
        if(second[i] != input[i])
            lose = 0;
    }

    printf("\n");
    if(lose)
        printf("gg");
    else
        printf("fail");
}

我的 angr 脚本

import angr
import logging



start_at = 0x400550

fail = (0x400681,0x400751)
success = (0x400680, 0x400740)




p = angr.Project("./a.out", load_options={'auto_load_libs': False})


state = p.factory.blank_state(addr=addr_main)

path = p.factory.path(state)
pathgroup = p.factory.path_group(path)
print "Searching..."
found_state = pathgroup.explore(find=success, avoid=fail)

if found_state.found:
    f = found_state.found[0].state

    print "Flag: %s" % (f.posix.dumps(0)[0:20],)
1个回答

由于 OP 没有指定硬编码偏移量所指的地址,我将假设他的意图。我假设中的值find匹配两个printf("gg")报表和两个值avoid匹配两个printf("fail");语句。

首先,我相信你的程序中有两个无意的相当大的错误,阻止了预期的解决方案的需要

  1. 两者中的第一个也是更重要的是lose在您的代码中使用。这是足以只提供正确的一个永久设定的密码losefalse否则,使用第一密码的手段lose将是false双方,有效地达到你指定的两个成功的标准。简单的说,因为在第一次密码验证后lose没有被重置为默认值,true所以提供第一个密码就足以得到两个ggs。
  2. 这并没有真正干扰解决挑战,但也可能是一个错误。我们将在几段中讨论为什么它是 angr 的一个问题,但现在我只是指出您的字符串比较缺乏对正确长度的任何验证,或者输入字符串在硬编码密码时终止。

我发现您的示例代码非常有趣,因为这是两个相对常见的安全漏洞。

忽略上面的第一个逻辑错误,我认为问题在于您配置的成功标准;根据angr 文档(重点是我的):

.explore()使用find参数启动,执行将运行直到找到与查找条件匹配的状态,该状态可以是要停止的指令的地址、要停止的地址列表或接受状态并返回是否它符合某些标准。

当活动存储中的任何状态与find条件匹配时,它们被放置在找到的存储中,并且执行终止然后,您可以探索找到的状态,或决定放弃它并继续其他状态。您还可以指定avoidfind. 当一个状态与avoid条件匹配时,它被放入避免的存储中,并继续执行。最后,num_find参数控制在返回之前应该找到的状态数,默认值为1当然,如果您在找到这么多解决方案之前用完了活动存储中的状态,则无论如何执行都会停止。

explorer's 中提供一系列地址find,就像您所做的那样,意味着其中任何一个都是可接受的成功条件。因为您没有提供任何num_find默认值1is 假定并execute返回第一个成功找到

显然,这是第一个密码成功的标准。由于avoid您提供参数,这是 angr 强制执行的如果avoid 达到条件,则终止当前探索路径,防止沿着该路径进行任何额外探索。在您的情况下,提供avoid第一个密码失败的任何路径意味着将立即丢弃第一个密码失败的任何路径。因此,无论有意与否 - 多亏了您的avoid,不可能在第一次密码测试中取得成功。

有几种方法可以正确解决这一挑战,我将详细介绍它们。

增加num_find一个以上的结果

有人可能认为适当的解决方案可能是增加到num_find一个以上,允许foundexplorer返回之前达到多个状态然而,这是一个错误。因为你的示例代码不确保密码实际上是相同的字符串,因为没有长度的验证和空终止不比较,它出现任何字符串开始"first_pass"应被视为一个有效的解决方案。有趣的是,这意外地允许有多个解决方案(实际上,相当多),因此增加num_find任何计算上可行的值将产生第一个密码的额外解决方案,永远不会达到第二个。

顺便说一句,要修复错误的密码验证条件,您可以验证长度匹配,使用字符串比较函数(例如strcmp和 导数),或者将条件从 更改<<=以使其包含空终止符。

分离两个测试

由于这两个场景彼此完全独立,因此您可以执行执行。每个密码一个,为每个密码设置一个findavoid值,并为第二个设置入口点。拆分两次执行将有效地创建一组更简单的约束,这些约束应该执行得更快。当然,您甚至可以并行运行它们以获得额外的性能优势。

正确指定成功标准

尽管您对find的价值观avoid最初看起来是正确的,但我们已经讨论了为什么它们实际上无法准确描述您的意图的几个原因。这些参数的更准确规范将设置avoid为两个printf("fail");语句,就像您所做的那样。但是,您需要删除第一个printf("gg")并只保留第二个,因为如果不传递两个密码就无法访问它(假设上面提到的逻辑错误已修复)。