angr 漏洞发现官方例子 —— strcpy_find

这个例子的程序比较简单

main函数

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t v3; // rbx@4
size_t v4; // rax@5
char s[8]; // [sp+10h] [bp-30h]@2
int i; // [sp+2Ch] [bp-14h]@2

if ( argc <= 2 )
{
func3(*argv, argv, envp);
}
else
{
strcpy(s, "Unu`mmx!onu!uid!q`rrvnse///");
for ( i = 0; ; ++i )
{
v3 = i;
if ( v3 >= strlen(s) )
break;
s[i] ^= 1u;
}
v4 = strlen(s);
if ( !strncmp(argv[1], s, v4) )
func(argv[2]);
else
func("Wrong password!");
}
return 0;
}

func3

1
2
3
4
5
6
7
int __fastcall func3(__int64 a1)
{
return printf(
"Usage: %s <password> <message_to_store>\n"
"Note: You can only post a custom message if you give the right password!\n",
a1);
}

func

1
2
3
4
5
6
7
int __fastcall func(const char *a1)
{
char dest; // [sp+10h] [bp-30h]@1

strcpy(&dest, a1);
return printf("Your Message: %s\n", &dest);
}

可以看到漏洞点是在func,那我们怎么写脚本呢

  1. 载入程序,获得控制流程图,进而获取strcpy函数和func3函数的地址
  2. 构造命令行参数,创建一个entry_state和simulation_manager
  3. 从simulation_manager中找到满足当前执行到strcpy,而且strcpy的源地址真是我们输入的argv[2]那时候的状态,我们再输出argv[1],那么就找到了如何到达那个strcpy的argv[1]了

完整代码如下:(跟着官方的代码敲了一下)

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# -*- coding: utf-8 -*-
import angr
import claripy

def main():
def getFuncAddress(func_name, plt = None):
# for addr,func in cfg.kb.functions.iteritems():
# if func.name == func_name and (plt is None or func.is_plt == plt):
# return func.addr
found = [
addr for addr,func in cfg.kb.functions.iteritems()
if func_name == func.name and (plt is None or func.is_plt == plt)
]
if len( found ) > 0:
print "Found "+func_name+"'s address at "+hex(found[0])+"!"
return found[0]
else:
raise Exception("No address found for function : "+func_name)

def check(state):
if (state.ip.args[0] == strcpy_addr):
BV_strCpySrc = state.memory.load(state.regs.rsi, len(argv[2]))
strCpySrc = state.solver.eval( BV_strCpySrc , cast_to=str )
return True if argv[2] in strCpySrc else False
else:
return False


project = angr.Project("./strcpy_test", load_options={'auto_load_libs':False})
# get control flow graph
cfg = project.analyses.CFG(fail_fast=True)
strcpy_addr = getFuncAddress("strcpy", True)
func3_addr = getFuncAddress("func3")

argv = [project.filename] #argv[0]
sym_arg_size = 40
sym_arg = claripy.BVS('sym_arg', 8*sym_arg_size)
argv.append(sym_arg) #argv[1]
argv.append("HAHAHAHA") # argv[2]

state = project.factory.entry_state(args=argv)
sm = project.factory.simulation_manager(state)

sm = sm.explore(find=check, avoid=(func3_addr,))
found = sm.found

if ( len( found ) > 0 ): # Make sure we found a path before giving the solution
found = sm.found[0]
result = found.solver.eval(argv[1], cast_to=str)
try:
result = result[:result.index('\0')]
except ValueError:
pass
else: # Aww somehow we didn't find a path. Time to work on that check() function!
result = "Couldn't find any paths which satisfied our conditions."

print 'The password is "%s"' % result

main()

运行结果:

1
2
3
4
(angr) angr@e766290917a5:~/angr-workdir/examples/strcpy_find$ python angrexp.py 
Found strcpy's address at 0x4004a0L!
Found func3's address at 0x40061d!
The password is "Totally not the password..."

验证

1
2
(angr) angr@e766290917a5:~/angr-workdir/examples/strcpy_find$ ./strcpy_test "Totally not the password..." "giantbranch test"
Your Message: giantbranch test

Reference

https://docs.angr.io/docs/examples.html#vulnerability-discovery

打赏专区