AUCTF2020] Writeups

노션으로 옮김·2020년 4월 6일
1

wargame

목록 보기
39/59
post-thumbnail

Reversing

mobile1

mobile1
My friend sent this file to me and said that there was a flag in it. Can you help me?

https://drive.google.com/open?id=1iglx4cQ_iVi1RABBa1eF0OXcOGECjVLy

.ipa 파일을 받을 수 있다.
위키를 보면 .ipa파일은 .zip으로 변경 후 압축해제가 가능하다고 나와있다.

https://en.wikipedia.org/wiki/.ipa

Files with the .ipa extension can be uncompressed by changing the extension to .zip and unzipping.

또한 위키와 다음 링크에서 info.plist 파일에는 메타데이터와 같은 다양한 정보가 담겨있다고 한다.

https://www.hexnode.com/blogs/ipa-file

info.plist 파일의 문자열을 확인해보면

Sora

This obnoxious kid with spiky hair keeps telling me his key can open all doors.

Can you generate a key to open this program before he does?

Connect to challenges.auctf.com 30004

  sub_10C0("Give me a key!");
  sub_1100(&v7, 30LL, stdin);
  v4 = (const char *)&v7;
  if ( encrypt(&v7) )
  {
    print_flag();
    result = 0;
  }

입력받은 시리얼을 인자로 encrypt 함수를 실행하여 반환값이 True 일 경우에 플래그를 출력해준다.

signed __int64 __fastcall encrypt(__int64 a1)
{
  unsigned __int64 v2; // rax@5
  int i; // [sp-20h] [bp-20h]@1

  __asm { rep nop edx }
  for ( i = 0; ; ++i )
  {
    LODWORD(v2) = sub_10D0(secret);
    if ( i >= v2 )
      break;
    if ( (8 * *(_BYTE *)(i + a1) + 19) % 61 + 65 != secret[i] )
      return 0LL;
  }
  return 1LL;
}

입력된 시리얼 각각에 특정 연산을 수행하여 그 값이 secret과 모두 동일하면 1을 반환한다.

모든 아스키 문자에 대해 동일한 연산 후의 결과가 secret인 값을 추출하여 원래의 시리얼 값을 구할 수 있다.

from pwn import *

table_ascii = 'abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ1234567890_+-={}[];\':",./<>?!@#$%^&*()'
secret = 'aQLpavpKQcCVpfcg'


def dec(ch):
    global table_ascii
    for x in table_ascii:
        calc = (ord(x) * 8 + 19) % 61 + 65
        if ord(ch) == calc:
            return x

            
#calc dec
serial = bytearray(map(dec, list(secret)))


print 'serial is ['+serial+']'
        
p = remote('challenges.auctf.com', 30004)

print p.recvrepeat(0.2)


p.sendline(serial)


p.interactive()

플래그 값은 auctf{that_w@s_2_ezy_29302}

Plain Jane

I'm learning assembly. Think you can figure out what this program returns?

Note: Not standard flag format. Please provide either the unsigned decimal equivalent or hexadecimal equivalent.

어셈블리 언어를 해석하는 문제다.

	.file	"plain_asm.c"
	.intel_syntax noprefix
	.text
	.globl	main
	.type	main, @function
main:
.LFB6:
	.cfi_startproc
	push	rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	mov	rbp, rsp
	.cfi_def_cfa_register 6
	sub	rsp, 16
	mov	eax, 0
	call	func_1
	mov	DWORD PTR -4[rbp], eax
	mov	eax, 0
	call	func_2
	mov	DWORD PTR -8[rbp], eax
	mov	edx, DWORD PTR -8[rbp]
	mov	eax, DWORD PTR -4[rbp]
	mov	esi, edx
	mov	edi, eax
	call	func_3
	mov	DWORD PTR -12[rbp], eax
	mov	eax, 0
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE6:
	.size	main, .-main
	.globl	func_1
	.type	func_1, @function
