时时勤拂拭,勿使惹尘埃

TOC

Categories

Angr CTF(二)


04_angr_symbolic_stack——栈 

$ ./04_angr_symbolic_stack 
Enter the password: asdf
Try again.

这题跟上一题有点不一样,scanf获取数据后保存在栈上,不是寄存器

void handle_user(void)

{
  int local_14;
  int local_10 [3];

  __isoc99_scanf("%u %u",local_10,&local_14);
  local_10[0] = complex_function0(local_10[0]);
  local_14 = complex_function1(local_14);
  if ((local_10[0] == 0x5019fa83) && (local_14 == -0x13d421a4)) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }
  return;
}

查看汇编代码,两个参数分别保存在[ebp+var_10]和[ebp+var_C],scanf返回后的第一行代码“add esp, 10h”,是用于“ call ___isoc99_scanf”后平衡堆栈的,那么start_address选择后面第二行代码“.text:08048697 mov eax, [ebp+var_C]”

.text:08048679 var_10          = dword ptr -10h
.text:08048679 var_C           = dword ptr -0Ch
.text:08048679
.text:08048679 ; __unwind {
.text:08048679                 push    ebp
.text:0804867A                 mov     ebp, esp
.text:0804867C                 sub     esp, 18h
.text:0804867F                 sub     esp, 4
.text:08048682                 lea     eax, [ebp+var_10]
.text:08048685                 push    eax
.text:08048686                 lea     eax, [ebp+var_C]
.text:08048689                 push    eax
.text:0804868A                 push    offset aUU      ; "%u %u"
.text:0804868F                 call    ___isoc99_scanf
.text:08048694                 add     esp, 10h;平衡堆栈
.text:08048697                 mov     eax, [ebp+var_C];start_address
.text:0804869A                 sub     esp, 0Ch
.text:0804869D                 push    eax
.text:0804869E                 call    complex_function0
.text:080486A3                 add     esp, 10h
.text:080486A6                 mov     [ebp+var_C], eax
.text:080486A9                 mov     eax, [ebp+var_10]
.text:080486AC                 sub     esp, 0Ch
.text:080486AF                 push    eax
.text:080486B0                 call    complex_function1
.text:080486B5                 add     esp, 10h
.text:080486B8                 mov     [ebp+var_10], eax
.text:080486BB                 mov     eax, [ebp+var_C]
.text:080486BE                 cmp     eax, 773024D1h
.text:080486C3                 jnz     short loc_80486CF
.text:080486C5                 mov     eax, [ebp+var_10]
.text:080486C8                 cmp     eax, 0BC4311CFh
.text:080486CD                 jz      short loc_80486E1
.text:080486CF
.text:080486CF loc_80486CF:                            ; CODE XREF: handle_user+4A↑j
.text:080486CF                 sub     esp, 0Ch
.text:080486D2                 push    offset s        ; "Try again."
.text:080486D7                 call    _puts
.text:080486DC                 add     esp, 10h
.text:080486DF                 jmp     short loc_80486F1
.text:080486E1 ; ---------------------------------------------------------------------------
.text:080486E1
.text:080486E1 loc_80486E1:                            ; CODE XREF: handle_user+54↑j
.text:080486E1                 sub     esp, 0Ch
.text:080486E4                 push    offset aGoodJob ; "Good Job."
.text:080486E9                 call    _puts
.text:080486EE                 add     esp, 10h
.text:080486F1
.text:080486F1 loc_80486F1:                            ; CODE XREF: handle_user+66↑j
.text:080486F1                 nop
.text:080486F2                 leave
.text:080486F3                 retn
.text:080486F3 ; } // starts at 8048679

修改angr脚本,需要注意实际栈空间

import angr
import claripy
import sys

