PWNABLE] angr

노션으로 옮김·2020년 5월 18일
2

uitility

목록 보기
14/18
post-thumbnail

angr

분석이나 크랙시 사용할 수 있는 자동화 도구이다.
자동으로 시리얼을 찾아주는 기능 때문에 CTF 리버싱 문제에서 많이 활용된다.

자세히는 github를 보면 다음과 같은 기능을 사용할 수 있다고 설명한다.

angr is a suite of Python 3 libraries that let you load a binary and do a lot of cool things to it:

  • Disassembly and intermediate-representation lifting
  • Program instrumentation
  • Symbolic execution
  • Control-flow analysis
  • Data-dependency analysis
  • Value-set analysis (VSA)
  • Decompilation

python 3 라고 설명되어 있지만 python 2.7 버전도 사용할 수 있다.

설치

sudo apt update
sudo apt-get install python3-dev libffi-dev build-essential virtualenvwrapper
pip install angr

angr 설치시 다음과 같은 에러가 발생할 수 있다.

Could not find a version that satisfies the requirement gitdb>=4.0.1

https://github.com/gitpython-developers/gitdb/issues/61

다음의 패키지를 설치하여 해결한다.

pip install gitdb2==2.0.6
pip install GitPython==2.1.14

예제

angr은 내용이 많아 예제를 풀어보며 사용법을 숙지해야 한다.

fauxware

angr에서 제공하는 기본 사용법을 위한 예제 프로그램이다.

main에서 유저이름과 패스워드를 입력받고

main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [sp+1Ch] [bp-24h]@1
  char v5; // [sp+20h] [bp-20h]@1
  char v6; // [sp+28h] [bp-18h]@1
  char buf; // [sp+30h] [bp-10h]@1
  char v8; // [sp+38h] [bp-8h]@1

  v8 = 0;
  v6 = 0;
  puts("Username: ");
  read(0, &buf, 8uLL);
  read(0, &v4, 1uLL);
  puts("Password: ");
  read(0, &v5, 8uLL);
  read(0, &v4, 1uLL);
  v4 = authenticate(&buf, &v5);
  if ( !v4 )
    rejected(&buf);
  return accepted();
}

authenticate에서 유저이름이 sneaky에 저장된 값이면 1을 반환한다. 아닐 경우 유저이름에 해당하는 파일을 읽어서 패스워드와 비교한다.

authenticate

signed __int64 __fastcall authenticate(const char *a1, const char *a2)
{
  signed __int64 result; // rax@2
  char *s1; // ST00_8@3
  char buf; // [sp+10h] [bp-10h]@3
  char v5; // [sp+18h] [bp-8h]@1
  int fd; // [sp+1Ch] [bp-4h]@3

  v5 = 0;
  if ( !strcmp(a2, sneaky) )
  {
    result = 1LL;
  }
  else
  {
    fd = open(a1, 0, a2);
    read(fd, &buf, 8uLL);
    result = strcmp(s1, &buf) == 0;
  }
  return result;
}

angr 스크립트를 작성한다.

import angr
import sys


def basic_symbolic_execution():
    # execution. First, we load the binary into an angr project.
    p = angr.Project('fauxware')

    # The entry_state constructor generates a SimState that is a very generic representation of the possible program states at the program's entry point.
    state = p.factory.entry_state()

    # we have a SimulationManager. SimulationManager is just collections of states with various tags attached with a number of convenient interfaces for managing them.
    sm = p.factory.simulation_manager(state)

    # Now, we begin execution. This will symbolically execute the program until we reach a branch statement for which both branches are satisfiable.
    sm.run(until=lambda sm_: len(sm_.active) > 1)


    #These are the constraints that will eventually be passed to our constraint solver (z3) to produce a set of concrete inputs satisfying them.
    input_0 = sm.active[0].posix.dumps(0)
    input_1 = sm.active[1].posix.dumps(0)
    r = None
    print input_0
    print input_1

basic_symbolic_execution()

fauxware을 로드한 후, 두 개 이상의 코드흐름을 찾으면 그 때의 입력값을 출력한다.

위 코드를 실행하면 sneaky에 해당하는 문자열을 확인할 수 있다.

root@hp-virtual-machine:/work/tools/angr/fauxware# python ex.py
WARNING | 2020-05-18 18:46:08,691 | angr.analyses.disassembly_utils | Your version of capstone does not support MIPS instruction groups.
SOSNEAKY
S������

r100

defcamp의 리버싱 문제이다.
mains에 입력을 받은 뒤, sub_4006fd의 결과값으로 일치 여부를 출력해준다.

main

signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  signed __int64 result; // rax@3
  __int64 v4; // rcx@6
  char s; // [sp+0h] [bp-110h]@1
  __int64 v6; // [sp+108h] [bp-8h]@1

  v6 = *MK_FP(__FS__, 40LL);
  printf("Enter the password: ", a2, a3);
  if ( fgets(&s, 255, stdin) )
  {
    if ( sub_4006FD(&s, 255LL) )
    {
      puts("Incorrect password!");
      result = 1LL;
    }
    else
    {
      puts("Nice!");
      result = 0LL;
    }
  }
  else
  {
    result = 0LL;
  }
  v4 = *MK_FP(__FS__, 40LL) ^ v6;
  return result;
}

sub_4006fdv3에 저장된 어떤 문자열과 입력값 a1에 어떤 연산을 한 결과값이 일치하면 1을 반환한다.

sub_4006fd

signed __int64 __fastcall sub_4006FD(__int64 a1)
{
  signed int i; // [sp+14h] [bp-24h]@1
  const char *v3; // [sp+18h] [bp-20h]@1
  const char *v4; // [sp+20h] [bp-18h]@1
  const char *v5; // [sp+28h] [bp-10h]@1

  v3 = "Dufhbmf";
  v4 = "pG`imos";
  v5 = "ewUglpt";
  for ( i = 0; i <= 11; ++i )
  {
    if ( (&v3)[8 * (i % 3)][2 * (i / 3)] - *(_BYTE *)(i + a1) != 1 )
      return 1LL;
  }
  return 0LL;
}

스크립트를 작성한다.

import angr

def main():
    p = angr.Project("r100")
    simgr = p.factory.simulation_manager(p.factory.full_init_state())
    simgr.explore(find=0x400844, avoid=0x400855)

    return simgr.found[0].posix.dumps(0).strip(b'\0\n')

if __name__ == '__main__':
    print(main())

explorefind를 찾고 avoid를 회피하는 입력값을 찾는다.
0x400844는 일치할 경우, 0x400855는 불일치할 경우 분기하는 주소값이다.

코드를 실행하면 올바른 입력값을 확인할 수 있다.

root@hp-virtual-machine:/work/tools/angr/r100# python ex.py
WARNING | 2020-05-18 20:33:52,912 | angr.analyses.disassembly_utils | Your version of capstone does not support MIPS instruction groups.
Code_Talkers�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

0개의 댓글