PWN學習之棧溢出


PWN學習之棧溢出

前言

我記得我在最開始學編程的時候,經常會聽到老師說輸入的時候要注意大小,不要超過數組大小否則會造成緩沖區溢出導致程序崩潰的。

當時就覺得溢出就溢出咯,崩潰就崩潰咯,難不成還能導致電腦被攻擊嗎?就偏偏不控制輸入長度。

img

寫bug

先讓我們來寫個bug體驗一下,下面這段程序要求用戶輸入字符串並且把數據給buffer數組,如果超過12長度的字符串就會造成緩沖區溢出!

bug.cpp源碼

#include <stdio.h>
void bug()
{
  char buffer[12]={"1"};
  scanf("%s",buffer);
}
int main()
{
  __asm   //加nop是為了方便在OD里面定位到
  {
    nop;
    nop;
    nop;
  }
  bug();
  __asm   //可以忽略不用理會
  {
    nop;
    nop;
    nop;
  }
  return 0;
}

目標環境:

  • Windows 7旗艦版(64位)
  • VC++ 6.0
  • Notepad++

用cl來編譯源碼並且生成帶.asm的匯編源碼文件,編譯命令是cl.exe -FAS ./bug.cpp

進入到lab01目錄編譯源碼后,生成了.exe 和 .asm文件。

image-20211019173957004

然后我們運行bug.exe測試下輸入超過12長度的字符串看看程序反應會怎么樣,程序不出意外的崩潰掉了。

image-20211019174324881

OD動態調試bug.exe

OK我們用OD進行調試來看看堆棧中實際的情況。

我這里用的是x32dbg,載入后找到3條nop指令就可以定位到調用bug函數附近的匯編代碼了。

0x0040103A位置下斷點。

image-20211019174638472

接着F9讓程序運行到這里,此時我們注意觀察堆棧,接着馬上要按F7了。

image-20211019174914185

按F7讓程序進入bug函數的內部,並且這時候仔細看堆棧。

我們會發現進入函數內部后首先是棧頂發生了變化,之前的棧頂為0x0018FF3C,現在的棧頂為0x0018FF38,棧頂減少了4字節,說明發生了push操作,push xx等於esp-4也就是0x0018FF38的地址,並且此時把內容送入esp(棧頂)地址。

image-20211019175546045

可以發現送入esp地址的內容是調用bug函數的下一句代碼的地址,也就是當F7 進入call bug函數的時候,其實是先執行了push eip的操作,因為此時eip=下一句代碼地址,然后再jmp bug函數處執行代碼。

image-20211019175652043

OD調試觀察溢出

好了接下來我們繼續F8單步執行,當F8后此時注意ebp被壓入了堆棧,估計這個問題會有很多新手做ctf pwn的時候被坑,他們計算出溢出大小后直接+4進行了此地址的覆蓋,以為覆蓋到了ret地址,其實ret地址在下面,導致拿不到flag。

image-20211019181356708

當F8執行完sub esp,0xC匯編指令后,棧頂位置減了12字節,用來存放buffer數組的數據。

image-20211019182156179

之后我們一路執行到call函數這里的時候,發現又有兩個數據入棧了,這是調用scanf函數術后的參數,我們可以不用管它,然后我們在F8步過call后,程序要求我們輸入字符串,我們輸入AAAAAAAAAAAABBBBCCCC 12個A代表填滿buffer數組,BBBB代表溢出覆蓋ebp寄存器,CCCC代表溢出覆蓋ret地址。

image-20211019183411343

-----------------------------------溢出前--------------------------------
0018FF20     00407034    bug.00407034   |參數1:"%s"
0018FF24     0018FF28    L"1"           |參數2:buffer數組地址
0018FF28     00000031    ;buffer數組起始位置
0018FF2C     00000000    ;buffer
0018FF30     00000000    ;buffer
0018FF34     0018FF48    ;ebp寄存器
0018FF38     0040103F    ;call bug函數的下一句匯編指令地址 |返回到 bug.00401046 自 bug.00401000

-----------------------------------溢出后----------------------------------
0018FF20     00407034    bug.00407034           |參數1:"%s"
0018FF24     0018FF28    "AAAAAAAAAAAABBBBCCCC" |參數2:buffer數組地址
0018FF28     41414141    ;buffer數組起始位置
0018FF2C     41414141    ;buffer
0018FF30     41414141    ;buffer
0018FF34     42424242    ;ebp寄存器
0018FF38     43434343    ;call bug函數的下一句匯編指令地址 |返回到 bug.00401046 自 bug.00401000

棧溢出攻擊之突破密碼驗證

這里其實主要就是利用棧溢出覆蓋掉局部變量的值,讓其改變流程。

#include <stdio.h>
#define PASSWORD "1234567"
int verify_password(char *password)
{
  int authenticated;
  char buffer[8];
  authenticated = strcmp(password,PASSWORD);
  _asm nop;
  strcpy(buffer,password);//緩沖區溢出!!!!
  return authenticated;
}

int main()
{
  int valid_flag = 0;
  char password[1024];
  while(1)
  {
    printf("Please input password:");
    scanf("%s",password);
    valid_flag = verify_password(password);
    if(valid_flag)
    {
      printf("密碼錯誤\n\n");
    }else
    {
      printf("恭喜!密碼輸入正確!\n");
      break;
    }
  }
}

