1. 程式人生 > >堆溢位----Off-By-One

堆溢位----Off-By-One

學習資料:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/heap/off_by_one/#_5

off-by-one 指程式向緩衝區中寫入時,寫入的位元組數超過了這個緩衝區本身所申請的位元組數並且只越界了一個位元組,emmm

看一下CTFWIKI給的第一個例子(迴圈邊界)

int my_gets(char *ptr,int size)
{
    int i;
    for(i=0;i<=size;i++)
    {
        ptr[i]=getchar();
    }
    return i;
}
int main()
{
    void *chunk1,*chunk2;
    chunk1=malloc(16);
    chunk2=malloc(16);
    puts("Get Input:");
    my_gets(chunk1,16);
    return 0;
}

可以看到因為迴圈次數多了一次,存在溢位,這是一種

另一種和字串有關(字串操作)----NULL byte off-by-one

int main(void)
{
    char buffer[40]="";
    void *chunk1;
    chunk1=malloc(24);
    puts("Get Input");
    gets(buffer);
    if(strlen(buffer)==24)
    {
        strcpy(chunk1,buffer);
    }
    return 0;

}

乍一見沒毛病啊,程式也跑得起來,但是溢位了

因為strlen函式計算字串長度的時候是不包括結尾的\x00的,但是strcpy會,那就正好溢位了一個位元組

例題:

1.2016 Asis b00ks----NULL byte off-by-one

熟悉的PIE\NX\Stack

看一下堆山有什麼資訊

我們可以看到每次建立一個book,需要開闢0x20個位元組(IDA裡可以看到建立book的函式裡上來就是一個sub rsp,20h,對應的變數個數也能猜出來,之後還有malloc 0x20h)

struct book
{
    int id;
    char *name;
    char *description;
    int size;
}

其中name與description使用 malloc在堆山分配,大小都是自訂的,建立完後

book = malloc(0x20uLL);
if ( book )
{
    *((_DWORD *)book + 6) = size;
    *((_QWORD *)off_202010 + v2) = book;
    *((_QWORD *)book + 2) = description;
    *((_QWORD *)book + 1) = name;
    *(_DWORD *)book = ++unk_202024;
    return 0LL;
}                                          //推一下就好了

接下來就是找漏洞的過程了,第一次找這類的還是花了我一些時間的

終於在被突出的函式中找到了可能存在漏洞的地方,分析如下

我們可以看到迴圈執行了a2+1次,其中a2是我們選擇建立的(size-1),但最後將第33個位元組賦值\x00,所以其實我們read出的資料比我們需要的size超出了一個位元組,存在null byte off-by-one

這個函式在read author name的時候也呼叫了,所以我們只要輸入32個字元的話,最後的\x00會寫入第一個建立的book中,同時建立book時會把殘留的\x00覆蓋掉,從而輸出author name 就可以洩露book的資訊了

打算gdb看一波堆疊的變化,但是開啟了PIE保護,先用斷點打炸然後vmmap

可以看到資料段在0x555555755000~0x555555757000上,程式碼段在0x555555554000~0x555555556000上,除錯後函式9F5在0x555555554ff8處

……………………