Angr-CTF学习笔记11-14
2022-10-29 21:28:46 Author: 安全狗的自我修养(查看原文) 阅读量:16 收藏

11_angr_sim_scanf

汇编代码:

.text:0804862A                 push    14h             ; n
.text:0804862C push 0 ; c
.text:0804862E lea eax, [ebp+key_string]
.text:08048631 push eax ; s
.text:08048632 call _memset
.text:08048637 add esp, 10h
.text:0804863A lea eax, [ebp+key_string]
.text:0804863D mov dword ptr [eax], 444E4848h
.text:08048643 mov dword ptr [eax+4], 50484156h ; 初始化Key-String
.text:0804864A mov [ebp+index], 0
.text:08048651 jmp short loc_8048680
.text:08048653 ; ---------------------------------------------------------------------------
.text:08048653
.text:08048653 loc_8048653: ; CODE XREF: main+8F↓j
.text:08048653 lea edx, [ebp+key_string]
.text:08048656 mov eax, [ebp+index]
.text:08048659 add eax, edx
.text:0804865B movzx eax, byte ptr [eax]
.text:0804865E movsx eax, al
.text:08048661 sub esp, 8
.text:08048664 push [ebp+index]
.text:08048667 push eax
.text:08048668 call complex_function ; complex_function() 对key-string 进行计算
.text:0804866D add esp, 10h
.text:08048670 mov ecx, eax
.text:08048672 lea edx, [ebp+key_string]
.text:08048675 mov eax, [ebp+index]
.text:08048678 add eax, edx
.text:0804867A mov [eax], cl
.text:0804867C add [ebp+index], 1
.text:08048680
.text:08048680 loc_8048680: ; CODE XREF: main+5C↑j
.text:08048680 cmp [ebp+index], 7
.text:08048684 jle short loc_8048653 ; for (index =0 index <=7 ; ++ index )

程序第一步先对Key-String 进行运算,该题的难点在于main() 里面多个scanf() .

text:08048689                 push    offset aEnterThePasswo ; "Enter the password: "
.text:0804868E call _printf
.text:08048693 add esp, 10h
.text:08048696 cmp [ebp+var_24], 0DEADBEEFh
.text:0804869D jnz loc_804C196
.text:080486A3 cmp [ebp+var_24], 0DEADBEEFh
.text:080486AA jnz loc_804A423
.text:080486B0 cmp [ebp+var_24], 0DEADBEEFh
.text:080486B7 jz loc_8049570
.text:080486BD cmp [ebp+var_24], 0DEADBEEFh
.text:080486C4 jnz loc_8048E1D
.text:080486CA cmp [ebp+var_24], 0DEADBEEFh
.text:080486D1 jz loc_8048A7A
.text:080486D7 cmp [ebp+var_24], 0DEADBEEFh
.text:080486DE jz loc_80488AF
.text:080486E4 cmp [ebp+var_24], 0DEADBEEFh
.text:080486EB jnz loc_80487D0
.text:080486F1 cmp [ebp+var_24], 0DEADBEEFh
.text:080486F8 jnz short loc_8048765
.text:080486FA sub esp, 4
.text:080486FD push offset buffer1
.text:08048702 push offset buffer0
.text:08048707 push offset aUU ; "%u %u"
.text:0804870C call ___isoc99_scanf
.text:08048711 add esp, 10h
.text:08048714 cmp [ebp+var_2C], 0
.text:08048718 jz short loc_8048758
.text:0804871A sub esp, 4
.text:0804871D push 4 ; n
.text:0804871F lea eax, [ebp+key_string]
.text:08048722 push eax ; s2
.text:08048723 push offset buffer0 ; s1
.text:08048728 call _strncmp
.text:0804872D add esp, 10h
.text:08048730 test eax, eax
.text:08048732 jnz short loc_8048758
.text:08048734 sub esp, 4
.text:08048737 push 4 ; n
.text:08048739 lea eax, [ebp+key_string]
.text:0804873C add eax, 4
.text:0804873F push eax ; s2
.text:08048740 push offset buffer1 ; s1
.text:08048745 call _strncmp
.text:0804874A add esp, 10h
.text:0804874D test eax, eax
.text:0804874F jnz short loc_8048758
.text:08048751 mov eax, 1
.text:08048756 jmp short loc_804875D
.text:08048758 ; ---------------------------------------------------------------------------
.text:08048758
.text:08048758 loc_8048758: ; CODE XREF: main+123↑j
.text:08048758 ; main+13D↑j ...
.text:08048758 mov eax, 0
.text:0804875D
.text:0804875D loc_804875D: ; CODE XREF: main+161↑j
.text:0804875D mov [ebp+var_2C], eax
.text:08048760 jmp loc_804FC81
.text:08048765 ; ---------------------------------------------------------------------------
.text:08048765
.text:08048765 loc_8048765: ; CODE XREF: main+103↑j
.text:08048765 sub esp, 4
.text:08048768 push offset buffer1
.text:0804876D push offset buffer0
.text:08048772 push offset aUU ; "%u %u"
.text:08048777 call ___isoc99_scanf
.text:0804877C add esp, 10h
.text:0804877F cmp [ebp+var_2C], 0
.text:08048783 jz short loc_80487C3
.text:08048785 sub esp, 4
.text:08048788 push 4 ; n
.text:0804878A lea eax, [ebp+key_string]
.text:0804878D push eax ; s2
.text:0804878E push offset buffer0 ; s1
.text:08048793 call _strncmp
.text:08048798 add esp, 10h
.text:0804879B test eax, eax
.text:0804879D jnz short loc_80487C3
.text:0804879F sub esp, 4
.text:080487A2 push 4 ; n
.text:080487A4 lea eax, [ebp+key_string]
.text:080487A7 add eax, 4
.text:080487AA push eax ; s2
.text:080487AB push offset buffer1 ; s1
.text:080487B0 call _strncmp
.text:080487B5 add esp, 10h
.text:080487B8 test eax, eax
.text:080487BA jnz short loc_80487C3
.text:080487BC mov eax, 1
.text:080487C1 jmp short loc_80487C8

