[Re] Rechall Writeup

2022 ISG CTF攻防线上赛题

题目描述

题目给出一个二进制文件 rechall,要求输入正确的 flag 通过验证。 运行程序示例: 1

解题步骤

0x01 初步分析

  1. 使用file命令检查文件类型

    1
    2
    
    $ file rechall
    rechall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=5e62deaec60953b0ae6e9e22f18f228f1d75e2f2, for GNU/Linux 3.2.0, not stripped
    
  2. strings查找字符串

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    $ strings ./rechall
    /lib64/ld-linux-x86-64.so.2
    libc.so.6
    sprintf
    ....
    flag{%08x-%08x-%08x-%08x}
    Right!
    Your flag is: %s
    ...
    

    发现flag格式

0x02 静态分析

  1. 用IDA打开反编译程序,重命名部分变量。发现flag是由四个十六进制数字组成的

    2

  2. 透过观察发现要输入的四个数字都通过checker1-4的检查才能拿到flag,并且输入的数字转换成16进制拼在一起就是flag。

  3. 分别进入checker1-4把代码提取出来

     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
    
    __int64 __fastcall checker1(int a1, int a2)
    {
      __int64 result; // rax
    
      if ( a1 - a2 == 0x956438B9 )
        result = 0LL;
      else
        result = 4294967295LL;
      return result;
    }
    __int64 __fastcall checker2(int a1)
    {
      __int64 result; // rax
    
      if ( 3 * (a1 & 0x52E3F96B) - (a1 | 0xAD1C0694) - a1 == -477662227 )
        result = 0LL;
      else
        result = 0xFFFFFFFFLL;
      return result;
    }
    __int64 __fastcall checker3(unsigned int a1)
    {
      if ( a1 > 0x10000000 )
        return 0xFFFFFFFFLL;
      if ( -3 * (~a1 | 0xD68E3FE9)
         + 3 * ~(a1 | 0xD68E3FE9)
         + 4 * ((~a1 & 0xD68E3FE9) + 2 * ~(~a1 | 0xD68E3FE9))
         + 10 * (a1 & 0xD68E3FE9)
         - (a1 ^ 0xD68E3FE9) == 0x251BC4BD )
        return 0LL;
      return 0xFFFFFFFFLL;
    }
    __int64 __fastcall checker4(unsigned int a1)
    {
      if ( a1 > 0x10000000 )
        return 0xFFFFFFFFLL;
      if ( 11 * (a1 ^ 0xAE7C284F)
         + 4 * (a1 & 0xAE7C284F)
         - (6 * (a1 & 0x5183D7B0)
          + 12 * ~(a1 | 0x5183D7B0))
         + -5 * a1
         - 2 * ~(a1 | 0x4EDA6B7C)
         - (a1 | 0xB1259483)
         + 3 * (a1 & 0x4EDA6B7C)
         + 4 * (a1 & 0xB1259483)
         - -2 * (a1 | 0xB1259483) == 0xD9E7538A )
        return 0LL;
      return 0xFFFFFFFFLL;
    }
    

    这里一定要让checkers返回0LL才能继续

  4. 使用z3库编写求解器脚本,先安装一下z3

    1
    
    pip install z3-solver
    

0x03 编写解密脚本

  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
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    
    from z3 import *
    
    def solve_checker4():
        solver = Solver()
        a1 = BitVec('a1', 32)
        solver.add(a1 <= 0x10000000)
        expr = (
            11 * (a1 ^ 0xAE7C284F)
            + 4 * (a1 & 0xAE7C284F)
            - (6 * (a1 & 0x5183D7B0) + 12 * ~(a1 | 0x5183D7B0))
            + -5 * a1
            - 2 * ~(a1 | 0x4EDA6B7C)
            - (a1 | 0xB1259483)
            + 3 * (a1 & 0x4EDA6B7C)
            + 4 * (a1 & 0xB1259483)
            - -2 * (a1 | 0xB1259483)
        )
        solver.add(expr == 0xD9E7538A)
        if solver.check() == sat:
            model = solver.model()
            return model[a1].as_long()
        else:
            return None
    
    def solve_checker3():
        solver = Solver()
        a1 = BitVec('a1', 32)
        solver.add(a1 <= 0x10000000)
        expr = (
            -3 * (~a1 | 0xD68E3FE9)
            + 3 * ~(a1 | 0xD68E3FE9)
            + 4 * ((~a1 & 0xD68E3FE9) + 2 * ~(~a1 | 0xD68E3FE9))
            + 10 * (a1 & 0xD68E3FE9)
            - (a1 ^ 0xD68E3FE9)
        )
        solver.add(expr == 0x251BC4BD)
        if solver.check() == sat:
            model = solver.model()
            return model[a1].as_long()
        else:
            return None
    
    def solve_checker2():
        solver = Solver()
        a1 = BitVec('a1', 32)
        solver.add(a1 <= 0x10000000)
        expr = (
            3 * (a1 & 0x52E3F96B)
            - (a1 | 0xAD1C0694)
            - a1
        )
        solver.add(expr == 0xE38773ED)
        if solver.check() == sat:
            model = solver.model()
            return model[a1].as_long()
        else:
            return None
    
    # 这里其实可以直接算出来checker1需要的值是多少,
    # 因为伪代码里面已经往checker1里面传入了0x5294B771
    def solve_checker1():
        return 0x956438B9 + 0x5294B771
    
    solvers = [
        solve_checker1,
        solve_checker2,
        solve_checker3,
        solve_checker4
    ]
    times = 0
    for i in solvers:
        times += 1
        print(f"flag{times}: {int(i()) if i() is not None else 'No solution'}")
    
  2. 运行脚本得到十进制的flag:

    1
    2
    3
    4
    5
    
    $ python -u ./decryptor.py
    flag1: 3891851306
    flag2: 2426636161
    flag3: 88939547
    flag4: 232752086
    
  3. 把flag输入进程序: 3

0x04 Flag!

  1. 得到最终flag flag{e7f8f02a-90a38781-054d1c1b-0ddf83d6}
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计