首页
社区
课程
招聘
[原创] 强网杯2025 Secured Personal Vault 预期解WP
发表于: 2025-10-22 03:03 4052

[原创] 强网杯2025 Secured Personal Vault 预期解WP

2025-10-22 03:03
4052

该题唯一解是非预期。
然后该题为蓝屏DUMP,需要分析内核,那必须搞一下。
image.png
下载地址:
c01K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Z5j5i4u0W2i4K6u0r3K9h3&6A6N6q4)9K6c8Y4y4#2M7X3I4Q4x3@1c8x3L8K6b7#2K9@1k6%4f1%4N6J5h3s2A6E0y4e0u0s2c8i4j5#2P5X3c8%4
取码(GAME)

其拿到手是个DMP文件,8G fulldump。

直接看一下堆栈情况,发现是一个从EXE 到 SYS中 并触发蓝屏的情况。
image.png
把栈帧切到驱动里面看一下
image.png
显然的他是故意触发蓝屏,解引用0指针。
肯定要开始分析了,那么需要把驱动、应用,DUMP出来分析。

显然可以直接.writemem来完成,但是会有问题。
image.png
问题就是,内存被换出了,并没有驻留,故此内存dump会缺页。

驱动并不明显,但是到了EXE这里就会出大问题,缺很多东西,那么需要换一种办法dump了。

我选择的是折磨自己的办法,写个windbg插件,这玩意AI竟然写不明白。只能自己摸索。

这里花了大量时间
代码如下:

驱动真正入口点很容易定位,无论是字符串还是什么。
image.png
可以往下分析一下。
image.png
稍微往下看一下就能发现其获取了ntoskrnl的基地址,并通过nt!HalPrivateDispatchTable表获取了HalTimerConvertAuxiliaryCounterToPerformanceCounter的地址(nt!HalPrivateDispatchTable + 0x398)并替换了指针,显然用于通信

后面则是拿了全局的进程链表。

那么下一步就是看通信函数了。
image.png
其中需要恢复一下结构,其轻而易举。

显然的,当function为1的时候就是蓝屏,当2的时候根据pid拿到该进程的CreateTime。

EPROCESS 的偏移如下:
image.png
观察一下所有函数,就可以发现此时驱动已经分析完了,并没有什么有用的函数了。
image.png

首先查看一下进程列表,会发现有2个进程名字一样的进程(就是一个exe开了2),其中一个引发了蓝屏。
image.png
蓝屏的进程为ffffef063fbc1080(EPROCESS)
image.png
这里需要用我的插件dump出来并修复PE结构(我反正是手修的)。把2个EXE都dump出来,并把引发蓝屏的进程称之为A,另一个称之为B

由于2个进程是一样的,这里选取A进程的dump着重分析。

当修好PE结构后,其导入表应该是都在的。
image.png
入口点一眼盯真发现其是创建了一个窗口程序,处理程序为sub_7FF611D11D10。直接点进去分析。

可以发现其创建了2按钮(一个是创建,一个是检查)2个输入的地方(一个是用户名,一个是秘密)。
image.png

image.png
这里比较长,就直接讲是什么意思。

那么这一时刻又有老铁问了?为什么是AES。
其实FindCrypt已经给了答案。
image.png

image.png
这里比较长,就直接讲是什么意思。

那么我们需要考虑其为什么蓝屏?

那么为什么会出现这个问题?

我们抓住一个点:其邮件槽的句柄是通过共享内存来获取的,共享内存则是根据用户名一一对应的。 进而当2个进程输入一个同一个用户名则会导致混乱。

此时我们便可以进行现场还原,根据dumpfile。
在引起蓝屏的进程中查看句柄,情况真相大白。
image.png
不难看出mailslot_1359e83被引用了2次,其表明是A、B进程都引用了他,进而A进程用自己的密钥,解密B进程密钥加密的数据,导致与自身不匹配引发的蓝屏

所以要解密其秘密数据,需要先用A进程的密钥加密回去,再用B进程的密钥解密。

mailslot_e60a23e2是A进程自身的秘密数据,用A进程的密钥即可解密。

此时我们逻辑理清了,那么现在需要数据。Key、IV、CreateTime都可以直接拿到,难点则在邮件槽的数据。

邮件槽(mailslot)是一种跨进程通信的Windows服务,其基于文件。

具体的邮件槽原理在《Windows内核原理与实现》中写的很清楚,这里不在赘述,而是讲述一种分析思路。

我们从只有一个引用的邮件槽开始分析。

首先查看其对象类型,发现其是文件对象
image.png
那么此时先按文件对象的结构进行解析。
此时需要关注其是什么驱动管理了该文件对象。
image.png
现在真相大白了,msfs驱动管理了邮件槽的对象,我们需要对其分析。
image.png

直接把本地的驱动丢IDA里看一下。
既然邮件槽的对象归msfs驱动管理,我们需要关注其IRP处理函数,就可以知道读写邮件槽时如何对其操作了
image.png

此时转到MsFsdRead函数看一下。
image.png
此时可以发现,_FILE_OBJECTFsContext成员为关键成员,其0x118处存储了数据。

进入MsReadDataQueue进一步分析。
image.png
其中会发现在0x18处是其数据的LIST_ENTRY- 8则是原始数据结构的头部,+ 0x28则是数据指针,+ 0x20则是数据长度。

回归到具体数据上则是这样,0xffffe70b`7e814260是上文中的FsContext
image.png

CreateTime只需要dt _EPROCESS的方式就能看到了。

此时我们理一下数据大致如下:

那么请注意,由于AESkey和IV都是亦或过自己的CreateTime,解密时仍然需要。

先看简单的,0x80长度的密文对应的时只有一个引用的(A进程)。
此时需要把AESkey和IV与A进程的CreateTime逐字节亦或并解密AES即可。
image.png
可以发现能解出来一句话,说明另一个才有flag。

那么按照刚才的逻辑来捋一下。

image.png
此时成功解密:
flag{Making_challenge_is_hard_manage_a_secure_vault_is_more_difficult}

function initializeScript()
{
    return [new host.functionAlias(safeDumpMemory, "MDump")];
}
 
 
async function safeDumpMemory(filePath, startAddress, imageSize)
{
    const pageSize = 4096;
    let totalBytesWritten = 0;
    let log = host.diagnostics.debugLog;
 
    log(`[*] 开始安全转储内存...\n`);
    log(`    - 输出文件: ${filePath}.py\n`);
    log(`    - 起始地址: 0x${startAddress.toString(16)}\n`);
    log(`    - 总大小:   0x${imageSize.toString(16)} bytes\n`);
 
    try
    {
        // 创建或覆盖目标文件
        let file = host.namespace.Debugger.Utility.FileSystem.CreateFile(filePath + ".py", "CreateAlways");
        var textWriter = host.namespace.Debugger.Utility.FileSystem.CreateTextWriter(file,'Utf8');
        // 创建一个4KB的全零缓冲区,用于填充坏页
        let nullBuffer = new ArrayBuffer(pageSize);
        let nullView = new Uint8Array(nullBuffer);
        textWriter.WriteLine(`# python ${filePath}.py
buffer = [
        `);
        // 逐页循环
        for (let offset = host.Int64(0); offset.compareTo(imageSize) < 0; offset = offset.add(pageSize))
        {
            let currentAddress = startAddress.add(offset);
            let bytesToWrite = (imageSize.subtract(offset).compareTo(pageSize) < 0) ? imageSize.subtract(offset) : host.Int64(pageSize);
 
            try
            {
                // 尝试读取当前内存页
                let buffer = host.memory.readMemoryValues(currentAddress,bytesToWrite);
                textWriter.WriteLine(buffer);
                textWriter.WriteLine(",");
            }
            catch(e)
            {
                log(`[!] 错误: ${e}\n`);
                // 如果读取失败,就写入零
                log(`[!] 无法读取地址: 0x${currentAddress.toString(16)}。正在写入0x${bytesToWrite.toString(16)}字节的0... \n`);
                textWriter.WriteLine(nullView);
                textWriter.WriteLine(",");
                 
            }
 
            totalBytesWritten += bytesToWrite;
            // 每转储1页打印一次进度
            if (offset.bitwiseAnd(0xFFFFF) == 0 && offset.compareTo(0) > 0) {
                log(`    ... 已写入 0x${offset.toString(16)} bytes\n`);
            }
        }
        textWriter.WriteLine(`]
