티스토리 뷰

카테고리 없음

2020 M0lecon CTF Final?

mhibio 2020. 11. 17. 08:31

 

지난번에 RGBsec 친구들과 본선권을 얻었던 2020 m0lecon ctf를 진행했다. ( 2020-11-15 ~ 2020-11-16 )

2020 balsn ctf 도 겹쳐있어서 둘다 신경써봤는데 결국 문제를 푼건 m0lecon 밖에 없었다..

분명 본선이 이탈리아였던거로 기억하는데 코로나로 인해서 온라인으로 진행했다 ㅜㅜ

포너블을 많이 봤음에도 불구하고 립싱만 2문제 풀었다.... 지난번 레쎄 씨탭때도 진짜 못했었는데 요즘 자극을 많이 받는다.

 

The RickShank RuckDemption

포켓몬 비슷한 게임을 만들어서 맵을 돌아다니면서 결투하고, 수집하는 게임이다.

처음에는 다 깨야 되는줄 알고 내 몬스터의 캐릭터랑 체력을 올려서 맵을 돌아다녀봤다.

몬스터 다잡고 맵 다 돌아다녀봐도 플래그 없길래 문제분석을 다시했는데,

 

게임 로드할때 winFunc이란 함수가 존재했고, 그냥 해당함수를 호출해주기만 하면 플래그를 얻을 수 있었다.

ptm{_P34ac3_4m0ng_wor1d5_b1c20a1a234a46e26dc7dcbfb69}

 

Virtual M0lecon

솔버가 적고 꽤 뇌지컬이 필요했던거 같은데 결국은 하라는대로만 하면 답이 나오는 문제였다 ㅎㅎ

VM인데 입력을 받지 않았다... 무조건 루틴 분석해서 풀라는 뜻인가보다..

기본적인 컨셉은 다음과 같다.

 

STACK, MEMORY

STACK은 그냥 값 push, pop 하는 스택 값 할당하고 SP로 관리했다.

좀 이상했던게 MEMORY였는데, 바이너리에서는 MEMORY를 4개만 할당해 줬었는데 opcode에서는 0x70 까지 사용했기때문에

바이너리 패치를 해서 문제를 봤어야 했다.

 

Instruction

바이너리 분석해서 어셈들 쫙 뽑아보면 꽤 많이 나온다..

대충 한 400~500줄? 도저히 어셈만 보고 풀수는 없을거라 생각해서 좀 자세히 분석을 해봤다.

from pwn import *

with open("./opcode", 'rb') as f:
        program = f.read(0x1000)

def fetch():
        global program
        global i
        res = program[i]
        i += 1
        return u8(res)

print(hexdump(program))
i = 0

while True:
        op = fetch()
        if op == 0: # AND
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      pop r2 ".format(i))
                print("0x{:x}:      push ( r1 & r2 )".format(i))

        elif op == 1: # OR
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      pop r2 ".format(i))
                print("0x{:x}:      push ( r1 | r2 )".format(i))

        elif op == 2: # XOR
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      pop r2 ".format(i))
                print("0x{:x}:      push ( r1 ^ r2 )".format(i))

        elif op == 3: # NOT
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      push ( not r1 )".format(i))

        elif op == 4: # ADD
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      pop r2 ".format(i))
                print("0x{:x}:      push ( r1 + r2 )".format(i))

        elif op == 5: # SUB
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      pop r2 ".format(i))
                print("0x{:x}:      push ( r2 - r1 )".format(i))

        elif op == 6: # MUL
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      pop r2 ".format(i))
                print("0x{:x}:      push ( r1 * r2 )".format(i))

        elif op == 7: # DIV
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      pop r2 ".format(i))
                print("0x{:x}:      push ( r2 %% r1 )".format(i))
                print("0x{:x}:      push ( r2 / r1 )".format(i))

        elif op == 8: # SHL
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      pop r2 ".format(i))
                print("0x{:x}:      push ( r2 << r1 )".format(i))

        elif op == 9: # SHR
                print("0x{:x}:      pop r1 ".format(i))
                print("0x{:x}:      pop r2 ".format(i))
                print("0x{:x}:      push ( r2 >> r1 )".format(i))

        elif op == 10: # PRT
                print("0x{:x}:      pop r1".format(i))
                print("0x{:x}:      PutChar r1".format(i))

        elif op == 11: # JMP
                v1 = 0
                for z in range(8):
                        v1 = fetch() + (v1 << 8)
                print("0x{:x}:      jmp {}:     ".format(i, hex(i+v1)))

        elif op == 12: # Jif
                v1 = 0
                for z in range(8):
                        v1 = fetch() + (v1 << 8)
                print("0x{:x}:      pop r1".format(i))
                print("0x{:x}:      if r1".format(i))
                print("0x{:x}:          jmp {}:     ".format(i, hex(i+v1)))


        elif op == 13: # SET
                print("0x{:x}:      pop r1".format(i))
                print("0x{:x}:      pop r2".format(i))
                print("0x{:x}:      MEMORY[r2] = r1".format(i))

        elif op == 14: # GET
                print("0x{:x}:      pop r1".format(i))
                print("0x{:x}:      push MEMORY[r1]".format(i))

        elif op == 15: # INT
                print("0x{:x}:      push {}:     ".format(i, hex(fetch())))

        elif op == 16: # DUP
                print("0x{:x}:      pop r1".format(i))
                print("0x{:x}:      push r1".format(i))
                print("0x{:x}:      r2 = DUP r1".format(i))
                print("0x{:x}:      push r2".format(i))

        elif op == 17: # DEL
                print("0x{:x}:      pop r1".format(i))
                print("0x{:x}:      free(r1)".format(i))

        elif op == 18: # NOP
                pass

        elif op == 19: # RET
                print("0x{:x}:      END".format(i))
                break

        elif op == 20: # RED
                print("0x{:x}:      GetChar r1".format(i))
                print("0x{:x}:      push r1".format(i))

        else:
                print('error')

디스어셈 돌리면 한 400~500줄의 어셈이 나온다.

어셈만 보고 대충 풀기에는 무리가 있어서 직접 뇌디컴 돌려봐야한다....

 

Decompile

디컴 돌리면 아래와 같은 코드가 나온다.

for(int i = 0; i < 0x14; i++) {
    MEMORY[0x59 + i] = 0;
}

MEMORY[0x1f] = 0;

ans_table = [3, 0xc4, 0x80, 0x16, 0xa2, 0xf8, 0x99, 0xca, 0xe6, 0x87, 0xe3, 0x49, 0x9f, 0x8b, 0xe7, 0xde, 0xe9, 4, 0x4c, 0x86];

for(int j = 0; j < 0x14; j++) {
    MEMORY[0x10] = MEMORY[0x59+j];
    MEMORY[0x11] = MEMORY[0x10] % 0x10
    MEMORY[0x11] *= 0x30
    MEMORY[0x12] = MEMORY[0x10] % 0x10 + ((MEMORY[0x10] / 0x10) * 5) % 0x10 - MEMORY[0x10] % 0x10
    if(ans_table[j] != MEMORY[0x11] + MEMORY[0x12] + MEMORY[0x1f]) {
        puts("Wrong");
        exit(0)
    }
    MEMORY[0x1f] += 1;
}

puts("Correct");

 

dec.py

ans_table = [3, 0xc4, 0x80, 0x16, 0xa2, 0xf8, 0x99, 0xca, 0xe6, 0x87, 0xe3, 0x49, 0x9f, 0x8b, 0xe7, 0xde, 0xe9, 4, 0x4c, 0x86]
print(len(ans_table))
flag = ''

for z in range(19):
        for i in range(0x20, 0x80):
                a = i % 0x10
                a *= 0x30
                b = (i%0x10) + ((i / 0x10) * 5) % 0x10 - (i%0x10)
                c = z
                res = a + b + c
                if (res & 0xff) == ans_table[z]:
                        flag += chr(i)
                        break

        print(flag)

그대로 브포돌리면 된다.

ptm{custom_asm_4_u!}

 

Outro

외국인 친구들과 디코도 하면서 했었는데 그 친구들도 잘 받아줘서 재밌게 했었다 :)

성적은 좋지 않지만, 문제도 좀 풀고 과정이 재밌어서 매우 만족한 대회였다.

포너블이 몇문제가 어렵게 나오고, house of husk문제가 하나 나왔는데 언솔빈 어택으로 로컬 땃는데 리모트 못따서 아쉬었다.

댓글
댓글쓰기 폼
공지사항
Total
7,553
Today
0
Yesterday
4
TAG
more
«   2022/01   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
글 보관함