深入解析2021年信息安全挑战赛:从取证到二进制漏洞利用的全方位攻略

本文详细解析了2021年信息安全挑战赛的10个关卡,涵盖数字取证、网络流量分析、二进制漏洞利用、逆向工程等多个技术领域,展示了从基础取证到高级漏洞利用的完整技术栈。

关卡1:初探表面

第一部分

领域:数字取证
我们通过秘密渠道发送了以下秘密消息。请以以下格式提交您的标志:TISC{解码后的消息,小写}
文件:file1.wav

“秘密渠道”提示了通过音频通道进行数据隐藏,这是一种常见的隐写术技术。file1.wav播放了一段欢快的曲调,但我无法识别。我快速应用了常见的工具和技术,如binwalk,但一无所获。我甚至尝试对两个通道进行异或操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import wave
import struct

wav = wave.open("file1.wav", mode='rb')
frame_bytes = bytearray(list(wav.readframes(wav.getnframes())))
shorts = struct.unpack('H'*(len(frame_bytes)//2), frame_bytes)

shorts_three = struct.unpack('H'*(len(frame_bytes)//4), frame_bytes)

extracted_left = shorts[::2] 
extracted_right = shorts[1::2]
print(len(extracted_left))
print(len(extracted_right))
extracted_secret = shorts[2::3]
print(len(extracted_secret))

extractedLSB = ""
for i in range(0, len(extracted_left)):
    extractedLSB += str((extracted_left[i] & 1) ^ (extracted_right[i] & 1))
    
string_blocks = (extractedLSB[i:i+8] for i in range(0, len(extractedLSB), 8))
decoded = ''.join(chr(int(char, 2)) for char in string_blocks)
print(decoded[0:500])
wav.close()

在这个简单的挑战中稍感恐慌后,我回到了“秘密渠道”的提示。我使用来自Stack Overflow的命令从文件中分离出每个音频通道:ffmpeg -i file1.wav -map_channel 0.0.0 ch0.wav -map_channel 0.0.1 ch1.wav。我播放了ch1.wav,听到的不是时髦的音乐,而是一系列哔哔声——莫尔斯电码!我使用了一个在线的莫尔斯电码音频解码器,得到了标志。

TISC{csitislocatedinsciencepark}

第二部分

领域:数字取证
这是一张通用图片。这张照片的修改时间是什么?
请以以下格式提交您的标志:TISC{YYYY:MM:DD HH:MM:SS}
文件:file2.jpg

exiftool立即解决了这个问题。
TISC{2021:10:30 03:40:49}

第三部分

领域:数字取证,密码学
新加坡的标志没有什么不寻常的,对吧?
请以以下格式提交您的标志:TISC{答案}
文件:file3.jpg

密码学领域的首次出现!我在010 Editor十六进制编辑器中打开了文件,突出显示了文件末尾的一个异常数据块。

PK魔术字节标识了这个块是一个zip文件。我使用binwalk -e file3.jpg提取了它,揭示了另一个图像文件picture_with_text.jpg。我在010 Editor中打开了它,并在文件开头发现了一些垃圾字节。

NAFJRE GB GUVF PUNYYRATR VF URER NCCYRPNEEBGCRNE看起来像是一个简单的文本密码。我进入CyberChef,很快发现它是ROT13“加密”。
TISC{APPLECARROTPEAR}

第四部分

领域:数字取证
太好了!既然你已经展示了你的能力,CSIT SOC团队给了你一个.OVA虚拟镜像,用于调查一台被PALINDROME入侵的机器的快照。你能从镜像中发现什么?
一旦你下载了VM,使用这个免费标志TISC{Yes, I’ve got this.}解锁第4到10关。
https://transfer.ttyusb.dev/I6aQoOSuUuAoIIaqMWWkCcKyOk/windows10.ova
检查MD5哈希:c5b401cce9a07a37a6571ebe5d4c0a48
关于如何将ova文件导入VirtualBox的指南,请附上VM导入指南。
请下载并安装Virtualbox版本6.1.26,而不是版本6.1.28,因为据报道在尝试安装Win 10 VM镜像时会出现错误。

这个挑战包含了六个标志,但没有过山车。我天真地将VM导入到Virtualbox中并开始工作。

用户名是什么?
以格式提交您的标志:TISC{名称}。
什么是whoami?
TISC{adam}

用户最近一次登录的时间是什么?在提交之前将其转换为UTC。
以UTC格式提交您的标志:TISC{DD/MM/YYYY HH:MM:SS}。

我经历了比赛中的第一次facepalm时刻(还会有更多)。最近一次登录时间在我登录VM后被重置,所以是时候下载Autopsy了。
在Autopsy导入并处理OVA文件后,我在OS Accounts > adam > Last Login下找到了最近一次登录时间,并将时区转换为UTC。
TISC{17/06/2021 02:41:37}

一个7z存档被删除,7z存档内部文件的CRC32哈希值是什么?
以以下格式提交您的标志:TISC{CRC32哈希,大写}。

我在Data Artifacts > Recycle Bin下找到了被删除的存档,并使用7-Zip生成了CRC32哈希。
TISC{040E23DA}

问题1:机器上有多少用户的RID为1000或以上?
问题2:RID为501的账户名是什么?
问题3:RID为503的账户名是什么?
以以下格式提交您的标志:TISC{答案1-答案2-答案3}。答案使用与找到时相同的大小写。

我在OS Accounts下得到了所有答案,尽管我对系统用户感到有些困惑。
TISC{1-Guest-DefaultAccount}

问题1:用户访问https://www.csit.gov.sg/about-csit/who-we-are多少次?
问题2:用户访问https://www.facebook.com多少次?
问题3:用户访问https://www.live.com多少次?
以以下格式提交您的标志:TISC{答案1-答案2-答案3}。

Data Artifacts > Web History
TISC{2-0-0}

一个驱动器号为“Z”的设备在VirtualBox中作为共享文件夹连接。卷的标签是什么?也许注册表可以告诉我们“已连接”的驱动器?
以以下格式提交您的标志:TISC{卷的标签}。

我发现这有点困难。我通过在VM中添加另一个共享文件夹,然后在注册表编辑器中搜索标签名称来找出哪个注册表键控制卷标签。这引导我到了注册表路径Computer\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2,其中包含了所有卷标签。
TISC{vm-shared}

一个SHA1为0D97DBDBA2D35C37F434538E4DFAA06FCCC18A13的文件在VM中的某个地方。感兴趣文件的原始名称是什么?
以以下格式提交您的标志:TISC{原始文件名,包括正确的文件扩展名}。

由于Autopsy只支持SHA256和MD5哈希,我猜测它是Data Artifacts > Recent Documents下的文件之一。我提取了所有文件,并运行了Get-FileHash -Algorithm SHA1 *.otter-singapore.lnk,它曾经指向otter-singapore.jpg,匹配了SHA1哈希。
TISC{otter-singapore.jpg}

关卡2:Dee Na Saw as a need

领域:网络取证
我们检测并捕获了从一台被PALINDROME入侵的服务器发出的一系列异常DNS网络流量。发现的域名都没有活跃。要么PALINDROME已经关闭了它们,要么还有更多内容。
这个关卡包含2个标志,两个标志都可以从同一个pcap文件中独立找到。
标志1的格式为TISC{16个字符}。
标志2的格式为TISC{17个字符}。
文件:traffic.pcap

作为一个隐写术新手,我觉得这个关卡是最“CTF-like”的,实际上在寻找标志1时卡了两天,并一度愤怒退出。幸运的是,在冷静下来后我成功解决了它。

标志2

traffic.pcap包含了一系列简短的DNS查询响应。

一些异常引起了我的注意:

  • 域名明显包含某种形式的数据外泄,并匹配格式d33d<9个十六进制字符>.toptenspot.net。
  • 生存时间(TTL)值不断变化,这在典型的DNS服务器中不应该发生。
  • 序列号也在不断变化。

对于域名,我注意到前两个十六进制字符总是数字,例如10, 11, 12。我使用scapy提取了十六进制字符,并尝试对它们进行十六进制解码,但只产生了乱码。在尝试了一些变体,如对连续字节进行异或操作后,我遇到了一个CTF writeup,描述了在DNS查询名称中对数据进行Base32编码。Base32编码使用了与十六进制数字相似的字符集。我尝试使用CyberChef对“十六进制字符”进行Base32解码,并立即发现了一些有趣的输出,如<非ASCII字符>ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghij。在调整偏移量后,我意识到前两个(数字)字符是坏字节,而其余字符组成了一个有效的base32字符串。

我使用一个快速脚本自动化了解码过程。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from scapy.all import *
from scapy.layers.dns import DNS
import base64

dns_packets = rdpcap('traffic.pcap')
encoded = ''

for packet in dns_packets:
    if packet.haslayer(DNS):
        encoded += packet[DNS].qd.qname[6:13].decode('utf-8')

decoded = base64.b32decode(encoded[:-(len(encoded) % 8)]).decode('utf-8')
print(decoded)

这产生了一堆lorem ipsum文本以及第二个标志。
TISC{n3vEr_0dd_0r_Ev3n}

标志1

解决了第一个异常属性后,我专注于TTL和序列号,浪费了许多小时追逐最终证明是红鲱鱼的线索。TTL和序列号通常匹配一个模式——序列号 + TTL = Unix时间戳——这让我觉得我走对了路。在花费了许多无果的时间以越来越疯狂的排列组合方式改变这些值后,我放弃了并休息了一下。

当我回来时,我回到了基础,并考虑了DNS域名中的数字“坏字节”。我决定检查这些值的范围。它们从01到64……可能吗?我将数字转置到base64字母表,然后进行base64解码……是的,它是一个DOCX文件。

下图是挑战创建者想到TTL红鲱鱼时的时刻。

继续,我使用scapy提取了DOCX文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from scapy.all import *
from scapy.layers.dns import DNS
import base64

dns_packets = rdpcap('traffic.pcap')

alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
encoded = ''

for packet in dns_packets:
    if packet.haslayer(DNS):
        encoded += alphabet[int(packet[DNS].qd.qname[4:6].decode('utf-8'))-1]

decoded = base64.b64decode(encoded + '==')
file = open('output.docx', 'wb')
file.write(decoded)
file.close()

Word文档包含了现在非常明显的线索“now you see me, what you seek is within”。由于DOCX文件实际上是伪装成ZIP文件,我解压了DOCX并在文件中搜索标志格式TISC{。我在word/theme/theme1.xml中找到了我要找的东西。
TISC{1iv3_n0t_0n_3vi1}

关卡3:Greystack中的针

领域:逆向工程
在一个内部网络上检测到一次攻击,阻止了所有类型的可执行文件。这是怎么发生的?
在进一步调查中,我们恢复了这两张灰度图像。它们可能是什么?
1.bmp
2.bmp

我在010 Editor中打开了两个文件,注意到1.bmp和2.bmp都以相反的顺序在BMP像素颜色字节中嵌入了数据。1.bmp包含一个Windows可执行文件,而2.bmp包含简单的ASCII文本。

我使用一个简单的Python脚本提取了它们。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
with open("1.bmp", "rb") as bmp_1, open("1.exe", "wb") as out_file:
    data = bmp_1.read()

    output = data[-148:][:-3]
    for i in range(1, 145):
        output += data[-((i + 1) * 148):-(i * 148)][:-3]

    out_file.write(output)

with open("2.bmp", "rb") as bmp_1, open("2.txt", "wb") as out_file:
    data = bmp_1.read()

    output = data[-100:][:-1]
    for i in range(1, 99):
        output += data[-((i + 1) * 100):-(i * 100)][:-1]

    out_file.write(output)

运行1.exe,我收到了以下输出:

1
2
3
> .\1.exe
HELLO WORLD
flag{THIS_IS_NOT_A_FLAG}

更深入挖掘,我使用IDA反编译了可执行文件,并注意到主函数检查第一个参数中的.txt文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
puts("HELLO WORLD");
if ( argc < 2 )
    goto LABEL_34;
v3 = argv[1];
v4 = strrchr(v3, 46);
if ( !v4 || v4 == v3 )
    v5 = (const char *)&unk_40575A;
else
    v5 = v4 + 1;
v6 = strcmp("txt", v5);
if ( v6 )
    v6 = v6 < 0 ? -1 : 1;
if ( v6 )
{
LABEL_34:
    puts("flag{THIS_IS_NOT_A_FLAG}");
    return 1;
}
fopen_s(&Stream, argv[1], "rb");
v7 = (void (__cdecl *)(FILE *, int, int))fseek;
if ( Stream )
{
    fseek(Stream, 0, 2);
    v8 = ftell(Stream);
    v23 = v8 >> 31;
    v24 = v8;
    fclose(Stream);
}

我用一个随机文本文件测试了这个,产生了以下输出。

1
2
3
> .\1.exe .\2.txt
HELLO WORLD
Almost There!!

进一步查看main的伪代码,我注意到它调用了一个函数,该函数VirtualAlloc了一些内存,将数据复制到其中,然后运行LoadLibraryA。由于“Almost There!!”没有作为字符串出现在1.exe中,我怀疑它来自动态加载的库。

我在memcpy处设置了一个断点,并运行了IDA调试器。在断点处检查memcpy的参数,我确认它复制了一个包含魔术字节MZ后跟“This program cannot be run in DOS mode”的可执行文件。

现在我需要转储这些数据。我通过检查源缓冲区末尾出现的Application Manifest XML文本来手动确定文件的大小。接下来,我在WinDBG中使用.writemem b.exe ebx L2600转储了它。

可执行文件结果是一个DLL,在dllmain_dispatch函数中包含了解码例程,该函数在1.exe每次使用LoadLibraryA加载它时执行。

DLL反编译为伪代码,我由于256次迭代循环而将其识别为RC4密钥调度算法(KSA)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
if ( Block )
    {
    v4 = strcmp(Block, "Words of the wise may open many locks in life.");
    if ( v4 )
        v4 = v4 < 0 ? -1 : 1;
    if ( !v4 )
        puts("*Wink wink*");
    }
    memset(v18, 0, 0xFFu);
    for ( i = 0; i < 256; ++i )           // RC4 Key Scheduling Algorithm
    *((_BYTE *)&Stream[1] + i) = i;
    v6 = 0;
    Stream[0] = 0;
    do
    {
    v7 = *((_BYTE *)&Stream[1] + v6);
    v8 = (FILE *)(unsigned __int8)(LOBYTE(Stream[0]) + Block[v6 % 0xEu] + v7);
    Stream[0] = v8;
    *((_BYTE *)&Stream[1] + v6++) = *((_BYTE *)&Stream[1] + (_DWORD)v8);
    *((_BYTE *)&Stream[1] + (_DWORD)v8) = v7;
    }

伪代码包含了两个更重要的信息。首先,“Words of the wise may open many locks in life”看起来像一个提示。其次,KSA循环使用0xE作为模数,告诉我RC4密钥是14字节长。

起初,我陷入了一个兔子洞,试图猜测密钥。考虑到挑战的名称和“Words of the wise”,我认为它与《指环王》中的甘道夫有关,并尝试了所有与他相关的短语,包括youwillnotpass。很长一段时间后,我恢复了理智,意识到密钥可能存在于我之前提取的第二个文件中。它包含了一个巨大的单词列表,包括rubywise——这可能是“Words of the wise”提示所指的内容。

我使用一个快速的Python脚本暴力破解了密钥。

1
2
3
4
5
6
7
8
import subprocess
import os

with open('keys.txt') as file:
    lines = file.readlines()
    lines = [line.rstrip() for line in lines]
    for line in lines:
        with open('key.txt', 'w') as
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计