hack.lu CTF

jking http://www.theamazingking.com/ and I worked on ELF

 

first disassembled it with IDA, pulled out C code and attacked it from there, working backwards with what the key ought to be, one value at first just seemed to be anti debug , which was just the ptrace test, which would increment it.

Also as eventually noted by fluxfingers team, if you happened to be running non root on ubuntu ( I was ) you’d get the wrong results because ubuntu doesn’t let child procs ptrace as a non root user…which would have been a big clue.

 

unsigned char some_counter = 0xA ;

unsigned char fluxFluxFLUX[] = "fluxFluxfLuxFLuxflUxFlUxfLUxFLUxfluXFluXfLuXFLuXflUXFlUXfLUXFLUX";

int __cdecl ld_preload_ptrace()
{
    int result; // eax@4
    int stat_loc; // [sp+14h] [bp-14h]@4
    int v2; // [sp+18h] [bp-10h]@6
    int v3; // [sp+1Ch] [bp-Ch]@3

    if ( getenv ( "LD_PRELOAD" ) )
    { ++counter; }

    v3 = fork();

    if ( !v3 ) {
        v2 = getppid();

        if ( ptrace ( PTRACE_ATTACH, v2, 0, 0 ) < 0 )
        { exit ( 1 ); }

        sleep ( 1u );
        ptrace ( PTRACE_DETACH, v2, 0, 0 );
        exit ( 0 );
    }

    wait ( &stat_loc );
    result = stat_loc;

    if ( stat_loc ) {
        sleep ( 1u );
        result = counter++ + 1;
    }

    return result;
}

int __cdecl main ( int argc, char *argv[] )
{
    size_t password_length; // eax@4
    char v9[300]; // [sp+28h] [bp-374h]@8
    unsigned char *v10; // [sp+368h] [bp-34h]@13
    unsigned char *v11; // [sp+36Ch] [bp-30h]@10
    unsigned char *phase1_buffer; // [sp+370h] [bp-2Ch]@4
    const char *ptr_to_password; // [sp+374h] [bp-28h]@4

    unsigned int flag4; // [sp+378h] [bp-24h]@40
    unsigned int flag3; // [sp+37Ch] [bp-20h]@40
    unsigned int flag2; // [sp+380h] [bp-1Ch]@40
    unsigned int flag1; // [sp+384h] [bp-18h]@40

    size_t j; // [sp+388h] [bp-14h]@23
    size_t i; // [sp+38Ch] [bp-10h]@4

    if ( argc != 2 ) {
        printf ( "Usage: %s <flag>\n",  argv[0] );
        exit ( 0 );
    }

    puts ( "Calculating phase 1 …" );

    ptr_to_password =  argv[1];

    password_length = strlen ( argv[1] );
    phase1_buffer = ( unsigned char * ) malloc ( password_length + 1 );

    memset ( phase1_buffer, 0, password_length + 1 );

    for ( i = 0;  password_length > i; ++i ) {
        int i2;
        i2 = ( i – some_counter );

        phase1_buffer[ i ]  = ptr_to_password[ ( i – some_counter ) % password_length ];
    }

    sleep ( 1u );
    puts ( "done\n" );

    ++some_counter;

    for ( i = 0; i <= 207; ++i ) {
        v9[i] =  65;
    }

    v11 = ( unsigned char * ) malloc ( password_length + 1 );
    memset ( v11, 0, password_length + 1 );

    puts ( "Calculating phase 2 …" );

    for ( i = 0; ; ++i ) {

        if ( password_length <= i ) {
            break;
        }

        v11[i]  = some_counter ^ fluxFluxFLUX[i] ^  phase1_buffer[ i ];
    }

    sleep ( 1u );
    puts ( "done\n" );

    some_counter += 3;

// I added the +1 for for dbg

    v10 = ( unsigned char* ) malloc ( password_length + 1 );

    memset ( v10, 0, password_length + 1 );

    for ( i = 0; ; ++i ) {

        if ( password_length <= i ) {
            break;
        }

        v10[i] = some_counter;
    }

    for ( i = 0; i <= 207; ++i ) {
        v9[i] =  66;
    }

    for ( i = 0; i <= 0xCF; ++i ) {
        v9[i] = 70;
    }

    // 3 on

    unsigned char index = 0;

    //memset ( v11, 0, password_length );

    some_counter  = 4;

loop:

    for ( i = 0; i <= 2; ++i ) {

        printf ( "Calculating phase  %u …\n", i + 3 );

        for ( j = 0; ; ++j ) {

            if ( password_length <= j ) {
                break;
            }

            v10[j]  ^= v11[ j ] ^ fluxFluxFLUX[ ( i + j + some_counter ) % password_length];
        }
    }

 

    for ( i = 0; i <= 0xCF; ++i ) {

        v9[i] =  69;
        v9[i] =  67;

        if ( v9 [ ( i + 3 ) % 0xD0] ==  65 ) {
            v9 [ ( i + 4 ) % 0xD0] =  83;
        }
    }

    for ( i = 0; i <= 0xCF; ++i ) {

        v9[i] = 67;

        if ( v9[ ( i + 3 ) % 0xD0] ==  65 ) {
            v9[ ( i + 4 ) % 0xD0] = 83;
        }

        if ( ( v9 ) [ ( i + 3 ) % 0xD0] ==  66 ) {
            v9[ ( i + 4 ) % 0xD0] = 83;
        }
    }

 

    flag1 = 0;
    flag2 = 0;
    flag3 = 0;
    flag4 = 0;

 

// working backwards from below we get

v10[0] = 17;
v10[1] = 96;
v10[2] = 50;
v10[3] = 88;
v10[4] = 97;
v10[5] = 101;
        v10[6] = 81;
        v10[7] = 34;
        v10[8] = 102;
        v10[9] = 98;
        v10[10] = 107;
        v10[11] = 94;
        v10[12] = 75;
        v10[13] = 69;
        v10[14] = 110;
        v10[15] = 85;
 

 

for ( i = 0; i <= 3; ++i ) {
    flag1 |= ( unsigned char ) v10[i] << 8 * i;
}

for ( i = 0; i <= 3; ++i ) {
    flag2 |= ( unsigned char ) v10[i + 4] << 8 * i;
}

for ( i = 0; i <= 3; ++i ) {
    flag3 |= ( unsigned char ) v10[i + 8] << 8 * i;
}

for ( i = 0; i <= 3; ++i ) {
    flag4 |= ( unsigned char ) v10[i + 12] << 8 * i;

}

//printf ( "%x %x %x %x %x\n", some_counter, flag1, flag2, flag3, flag4 );

if ( flag1 != 0x58326011 || flag2 != 0x22516561 || flag3 != 0x5E6B6266 || flag4 != 0x556E454B ) {
    puts ( "Flag wrong!" );

}

else {
    puts ( "Flag correct!" );
}

return 0;

 

}

 