def main(argv):
  path_to_binary = "./04_angr_symbolic_stack"
  project = angr.Project(path_to_binary)

  # start_address选择scanf返回后的位置
  start_address = 0x8048697
  initial_state = project.factory.blank_state(addr=start_address)

  # 将ESP指针恢复到和EBP指针一致,恢复栈帧初始状态,方便计算
  initial_state.regs.ebp = initial_state.regs.esp

  # 程序需要两个无符号的整数值(%u %u,即unsigned int),因此符号位向量的大小将为32位
  passwd_size_in_bits = 32
  passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
  passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)

  # 两个参数都是32bits,也就是4字节,所以总共需要8字节,也就是ESP - 0x8
  padding_length_in_bytes = 0x8  # :integer
  initial_state.regs.esp -= padding_length_in_bytes

  initial_state.stack_push(passwd0)  
  initial_state.stack_push(passwd1)

  simulation = project.factory.simgr(initial_state)

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

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.\n' 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 = (solution_state.solver.eval(passwd0))
        solution1 = (solution_state.solver.eval(passwd1))
        print("[+] Success! Solution is: {0} {1}".format(solution0, solution1))

  else:
    raise Exception('Could not find the solution')

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

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

$ python3 scaffold04.py 
[+] Success! Solution is: 1704280884 2382341151
$ ./04_angr_symbolic_stack 
Enter the password: 1704280884
2382341151
Good Job.

05_angr_symbolic_memory——内存

$ ./05_angr_symbolic_memory 
Enter the password: asdfa
asdf
dfs
adg
Try again.

程序将四个8字节长的字符串作为输入,字符串分别位于以下地址[user_input=0xA1BA1C0, 0xA1BA1C8, 0xA1BA1D0, 0xA1BA1D8],输入的字符串循环输入complex_function()函数进行变换,循环变换后的字符串与 “NJPURZPCDYEAXCSJZJMPSOMBFDDLHBVN”比较前0x20个字符

undefined4 main(void)

{
  undefined uVar1;
  int iVar2;
  int local_14;

  memset(user_input,0,0x21);
  printf("Enter the password: ");
  __isoc99_scanf("%8s %8s %8s %8s",user_input,0xa623108,0xa623110,0xa623118);
  for (local_14 = 0; local_14 < 0x20; local_14 = local_14 + 1) {
    uVar1 = complex_function((int)(char)user_input[local_14],local_14);
    user_input[local_14] = uVar1;
  }
  iVar2 = strncmp(user_input,"GLCOREPCFIMXROLPXJNEXMLKSEIMFROR",0x20);
  if (iVar2 == 0) {
    puts("Good Job.");
  }
  else {
    puts("Try again.");
  }
  return 0;
}

start_address照样选择scanf返回后的地址:08048601

     080485e0 68 d8       PUSH     user_input[24]
             a1 1b 0a
     080485e5 68 d0       PUSH     user_input[16]
             a1 1b 0a
     080485ea 68 c8       PUSH     user_input[8]
             a1 1b 0a
     080485ef 68 c0       PUSH     user_input                            = ??
             a1 1b 0a
     080485f4 68 33       PUSH     s_%8s_%8s_%8s_%8s_08048733            = "%8s %8s %8s %8s"
             87 04 08
     080485f9 e8 02       CALL     <EXTERNAL>::__isoc99_scanf            undefined __isoc99_scanf()
             fe ff ff
     080485fe 83 c4 20    ADD      ESP,0x20
     08048601 c7 45       MOV      dword ptr [EBP + local_14],0x0
             f4 00 
             00 00 00
     08048608 eb 2d       JMP      LAB_08048637
                      LAB_0804860a                          XREF[1]:  0804863b(j)  
     0804860a 8b 45 f4    MOV      EAX,dword ptr [EBP + local_14]
     0804860d 05 c0       ADD      EAX,user_input                        = ??
             a1 1b 0a
     08048612 0f b6 00    MOVZX    EAX,byte ptr [EAX]=>user_input        = ??
     08048615 0f be c0    MOVSX    EAX,AL
     08048618 83 ec 08    SUB      ESP,0x8
     0804861b ff 75 f4    PUSH     dword ptr [EBP + local_14]
     0804861e 50          PUSH     EAX
     0804861f e8 25       CALL     complex_function                      undefined complex_functio
             ff ff ff

修改angr脚本,这里有四个内存值都需要计算

