Linux下pwn從入門到放棄

0x0 簡介
pwn,在安全領域中指的是通過二進制/系統調用等方式獲得目標主機的shell。
雖然web系統在互聯網中佔有比較大的分量,但是隨着移動端,ioT的逐漸流行,傳統的緩衝區溢出又一次有了用武之處
0x01 工欲善其事,必先利其器
Linux下的pwn常用到的工具有:
(1)gdb:Linux調試中必要用到的
(2)gdb-peda:gdb方便調試的工具,類似的工具有gef,gdbinit,這些工具的安裝可以參考:http://blog.csdn.net/gatieme/article/details/63254211
(3)pwntools:寫exp和poc的利器
(4)checksec:可以很方便的知道elf程序的安全性和程序的運行平臺
(5)objdump和readelf:可以很快的知道elf程序中的關鍵信息
(6)ida pro :強大的反編譯工具
(7)ROPgadget:強大的rop利用工具
(8)one_gadget:可以快速的尋找libc中的調用exec(‘bin/sh’)的位置
(9)libc-database: 可以通過泄露的libc的某個函數地址查出遠程系統是用的哪個libc版本
0x02 檢測elf的安全性:
(1)拿到efl,首先要用checksec來檢測elf運行於哪個平臺,開啓了什麼安全措施,如果用gcc的編譯後,默認會開啓所有的安全措施。
image
【1】RELRO:RELRO會有Partial RELRO和FULL RELRO,如果開啓FULL RELRO,意味着我們無法修改got表
【2】Stack:如果棧中開啓Canary found,那麼就不能用直接用溢出的方法覆蓋棧中返回地址,而且要通過改寫指針與局部變量、leak canary、overwrite canary的方法來繞過
【3】NX:NX enabled如果這個保護開啓就是意味着棧中數據沒有執行權限,以前的經常用的call esp或者jmp esp的方法就不能使用,但是可以利用rop這種方法繞過
【4】PIE:PIE enabled如果程序開啓這個地址隨機化選項就意味着程序每次運行的時候地址都會變化,而如果沒有開PIE的話那麼No PIE (0x400000),括號內的數據就是程序的基地址
【5】FORTIFY:FORTIFY_SOURCE機制對格式化字符串有兩個限制(1)包含%n的格式化字符串不能位於程序內存中的可寫地址。(2)當使用位置參數時,必須使用範圍內的所有參數。所以如果要使用%7$x,你必須同時使用1,2,3,4,5和6。

0x03 調試技巧
gdb常用的調試指令:
n: 執行一行源代碼但不進入函數內部
ni: 執行一行彙編代碼但不進入函數內部
s: 執行一行源代碼而且進入函數內部
si: 執行一行彙編代碼而且進入函數內部
c: 繼續執行到下一個斷點
b *地址: 下斷點
directory+源碼所在目錄:加載程序源碼
set follow-fork-mode parent :只調試主進程
stack: 顯示棧信息
x : 按十六進制格式顯示內存數據,其中x/{字節數}x 以16進制顯示指定地址處的數據;{字節數}表示字節數制定(b 單字節;h 雙字節;w 四字節;g 八字節;默認爲四字節)

程序沒有開啓地址隨機化:

def debug(addr):
    raw_input('debug:')
    gdb.attach(r, "b *" + addr)

在程序運行時調用這個函數就可以調試了

程序開啓地址隨機化:

wordSz = 4
hwordSz = 2
bits = 32
PIE = 0
mypid=0
def leak(address, size):
   with open('/proc/%s/mem' % mypid) as mem:
      mem.seek(address)
      return mem.read(size)

def findModuleBase(pid, mem):
   name = os.readlink('/proc/%s/exe' % pid)
   with open('/proc/%s/maps' % pid) as maps:
      for line in maps:
         if name in line:
            addr = int(line.split('-')[0], 16)
            mem.seek(addr)
            if mem.read(4) == "\x7fELF":
               bitFormat = u8(leak(addr + 4, 1))
               if bitFormat == 2:
                  global wordSz
                  global hwordSz
                  global bits
                  wordSz = 8
                  hwordSz = 4
                  bits = 64
               return addr
   log.failure("Module's base address not found.")
   sys.exit(1)

def debug(addr = 0):
    global mypid
    mypid = proc.pidof(r)[0]
    raw_input('debug:')
    with open('/proc/%s/mem' % mypid) as mem:
        moduleBase = findModuleBase(mypid, mem)
        gdb.attach(r, "set follow-fork-mode parent\nb *" + hex(moduleBase+addr))