func_1:
.LFB7:
	.cfi_startproc
	push	rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	mov	rbp, rsp
	.cfi_def_cfa_register 6
	mov	BYTE PTR -1[rbp], 25
	mov	DWORD PTR -8[rbp], 0
	jmp	.L4
.L5:
	mov	eax, DWORD PTR -8[rbp]
	add	eax, 10
	mov	edx, eax
	mov	eax, edx
	sal	eax, 2
	add	eax, edx
	lea	edx, 0[0+rax*4]
	add	eax, edx
	add	BYTE PTR -1[rbp], al
	add	DWORD PTR -8[rbp], 1
.L4:
	cmp	DWORD PTR -8[rbp], 9
	jle	.L5
	movzx	eax, BYTE PTR -1[rbp]
	mov	DWORD PTR -12[rbp], eax
	mov	eax, DWORD PTR -12[rbp]
	pop	rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE7:
	.size	func_1, .-func_1
	.globl	func_2
	.type	func_2, @function
func_2:
.LFB8:
	.cfi_startproc
	push	rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	mov	rbp, rsp
	.cfi_def_cfa_register 6
	mov	DWORD PTR -4[rbp], 207
	mov	eax, DWORD PTR -4[rbp]
	pop	rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE8:
	.size	func_2, .-func_2
	.globl	func_3
	.type	func_3, @function
func_3:
.LFB9:
	.cfi_startproc
	push	rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	mov	rbp, rsp
	.cfi_def_cfa_register 6
	mov	DWORD PTR -36[rbp], edi
	mov	DWORD PTR -40[rbp], esi
	cmp	DWORD PTR -36[rbp], 64
	jg	.L10
	mov	eax, 24
	jmp	.L11
.L10:
	cmp	DWORD PTR -40[rbp], 211
	jle	.L12
	mov	eax, 20
	jmp	.L11
.L12:
	cmp	DWORD PTR -36[rbp], 0
	je	.L13
	cmp	DWORD PTR -40[rbp], 0
	jne	.L13
	mov	eax, 120
	jmp	.L11
.L13:
	cmp	DWORD PTR -36[rbp], 0
	jne	.L14
	cmp	DWORD PTR -40[rbp], 0
	je	.L14
	mov	eax, 220
	jmp	.L11
.L14:
	mov	eax, DWORD PTR -36[rbp]
	or	eax, DWORD PTR -40[rbp]
	mov	DWORD PTR -12[rbp], eax
	mov	eax, DWORD PTR -36[rbp]
	and	eax, DWORD PTR -40[rbp]
	mov	DWORD PTR -16[rbp], eax
	mov	eax, DWORD PTR -36[rbp]
	xor	eax, DWORD PTR -40[rbp]
	mov	DWORD PTR -20[rbp], eax
	mov	DWORD PTR -4[rbp], 0
	mov	DWORD PTR -8[rbp], 0
	jmp	.L15
.L16:
	mov	eax, DWORD PTR -16[rbp]
	sub	eax, DWORD PTR -8[rbp]
	mov	edx, eax
	mov	eax, DWORD PTR -12[rbp]
	add	eax, edx
	add	DWORD PTR -4[rbp], eax
	add	DWORD PTR -8[rbp], 1
.L15:
	mov	eax, DWORD PTR -8[rbp]
	cmp	eax, DWORD PTR -20[rbp]
	jl	.L16
	mov	eax, DWORD PTR -4[rbp]
.L11:
	pop	rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE9:
	.size	func_3, .-func_3
	.ident	"GCC: (Debian 9.2.1-22) 9.2.1 20200104"
	.section	.note.GNU-stack,"",@progbits

형변환과 반복문으로 인해 눈으로 풀 순 없었다.
어셈블리 언어를 그대로 파이썬으로 작성하여 문제를 해결했다.
피연산자 크기에 따라 다른 결과가 나오므로, 연산자를 다시 정의하여 사용했다.

rax = 0
rdx = 0

high = 0xffffffff00000000
low = 0xffffffff
def b_plus(val, add):
    return (val & 0xffffffffffffff00) + ((val + add) & 0xff)

def dw_and(val, add):
    return (val & high) | ((val & add) & low)

def dw_xor(val, add):
    return (val & high) | ((val ^ add) & low)

def dw_or(val, add):
    return (val & high) | ((val | add) & low)

def dw_plus(val, add):
    return (val & high) | ((val + add) & low)
    
def dw_minus(val, add):
    return (val & high) | ((val - add) & low)

def dw_mul(val, add):
    return (val & high) | ((val * add) & low)

def dw_sal(val, count):
    sign = 1 << (32 - 1)
    if val | sign == val:
        val = val << count
        val = val | (1 << (32 - 1))
    else:
        val = val << count
    return val

def func1():
    rbp_1 = 25
    rbp_8 = 0
    while rbp_8 <= 9:
        rax = rbp_8
        rax = dw_plus(rax, 10)
        rdx = rax 
        rax = rdx
        rax = dw_sal(rax, 2)
        rax = dw_plus(rax, rdx)
        rdx = dw_mul(rax, 4) 
        rax = dw_plus(rax, rdx)
        rbp_1 = b_plus(rbp_1, rax)
        rbp_8 += 1
    rax = rbp_1
    rbp_12 = rax
    rax = rbp_12
    return rax

def func2():
    rbp_4 = 207
    rax = rbp_4
    return rax

def func3(rdi, rsi):
    rbp_36 = rdi
    rbp_40 = rsi
    if rbp_36 > 64:
        if rbp_40 <= 211:
            #L12
            if rbp_36 == 0 or rbp_40 != 0:
                #L13
                if rbp_36 != 0 or rbp_40 == 0:
                    #L14
                    rax = rbp_36
                    rax = dw_or(rax, rbp_40)
                    rbp_12 = rax
                    rax = rbp_36
                    rax = dw_and(rax, rbp_40)
                    rbp_16 = rax
                    rax = rbp_36
                    rax = dw_xor(rax, rbp_40)
                    rbp_20 = rax
                    rbp_4 = 0
                    rbp_8 = 0
                    #L15
                    while rbp_8 < rbp_20:
                        #L16
                        rax = rbp_16
                        rax = dw_minus(rax, rbp_8)
                        rdx = rax
                        rax = rbp_12
                        rax = dw_plus(rax, rdx)
                        rbp_4 = dw_plus(rbp_4, rax)
                        rbp_8 += 1
                    rax = rbp_4
                    return rax
                else:
                    rax = 220
                    return rax
            else:
                rax = 120
                return rax
        else:
            rax = 20
            return rax
    else:
        rax = 24
        return rax

rbp_4 = func1()
print hex(rbp_4)
rax = 0
rbp_8 = func2()

ret = func3(rbp_4, rbp_8)

print 'result: ' + str(ret)

플래그 값은 28623

Don't Break Me!

I've been working on my anti-reversing lately. See if you can get this flag!

Connect at challenges.auctf.com 30005

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [sp+0h] [bp-2018h]@1
  char *key; // [sp+2000h] [bp-18h]@1
  char *input_enc; // [sp+2004h] [bp-14h]@1
  int v7; // [sp+2008h] [bp-10h]@1
  int v8; // [sp+200Ch] [bp-Ch]@1
  int *v9; // [sp+2014h] [bp-4h]@1

  v9 = &argc;
  setvbuf(stdout, 0, 2, 0);
  puts("54 68 65 20 6d 61 6e 20 69 6e 20 62 6c 61 63 6b 20 66 6c 65 64 20 61 63 72 6f 73 73 20 74 68 65 20 64 65 73 65 72 74 2c 20 61 6e 64 20 74 68 65 20 67 75 6e 73 6c 69 6e 67 65 72 20 66 6f 6c 6c 6f 77 65 64 2e");
  debugger_check();
  v8 = 17;
  v7 = 12;
  printf("Input: ");
  fgets(&s, 0x2000, stdin);
  remove_newline(&s);
  input_enc = encrypt(&s, v8, v7);
  key = calloc(0x20u, 4u);
  getString(key);
  if ( !strcmp(key, input_enc) )
    print_flag();
  else
    printf("Not quite");
  return 0;
}