the thing that bothered me about my C version vs the elf binary was the speed difference, mine ran much faster for no apparent reason, so I looked harder at the initial ptrace test but even though it was forking I saw no way that it could be hooking and repeating itself, noping out the sleep code didn’t alter the speed.

 

stracing showed that it was forking and sleeping again. so single stepping I saw that some of the libc’s were indeed going to different places. looking at the plt

 

— SIGCHLD (Child exited) @ 0 (0) —
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0xffeca9f8)           = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0) = 18192
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 18192
— SIGCHLD (Child exited) @ 0 (0) —
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0xffeca9f8)           = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), …}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfffffffff7792000
write(1, "Calculating phase 1 …\n", 24Calculating phase 1 …
) = 24
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0) = 18193
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 18193
— SIGCHLD (Child exited) @ 0 (0) —
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, ^C <unfinished …>

 

 

.got.plt:0804A150 18 89 7B F7                   off_804A150 dd offset dword_F77B8918    ; DATA XREF: .got.plt:0804A154                               ; int (*off_804A154)(void)
.got.plt:0804A154 A0 B6 7A F7                   off_804A154 dd offset unk_F77AB6A0      ; DATA XREF: .got.plt:0804A158 AF 91 04 08                   ptr_to_printf dd offset another_ptrace_counter_increment_0
.got.plt:0804A15C 76 84 04 08                   ptr_sleep dd offset loc_8048476         ; DATA XREF: _sleepr
.got.plt:0804A160 86 84 04 08                   ptr_wait dd offset word_8048486         ; DATA XREF: _waitr
.got.plt:0804A164 96 84 04 08                   ptr_getenv dd offset word_8048496       ; DATA XREF: _getenvr
.got.plt:0804A168 D1 92 04 08                   ptr_malloc dd offset another_ptrace_counter_increment_1
.got.plt:0804A168                                                                       ; DATA XREF: _mallocr
.got.plt:0804A168                                                                       ; setup_hooks+1Aw
.got.plt:0804A16C 60 90 04 08                   p_io_puts dd offset another_ptrace_counter_increment
.got.plt:0804A16C                                                                       ; DATA XREF: _putsr
.got.plt:0804A16C                                                                       ; setup_hooks+6w
.got.plt:0804A170                               ; int (*off_804A170)(void)
.got.plt:0804A170 C6 84 04 08                   off_804A170 dd offset word_80484C6      ; DATA XREF: ___gmon_start__r
.got.plt:0804A174 D6 84 04 08                   off_804A174 dd offset word_80484D6      ; DATA XREF: _exitr
.got.plt:0804A178 5F 94 04 08                   check_cc_buffer dd offset another_ptrace_counter_increment_2
.got.plt:0804A178                                                                       ; DATA XREF: _strlenr
.got.plt:0804A178                                                                       ; setup_hooks+24w
.got.plt:0804A17C E0 53 5F F7                   ptr_libc_main dd offset __libc_start_main
.got.plt:0804A17C                                                                       ; DATA XREF: ___libc_start_mainr
.got.plt:0804A180 06 85 04 08                   ptr_libc_fork dd offset word_8048506    ; DATA XREF: _forkr
.got.plt:0804A184 16 85 04 08                   off_804A184 dd offset word_8048516      ; DATA XREF: _getppidr
.got.plt:0804A188                               ; int (*ptr_ptrace)(void)
.got.plt:0804A188 26 85 04 08                   ptr_ptrace dd offset word_8048526       ; DATA XREF: _ptracer

 

