时时勤拂拭,勿使惹尘埃

TOC

Categories

Angr CTF(一)


0x0 前言

最近打了一个ctf,几个Re题尝试用angr来做,但写的脚本跑了一天都没跑出结果,太菜了,还是不太会用angr。

遂找了一套angr ctf题来练练:https://github.com/jakespringer/angr_ctf

参考:https://github.com/ZERO-A-ONE/AngrCTF_FITM

00_angr_find——基础用法

这是一道最基本的入门题,运行会提示要求输入密码:

$ ./00_angr_find 
Enter the password: asdf;kl
Try again.

反编译伪代码如下,密码变换处理后最后与”JACEJGCS”比较,正确输出”Good Job.”,错误输出”Try again.”:

  local_c = (undefined *)&param_1;
  local_14 = *(int *)(in_GS_OFFSET + 0x14);
  printf("Enter the password: ");
  __isoc99_scanf(&DAT_08048753,local_1d);
  for (local_24 = 0; local_24 < 8; local_24 = local_24 + 1) {
    cVar1 = complex_function((int)local_1d[local_24],local_24);
    local_1d[local_24] = cVar1;
  }
  iVar2 = strcmp(local_1d,"JACEJGCS");
  if (iVar2 == 0) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }

使用angr求解,主要需要找到输出”Good Job.”的代码位置:08048678

                      LAB_08048675                          XREF[1]:  08048661(j)  
     08048675 83 ec 0c    SUB      ESP,0xc
     08048678 68 60       PUSH     s_Good_Job._08048760                  = "Good Job."
             87 04 08
     0804867d e8 7e       CALL     <EXTERNAL>::puts                      int puts(char * __s)
             fd ff ff
     08048682 83 c4 10    ADD      ESP,0x10
                      LAB_08048685                          XREF[1]:  08048673(j)  
     08048685 b8 00       MOV      EAX,0x0
             00 00 00
     0804868a 8b 4d f4    MOV      ECX,dword ptr [EBP + local_14]
     0804868d 65 33       XOR      ECX,dword ptr GS:[0x14]
             0d 14 
             00 00 00
     08048694 74 05       JZ       LAB_0804869b
     08048696 e8 55       CALL     <EXTERNAL>::__stack_chk_fail          undefined
__stack_chk_fai
             fd ff ff
                      -- Flow Override: CALL_RETURN (CALL_TERMINATOR)

修改angr脚本,angr ctf给每个题都写好了模板,只需要修改几个关键信息即可:

# 加载angr模块
import angr
import sys

def main(argv):
  # 加载目标文件,创建project
  path_to_binary = "./00_angr_find"  # :string
  project = angr.Project(path_to_binary)

  # 设置state
  # state代表程序的一个实例镜像,模拟执行某个时刻的状态,就类似于快照。保存运行状态的上下文信息,如内存/寄存器等,这里使用project.factory.entry_state()告诉符号执行引擎从程序的入口点开始符号执行
  initial_state = project.factory.entry_state()

  # 设置 Simulation Managers
  # SimState 对象包含程序运行时信息,如内存/寄存器/文件系统数据等。SM(Simulation Managers)是angr中最重要的控制接口,它使你能够同时控制一组状态(state)的符号执行,应用搜索策略来探索程序的状态空间。
  simulation = project.factory.simgr(initial_state)

  # 运行,探索满足路径需要的值
  # 符号执行最普遍的操作是找到能够到达某个地址的状态,同时丢弃其他不能到达这个地址的状态。SM为使用这种执行模式提供了.explore()方法
  # 当使用find参数启动.explore()方法时,程序将会一直执行,直到发现了一个和find参数指定的条件相匹配的状态。find参数的内容可以是想要执行到的某个地址、或者想要执行到的地址列表、或者一个获取state作为参数并判断这个state是否满足某些条件的函数。当activestash中的任意状态和find中的条件匹配的时候,它们就会被放到found stash中,执行随即停止。之后你可以探索找到的状态,或者决定丢弃它,转而探索其它状态。
  print_good_address = 0x08048678  # :integer (probably in hexadecimal)
  simulation.explore(find=print_good_address)

  # 获取执行结果
  if simulation.found:
    # 此时相关的状态已经保存在了simgr当中,可以通过simgr.found来访问所有符合条件的分支,这里为了解题,就选择第一个符合条件的分支即可
    solution_state = simulation.found[0]
    print(solution_state.posix.dumps(sys.stdin.fileno()))
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)

运行angr脚本,获取密码,验证通过