import angr
import claripy
import sys

def main(argv):
  path_to_binary = "./05_angr_symbolic_memory" 
  project = angr.Project(path_to_binary)

  start_address = 0x08048601
  initial_state = project.factory.blank_state(addr=start_address)

  # The binary is calling scanf("%8s %8s %8s %8s").
  # 每个值长度为8字节,即8*8=64bits
  passwd_size_in_bits = 64
  passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
  passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)
  passwd2 = claripy.BVS('passwd2', passwd_size_in_bits)
  passwd3 = claripy.BVS('passwd3', passwd_size_in_bits)

  passwd0_address = 0xA1BA1C0
  #passwd1_address = 0xA1BA1C8
  #passwd2_address = 0xA1BA1D0
  #passwd3_address = 0xA1BA1D8
  initial_state.memory.store(passwd0_address, passwd0)
  initial_state.memory.store(passwd0_address + 0x8,  passwd1)
  initial_state.memory.store(passwd0_address + 0x10, passwd2)
  initial_state.memory.store(passwd0_address + 0x18, passwd3)
  simulation = project.factory.simgr(initial_state)

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

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.\n' 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 = solution_state.solver.eval(passwd0,cast_to=bytes)
        solution1 = solution_state.solver.eval(passwd1,cast_to=bytes)
        solution2 = solution_state.solver.eval(passwd2,cast_to=bytes)
        solution3 = solution_state.solver.eval(passwd3,cast_to=bytes)
        solution = solution0 + b" " + solution1 + b" " + solution2 + b" " + solution3
        print("[+] Success! Solution is: {}".format(solution.decode("utf-8")))
  else:
    raise Exception('Could not find the solution')

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

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

$ python3 scaffold05.py 
WARNING  | 2023-12-25 21:39:32,058 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-25 21:39:32,058 | 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-25 21:39:32,058 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-25 21:39:32,059 | 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-25 21:39:32,059 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-25 21:39:32,059 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048601 (main+0x59 in 05_angr_symbolic_memory (0x8048601))
[+] Success! Solution is: NAXTHGNR JVSFTPWE LMGAUHWC XMDCPALU
$ ./05_angr_symbolic_memory 
Enter the password: NAXTHGNR
JVSFTPWE
LMGAUHWC
XMDCPALU
Good Job.

06_angr_symbolic_dynamic_memory——动态内存

$ ./06_angr_symbolic_dynamic_memory 
Enter the password: ASDF
AG
Try again.

跟上一题基本一致,不过字符串的内存是通过堆malloc(),而不是栈分配

undefined4 main(void)

{
  char *pcVar1;
  char cVar2;
  int iVar3;
  int local_14;

  buffer0 = (char *)malloc(9);
  buffer1 = (char *)malloc(9);
  memset(buffer0,0,9);
  memset(buffer1,0,9);
  printf("Enter the password: ");
  __isoc99_scanf("%8s %8s",buffer0,buffer1);
  for (local_14 = 0; local_14 < 8; local_14 = local_14 + 1) {
    pcVar1 = buffer0 + local_14;
    cVar2 = complex_function((int)buffer0[local_14],local_14);
    *pcVar1 = cVar2;
    pcVar1 = buffer1 + local_14;
    cVar2 = complex_function((int)buffer1[local_14],local_14 + 0x20);
    *pcVar1 = cVar2;
  }
  iVar3 = strncmp(buffer0,"UODXLZBI",8);
  if (iVar3 == 0) {
    iVar3 = strncmp(buffer1,"UAORRAYF",8);
    if (iVar3 == 0) {
      puts("Good Job.");
      goto LAB_08048766;
    }
  }
  puts("Try again.");
LAB_08048766:
  free(buffer0);
  free(buffer1);
  return 0;
}