我们的关注点在于scanf() ,这里标明了用户有两个输入,分别为4 字节.然后我们就需要Hook scanf() 来对buffer 进行符号构造,代码如下:

def main(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary)

initial_state = project.factory.entry_state()

class ReplacementScanf(angr.SimProcedure):

def run(self, format_string, scanf0_address, scanf1_address ):
scanf0 = claripy.BVS('scanf0', 4 * 8)
scanf1 = claripy.BVS('scanf1', 4 * 8)

self.state.memory.store(scanf0_address, scanf0, endness=project.arch.memory_endness)
self.state.memory.store(scanf1_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())

simulation = project.factory.simgr(initial_state)

def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job' in str(stdout_output)

def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again' in str(stdout_output)

simulation.explore(find=is_successful, avoid=should_abort)

if simulation.found:
solution_state = simulation.found[0]
stored_solutions0 = solution_state.globals['solution0']
stored_solutions1 = solution_state.globals['solution1']
solution0 = solution_state.se.eval(stored_solutions0)
solution1 = solution_state.se.eval(stored_solutions1)

print(solution0,solution1)

12_angr_veritesting

汇编代码:

.text:080485ED                 push    21h             ; n
.text:080485EF push 0 ; c
.text:080485F1 lea eax, [ebp+input_buffer]
.text:080485F4 push eax ; s
.text:080485F5 call _memset
.text:080485FA add esp, 10h
.text:080485FD sub esp, 0Ch
.text:08048600 push offset aEnterThePasswo ; "Enter the password: "
.text:08048605 call _printf
.text:0804860A add esp, 10h
.text:0804860D sub esp, 8
.text:08048610 lea eax, [ebp+input_buffer]
.text:08048613 push eax
.text:08048614 push offset a32s ; "%32s"
.text:08048619 call ___isoc99_scanf ; 用户输入一个32字节的Buffer
.text:0804861E add esp, 10h
.text:08048621 mov [ebp+var_3C], 0
.text:08048628 mov [ebp+var_34], 0
.text:0804862F mov [ebp+index], 0
.text:08048636 jmp short loc_8048666
.text:08048638 ; ---------------------------------------------------------------------------
.text:08048638
.text:08048638 loc_8048638: ; CODE XREF: main+A5↓j
.text:08048638 lea edx, [ebp+input_buffer]
.text:0804863B mov eax, [ebp+index]
.text:0804863E add eax, edx
.text:08048640 movzx eax, byte ptr [eax]
.text:08048643 movsx ebx, al
.text:08048646 mov eax, [ebp+index]
.text:08048649 add eax, 5Bh
.text:0804864C sub esp, 8
.text:0804864F push eax
.text:08048650 push 4Fh
.text:08048652 call complex_function ; 对用户输入进行计算
.text:08048657 add esp, 10h
.text:0804865A cmp ebx, eax
.text:0804865C jnz short loc_8048662
.text:0804865E add [ebp+var_3C], 1
.text:08048662
.text:08048662 loc_8048662: ; CODE XREF: main+97↑j
.text:08048662 add [ebp+index], 1
.text:08048666
.text:08048666 loc_8048666: ; CODE XREF: main+71↑j
.text:08048666 cmp [ebp+index], 1Fh
.text:0804866A jle short loc_8048638
.text:0804866C cmp [ebp+var_3C], ' '
.text:08048670 jnz short loc_804868C
.text:08048672 movzx eax, byte ptr [ebp+var_C]
.text:08048676 test al, al
.text:08048678 jnz short loc_804868C ; 对输入进行计算
.text:0804867A sub esp, 0Ch
.text:0804867D push offset aGoodJob ; "Good Job."
.text:08048682 call _puts
.text:08048687 add esp, 10h
.text:0804868A jmp short loc_804869C
.text:0804868C ; ---------------------------------------------------------------------------
.text:0804868C
.text:0804868C loc_804868C: ; CODE XREF: main+AB↑j
.text:0804868C ; main+B3↑j
.text:0804868C sub esp, 0Ch
.text:0804868F push offset s ; "Try again."
.text:08048694 call _puts
.text:08048699 add esp, 10h

这个示例和01 题是一样的,唯独不同的一点是这个循环比之前的要大,导致直接用01 题的解题方法不能直接计算出结果,因为循环过大导致路径爆炸,所以在执行的时候会消耗很多资源.

幸运的是,project.factory.simgr() 函数提供veritesting 参数来指定是否要自动合并路径,避免路径爆炸的问题.具体细节参考论文:https://users.ece.cmu.edu/~dbrumley/pdf/Avgerinos et al._2014_Enhancing Symbolic Execution with Veritesting.pdf

import angr
import sys

project = angr.Project(sys.argv[1])
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state,veritesting = True)

def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output) # :boolean

def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output) # :boolean

simulation.explore(find = is_successful,avoid = should_abort)

if simulation.found :
solution_state = simulation.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))

Angr函数使用总结:

project.factory.simgr(初始化状态,veritesting = True) => veritesting 默认为False

13_angr_static_binary

汇编代码:

.text:08048953                 push    offset aEnterThePasswo ; "Enter the password: "
.text:08048958 call printf
.text:0804895D add esp, 10h
.text:08048960 sub esp, 8
.text:08048963 lea eax, [ebp+s1]
.text:08048966 push eax
.text:08048967 push offset a8s ; "%8s"
.text:0804896C call __isoc99_scanf ; 用户输入
.text:08048971 add esp, 10h
.text:08048974 mov [ebp+var_38], 0
.text:0804897B jmp short loc_80489AA
.text:0804897D ; ---------------------------------------------------------------------------
.text:0804897D
.text:0804897D loc_804897D: ; CODE XREF: main+B0↓j
.text:0804897D lea edx, [ebp+s1]
.text:08048980 mov eax, [ebp+var_38]
.text:08048983 add eax, edx
.text:08048985 movzx eax, byte ptr [eax]
.text:08048988 movsx eax, al
.text:0804898B sub esp, 8
.text:0804898E push [ebp+var_38]
.text:08048991 push eax
.text:08048992 call complex_function
.text:08048997 add esp, 10h
.text:0804899A mov ecx, eax
.text:0804899C lea edx, [ebp+s1]
.text:0804899F mov eax, [ebp+var_38]
.text:080489A2 add eax, edx
.text:080489A4 mov [eax], cl
.text:080489A6 add [ebp+var_38], 1
.text:080489AA
.text:080489AA loc_80489AA: ; CODE XREF: main+7D↑j
.text:080489AA cmp [ebp+var_38], 7
.text:080489AE jle short loc_804897D ; 使用for 循环不断调用complex_function() 对数据进行计算
.text:080489B0 sub esp, 8
.text:080489B3 lea eax, [ebp+s2]
.text:080489B6 push eax ; s2
.text:080489B7 lea eax, [ebp+s1]
.text:080489BA push eax ; s1
.text:080489BB call _strcmp
.text:080489C0 add esp, 10h
.text:080489C3 test eax, eax
.text:080489C5 jz short loc_80489D9
.text:080489C7 sub esp, 0Ch
.text:080489CA push offset aTryAgain ; "Try again."
.text:080489CF call puts
.text:080489D4 add esp, 10h
.text:080489D7 jmp short loc_80489E9
.text:080489D9 ; ---------------------------------------------------------------------------
.text:080489D9
.text:080489D9 loc_80489D9: ; CODE XREF: main+C7↑j
.text:080489D9 sub esp, 0Ch
.text:080489DC push offset aGoodJob ; "Good Job."
.text:080489E1 call puts
.text:080489E6 add esp, 10h
.text:080489E9

