[CODEGATE 2014] 코드게이트 2014 Clone Technique Writeup



 □ description

==========================================

Limited processes will be generated. 

Which one has the flag?


http://58.229.183.26/files/clone_technique.exe_b9b0870fb1b877cecf5ea2ae615e12eb

==========================================


개인적으로 윈도우 리버싱 문제를 상당히 좋아하는 편인데 

이 문제는 나를 멘붕 시키기에 충분했다.


400개의 프로세스가 무한대로 생성되는데 여기서 한 개라도 비정상종료(사용자에 의해) 된다면 또 다시 무한대로 생성되는(?) 듯 했다. 즉 자식의 자식의 자식의 자식의 자식 프로세스 인듯...

WriteProcessMemory 로 자기자신의 프로세스에 무언가 연산을 거친 값을 Write 한다.

(멘붕에 의해 잘못된 분석일수도 있습니다 일침 가해주세요)


여러가지 꼼수를 이용해서 풀 수 있겠지만 (코드패치 or memory dump) 대회 당시에 딱히 생각이 나지 않아서 Key Verify 루틴을 코딩하여 풀이했다.



눈여겨 볼 함수는 sub_401070 이다. ROL,ROR 을 통해 인자로 들어온 값을 verify 한다.


그 뒤 return 값을 가지고 XOR 을 거쳐서 자기자신의 프로세스의 임의 영역에 Write 한다.



분석은 여기까지 했는데 문제의 참신성에 비해 암호화 루틴이 간결한 편이었다.




char *str=(\x0F,\x8E,\x9E,\x39,\x3D,\x5E,\x3F,\xA8,

                 \x7A,\x68,\x0C,\x3D,\x8B,\xAD,\xC5,\xD0,

                 \x7B,\x09,\x34,\xB6,\xA3,\xA0,\x3E,\x67,\x5D,\xD6");


inline int ROL(int a,int b){

    return (a<<b)|((unsigned)a>>(32-b));

}


char* verify(char *a1,int a2,int a3){

    int i;

    int len;

    char *v8;


    len = strlen(a1);

    v8 = (char*)malloc(len+1);

    memset(v8,0,len);

    for(i = 0;i < (len - 1);i += 2)

    {

        v8[i] = a2 ^ a1[i];

        a2 = ROL(a2,5)^0x2f;

        if(!a1[i + 1]){

            break;

        }

        v8[i + 1] = a3 ^ a1[i + 1];

        a3 = ((unsigned char)a2)^ROL(a3,11);

    }


    return v8;

}


inline int cast(int a,int b)

{

    if(b==0)

    {

     return 1;

    }

    int d = cast(a,b/2);

    if(b%2)

   {

        return d*d*a;

    }

   else

   {

        return d*d;

    }

}


int gen(int a1,int a2,int ct){

    printf("Key : %s\n",verify(str,a1,a2));

    a1^=0xB72AF098u;

    a2 = (a1*a2)^a2;

    int v1 = a1+7*cast(a1,2)*29;

    int v6 = cast(v1^a2,((unsigned)v1%2)+5);

    if((unsigned int)a1<=0xD0000000u)

    {

        int ret;

        do{

            if(ct>400) return 0;

            ct++;

            ret = gen(v1,v6,ct);

            v1 = ret;

            v6 = cast(ret^v6,ret%30);

        }while(ret);

        return 0;

    }

  else

  {

        return (13*((unsigned)v1/27))^0x1f2a990d;

    }

}


int main()

{

    gen(0xA8276BFA,0x92F837ED,1);

    return 0;

}





외계어들 중에 유일하게 읽을 수 있는 키가 존재한다


KEY : And Now His Watch is Ended


[CODEGATE 2014] 코드게이트 2014 dodoCrackme Writeup



 □ description

==========================================

http://58.229.183.26/files/crackme_d079a0af0b01789c01d5755c885da4f6

==========================================


 □ number of solvers : 206

 □ breakthrough by

    1 : Hardc0de (02/22 09:14)

    2 : spamandhexlite (02/22 09:19)

    3 : bamb00 (02/22 09:23)


 


대회 문제중 풀이자가 2번째로 많았던 (포렌식 문제 다음) 문제이다.




64bit 환경의 리눅스 바이너리 이다.


IDA 로 열어본다.



Imports 가 매우 깔끔하다.

함수 호출없이 작성된 바이너리라고 생각된다.



시스템콜을 사용하는 것을 알 수있다.

64비트 시스템콜 목록(http://blog.rchapman.org/post/36801038863/linux-system-call-table-for-x86-64)




사용자로 부터 입력을 받을때는 read 함수를 사용할 것이다.

따라서 read system call 에 브레이크 포인트를 걸고 분석하면 된다.




read 함수는 RAX(EAX) 가 0일때 호출된다.



1바이트만 read 하도록 되어 있다.

저 부분에 break point 를 걸면 입력받을때 멈출 것이다.


이미 define 되어있는 문자열을 inc, dec 를 통해 키 값을 생성한다.

키가 틀렸다고 뜨는 두번째 write 함수에 break point 를 걸고 run(F9) 을 한다.

스택을 살펴보면 키 값이 생성 되어 있다.



KEY : H4PPY_C0DEGaTE_2014_CU_1N_KORE4

[Defcon21 CTF] gnireengne (reverse engineering) - 100 (policebox)

 

core

 

policebox

 

두 개의 파일이 주어집니다.

 

 

 

policebox 는 32비트 리눅스에서 실행가능한 바이너리 입니다.

core 는 core 입니다. :)

 

 

 

파일 구조는 매우 간단합니다.

tty 상태 인지 (not pts) 체크를 합니다.

13 (Ctrl+M) 을 입력받을 때 까지 지속해서 getchar() 로 문자를 입력받습니다.

\r 를 이용해 커서를 맨 앞으로 이동시키며 그 외에 별 동작은 없습니다.

 

따라서 이 파일에서 얻을 수 있는 정보는 이것 뿐이고 core 파일에는 실제로 실행되었던 메모리 상태가 그대로 남아있기 때문에 core 파일로 부터 실행 당시의 정보를 분석해야합니다.

 

 

 

core 파일로 부터 record 정보를 불러옵니다.

 

 

 

 

getchar() 가 호출된 이후의 반환값을 보기위해 적당히 브레이크 포인트를 걸어준 후 EAX 레지스터를 보면 됩니다.

 

 

 

EAX 에 반환 된 값을 ascii 로 출력하면 됩니다.

 

 

The Key is : w0rlds.w0rst.k3yl0gger!

 

티스토리 툴바