2019 Codegate open CTF Writeup

Reversing

seori

C++로 만들어진 프로그램이다.

int __cdecl sub_3011E0(int a1)
{
  int v1; // eax
  int v2; // eax
  int v3; // eax
  int v4; // eax
  int v5; // eax
  int v6; // eax
  int v7; // ST0C_4
  int v8; // eax
  int v9; // eax
  int v10; // eax
  char v12; // [esp+4h] [ebp-1Ch]
  int v13; // [esp+8h] [ebp-18h]
  void *Dst; // [esp+14h] [ebp-Ch]
  DWORD v15; // [esp+18h] [ebp-8h]
  DWORD i; // [esp+1Ch] [ebp-4h]

  v1 = sub_301400(std::cout, "Hi FRIEND!");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v1, sub_301740);
  v2 = sub_301400(std::cout, "I HAVE PRETTY CAT. DO YOU WANT TO SEE A CAT? ");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v2, sub_301740);
  v3 = sub_301400(std::cout, "UNFORTUNATELY THE CAT IS HIDING :( ");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v3, sub_301740);
  v4 = sub_301400(std::cout, "FIND MY CAT!");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v4, sub_301740);
  sub_3010F0();
  v12 = sub_301080(a1);
  hModule = LoadLibraryW(L"Seori.exe");
  hResInfo = FindResourceW(hModule, 0x65, L"SEORI");
  v15 = SizeofResource(hModule, hResInfo);
  hResData = LoadResource(hModule, hResInfo);
  dword_305380 = LockResource(hResData);
  v13 = dword_305380;
  Dst = malloc((v15 + 1) | -__CFADD__(v15, 1));
  memset(Dst, 0, v15 + 1);
  for ( i = 0; i < v15; ++i )
    *(Dst + i) = v12 ^ *(i + v13);
  v5 = std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, sub_301740);
  v6 = std::basic_ostream<char,std::char_traits<char>>::operator<<(v5, -122569430);
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v6, v7);
  v8 = sub_301400(std::cout, "HAVE YOU SEEN MY CAT?");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v8, sub_301740);
  v9 = sub_301400(std::cout, "I THINK MY CAT IS REALLY CUTE.");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v9, sub_301740);
  v10 = sub_301400(std::cout, "I HOPE TO FIND MY CAT!");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v10, sub_301740);
  return 0;
}

C++로 보기는건 아직 익숙치 않아서 어셈으로 보는게 훨씬 편했다.

이쪽 부분을 보게되면 ebp+var_4를 1씩 증가시키면서(증가 시키는 부분은 그래프 밑쪽에 있다..) ebp-8과 같을 때까지 밑에 연산을 한다. ebp-8 값은 98929 였다.

동적 디버깅해서 xor 이후 [eax]에 넣는 dl의 값을 보니 JPEG 헤더의 값이 보였다.

그러면 이 리소스들을 추출해서 파일을 쓰면 플래그가 써 있는 JPEG가 나온다.

from idaapi import *
from idautils import *

value = []
for i in range(98928):
	value.append(hex(Byte(0x139b398+i)))
	"""
	value[i] = value[i].replace('0x','')
	if len(value[i]) == 1:
		value[i] = "0" + value[i]
	"""

f = open('flag.jpeg','wb')
data = ''.join(chr(int(value[i],16)) for i in range(98928))
f.write(data)
f.close()

IDA Python을 이용해서 스크립트를 짜면 된다. 그러면 이미지 파일 하나가 생성된다.

FLAG : SeoRi's_Meow


J._.n3utr0n

` process hallow ` 기법을 사용했다.

아직 좀 더 분석해야 하는 문제이다. drop.exe 파일을 드랍한 다음 svchost.exe 프로세스를 생성하고 이 프로세스에 drop.exe의 내용을 삽입하고 삭제한다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  CHAR *lpBuffer; // ST54_4
  HANDLE hFile; // ST28_4
  size_t i; // [esp+34h] [ebp-13450h]
  DWORD NumberOfBytesWritten; // [esp+40h] [ebp-13444h]
  size_t Size; // [esp+44h] [ebp-13440h]
  char Src; // [esp+48h] [ebp-1343Ch]
  char Dst; // [esp+49h] [ebp-1343Bh]
  char Buffer; // [esp+9A4Ch] [ebp-9A38h]
  char v12; // [esp+9A4Dh] [ebp-9A37h]
  CHAR CommandLine[4]; // [esp+13450h] [ebp-34h]
  CHAR CmdLine; // [esp+13458h] [ebp-2Ch]
  char v15; // [esp+13459h] [ebp-2Bh]
  char v16; // [esp+1345Ah] [ebp-2Ah]
  char v17; // [esp+1345Bh] [ebp-29h]
  char v18; // [esp+1345Ch] [ebp-28h]
  char v19; // [esp+1345Dh] [ebp-27h]
  char v20; // [esp+1345Eh] [ebp-26h]
  char v21; // [esp+1345Fh] [ebp-25h]
  char v22; // [esp+13460h] [ebp-24h]
  char v23; // [esp+13461h] [ebp-23h]
  char v24; // [esp+13462h] [ebp-22h]
  char v25; // [esp+13463h] [ebp-21h]
  char v26; // [esp+13464h] [ebp-20h]
  char v27; // [esp+13465h] [ebp-1Fh]
  char v28; // [esp+13466h] [ebp-1Eh]
  char v29; // [esp+13467h] [ebp-1Dh]
  char v30; // [esp+13468h] [ebp-1Ch]
  char v31; // [esp+13469h] [ebp-1Bh]
  char v32; // [esp+1346Ah] [ebp-1Ah]
  char v33; // [esp+1346Bh] [ebp-19h]
  char v34; // [esp+1346Ch] [ebp-18h]
  char v35; // [esp+1346Dh] [ebp-17h]
  char v36; // [esp+1346Eh] [ebp-16h]
  char v37; // [esp+1346Fh] [ebp-15h]
  char v38; // [esp+13470h] [ebp-14h]
  char v39; // [esp+13471h] [ebp-13h]
  char v40; // [esp+13472h] [ebp-12h]
  char v41; // [esp+13474h] [ebp-10h]
  char v42; // [esp+13475h] [ebp-Fh]
  char v43; // [esp+13476h] [ebp-Eh]
  char v44; // [esp+13477h] [ebp-Dh]
  char v45; // [esp+13478h] [ebp-Ch]
  char v46; // [esp+13479h] [ebp-Bh]
  char v47; // [esp+1347Ah] [ebp-Ah]
  char v48; // [esp+1347Bh] [ebp-9h]
  char v49; // [esp+1347Ch] [ebp-8h]

  v41 = 117;
  v42 = 99;
  v43 = 126;
  v44 = 97;
  v45 = 63;
  v46 = 116;
  v47 = 105;
  v48 = 116;
  v49 = 0;
  CmdLine = 114;
  v15 = 124;
  v16 = 117;
  v17 = 63;
  v18 = 116;
  v19 = 105;
  v20 = 116;
  v21 = 49;
  v22 = 62;
  v23 = 122;
  v24 = 49;
  v25 = 117;
  v26 = 116;
  v27 = 125;
  v28 = 49;
  v29 = 82;
  v30 = 43;
  v31 = 77;
  v32 = 117;
  v33 = 99;
  v34 = 126;
  v35 = 97;
  v36 = 63;
  v37 = 116;
  v38 = 105;
  v39 = 116;
  v40 = 0;
  Src = 0;
  memset(&Dst, 0, 0x9A00u);
  Size = 0;
  Buffer = 0;
  memset(&v12, 0, 0x9A00u);
  strcpy(CommandLine, "svchost");
  NumberOfBytesWritten = 0;
  sub_401770(&CmdLine, 26);
  sub_401770(&v41, 8);
  if ( !__FrameUnwindToState(0, &Src, (int)&Size) )
    return 0;
  memset(&Buffer, 0, Size + 1);
  memcpy(&Buffer, &Src, Size);
  for ( i = 0; i <= Size; ++i )
    *(&Buffer + i) = ~*(&Buffer + i) ^ 0x41;
  lpBuffer = (CHAR *)operator new[](0x104u);
  GetTempPathA(0x104u, lpBuffer);
  *(_BYTE *)(sub_401000(lpBuffer, 92) + 1) = 0;
  qmemcpy(&lpBuffer[strlen(lpBuffer)], &v41, &v41 + strlen(&v41) + 1 - &v41);
  hFile = CreateFileA(lpBuffer, 0x40000000u, 0, 0, 2u, 0x80u, 0);
  WriteFile(hFile, &Buffer, 0x9A00u, &NumberOfBytesWritten, 0);
  sub_4010C0(CommandLine, lpBuffer);
  WinExec(&CmdLine, 5u);
  return 0;
}

Ollydbg를 이용해서 마지막에 drop.exe 파일을 쓰고 이후에 삭제하는 부분을 코드패치해서 삭제 안되게 해서 C 드라이브 밑에 drop.exe 파일이 생성되게 하면 된다.

여기를 보면 svchost.exe 프로세스에서 drop.exe 파일을 생성하고 그 파일을 쓰고 마지막에 삭제해주는데 삭제해주는 부분에서 삭제파일 이름을 NOP 패치해주면 파일이 삭제되지 않을 것이다.