只有當密碼是1234567的時候才會停止循環,並且輸出密碼正確。

image-20211019201723177

用溢出方式來破解密碼程序,首先strcmp函數的返回值是當s1<s2時,返回為負數;當s1=s2時,返回值= 0;當s1>s2時,返回正數。所以我們想辦法讓authenticated變量等於0,這樣條件才能為假,才能進入恭喜!密碼輸入正確!的分支。

image-20211019204744105

所以我們只要輸入8位數字,這時候\0 結尾符剛好能覆蓋到 authenticated變量使其等於0,從而改變程序判斷。

image-20211019205008543

可以看到je會走到密碼正確的分支。

image-20211019205037069

image-20211019205130235

x64位棧溢出

源碼還是采用bug.cpp的然后用VisualStudio編譯成x64的。

image-20211021101349789

載入IDA來看一下他的匯編代碼,可以發現32位寄存器都變成了64位寄存器。

image-20211021101718087

還有x64和x86的還有個區別就是函數調用棧的區別,在x64中只有fastcall函數調用約定,定義如下。

參數1、參數2、參數3、參數4分別保存在 RCX、RDX、R8D、R9D ,剩下的參數從右往左依次入棧,被調用者實現棧平衡,返回值存放在 RAX 中。
#C語言代碼
int fastcall_sum = fastcall_add(1, 2, 3, 4, 5, 6, 7);
#----------------------匯編代碼--------------------------------
00007FF6577A366E  mov         dword ptr [rsp+30h],7 ;超過參數4保存在棧中
00007FF6577A3676  mov         dword ptr [rsp+28h],6 ;超過參數4保存在棧中
00007FF6577A367E  mov         dword ptr [rsp+20h],5 ;超過參數4保存在棧中 
00007FF6577A3686  mov         r9d,4  ;參數4
00007FF6577A368C  mov         r8d,3  ;參數3
00007FF6577A3692  mov         edx,2  ;參數2
00007FF6577A3697  mov         ecx,1  ;參數1
#調用fastcall_add函數
00007FF6577A369C  call        fastcall_add (07FF6577A11C2h) 
00007FF6577A36A1  mov         dword ptr [fastcall_sum],eax  # 返回值
#--------------------------------------------------------------

#---------------------fastcall_add函數--------------------------
int __fastcall fastcall_add(int a, int b, int c, int d, int e, int f, int g)
{
00007FF6D22D1790  mov         dword ptr [rsp+20h],r9d ;參數4給臨時變量
00007FF6D22D1795  mov         dword ptr [rsp+18h],r8d ;參數3給臨時變量
00007FF6D22D179A  mov         dword ptr [rsp+10h],edx ;參數2給臨時變量
00007FF6D22D179E  mov         dword ptr [rsp+8],ecx   ;參數1給臨時變量
00007FF6D22D17A2  push        rbp      ;rbp入棧 ,和x86一樣待會會用到
00007FF6D22D17A3  push        rdi      ;rdi入棧 , 不清楚
00007FF6D22D17A4  sub         rsp,0E8h ;將棧頂向下拉232個字節
00007FF6D22D17AB  mov         rbp,rsp  ;把rsp棧頂給了rbp  方便尋址定位
00007FF6D22D17AE  mov         rdi,rsp  ;把rsp棧頂也給了rdi
00007FF6D22D17B1  mov         ecx,3Ah  ;循環變量58
00007FF6D22D17B6  mov         eax,0CCCCCCCCh ;燙燙燙燙
00007FF6D22D17BB  rep stos    dword ptr [rdi] ;循環58此
00007FF6D22D17BD  mov         ecx,dword ptr [rsp+108h] 

int sum = a+b+c+d+e+f+g;
00007FF6D22D17C4  mov         eax,dword ptr [b] 
00007FF6D22D17CA  mov         ecx,dword ptr [a] 
00007FF6D22D17D0  add         ecx,eax ;a+b
00007FF6D22D17D2  mov         eax,ecx 
00007FF6D22D17D4  add         eax,dword ptr [c];+c 
00007FF6D22D17DA  add         eax,dword ptr [d];+d
00007FF6D22D17E0  add         eax,dword ptr [e];+e
00007FF6D22D17E6  add         eax,dword ptr [f];+f
00007FF6D22D17EC  add         eax,dword ptr [g];+g
return sun;
00007FF6D22D17F2  mov         dword ptr [sum],eax;存放總和

}
00007FF6D22D17F8  lea         rsp,[rbp+0E8h] 
00007FF6D22D17FF  pop         rdi 
00007FF6D22D1800  pop         rbp 
00007FF6D22D1801  ret                                   # 沒做棧平衡

我們在匯編代碼中找到調用bug函數的地方,因為我們bug.cpp中bug函數並沒有參數,所以不需要關心這些參數調用方式。

然后注意看bug函數的這個地方,我們發現棧頂被拉低了96個字節,也就是12*8其中在這里一個char占用了8位,然后12個元素x8就是96個字節了。然后我們需要輸入超過8x4=32個字符才能溢出。

image-20211021104912758

在x64dbg中進行實驗,看看真實效果。

image-20211021105544865

image-20211021110024421


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM