codegate2016 oldschool 음주ver

By passket | April 2, 2016

Codegate 2016 oldschool 롸업 음주ver

에…. passket 님이 술 한잔 하시고 빡치셔서(…) 써둔 writeup 입니다.
공개할까 말까 고민하다가 공개합니다. ㅇ<-<
음주 롸잇업으로 스알짝 말투가 격하십니다만,
그래도 0-day 드리니 양해해주세요 ;3





“The top-point pwnable was a simple format string vuln, in 2016…”


라고 ctftime에 누가 올려놨길래
손수 가르침을 좀 드림.


#include
#include
#include
 
int main( )
{
    char buf[1024] = { 0, };
 
    fgets( buf, 1024, stdin );
 
    printf( buf );
 
    return 0;
}


그렇다. 존나 단순한 format string bug 다.

그렇지만 문제는…

printf 로 fsb 가 일어나고 나서 함수가 끝난다는 거다.

fsb로 메모리는 보거나 고칠 수 있는데

어디를 고쳐야함 ?

을 물어보는 문제이다.


무려

attribute destrucctor를 만들어 보면

삼척동자도 여기(0x08049644)를 고쳐야 한다는 사실은 알 수 있다.

주니어에도 낼 문제여서 좀 봐줌


사실 비밀이 좀 있는데

08048000-08049000 r-xp 00000000 fc:00 125831508 /home/passket/pwn/chals/oldschool/test
08049000-0804a000 r–p 00000000 fc:00 125831508 /home/passket/pwn/chals/oldschool/test
0804a000-0804b000 rw-p 00001000 fc:00 125831508 /home/passket/pwn/chals/oldschool/test
f7e23000-f7e24000 rw-p 00000000 00:00 0
f7e24000-f7fc9000 r-xp 00000000 fc:00 60555283 /lib32/libc-2.19.so
f7fc9000-f7fcb000 r–p 001a5000 fc:00 60555283 /lib32/libc-2.19.so
f7fcb000-f7fcc000 rw-p 001a7000 fc:00 60555283 /lib32/libc-2.19.so
f7fcc000-f7fd0000 rw-p 00000000 00:00 0
f7fda000-f7fdb000 rw-p 00000000 00:00 0
f7fdb000-f7fdc000 r-xp 00000000 00:00 0 [vdso]
f7fdc000-f7ffc000 r-xp 00000000 fc:00 60555276 /lib32/ld-2.19.so
f7ffc000-f7ffd000 r–p 0001f000 fc:00 60555276 /lib32/ld-2.19.so
f7ffd000-f7ffe000 rw-p 00020000 fc:00 60555276 /lib32/ld-2.19.so
fffdd000-ffffe000 rw-p 00000000 00:00 0 [stack]

원래 정상적으로 컴파일하면

dtors 영역에

w 를 할 수 가 없다.

그래서 w 권한 줘서 문제내줌.

아 나란남자 멋진 실력에 친절함도 같이 있다니

유부남인게 존나 아까움

사실 w 권한 다시 빼서 본선에 문제 낼까 했음

근데 푼 팀중에 많은 팀들이 브포 해서 문제 푼 팀도 존나 많아서

중생들 구제하고자 좀 봐줌.


사실 이 문제를 2011년에 padocon CTF에 냈었음.

그러함. padocon CTF에 Karma와 goe flow 낸 사람이 나임. 한명 더 있음.


http://blog.disekt.org/node/38


요걸로 현재 oldschool 바이너리는 풀수 있음.

릭만 추가하면 댐

그러나, w 권한이 추가되면서

현재 니들은 못품.

힌트를 좀 더 주고 공부할 거리를 좀 더 주자면

ida로 libc 좀 열어봤음