由於開啓地址隨機化之後ida pro打開程序後,顯示的是程序的偏移地址,而不是實際的地址,當程序加載後程序的程序的實際地址是:基地址+偏移地址,調用debug函數的時候只要把偏移地址傳遞進去就好
0x4 泄露libc地址和版本的方法
【1】利用格式化字符串漏洞泄露棧中的數據,從而找到libc的某個函數地址,再利用libc-database來判斷遠程libc的版本,之後再計算出libc的基址,一般做題我喜歡找__libc_start_main的地址
【2】利用write這個函數,pwntools有個很好用的函數DynELF去利用這個函數計算出程序的各種地址,包括函數的基地址,libc的基地址,libc中system的地址
【3】利用printf函數,printf函數輸出的時候遇到0x00時候會停止輸出,如果輸入的時候沒有在最後的字節處填充0x00,那麼輸出的時候就可能泄露棧中的重要數據,比如libc的某個函數地址

0x05 簡單的棧溢出
程序沒有開啓任何保護:
方法一:傳統的教材思路是把shellcode寫入棧中,然後查找程序中或者libc中有沒有call esp或者jmp esp,比如這個題目: http://blog.csdn.net/niexinming/article/details/76893510
方法二:但是現代操作系統中libc中會開啓地址隨機化,所以先尋找程序中system的函數,再佈局棧空間,調用gets(.bss),最後調用system(‘/bin/sh’) 比如這個題目:http://blog.csdn.net/niexinming/article/details/78796408
方法三:覆蓋虛表方式利用棧溢出漏洞,這個方法是m4x師傅教我的方法,我覺得很巧妙,比如這個題目:http://blog.csdn.net/niexinming/article/details/78144301

0x06 開啓nx的程序
開啓nx之後棧和bss段就只有讀寫權限,沒有執行權限了,所以就要用到rop這種方法拿到系統權限,如果程序很複雜,或者程序用的是靜態編譯的話,那麼就可以使用ROPgadget這個工具很方便的直接生成rop利用鏈。有時候好多程序不能直接用ROPgadget這個工具直接找到利用鏈,所以就要手動分析程序來getshell了,比如這兩個題目: http://blog.csdn.net/niexinming/article/details/78259866

0x07 開啓canary的程序
開啓canary後就不能直接使用普通的溢出方法來覆蓋棧中的函數返回地址了,要用一些巧妙的方法來繞過或者利canary本身的弱點來攻擊
【1】利用canary泄露flag,這個方法很巧妙的運用了canary本身的弱點,當__stack_check_fail時,會打印出正在運行中程序的名稱,所以,我們只要將__libc_argv[0]覆蓋爲flag的地址就能將flag打印出來,比如這個題目: http://blog.csdn.net/niexinming/article/details/78522682
【2】利用printf函數泄露一個子進程的Canary,再在另一個子進程棧中僞造Canary就可以繞過Canary的保護了,比如這個題目:http://blog.csdn.net/niexinming/article/details/78681846

0x08 開啓PIE的程序
【1】利用printf函數儘量多打印一些棧中的數據,根據泄露的地址來計算程序基地址,libc基地址,system地址,比如這篇文章中echo2的wp: http://blog.csdn.net/niexinming/article/details/78512274
【2】利用write泄露程序的關鍵信息,這樣的話可以很方便的用DynELF這個函數了,比如這個文章中的rsbo2的題解:http://blog.csdn.net/niexinming/article/details/78620566

0x09 全部保護開啓
如果程序的棧可以被完全控制,那麼程序的保護全打開也會被攻破,比如這個題目:http://blog.csdn.net/niexinming/article/details/78666941

0x0a 格式化字符串漏洞
格式化漏洞現在很難在成熟的軟件中遇到,但是這個漏洞卻很有趣
【1】pwntools有很不錯的函數FmtStr和fmtstr_payload來自動計算格式化漏洞的利用點,並且自動生成payload,比如這個題目:http://blog.csdn.net/niexinming/article/details/78699413http://blog.csdn.net/niexinming/article/details/78512274 中echo的題解
【2】格式化漏洞也是信息泄露的好伴侶,比如這個題目中製造格式化字符串漏洞泄露各種數據 http://blog.csdn.net/niexinming/article/details/78768850

0x0b uaf漏洞
如果把堆釋放之後,沒有把指針指針清0,還讓指針保存下來,那麼就會引發很多問題,比如這個題目 http://blog.csdn.net/niexinming/article/details/78598635

0x0c 任意位置寫
如果程序可以在內存中的任意位置寫的話,那麼威力絕對很大
【1】雖然只能寫一個字節,但是依然可以控制程序的並getshell,比如這個題目 http://blog.csdn.net/niexinming/article/details/78542089
【2】修改got表是個控制程序流程的好辦法,很多ctf題目只要能通過各種方法控制got的寫入,就可以最終得到勝利,比如這個題目: http://blog.csdn.net/niexinming/article/details/78542089
【3】如果能計算出libc的基地址的話,控制top_chunk指針也是解題的好方法,比如這個題目: http://blog.csdn.net/niexinming/article/details/78759363

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