시리얼을 구하는 문제다.
입력값 sencrypt()에서 암호화되고
그 값이 key에 저장될 암호화된 시리얼값과 일치하면 플래그를 출력해준다.

_BYTE *__cdecl encrypt(char *input, int num_17, int num_12)
{
  size_t v3; // eax@1
  _BYTE *dst; // [sp+8h] [bp-10h]@1
  size_t i; // [sp+Ch] [bp-Ch]@1

  debugger_check();
  v3 = strlen(input);
  dst = calloc(v3, 1u);
  for ( i = 0; strlen(input) > i; ++i )
  {
    if ( input[i] == 32 )
      dst[i] = input[i];
    else
      dst[i] = (num_17 * (input[i] - 65) + num_12) % 26 + 65;
  }
  return dst;
}

암호화 과정은 간단하며, 복호화할 필요없이 아스키테이블에서 해당 암호화를 거친 후에 비교 대상인 암호화된 시리얼과 동일한 문자를 구하면 된다.

unsigned int __cdecl getString(int p_str)
{
  unsigned int result; // eax@5
  unsigned int i; // [sp+8h] [bp-Ch]@1
  int idx; // [sp+Ch] [bp-8h]@1

  debugger_check();
  idx = 0;
  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i > 0x1F )
      break;
    if ( !(i & 1) )
      *(15 - idx++ + p_str) = blah[i];
  }
  return result;
}

암호화된 시리얼 값을 가져오는 함수이다.
blah의 짝수 인덱스에 저장되있는 값을 가져와서 역순으로 나열한다.
blah의 원래 저장된 값은 상수이므로 IDA를 이용해서 확인할 수 있다.

파이썬으로 시리얼을 구하는 코드를 작성했다.

from pwn import *
import re
import time

table_ascii = 'abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ1234567890_+-={}[];\':",./<>?!@#$%^&*()'

key = """
X...A...P...R...M...
X...C...S...B...
C...E...D...I...
S...B...V...X...
I...S...X...W...
E...R...J...R...
W...S...Z...A...
R...S...Q...????
"""
pt = re.compile('[A-Z]')

key = bytearray(pt.findall(key))[0::2][::-1]

def dec(ch):
    #print 'ch: ' + str(ch) + ' & ' + chr(ch)
    if ch == 32:
        return ch
    else:
        for x in table_ascii:
            if ((ord(x) - 65) * 17 + 12) % 26 + 65 == ch:
                return x
def enc(ch):
    if ch == 32:
        return ch
    else:
        return ((ch - 65) * 17 + 12) % 26 + 65


answer = bytearray(map(dec, list(key)))
print 'answer : ' + answer

플래그 값은 auctf{static_or_dyn@mIc?_12923}


Pwnable

Easy as Pie

My friend just spent hours making this custom shell! He's still working on it so it doesn't have much. But we can do some stuff! He even built a custom access control list for controlling if you can access files.

Check it out!

nc challenges.auctf.com 30010

서버에 접속하면 간단한 인터프리터가 실행되는데 help로 실행 가능한 명령어를 확인할 수 있다.
lsacl.txtflag.txt를 확인할 수 있고, flag.txt에 대한 권한이 없기 때문에 writeacl.txt에 권한을 추가해주면 cat flag.txt로 플래그값을 확인할 수 있다.

플래그 값은 auctf{h4_y0u_g0t_tr0ll3d_welC0m#_t0_pWN_l@nd}

House of Madness

https://velog.io/@woounnan/AUCTF2020-House-of-Made

Remote School

https://velog.io/@woounnan/AUCTF2020-Remote-School-9isz46lo

0개의 댓글