// local variable allocation has failed, the output may be wrong!
int __cdecl __noreturn _libc_start_main(int (__cdecl *main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void *stack_end)
{
void (*v7)(void); // rbp@1
int v8; // edx@2
void *v9; // rdx@5
int v10; // ebx@5
int v11; // er14@9
int v12; // eax@13
__int64 v13; // rax@16
__int64 v14; // rax@16
volatile signed __int32 *v15; // rax@16
__int64 v16; // r13@18
unsigned int v17; // er12@18
__int64 v18; // rbp@18
void (__fastcall *v19)(signed __int64, _QWORD, void *, void (*)(void), void (*)(void)); // rax@19
char **v20; // [sp+8h] [bp-B0h]@1
int v21; // [sp+14h] [bp-A4h]@1
char v22; // [sp+20h] [bp-98h]@12
__int64 v23; // [sp+68h] [bp-50h]@13
__int64 v24; // [sp+70h] [bp-48h]@13

v7 = init;
v21 = argc;
v20 = ubp_av;
if ( &dl_starting_up )
v8 = dl_starting_up == 0;
else
v8 = 0;
dword_3C4080 = v8;
if ( rtld_fini )
{
    (_QWORD *)&argc = 0LL;
    _cxa_atexit(rtld_fini, 0LL, 0LL, init, fini);
}
v9 = &rtld_global_ro;
v10 = rtld_global_ro & 2;
if ( rtld_global_ro & 2 )
{
    *(_QWORD *)&argc = *v20;
    (((void (fastcall **)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD))&rtld_global_ro + 25))(
        “\ninitialize program: %s\n\n”,
        *v20,
        &rtld_global_ro,
        init,
        fini
    );
}
if ( v7 )
{
    *(_QWORD *)&argc = v20;
    ((void (__fastcall *)(_QWORD, char **, char **, void (*)(void), void (*)(void)))v7)(
        (unsigned int)v21,
        v20,
        environ,
        init,
        fini
    );
}
v11 = *((_DWORD *)&rtld_global_ro + 72);
if ( v11 )
{
    v16 = *((_QWORD *)&rtld_global_ro + 35);
    v17 = 0;
    v18 = rtld_global;
    do
    {
    v19 = *(void (__fastcall **)(signed __int64, _QWORD, void *, void (*)(void), void (*)(void)))(v16 + 24);
    if ( v19 )
        v19(v18 + 16 * (v17 + 71LL), *(_QWORD *)&argc, v9, init, fini);
    ++v17;
    v16 = *(_QWORD *)(v16 + 64);
    }
    while ( v11 != v17 );
}
if ( v10 )
{
    *(_QWORD *)&argc = *v20;
    (*((void (__fastcall **)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD))&rtld_global_ro + 25))(
        “\ntransferring control: %s\n\n”,
        *v20,
        &rtld_global_ro,
        init,
        fini
    );
}
if ( setjmp((struct __jmp_buf_tag *__attribute((org_typedef(jmp_buf))) )&v22) )
{
    v13 = __ROR8(qword_3C9C50, 17);
    ((void (fastcall )(char *, _QWORD))(MK_FP(__FS, 48LL) ^ v13))(&v22, *(_QWORD *)&argc);
    v14 = ROR8(qword_3C9C40, 17);
    v15 = (volatile signed int32 )(MK_FP(__FS, 48LL) ^ v14);
    _InterlockedDecrement(v15);
    if ( v15 )
    {
        while ( 1 )
            asm { syscall }
    }
    v12 = 0;
}
else
{
    v23 = *MK_FP(__FS, 768LL);
    v24 = *MK_FP(FS, 760LL);
    *MK_FP(FS, 768LL) = &v22;
    v12 = ((int (__fastcall *)(_QWORD, char **, char **))main)((unsigned int)v21, v20, environ);
}
exit(v12);
}


끝에 exit 보임 ?

이거 우리가 아는 그 exit 아님


void __fastcall __noreturn sub_39C40(int status, __int64 *a2, char a3)
{
char v3; // r12@1
__int64 *v4; // rbp@1
int v5; // ebx@1
__int64 v6; // r13@2
__int64 v7; // rax@3
__int64 *v8; // rcx@3
__int64 v9; // rdx@4
bool v10; // zf@8
void (**v11)(void); // rbp@10
__int64 v12; // rsi@13
__int64 v13; // rax@13
__int64 v14; // rdi@14
__int64 v15; // rax@14
__int64 v16; // rax@15

v3 = a3;
v4 = a2;
v5 = status;
_call_tls_dtors();
while ( 1 )
{
    v6 = *v4;
    if ( !*v4 )
    {
        LABEL_9:
        if ( v3 )
        {
            v11 = (void ()(void))&off_3C0748;
            if ( &off_3C0748 < &off_3C0750 )
            {
                do
                {
                    (*v11)();
                    ++v11;
                }
                while ( v11 < (void ()(void))&off_3C0750 );
            }
        }
        exit(v5);
    }
    LABEL_3:
    while ( 1 )
    {
        v7 = *(_QWORD *)(v6 + 8);
        v8 = (int64 *)(v6 + 32LL * *(_QWORD *)(v6 + 8) – 16);
        if ( !v7 )
        break;
        while ( 1 )
        {
            *(_QWORD *)(v6 + 8) = –v7;
            v9 = *v8;
            if ( *v8 == 3 )
            break;
            if ( v9 == 4 )
            {
                v14 = *(_QWORD *)(v6 + 32 * v7 + 32);
                v15 = __ROR8(*(_QWORD *)(v6 + 32 * v7 + 24), 17);
                ((void (fastcall *)(__int64, _QWORD))(*MK_FP(__FS, 48LL) ^ v15))(v14, (unsigned int)v5);
                goto LABEL_3;
            }
            if ( v9 == 2 )
            {
                v12 = *(_QWORD *)(v6 + 32 * v7 + 32);
                v13 = ROR8(*(_QWORD *)(v6 + 32 * v7 + 24), 17);
                ((void (fastcall *)(_QWORD, __int64))(*MK_FP(__FS, 48LL) ^ v13))((unsigned int)v5, v12);
                goto LABEL_3;
            }
            v8 -= 4;
            if ( !v7 )
            goto LABEL_8;
        }
        v16 = ROR8(*(_QWORD *)(v6 + 32 * v7 + 24), 17);
        ((void (*)(void))(*MK_FP(FS, 48LL) ^ v16))();
    }
    LABEL_8:
    v10 = *(_QWORD *)v6 == 0LL;
    *v4 = *(_QWORD *)v6;
    if ( v10 )
        goto LABEL_9;
    free((void *)v6);
}

여기 보임 ? 여기에 무려 0-day가 3개는 됨.

다시 말하지만 opensource 프로그램 코드로 보는것과 ida로 보는것은

매우 다름. 직접 해봐야 암.

술도 먹었겠다. 기분도 나쁘겠다.

이정도 0-day는 심심하면 나오니

본선에서 보겠음

각오들 하시고 오는게 좋겠음.

나 라우터 만들었음.

comments powered by Disqus