Linux下pwn從入門到放棄

Linux下pwn從入門到放棄

0x0 簡介

pwn,在安全領域中指的是經過二進制/系統調用等方式得到目標主機的shell。web

雖然web系統在互聯網中佔有比較大的份量,可是隨着移動端,ioT的逐漸流行,傳統的緩衝區溢出又一次有了用武之處shell

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的編譯後,默認會開啓全部的安全措施。函數

【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 八字節;默認爲四字節)佈局

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

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

在程序運行時調用這個函數就能夠調試了操作系統

程序開啓地址隨機化:.net

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函數的時候只要把偏移地址傳遞進去就好debug

0x04 泄露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/78699413 和 http://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

相關文章
相關標籤/搜索