$ python scaffold00.py 
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-24 15:41:36,064 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-24 15:41:36,065 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80486b1 (__libc_csu_init+0x1 in 00_angr_find (0x80486b1))
WARNING  | 2023-12-24 15:41:36,066 | angr.storage.memory_mixi ns.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x80486b3 (__libc_csu_init+0x3 in 00_angr_find (0x80486b3)) 
WARNING  | 2023-12-24 15:41:37,355 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff60 with 4 unconstrained bytes referenced from 0x8199fa0 (strcmp+0x0 in libc.so.6 (0x99fa0))
b'JXWVXRKX'
$ ./00_angr_find 
Enter the password: JXWVXRKX
Good Job.

01_angr_avoid——条件收敛

这题跟上面一样

$ ./01_angr_avoid 
Enter the password: asdflk
Try again.

找到正确输出位置,定位到”Good Job.”

void maybe_good(char *param_1,char *param_2)
{
  int iVar1;
  if ((should_succeed != '\0') && (iVar1 = strncmp(param_1,param_2,8), iVar1 == 0)) {
    puts("Good Job.");
    return;
  }
  puts("Try again.");
  return;
}

确定”Good Job.”代码位置:080485e0

     080485d6 83 c4 10    ADD      ESP,0x10
     080485d9 85 c0       TEST     EAX,EAX
     080485db 75 12       JNZ      LAB_080485ef
     080485dd 83 ec 0c    SUB      ESP,0xc
     080485e0 68 1e       PUSH     s_Good_Job._080d461e                  = "Good Job."
             46 0d 08
     080485e5 e8 e6       CALL     <EXTERNAL>::puts                      int puts(char * __s)
             fd ff ff
     080485ea 83 c4 10    ADD      ESP,0x10
     080485ed eb 10       JMP      LAB_080485ff
                      LAB_080485ef                          XREF[2]:  080485c4(j), 080485db(j)  
     080485ef 83 ec 0c    SUB      ESP,0xc
     080485f2 68 13       PUSH     s_Try_again._080d4613                 = "Try again."
             46 0d 08
     080485f7 e8 d4       CALL     <EXTERNAL>::puts                      int puts(char * __s)
             fd ff ff
     080485fc 83 c4 10    ADD      ESP,0x10

完善angr脚本,不过这次不只查找达到print_good_address的状态,还要查找没有达到will_not_succeed_address的状态。
通常二进制文件都非常庞大,为了节省时间,添加一些收敛条件可以减少计算量。

import angr
import sys
def main(argv):
  path_to_binary = "./01_angr_avoid"
  project = angr.Project(path_to_binary)
  initial_state = project.factory.entry_state()
  simulation = project.factory.simgr(initial_state)

  print_good_address = 0x080485e0
  will_not_succeed_address = 0x080485a8
  simulation.explore(find=print_good_address, avoid=will_not_succeed_address)
  if simulation.found:
    solution_state = simulation.found[0]
    print(solution_state.posix.dumps(sys.stdin.fileno()))
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)

本次will_not_succeed_address选择多处调用的avoid_me

void avoid_me(void)
{
  should_succeed = 0;
  return;
}

运行angr脚本,获取密码,验证通过

$ python scaffold01.py 
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-24 16:09:32,082 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-24 16:09:32,083 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x80d4591 (__libc_csu_init+0x1 in 01_angr_avoid (0x80d4591))
WARNING  | 2023-12-24 16:09:32,084 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x80d4593 (__libc_csu_init+0x3 in 01_angr_avoid (0x80d4593))
WARNING  | 2023-12-24 16:09:35,100 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff3d with 11 unconstrained bytes referenced from 0x819a410 (strncmp+0x0 in libc.so.6 (0x9a410))
WARNING  | 2023-12-24 16:09:35,100 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff60 with 4 unconstrained bytes referenced from 0x819a410 (strncmp+0x0 in libc.so.6 (0x9a410))
b'HUJOZMYS'
$ ./01_angr_avoid 
Enter the password: HUJOZMYS
Good Job.

02_angr_find_condition——动态获取state

这题主要想教会如何动态获取state,即不用每次都分析具体的代码位置,而是通过检索字符串自动获取

$ ./02_angr_find_condition 
Enter the password: df;l
Try again.

代码逻辑依然是正确输出”Good Job.”,错误输出”Try again.”

  local_c = (undefined *)&param_1;
  local_14 = *(int *)(in_GS_OFFSET + 0x14);
  for (local_48 = 0; local_48 < 0x14; local_48 = local_48 + 1) {
    *(undefined *)((int)&local_28 + local_48) = 0;
  }
  local_28 = 0x52525856;
  local_24 = 0x5255454a;
  printf("Enter the password: ");
  __isoc99_scanf(&DAT_0804d333,local_3c);
  for (local_44 = 0; local_44 < 8; local_44 = local_44 + 1) {
    cVar1 = complex_function((int)local_3c[local_44],local_44 + 8);
    local_3c[local_44] = cVar1;
  }
  iVar2 = strcmp(local_3c,(char *)&local_28);
  if (iVar2 == 0) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }
  if (local_14 == *(int *)(in_GS_OFFSET + 0x14)) {
    return 0;
  }

由于使用动态获取state,直接修改脚本,检索字符串,但这里实际是字节对象,所以必须使用b’Good Job.’而不是仅”Good Job.”

import angr
import sys
def main(argv):
  path_to_binary = "./02_angr_find_condition" 
  project = angr.Project(path_to_binary)
  initial_state = project.factory.entry_state()
  simulation = project.factory.simgr(initial_state)
  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Good Job.' in stdout_output:
        return True
    else: 
        return False

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.' in  stdout_output:
        return True
    else: 
        return False
  simulation.explore(find=is_successful, avoid=should_abort)
  if simulation.found:
    solution_state = simulation.found[0]
    print(solution_state.posix.dumps(sys.stdin.fileno()))
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)

运行angr脚本,获取密码,验证通过

$ python scaffold02.py 
WARNING  | 2023-12-24 16:40:09,183 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-24 16:40:09,184 | angr.storage.memory_mixins.default_filler_mixin | Filling register edi with 4 unconstrained bytes referenced from 0x804d291 (__libc_csu_init+0x1 in 02_angr_find_condition (0x804d291))
WARNING  | 2023-12-24 16:40:09,185 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebx with 4 unconstrained bytes referenced from 0x804d293 (__libc_csu_init+0x3 in 02_angr_find_condition (0x804d293))
WARNING  | 2023-12-24 16:40:10,374 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff2d with 11 unconstrained bytes referenced from 0x8199fa0 (strcmp+0x0 in libc.so.6 (0x99fa0))
WARNING  | 2023-12-24 16:40:10,374 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffeff50 with 4 unconstrained bytes referenced from 0x8199fa0 (strcmp+0x0 in libc.so.6 (0x99fa0))
b'HETOBRCU'
$ ./02_angr_find_condition 
Enter the password: HETOBRCU
Good Job.

03_angr_symbolic_registers——寄存器

$ ./03_angr_symbolic_registers 
Enter the password: asdfa
Try again.

这次的输入是在get_user_input函数里,调用的scanf获取格式化字符串,”%x %x %x”说明使用三个十六进制值作为输入

undefined4 main(void)
{
  int iVar1;
  int iVar2;
  int iVar3;
  undefined4 unaff_EBX;
  undefined8 uVar4;
  printf("Enter the password: ");
  uVar4 = get_user_input();
  iVar1 = complex_function_1((int)uVar4);
  iVar2 = complex_function_2(unaff_EBX);
  iVar3 = complex_function_3((int)((ulonglong)uVar4 >> 0x20));
  if (((iVar1 == 0) && (iVar2 == 0)) && (iVar3 == 0)) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }
  return 0;
}
undefined8 get_user_input(void)
{
  int in_GS_OFFSET;
  undefined4 local_1c;
  undefined local_18 [4];
  undefined4 local_14;
  int local_10;
  local_10 = *(int *)(in_GS_OFFSET + 0x14);
  __isoc99_scanf("%x %x %x",&local_1c,local_18,&local_14);
  if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return CONCAT44(local_14,local_1c);
}