reverse_me:080491AF                               ; —————————————————————————
reverse_me:080491AF
reverse_me:080491AF                               loc_80491AF:
reverse_me:080491AF 50                            push    eax
reverse_me:080491B0 51                            push    ecx
reverse_me:080491B1 E8 55 02 00 00                call    near ptr unk_804940B
reverse_me:080491B6 B8 F4 00 00 00                mov     eax, 0F4h
reverse_me:080491BB
reverse_me:080491BB                               loc_80491BB:                            ; CODE XREF: reverse_me:080491DBj
reverse_me:080491BB 8A 88 60 90 04 08             mov     cl, byte ptr loc_8049060[eax]
reverse_me:080491C1 80 F9 CC                      cmp     cl, 0CCh
reverse_me:080491C4 75 0F                         jnz     short loc_80491D5
reverse_me:080491C6 50                            push    eax
reverse_me:080491C7 A1 94 A1 04 08                mov     eax, ds:just0x //the increment of the counter
reverse_me:080491CC 83 C0 01                      add     eax, 1
reverse_me:080491CF A3 94 A1 04 08                mov     ds:just0x, eax
reverse_me:080491D4 58                            pop     eax
reverse_me:080491D5
reverse_me:080491D5                               loc_80491D5:                            ; CODE XREF: reverse_me:080491C4j
reverse_me:080491D5 83 F8 00                      cmp     eax, 0
reverse_me:080491D8 74 03                         jz      short loc_80491DD
reverse_me:080491DA 48                            dec     eax
reverse_me:080491DB EB DE                         jmp     short loc_80491BB
reverse_me:080491DD                               ; —————————————————————————
reverse_me:080491DD
reverse_me:080491DD                               loc_80491DD:                            ; CODE XREF: reverse_me:080491D8j
reverse_me:080491DD B8 F0 00 00 00                mov     eax, 0F0h
reverse_me:080491E2
reverse_me:080491E2                               loc_80491E2:                            ; CODE XREF: reverse_me:08049202j
reverse_me:080491E2 8A 88 5F 94 04 08             mov     cl, byte_804945F[eax]
reverse_me:080491E8 80 F9 CC                      cmp     cl, 0CCh
reverse_me:080491EB 75 0F                         jnz     short loc_80491FC
reverse_me:080491ED 50                            push    eax
reverse_me:080491EE A1 94 A1 04 08                mov     eax, ds:just0x // the increment again
reverse_me:080491F3 83 C0 01                      add     eax, 1
reverse_me:080491F6 A3 94 A1 04 08                mov     ds:just0x, eax
reverse_me:080491FB 58                            pop     eax
reverse_me:080491FC
reverse_me:080491FC                               loc_80491FC:                            ; CODE XREF: reverse_me:080491EBj
reverse_me:080491FC 83 F8 00                      cmp     eax, 0
reverse_me:080491FF 74 03                         jz      short loc_8049204
reverse_me:08049201 48                            dec     eax
reverse_me:08049202 EB DE                         jmp     short loc_80491E2
reverse_me:08049204                               ; —————————————————————————
reverse_me:08049204
reverse_me:08049204                               loc_8049204:                            ; CODE XREF: reverse_me:080491FFj
reverse_me:08049204 59                            pop     ecx
reverse_me:08049205 58                            pop     eax
reverse_me:08049206 E9 5B F2 FF FF                jmp     near ptr word_8048466

 

similar code again, and there were a couple of others.

 

so from here we knew that it was incrementing the value during the run.

jk wrote a python bruter based on the c code and we had been trying different values with the counter.

He got “4v0iDsS3CtIOnSLd” the password was “Ld4v0iDsS3CtIOnS” I’d even rotated it since phase one did that, but the change of case on the Ld threw a spanner in that. unfortunately for us that was about 1400 seconds before the end of the CTF when we really started focusing on the change of value.

http://charliex.pastebay.com/1332967

 

Ahh well..

Advertisements