f = open('${filePath}','wb')
f.write(bytearray(buffer))`);
        file.Close();
        log(`\n[+] 转储完成! 总共写入 0x${imageSize.toString(16)} 字节 请运行${filePath}.py\n`);
    }
    catch(e)
    {
        log(`[!] 发生严重错误: ${e}\n`);
    }
}
function initializeScript()
{
    return [new host.functionAlias(safeDumpMemory, "MDump")];
}
 
 
async function safeDumpMemory(filePath, startAddress, imageSize)
{
    const pageSize = 4096;
    let totalBytesWritten = 0;
    let log = host.diagnostics.debugLog;
 
    log(`[*] 开始安全转储内存...\n`);
    log(`    - 输出文件: ${filePath}.py\n`);
    log(`    - 起始地址: 0x${startAddress.toString(16)}\n`);
    log(`    - 总大小:   0x${imageSize.toString(16)} bytes\n`);
 
    try
    {
        // 创建或覆盖目标文件
        let file = host.namespace.Debugger.Utility.FileSystem.CreateFile(filePath + ".py", "CreateAlways");
        var textWriter = host.namespace.Debugger.Utility.FileSystem.CreateTextWriter(file,'Utf8');
        // 创建一个4KB的全零缓冲区,用于填充坏页
        let nullBuffer = new ArrayBuffer(pageSize);
        let nullView = new Uint8Array(nullBuffer);
        textWriter.WriteLine(`# python ${filePath}.py
buffer = [
        `);
        // 逐页循环
        for (let offset = host.Int64(0); offset.compareTo(imageSize) < 0; offset = offset.add(pageSize))
        {
            let currentAddress = startAddress.add(offset);
            let bytesToWrite = (imageSize.subtract(offset).compareTo(pageSize) < 0) ? imageSize.subtract(offset) : host.Int64(pageSize);
 
            try
            {
                // 尝试读取当前内存页
                let buffer = host.memory.readMemoryValues(currentAddress,bytesToWrite);
                textWriter.WriteLine(buffer);
                textWriter.WriteLine(",");
            }
            catch(e)
            {
                log(`[!] 错误: ${e}\n`);
                // 如果读取失败,就写入零
                log(`[!] 无法读取地址: 0x${currentAddress.toString(16)}。正在写入0x${bytesToWrite.toString(16)}字节的0... \n`);
                textWriter.WriteLine(nullView);
                textWriter.WriteLine(",");
                 
            }
 
            totalBytesWritten += bytesToWrite;
            // 每转储1页打印一次进度
            if (offset.bitwiseAnd(0xFFFFF) == 0 && offset.compareTo(0) > 0) {
                log(`    ... 已写入 0x${offset.toString(16)} bytes\n`);
            }
        }
        textWriter.WriteLine(`]
f = open('${filePath}','wb')
f.write(bytearray(buffer))`);
        file.Close();
        log(`\n[+] 转储完成! 总共写入 0x${imageSize.toString(16)} 字节 请运行${filePath}.py\n`);
    }
    catch(e)

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 2025-10-31 13:09 被moshuiD编辑 ,原因:
收藏
免费 28
支持
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  smallzhong_   +1.00 2025/10/22 感谢分享~
最新回复 (19)
雪    币: 3595
活跃值: (6301)
能力值: ( LV5,RANK:65 )
在线值:
发帖
回帖
粉丝
2
前排支持!
2025-10-22 04:07
0
雪    币: 6099
活跃值: (4669)
能力值: ( LV7,RANK:116 )
在线值:
发帖
回帖
粉丝
3
墨水太强啦
2025-10-22 10:57
0
雪    币: 0
活跃值: (140)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
太强了 ,还是看雪好
2025-10-22 11:42
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
墨水太强啦
2025-10-22 14:32
0
雪    币: 337
活跃值: (1599)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
墨水太强啦
2025-10-22 14:43
0
雪    币: 4
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
太强了
2025-10-22 20:22
0
雪    币: 719
活跃值: (225)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
太强了,向大佬学习
2025-10-22 21:59
0
雪    币: 740
活跃值: (2552)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
9
太强了,向大佬学习
2025-10-25 15:54
0
雪    币: 4689
活跃值: (7498)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
2025-10-25 20:16
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
太强了
2025-10-27 08:37
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
太强了
2025-10-27 14:05
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
太强了
2025-10-28 18:34
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
11
2025-10-30 09:47
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
11
2025-10-30 18:19
0
雪    币: 0
活跃值: (145)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
太强了
2025-10-30 21:24
0
雪    币: 130
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
太强!
2025-10-31 18:52
0
雪    币: 6
活跃值: (85)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
太强了
2025-11-17 09:34
0
雪    币: 6
活跃值: (85)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
这题的非预期解是什么骚操作?
2025-11-21 16:47
0
雪    币: 7589
活跃值: (3406)
能力值: (RANK:166 )
在线值:
发帖
回帖
粉丝
20
ntdll 这题的非预期解是什么骚操作?
直接从dwm中恢复了桌面(很模糊版),看+猜到了flag
2025-11-21 23:17
0
游客
登录 | 注册 方可回帖
返回