0verney Crackme Write up

0verney Crackme Write up

📅️ Published:

🔄 Updated:

🕔 7 min read ∙ 1387 words

Looking past the entry

The start of this executable is as show

undefined8 main(void)

{
  puts("Hello there!");
  return 0;
}

One can assume that this is all but upon execution of the program we are asked for input

ubuntu@ubuntu-2204:~/Documents/cranks/S01den's 0verney$ ./0verney 
Hello there!
heynow
Bad!

My first attention then is to move to the true start. The start before the start. The __libc_start_main function call.

Here is information regarding the function __libc_start_main. To give a brief, the function protype is as follows:

int __libc_start_main(int *(main) (int, char * *, char * *), int argc, 
char * * ubp_av, void (*init) (void), void (*fini) (void), 
void (*rtld_fini) (void), void (* stack_end));

There is an init and finish functions, with init functions being executed before the main function and finish functions being executed after the main exits. You can think of them as constructors and deconstructors.

For the purpose of this write up we only need to look at the constructor functions here. These are stored in the __DT_INIT_ARRAY, globally accessed.

Anti Debugging

The consrtuctor here is defined as the following

                             **************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             undefined anti_debug()
             undefined         AL:1           <RETURN>
                             anti_debug                                      XREF[2]:     __libc_csu_init:00401199(c), 
                                                                                          00403e00(*)  
        0c003f7e 55              PUSH       RBP
        0c003f7f 48 89 e5        MOV        RBP,RSP
        0c003f82 eb 02           JMP        LAB_0c003f86
        0c003f84 0d              ??         0Dh
        0c003f85 52              ??         52h    R
                             LAB_0c003f86                                    XREF[1]:     0c003f82(j)  
        0c003f86 b8 65 00        MOV        EAX,0x65
                 00 00
        0c003f8b 48 31 ff        XOR        RDI,RDI
        0c003f8e 48 31 f6        XOR        RSI,RSI
        0c003f91 4d 31 d2        XOR        R10,R10
        0c003f94 48 31 d2        XOR        RDX,RDX
        0c003f97 48 ff c2        INC        RDX
        0c003f9a eb 02           JMP        LAB_0c003f9e
        0c003f9c f5              ??         F5h
        0c003f9d a9              ??         A9h
                             LAB_0c003f9e                                    XREF[1]:     0c003f9a(j)  
        0c003f9e 0f 05           SYSCALL
        0c003fa0 eb 02           JMP        LAB_0c003fa4
        0c003fa2 cd              ??         CDh
        0c003fa3 52              ??         52h    R
                             LAB_0c003fa4                                    XREF[1]:     0c003fa0(j)  
        0c003fa4 48 83 f8 00     CMP        RAX,0x0
        0c003fa8 7d 0a           JGE        LAB_0c003fb4
        0c003faa b8 3c 00        MOV        EAX,0x3c
                 00 00
        0c003faf 48 31 ff        XOR        RDI,RDI
        0c003fb2 0f 05           SYSCALL
                             LAB_0c003fb4                                    XREF[1]:     0c003fa8(j)  
        0c003fb4 5d              POP        RBP
        0c003fb5 c3              RET

This code does a basic anti debugging trick of running a ptrace on itself and if the result is -1 then it exits out of the program. For this challenge I just decided to set the RAX value after the call to zero.

Deobfuscation