[buffer1]=0abcc8ac; [buffer0]=0abcc8a4; start_address=08048699;

     0804867c 8b 15       MOV      EDX,dword ptr [buffer1]               = ??
             ac c8 
             bc 0a
     08048682 a1 a4       MOV      EAX,[buffer0]                         = ??
             c8 bc 0a
     08048687 83 ec 04    SUB      ESP,0x4
     0804868a 52          PUSH     EDX
     0804868b 50          PUSH     EAX
     0804868c 68 43       PUSH     s_%8s_%8s_08048843                    = "%8s %8s"
             88 04 08
     08048691 e8 ca       CALL     <EXTERNAL>::__isoc99_scanf            undefined __isoc99_scanf()
             fd ff ff
     08048696 83 c4 10    ADD      ESP,0x10
     08048699 c7 45       MOV      dword ptr [EBP + local_14],0x0
             f4 00 
             00 00 00
     080486a0 eb 64       JMP      LAB_08048706

根据上述信息修改配置脚本

import angr
import claripy
import sys

def main(argv):
  path_to_binary = "./06_angr_symbolic_dynamic_memory" 
  project = angr.Project(path_to_binary)

  start_address = 0x8048699
  initial_state = project.factory.blank_state(addr=start_address)

  passwd_size_in_bits = 64
  passwd0 = claripy.BVS('passwd0', passwd_size_in_bits)
  passwd1 = claripy.BVS('passwd1', passwd_size_in_bits)

  # fake_heap_address可以任意设置
  fake_heap_address0 = 0xffffc8a4
  pointer_to_malloc_memory_address0 = 0xabcc8a4
  fake_heap_address1 = 0xffffc8ac
  pointer_to_malloc_memory_address1 = 0xabcc8ac

  initial_state.memory.store(pointer_to_malloc_memory_address0, fake_heap_address0, endness=project.arch.memory_endness)
  initial_state.memory.store(pointer_to_malloc_memory_address1, fake_heap_address1, endness=project.arch.memory_endness)

  initial_state.memory.store(fake_heap_address0, passwd0)  
  initial_state.memory.store(fake_heap_address1, passwd1)

  simulation = project.factory.simgr(initial_state)

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

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    if b'Try again.\n' 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 = solution_state.solver.eval(passwd0, cast_to=bytes)
        solution1 = solution_state.solver.eval(passwd1, cast_to=bytes)
        print("[+] Success! Solution is: {0} {1}".format(solution0.decode('utf-8'), solution1.decode('utf-8')))
  else:
    raise Exception('Could not find the solution')

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

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

$ python3 scaffold06.py 
WARNING  | 2023-12-25 22:25:35,135 | angr.storage.memory_mixins.bvv_conversion_mixin | Unknown size for memory data 0xffffc93c. Default to arch.bits.
WARNING  | 2023-12-25 22:25:35,136 | angr.storage.memory_mixins.bvv_conversion_mixin | Unknown size for memory data 0xffffc94c. Default to arch.bits.
WARNING  | 2023-12-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing register with an unspecified value. This could indicate unwanted behavior.
WARNING  | 2023-12-25 22:25:35,141 | 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-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING  | 2023-12-25 22:25:35,141 | 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-25 22:25:35,141 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING  | 2023-12-25 22:25:35,142 | angr.storage.memory_mixins.default_filler_mixin | Filling register ebp with 4 unconstrained bytes referenced from 0x8048699 (main+0x8d in 06_angr_symbolic_dynamic_memory (0x8048699))
WARNING  | 2023-12-25 22:25:36,807 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0xffffc944 with 8 unconstrained bytes referenced from 0xac9a410 (strncmp+0x0 in libc.so.6 (0x9a410))
WARNING  | 2023-12-25 22:25:36,807 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0xffffc954 with 105 unconstrained bytes referenced from 0xac9a410 (strncmp+0x0 in libc.so.6 (0x9a410))
WARNING  | 2023-12-25 22:25:38,488 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0xffffc9bd with 16 unconstrained bytes referenced from 0xac9a410 (strncmp+0x0 in libc.so.6 (0x9a410))
[+] Success! Solution is: UBDKLMBV UNOERNYS
$ ./06_angr_symbolic_dynamic_memory 
Enter the password: UBDKLMBV
UNOERNYS
Good Job.

0 评论:

发表评论