这个示例的逻辑和01 题是一样的,主要不同的地方是在于这个程序是静态链接编译的,所以程序中包含了一些libc 的函数实现,但是这里可能会存在两个问题:1.这些函数里面隐藏一些出题人的坑;2.这些函数里面的实现可能会依赖其他的系统函数或者实现方式不相同.所以12 题主要是让我们通过Hook 的方式重定向函数中被调用的libc 的函数

首先,Linux 下启动main() 函数需要通过__libc_start_main 对程序进行初始化,然后再跳转到main() 函数;其次,在main() 函数里面调用了printf ,scanf ,puts ,所以我们需要通过Hook 来重定向它们.

幸运的是,我们不需要重新实现这些函数的实现,Angr 代码库里面已经帮我们实现了一部分libc 的函数库,所以我们只需要倒入它们即可.

import angr
import sys

project = angr.Project(sys.argv[1])
initial_state = project.factory.entry_state()
simulation = project.factory.simgr(initial_state,veritesting = True)

project.hook(0x804ed40, angr.SIM_PROCEDURES['libc']['printf']())
project.hook(0x804ed80, angr.SIM_PROCEDURES['libc']['scanf']())
project.hook(0x804f350, angr.SIM_PROCEDURES['libc']['puts']())
project.hook(0x8048d10, angr.SIM_PROCEDURES['glibc']['__libc_start_main']())

def is_successful(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Good Job.' in str(stdout_output) # :boolean

def should_abort(state):
stdout_output = state.posix.dumps(sys.stdout.fileno())
return 'Try again.' in str(stdout_output) # :boolean

simulation.explore(find = is_successful,avoid = should_abort)

if simulation.found :
solution_state = simulation.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))

Angr函数使用总结:

angr.SIM_PROCEDURES[ 系统库名 ] [ 系统函数名 ] () => 获取Angr 内部实现的系统函数

14_angr_shared_library

编译14_angr_shared_library 存在一个小坑,就是在执行命令Python generate.py 1234 14_angr_shared_library 时会报错,内容如下:

[email protected]:~/angr_ctf/14_angr_shared_library# python generate.py 1234 14_angr_shared_library
gcc: error: 14_angr_shared_library: No such file or directory

这是因为generate.py 里面有一个Bug ,在最后的一个gcc 编译命令因为-L 参数缺少了指定当前目录,导致在寻找lib14_angr_shared_library.so 的时候找到了系统库目录,所以gcc 抛出了这个找不到14_angr_shared_library: No such file or directory 的问题,代码修改如下:

  with tempfile.NamedTemporaryFile(delete=False, suffix='.c') as temp:
temp.write(c_code)
temp.seek(0)
- os.system('gcc -m32 -I . -L ' + '/'.join(output_file.split('/')[0:-1]) + ' -o ' + output_file + ' ' + temp.name + ' -l' + output_file.split('/')[-1])
+ os.system('gcc -m32 -I . -L . ' + '/'.join(output_file.split('/')[0:-1]) + ' -o ' + output_file + ' ' + temp.name + ' -l' + output_file.split('/')[-1])

程序汇编代码如下:

.text:080486A2                 push    10h             ; n
.text:080486A4 push 0 ; c
.text:080486A6 lea eax, [ebp+s]
.text:080486A9 push eax ; s
.text:080486AA call _memset
.text:080486AF add esp, 10h
.text:080486B2 sub esp, 0Ch
.text:080486B5 push offset format ; "Enter the password: "
.text:080486BA call _printf
.text:080486BF add esp, 10h
.text:080486C2 sub esp, 8
.text:080486C5 lea eax, [ebp+s]
.text:080486C8 push eax
.text:080486C9 push offset a8s ; "%8s"
.text:080486CE call ___isoc99_scanf ; 用户输入
.text:080486D3 add esp, 10h
.text:080486D6 sub esp, 8
.text:080486D9 push 8
.text:080486DB lea eax, [ebp+s]
.text:080486DE push eax
.text:080486DF call _validate ; 调用验证
.text:080486E4 add esp, 10h
.text:080486E7 test eax, eax
.text:080486E9 jz short loc_80486FD
.text:080486EB sub esp, 0Ch
.text:080486EE push offset s ; "Good Job."
.text:080486F3 call _puts

_validate() 函数是在另一个so 库中存在的,我们继续分析完当前程序的代码

.plt:08048550 _validate       proc near               ; CODE XREF: main+64↓p
.plt:08048550 jmp ds:off_804A020
.plt:08048550 _validate endp

.got.plt:0804A020 off_804A020 dd offset validate

extern:0804A04C extrn validate:near

我们来分析一下lib14_angr_shared_library.so 的代码:

.text:000006D7                 public validate
.text:000006D7 validate proc near ; DATA XREF: LOAD:00000250↑o
.text:000006D7
.text:000006D7 s2 = byte ptr -24h
.text:000006D7 var_10 = dword ptr -10h
.text:000006D7 var_C = dword ptr -0Ch
.text:000006D7 s1 = dword ptr 8
.text:000006D7 arg_4 = dword ptr 0Ch
.text:000006D7
.text:000006D7 ; __unwind {
.text:000006D7 push ebp
.text:000006D8 mov ebp, esp
.text:000006DA push esi
.text:000006DB push ebx
.text:000006DC sub esp, 20h
.text:000006DF call __x86_get_pc_thunk_bx
.text:000006E4 add ebx, 191Ch
.text:000006EA cmp [ebp+arg_4], 7
.text:000006EE jg short loc_6FA
.text:000006F0 mov eax, 0
.text:000006F5 jmp loc_77D
.text:000006FA ; ---------------------------------------------------------------------------
.text:000006FA

; .....

14 题主要是把程序逻辑分离在一个执行程序和动态链接库,我们直接对动态链接库中的_validate 函数进行符号执行,解决的solver.py 如下:

def main(argv):
path_to_binary = sys.argv[1] # 注意我们是要load so 库而不是执行程序

base = 0x400000 # base 基址是随意定的,可以随意修改
project = angr.Project(path_to_binary, load_options={
'main_opts' : {
'custom_base_addr' : base
}
})

buffer_pointer = claripy.BVV(0x3000000, 32) # 创建一个buffer 指针值
validate_function_address = base + 0x6D7
initial_state = project.factory.call_state(validate_function_address, buffer_pointer,claripy.BVV(8, 32)) # 调用validate_function,因为函数声明validata_function(buffer_point,buffer_length) ,所以我们构造出调用validata_function(0x3000000,0x8) .

password = claripy.BVS('password', 8 * 8) # 创建一个求解对象,大小为8 字节
initial_state.memory.store(buffer_pointer, password) # 保存到0x30000000

simulation = project.factory.simgr(initial_state)

simulation.explore(find = base + 0x783) # 执行到validate 函数的RETN 指令

if simulation.found:
solution_state = simulation.found[0]

solution_state.add_constraints(solution_state.regs.eax != 0) # 记得,我们要求validate 函数的返回值为1 的时候就是有解的,那么我们就需要在求解的时候添加上这么一个求解约束条件EAX 不能为False .
solution = solution_state.se.eval(password)
print(solution)


文章来源: http://mp.weixin.qq.com/s?__biz=MzkwOTE5MDY5NA==&mid=2247485809&idx=4&sn=89e5435f8a72802e7111a73130f32c4e&chksm=c13f3a38f648b32e8a3d859d64569ea0abfea1d962973af62045edd6c949ab9b6df228dd29bd#rd
如有侵权请联系:admin#unsafe.sh