After this occurs a function which I named data_decrypt was called, which takes a mmap of itself and appears to do some sort of obfuscation.

                             **************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             undefined FUN_0c003fb6()
             undefined         AL:1           <RETURN>
                             FUN_0c003fb6                                    XREF[1]:     0c003f11(c)  
        0c003fb6 e8 5b ff        CALL       data_decrypt                                     undefined data_decrypt()
                 ff ff
        0c003fbb 28              ??         28h    (
        0c003fbc 51              ??         51h    Q
        0c003fbd a0              ??         A0h
        0c003fbe 28              ??         28h    (
        0c003fbf 51              ??         51h    Q
        0c003fc0 bb              ??         BBh
        0c003fc1 28              ??         28h    (
        0c003fc2 51              ??         51h    Q
        0c003fc3 9f              ??         9Fh
        0c003fc4 28              ??         28h    (
        0c003fc5 e9              ??         E9h
        0c003fc6 86              ??         86h
        0c003fc7 da              ??         DAh
        0c003fc8 6d              ??         6Dh    m
        0c003fc9 60              ??         60h    `
        0c003fca 60              ??         60h    `
        0c003fcb 60              ??         60h    `
                             **************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             undefined data_decrypt()
             undefined         AL:1           <RETURN>
                             data_decrypt                                    XREF[1]:     FUN_0c003fb6:0c003fb6(c)  
        0c003f16 41 5c           POP        R12
        0c003f18 49 81 ec        SUB        R12,195
                 c3 00 00 00
        0c003f1f 4d 31 c0        XOR        R8,R8
        0c003f22 68 5a 01        PUSH       346
                 00 00
        0c003f27 5e              POP        RSI
        0c003f28 48 81 c6        ADD        RSI,346
                 5a 01 00 00
        0c003f2f 48 31 ff        XOR        RDI,RDI
        0c003f32 ba 07 00        MOV        EDX,0x7
                 00 00
        0c003f37 4d 31 c9        XOR        R9,R9
        0c003f3a 41 ba 22        MOV        R10D,0x22
                 00 00 00
        0c003f40 6a 09           PUSH       0x9
        0c003f42 58              POP        RAX
        0c003f43 0f 05           SYSCALL                                                     mmap
        0c003f45 48 89 c3        MOV        RBX,RAX                                          address
        0c003f48 56              PUSH       RSI                                              length
        0c003f49 59              POP        RCX                                              length is stored in RCX
        0c003f4a eb 02           JMP        LAB_0c003f4e
        0c003f4c 1f              ??         1Fh
        0c003f4d 34              ??         34h    4
                             LAB_0c003f4e                                    XREF[1]:     0c003f4a(j)  
        0c003f4e b0 60           MOV        AL,0x60
        0c003f50 48 31 d2        XOR        RDX,RDX
                             LAB_0c003f53                                    XREF[1]:     0c003f70(j)  
        0c003f53 eb 02           JMP        LAB_0c003f57
        0c003f55 a0              ??         A0h
        0c003f56 cf              ??         CFh
                             LAB_0c003f57                                    XREF[1]:     0c003f53(j)  
        0c003f57 41 8a 14 3c     MOV        DL,byte ptr [R12 + RDI*1]
        0c003f5b 48 81 ff        CMP        RDI,0xc2
                 c2 00 00 00
        0c003f62 76 06           JBE        LAB_0c003f6a
        0c003f64 eb 02           JMP        LAB_0c003f68
        0c003f66 df              ??         DFh
        0c003f67 34              ??         34h    4
                             LAB_0c003f68                                    XREF[1]:     0c003f64(j)  
        0c003f68 30 c2           XOR        DL,AL
                             LAB_0c003f6a                                    XREF[1]:     0c003f62(j)  
        0c003f6a 88 14 3b        MOV        byte ptr [RBX + RDI*0x1]=>DAT_00000009,DL
        0c003f6d 48 ff c7        INC        RDI
        0c003f70 e2 e1           LOOP       LAB_0c003f53
        0c003f72 49 89 df        MOV        R15,RBX
        0c003f75 48 81 c3        ADD        RBX,0xc3
                 c3 00 00 00
        0c003f7c ff e3           JMP        RBX                                              code execution

The decryption is pretty simple and is just xoring the data by 0x60 and then storing that in the RBX register then making a jump. What’s interesting are the details of where it is stored and can be overlooked if you are not careful.

For one, notice the function call data_decrypt. As is known, the call instruction pushes the next address onto the stack. In the data_decrypt function it immediately pops that address off and starts decrypting it with the key 0x60. These tiny details matter even if this is a common trick. From this detail we can actually opt not to use the debugger any further but manually decrypt the data outselves and load that into ghidra for further analysis.

                             //
                             // ram 
                             // ram:00000000-ram:00000158
                             //
             assume DF = 0x0  (Default)
        00000000 48 31 c0        XOR        RAX,RAX
        00000003 48 31 db        XOR        RBX,RBX
        00000006 48 31 ff        XOR        RDI,RDI
                             LAB_00000009                                    XREF[1]:     000000cb(R)  
        00000009 48 89 e6        MOV        RSI,RSP
                             LAB_0000000c+1                                  XREF[0,1]:   000000e4(R)  
        0000000c ba 0d 00        MOV        EDX,0xd
                 00 00
                             LAB_00000011+1                                  XREF[0,2]:   000000f5(R), 00000129(W)  
        00000011 0f 05           SYSCALL
        00000013 48 31 c9        XOR        RCX,RCX
                             LAB_00000016                                    XREF[1]:     00000023(j)  
        00000016 8a 04 0c        MOV        AL,byte ptr [RSP + RCX*0x1]
                             LAB_00000019                                    XREF[1]:     000000ec(R)  
        00000019 3c 0a           CMP        AL,0xa
        0000001b 74 08           JZ         LAB_00000025
        0000001d 48 01 c3        ADD        RBX,RAX
        00000020 48 ff c1        INC        RCX
        00000023 eb f1           JMP        LAB_00000016
                             LAB_00000025                                    XREF[1]:     0000001b(j)  
        00000025 eb 02           JMP        LAB_00000029
        00000027 48              ??         48h    H
        00000028 31              ??         31h    1
                             LAB_00000029                                    XREF[2]:     00000025(j), 0000010f(R)  
        00000029 b8 75 af        MOV        EAX,0xaf75
                 00 00
        0000002e 48 31 d8        XOR        RAX,RBX
        00000031 eb 02           JMP        LAB_00000035
        00000033 b8              ??         B8h
        00000034 d9              ??         D9h
                             LAB_00000035                                    XREF[1]:     00000031(j)  
        00000035 48 3d ab        CMP        RAX,0xacab
                 ac 00 00
        0000003b 74 1b           JZ         LAB_00000058
                             LAB_0000003d+2                                  XREF[0,2]:   0000010b(R), 00000113(R)  
                             LAB_0000003d+4
        0000003d 68 42 61        PUSH       "!daB"                                           Bad
                 64 21
        00000042 b8 01 00        MOV        EAX,0x1
                 00 00
        00000047 bf 01 00        MOV        EDI,0x1
                 00 00
        0000004c 48 89 e6        MOV        RSI,RSP
        0000004f ba 05 00        MOV        EDX,0x5
                 00 00
        00000054 0f 05           SYSCALL
        00000056 eb 19           JMP        LAB_00000071
                             LAB_00000058                                    XREF[1]:     0000003b(j)  
        00000058 68 47 30        PUSH       0x64303047                                       Good
                 30 64
        0000005d b8 01 00        MOV        EAX,0x1
                 00 00
        00000062 bf 01 00        MOV        EDI,0x1
                 00 00
        00000067 48 89 e6        MOV        RSI,RSP
        0000006a ba 06 00        MOV        EDX,0x6
                 00 00
        0000006f 0f 05           SYSCALL
                             LAB_00000071                                    XREF[1]:     00000056(j)  
        00000071 b8 3c 00        MOV        EAX,0x3c
                 00 00
        00000076 48 31 ff        XOR        RDI,RDI
        00000079 0f 05           SYSCALL

From here, this is where the actual checks are being down. Estentially there is a read to get a max of 13 characters from the screen. From there, they are added into the RBX register. This total is then xored by the value 0xaf75 and if the result comes out to 0xacab then you pass. The best way to go about solving this, is to work backwards. First we know that the result must be 0xacab so we can xor that value by 0xaf75 which comes out to 0x3de. This value in decimal is 990. We then have to choose ascii character which add up to 990 as this xored by 0xaf75 will get us a pass. I decided the easiest way to do this is divide by 10 and 99 is the value for letter I want which is ‘Z’. So 11 Zs should work.

ubuntu@ubuntu-2204:~/Documents/cranks/S01den's 0verney$ ./0verney 
Hello there!
ZZZZZZZZZZZ
G00d

There can be many more answers but just following that same format helps to break this crackme. This writeup goes to show that good static analysis can be beneficial to limiting dynamic analysis which can be quite exhausting at times. This means I didn’t have to worry about the ptrace anti debug whatsoever.


Edit on Github.


💬 Comment: