히공

HackPack CTF 2021 - Function Pointer Fun 본문

write up/HackPack CTF 2021

HackPack CTF 2021 - Function Pointer Fun

heegong 2021. 4. 18. 22:18
728x90

문제

 

 

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  bool changed; // [rsp+13h] [rbp-1Dh]
  int i; // [rsp+14h] [rbp-1Ch]
  int (*fp)(void); // [rsp+18h] [rbp-18h]
  char seed[5]; // [rsp+23h] [rbp-Dh] BYREF
  unsigned __int64 v8; // [rsp+28h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  *(_DWORD *)seed = 0;
  seed[4] = 0;
  printf("Hello, Mr. Eusk. \nPassword > ");
  __isoc99_scanf("%4s", seed);
  changed = 0;
  for ( i = 0; i <= 3; ++i )
  {
    if ( seed[i] )
      changed = 1;
  }
  if ( !changed )
  {
    puts("You gotta give an input!");
    result = 1;
  }
  else
  {
    fp = pickFunction(seed);
    ((void (__fastcall *)(char *))fp)(seed);
    result = 0;
  }
  return result;
}

메인 함수

 

 

seed는 총 4글자까지 입력받는다.

 

 

  for ( i = 0; i <= 3; ++i )
  {
    if ( seed[i] )
      changed = 1;
  }
  if ( !changed )
  {
    puts("You gotta give an input!");
    result = 1;
  }

이 부분은 null 바이트가 들어갔는지 체크 하는 루틴 이다.

 

 

 

 

 

 else
  {
    fp = pickFunction(seed);
    ((void (__fastcall *)(char *))fp)(seed);
    result = 0;
  }

이 부분은 널 바이트가 들어있지 않다면 실행하는 루틴이다.

 

 

 

 

int (*__cdecl pickFunction(char *seed))(void)
{
  char res; // [rsp+1Fh] [rbp-31h]

  res = (seed[2] | seed[3]) & (*seed | seed[1]);
  if ( res == 0x49 )
    return funTwo;
  if ( res > 0 && res <= 31 )
    return funOne;
  if ( res > 31 && res <= 63 )
    return funThree;
  if ( res <= 63 || res > 95 )
    return funFive;
  return funFour;
}

pickFunction 함수다.

 

 

 

 

int __cdecl funTwo()
{
  FILE *fp; // [rsp+8h] [rbp-38h]
  char flag[25]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v3; // [rsp+38h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  fp = fopen("flag", "r");
  fgets(flag, 25, fp);
  puts(flag);
  return 1;
}

funTwo 함수를 보니 flag라는 이름의 파일을 읽어와서 출력하는 걸 알 수 있다.

 

 

나머지 funOne같은 함수는 그냥 문자열만 출력하는 함수였다.

 

 

 

from z3 import *


seed0 = BitVec('seed0', 8)
seed1 = BitVec('seed1', 8)
seed2 = BitVec('seed2', 8)
seed3 = BitVec('seed3', 8)

s = Solver()

s.add(seed0 != 0)
s.add(seed1 != 0)
s.add(seed2 != 0)
s.add(seed3 != 0)

s.add(seed0 != 0x20)        # 띄어쓰기
s.add(seed1 != 0x20)
s.add(seed2 != 0x20)
s.add(seed3 != 0x20)


s.add(seed0 != 10)          # \n
s.add(seed1 != 10)
s.add(seed2 != 10)
s.add(seed3 != 10)

s.add(seed0 != 9)           # \t
s.add(seed1 != 9)
s.add(seed2 != 9)
s.add(seed3 != 9)

s.add((seed2 | seed3) & (seed0 | seed1) == 0x49)

print(s.check())
m = s.model()    # [seed3 = 32, seed2 = 73, seed1 = 128, seed0 = 73]
print(m)

input_bytes = bytearray()
for d in m.decls():
    input_bytes.append(int(str(m[d])))

input_bytes = input_bytes[::-1]

with open("input_bytes", 'wb') as f:
    f.write(input_bytes)

z3를 이용해서 입력해야하는 문자열을 구한 뒤 input_bytes라는 파일에 저장했다.

 

먼저 로컬에서 실행하기 위해 flag라는 파일을 만들고

FLAG{123}을 입력했다.

 

 

정상작동 하는걸 알 수 있다.

 

 

플래그 : flag{c1RcU1t5_R_fUn!2!}

Comments