查看汇编代码,这里使用EAX、EBX、EDX三个寄存器来存储

     08048919 89 4d f4    MOV      dword ptr [EBP + local_10],ECX
     0804891c 31 c9       XOR      ECX,ECX
     0804891e 8d 4d f0    LEA      ECX=>local_14,[EBP + -0x10]
     08048921 51          PUSH     ECX
     08048922 8d 4d ec    LEA      ECX=>local_18,[EBP + -0x14]
     08048925 51          PUSH     ECX
     08048926 8d 4d e8    LEA      ECX=>local_1c,[EBP + -0x18]       08048929 51          PUSH     ECX
     0804892a 68 93       PUSH     s_%x_%x_%x_08048a93                   = "%x %x %x"
             8a 04 0
     0804892f e8 9c       CALL     <EXTERNAL>::__isoc99_scanf            undefined __isoc99_scanf()
             fa ff ff
     08048934 83 c4 10    ADD      ESP,0x10
     08048937 8b 4d e8    MOV      ECX,dword ptr [EBP + local_1c]
     0804893a 89 c8       MOV      EAX,ECX
     0804893c 8b 4d ec    MOV      ECX,dword ptr [EBP + local_18]
     0804893f 89 cb       MOV      EBX,ECX
     08048941 8b 4d f0    MOV      ECX,dword ptr [EBP + local_14]
     08048944 89 ca       MOV      EDX,ECX     08048946 90          NOP
     08048947 8b 4d f4    MOV      ECX,dword ptr [EBP + local_10]
     0804894a 65 33       XOR      ECX,dword ptr GS:[0x14]
             0d 14 
             00 00 00
     08048951 74 05       JZ       LAB_08048958
     08048953 e8 48       CALL     <EXTERNAL>::__stack_chk_fail          undefined __stack_chk_fai
             fa ff ff
                      -- Flow Override: CALL_RETURN (CALL_TERMINATOR)

那么 start_address选择get_user_input返回后的地址:08048980

     0804896e 68 9c       PUSH     s_Enter_the_password:_08048a9c        = "Enter the password: "
             8a 04 08
     08048973 e8 18       CALL     <EXTERNAL>::printf                    int printf(char * __forma
             fa ff ff
     08048978 83 c4 10    ADD      ESP,0x10
     0804897b e8 8c       CALL     get_user_input                        undefined get_user_input()
             ff ff ff
     08048980 89 45 ec    MOV      dword ptr [EBP + local_1c],EAX
     08048983 89 5d f0    MOV      dword ptr [EBP + local_18],EBX
     08048986 89 55 f4    MOV      dword ptr [EBP + local_14],EDX
     08048989 83 ec 0c    SUB      ESP,0xc
     0804898c ff 75 ec    PUSH     dword ptr [EBP + local_1c]
     0804898f e8 75       CALL     complex_function_1                    undefined complex_functio
             fb ff ff
     08048994 83 c4 10    ADD      ESP,0x10
     08048997 89 c1       MOV      ECX,EAX
     08048999 89 4d ec    MOV      dword ptr [EBP + local_1c],ECX
     0804899c 83 ec 0c    SUB      ESP,0xc
     0804899f ff 75 f0    PUSH     dword ptr [EBP + local_18]
     080489a2 e8 b3       CALL     complex_function_2                    undefined complex_functio
             fc ff ff
     080489a7 83 c4 10    ADD      ESP,0x10
     080489aa 89 c1       MOV      ECX,EAX
     080489ac 89 4d f0    MOV      dword ptr [EBP + local_18],ECX
     080489af 83 ec 0c    SUB      ESP,0xc
     080489b2 ff 75 f4    PUSH     dword ptr [EBP + local_14]
     080489b5 e8 23       CALL     complex_function_3                    undefined complex_functio
             fe ff ff

修改angr脚本,这里需要计算3个寄存器的值

import angr
import claripy
import sys
def main(argv):
  path_to_binary = "./03_angr_symbolic_registers"
  project = angr.Project(path_to_binary)
  start_address = 0x08048980  # :intege (probably hexadecimal)  
  initial_state = project.factory.blank_state(addr=start_address)
  passwd_size_in_bits = 32
  passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
  passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
  passwd2 = claripy.BVS('passwd2', passwd_size_in_bits)
  initial_state.regs.eax = passwd0
  initial_state.regs.ebx = passwd1
  initial_state.regs.edx = passwd2
  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Good Job.' in stdout_output:
        return True
    else: 
        return False

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.' in  stdout_output:
        return True
    else: 
        return False

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

  if simulation.found:
    for i in simulation.found:
      solution_state = i
      solution0 = format(solution_state.solver.eval(passwd0), 'x')
      solution1 = format(solution_state.solver.eval(passwd1), 'x')
      solution2 = format(solution_state.solver.eval(passwd2), 'x')
      solution = solution0 + " " + solution1 + " " + solution2
      print(solution)
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main(sys.argv)

运行angr脚本,获取密码,验证通过

$ python scaffold03.py 
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-24 23:12:29,365 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048980 (main+0x26 in 03_angr_symbolic_registers (0x8048980))
b9ffd04e ccf63fe8 8fd4d959
$ ./03_angr_symbolic_registers 
Enter the password: b9ffd04e
ccf63fe8
8fd4d959
Good Job.

0 评论:

发表评论