이후 디버깅해서 실행하면 C 드라이브에 drop.exe 파일이 생성됐을 것이다.

그리고 생성된 drop.exe 파일을 보면 이렇게 되어있는데 아래처럼 그냥 v3 긁어와서 플래그 출력하면 플래그가 안나온다.

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  char v3; // [esp+8h] [ebp-24h]
  int v4; // [esp+9h] [ebp-23h]
  int v5; // [esp+Dh] [ebp-1Fh]
  int v6; // [esp+11h] [ebp-1Bh]
  int v7; // [esp+15h] [ebp-17h]
  int v8; // [esp+19h] [ebp-13h]
  int v9; // [esp+1Dh] [ebp-Fh]
  int v10; // [esp+21h] [ebp-Bh]
  __int16 v11; // [esp+25h] [ebp-7h]
  char v12; // [esp+27h] [ebp-5h]

  v3 = 246;
  v4 = 3532841874;
  v5 = 3469265295;
  v6 = 3667710604;
  v7 = 2631654864;
  v8 = 3654589574;
  v9 = 2631127248;
  v10 = 3503215563;
  v11 = 40408;
  v12 = 0;
  sub_401040("flag is : %s\n", &v3);
  exit(1);
}

sub_401080 함수를 보면 실제 플래그 복호화 루틴이 나온다.

이 함수를 보면 ~*(v3+i) 값과 0x43과 xor연산해준다.

int __cdecl sub_401080(int a1)
{
  signed int i; // [esp+4h] [ebp-4h]

  for ( i = 0; i < 31; ++i )
    *(i + a1) = ~*(i + a1) ^ 0x43;
  return sub_401040("flag is : %s\n", a1);
}

그러면 이제 역연산을 짜면 되겠다.

table = [0xf6,0x92,0xe3,0x92,0xd2,0x8f,0xc9,0xc8,0xce,0x8c,0xd2,0x9c,0xda,0xd0,0xdd,0xdb,0x9c,0x86,0x9c,0xd4,0xd9,0xd0,0xd0,0xd3,0x9c,0xcb,0xd3,0xce,0xd0,0xd8,0x9d]
#print ''.join(chr(255-x^0x43) for x in table)
print ''.join(chr((~x^0x43) & 255) for x in table)

FLAG : J._.n3utr0n flag : hello world!


babyarm

arm_asm.s 파일이 주어져서 핸드레이를 해야한다.

flag:
	.ascii	"]cX^r@VC`b*V+idVk_+eVD(gjt\000"
main:
	@ args = 0, pretend = 0, frame = 8
	@ frame_needed = 1, uses_anonymous_args = 0
	push	{fp, lr}
	lr fp 순으로 stack에 값을 넣는다. 함수 프롤로그 부분
	add	fp, sp, #4
	fp
	sub	sp, sp, #8
	sp -= 8이라고 볼 수 있다. 
	스택 사용 공간을 할당하는듯 하다.
	ldr	r0, .L5
	*(r0) = .L5
	bl	srand
	bl	rand
	mov	r2, r0
	ldr	r3, .L5+4
	smull	r1, r3, r2, r3
	asr	r1, r3, #2
	asr	r3, r2, #31
	sub	r1, r1, r3
	mov	r3, r1
	lsl	r3, r3, #2
	add	r3, r3, r1
	lsl	r3, r3, #1
	sub	r3, r2, r3
	r3 = r2 - r3
	str	r3, [fp, #-8]
	*(fp-8)에 r3를 넣는다
	mov	r3, #0
	r3 = 0으로 셋팅
	str	r3, [fp, #-12]
	*(fp-12)에 r3를 넣는다.
	b	.L2
	.L2 함수 호출한다.
.L3:
	ldr	r2, .L5+8
	ldr	r3, [fp, #-12]
	add	r3, r2, r3
	ldrb	r2, [r3]	@ zero_extendqisi2
	ldr	r3, [fp, #-8]
	and	r3, r3, #255
	add	r3, r2, r3
	and	r1, r3, #255
	ldr	r2, .L5+8
	ldr	r3, [fp, #-12]
	add	r3, r2, r3
	mov	r2, r1
	strb	r2, [r3]
	ldr	r3, [fp, #-12]
	add	r3, r3, #1
	str	r3, [fp, #-12]
.L2:
	ldr	r3, [fp, #-12]
	r3 = *(fp-12)
	cmp	r3, #25
	r3가 25인지 비교하고 25면 제로 플래그 0으로 세팅
	글자수만큼 계속 ~
	ble	.L3
	.L3 연산 결과가 작거나 같으면 .L3를 호출한다.
	ldr	r1, .L5+8
	ldr	r0, .L5+12
	bl	printf
	mov	r3, #0
	r3에 0을 넣는다.
	mov	r0, r3
	r0에도 0을 넣는다.
	리턴 값에 0을 넣은 것이다. return 0; 해준듯 하다.
	sub	sp, fp, #4
	@ sp needed
	pop	{fp, pc}
	함수 프롤로그 부분인듯하다.
a="]cX^r@VC`b*V+idVk_+eVD(gjt\000"
print ''.join(chr(ord(i)+9) for i in a)

FLAG : flag{I_Lik3_4rm_th4n_M1ps}


easy_rev

easy_rev: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=d6ab8e0c86636e8331cc465ae54a5013598dd79e, not stripped

64비트 바이너리다.

그냥 메인에서는 10개 입력 받아준다.

__int64 __fastcall swap(__int64 a1)
{
  unsigned int v2; // [rsp+18h] [rbp-48h]
  signed int v3; // [rsp+1Ch] [rbp-44h]
  int v4; // [rsp+20h] [rbp-40h]
  signed int i; // [rsp+24h] [rbp-3Ch]
  signed int j; // [rsp+28h] [rbp-38h]
  signed int k; // [rsp+2Ch] [rbp-34h]
  int v8; // [rsp+30h] [rbp-30h]
  int v9; // [rsp+34h] [rbp-2Ch]
  int v10; // [rsp+38h] [rbp-28h]
  int v11; // [rsp+3Ch] [rbp-24h]
  int v12; // [rsp+40h] [rbp-20h]
  int v13; // [rsp+44h] [rbp-1Ch]
  int v14; // [rsp+48h] [rbp-18h]
  int v15; // [rsp+4Ch] [rbp-14h]
  int v16; // [rsp+50h] [rbp-10h]
  int v17; // [rsp+54h] [rbp-Ch]
  unsigned __int64 v18; // [rsp+58h] [rbp-8h]

  v18 = __readfsqword(0x28u);
  v2 = 0;
  v3 = 3;
  v4 = 0;
  v8 = 79;
  v9 = 4;
  v10 = 36;
  v11 = 628;
  v12 = 117;
  v13 = 62;
  v14 = 2458;
  v15 = -101;
  v16 = 41;
  v17 = 239;
  for ( i = 0; i <= 9; ++i )
  {
    if ( v4 % 3 )
    {
      if ( v4 % 3 == 1 )
        v3 -= i;
      else
        v3 += i;
    }
    else
    {
      v3 *= i;
    }
    *(_DWORD *)(a1 + 4LL * i) = v3 ^ *(_DWORD *)(4LL * i + a1);
    ++v4;
  }
  for ( j = 0; j <= 9; ++j )
    *(_DWORD *)(4LL * j + a1) ^= 0xFu;
  for ( k = 0; k <= 9; ++k )
  {
    if ( *(_DWORD *)(4LL * k + a1) == *(&v8 + k) )
      ++v2;
  }
  return v2;
}

swap함수를 보면은 v3의 값을 구해서 a1[i]의 값과 xor연산을 해준다. 그리고 밑에 보면 0xF와 xor한 값이 *(v8[i])이면 된다.

이제 이것을 역연산을 해서 구하면 된다.

table = [79,4,36,628,117,62,2458,-101,41,239]
v3 = 3
a=[]
for i in range(10):
	if i % 3:
		if i % 3 == 1:
			v3 -= i
			a.append(v3)
		else:
			v3 += i
			a.append(v3)
	else:
		v3 *= i
		a.append(v3)

b=[]
for i in range(10):
	for j in range(-3000,3000):
		if table[i] == j^15^a[i]:
			b.append(j)
			break

print 'FLAG key is : ' + str(sum(b))

브루트포스해서 풀었다.

$ ./easy_rev
==========================================
       NEWBIE REV1 right here !!
solve the magic I putted, and get the flag
==========================================
>> 64 -12 42 632 -123 53 2445 -123 63 1
++++++++++++++++++++++++++++++++++++++++++
Let's See the result!!!!
++++++++++++++++++++++++++++++++++++++++++
>> Yes, You got right ( IF YOU CERTAINLY INSERTED EXACTLY 10 NUMBERS )
>> You just need to 'add' all the no for every index. That sum is key for flag file !!
>> (flag file is encryted aes-256-cbc of openssl)

FLAG : flag{R2versing_1s_b4sed_0n_H4cking_:)}


find_flag

파이썬으로 만들어진 exe 파일이다. python-exe-unpacker 를 이용해서 풀었다.

그냥 파일 추출해주면 플래그가 있다.

FLAG : Pyth0n_m4k2_2X2_B1n4ry_:D