目录导航
kCTF是在Kubernetes之上编写的CTF基础架构。它使您可以管理,部署和沙箱CTF挑战。
我们构建了kCTF,以帮助CTF组织者拥有出色的CTF基础架构工具,并为挑战作者提供一个环境,使他们可以在不了解Kubernetes的情况下构建和测试挑战,同时还允许Kubernetes专家使用他们习惯的所有功能。
KCTF特色
①可以在单个Dockerfile中配置一个简单的挑战。更复杂的部署可以使用所有Kubernetes功能。
②提供常见的CTF需求作为服务(工作证明,运行状况检查,DNS和SSL证书,网络文件共享)。
③kCTF是由Google的安全团队构建的,目的是在CTF竞赛中托管不受信任的易受攻击的应用程序。
安装演示
要在真实服务器上进行尝试,请尝试使用Google Cloudshell代码实验室。

在线演示
我们正在运行一个虚拟的挑战,您可以通过直接连接到以下位置来查看孤立的挑战的外观:
nc kctf.vrp.ctfcompetition.com 1337

如果您能够脱颖而出,则可以赚取高达$ 10,000 USD的收益。
提示:执行 bash -i 2>&1
以获取交互式bash会话。
本地测试演练方法
在本演练中,您将学习如何使用kCTF基础结构。
详细而言,这意味着:
初次设置
本演练,需要一台能够运行Docker的本地Linux机器。
设置正确的umask
由于nsjail将以其他用户身份运行命令,因此所有用户都必须读取质询文件。要启用此功能,请设置以下umask:
umask a+rx
如果您忘记设置正确的umask,则SDK会警告您。
安装依赖项
大多数人应该有wget
,curl
和xxd
已经安装,但如果你是在一个新的Debian的运行,运行以下命令:
sudo apt install xxd wget curl netcat
安装Docker
如果尚未安装Docker,则应按照官方说明进行安装。
在撰写本文时,受支持的安装Docker的方法之一是运行以下命令:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER && newgrp docker
如果您已经安装了Docker,该脚本将显示警告。
启用用户名称空间
某些Linux发行版默认没有用户名称空间。要在Debian或Ubuntu中启用它们,请执行:
echo 'kernel.unprivileged_userns_clone=1' | sudo tee -a /etc/sysctl.d/00-local-userns.conf
sudo service procps restart
请注意,由于内核攻击面的增加,这具有一定的安全隐患,这就是为什么Debian和Ubuntu在默认情况下不启用它们。
使用kCTF
下载并激活kCTF
kCTF有一个SDK,要求您在目录中安装kCTF。所有挑战都应位于kCTF SDK的安装目录下。
mkdir ctf-directory && cd ctf-directory
curl -sSL https://kctf.dev/sdk_1_0 | tar xz
source kctf/activate
完成此操作后,您应该在提示中看到已启用kCTF:
evn@evn:~/ctf-directory$ kCTF[ctf=ctf-directory] >
要退出环境,请运行deactivate
。
创建一个示例本地集群
要运行本地挑战,您需要创建本地Kubernetes集群。为此,请运行:
kctf cluster create local-cluster --start --type kind
在提示符下,您应该能够看到它正常工作(注意config=local-cluster
):
evn@evn:~/ctf-directory$ kCTF[ctf=ctf-directory,config=local-cluster] >
创建基本的演示挑战
要从骨架创建挑战,可以运行以下命令:
kctf chal create chal-sample && cd chal-sample
这会带来一个示例pwn
挑战,但是您也可以使用参数创建模板web
或xss-bot
模板--template
。
然后,您应该注意到提示提示您检测到您位于挑战目录(注意chal=chal-sample
)内:
evn@evn:~/ctf-directory/chal-sample$ kCTF[ctf=ctf-directory,config=local-cluster,chal=chal-sample] >
pwn模板带有一个Makefile来构建质询二进制文件。如果您要分发二进制文件作为播放器的附件,则建议这样做,例如,因为布局可能对ROP小工具很重要。如果布局无关紧要,您还可以将其作为Dockerfile的一部分构建在中间容器中。
要开始挑战,请运行:
make -C challenge && kctf chal start
构建并部署了挑战之后,您将看到challenge.kctf.dev/chal-sample created
。
迎接挑战
要连接到挑战,请运行以下命令:
kctf chal debug port-forward &
连接后,您将Forwarding from 127.0.0.1:[LOCAL_PORT] -> 1337
在终端中看到。连接到LOCAL_PORT:
nc 127.0.0.1 [external_port]
如果一切顺利,您应该可以连接并看到:
== proof-of-work: disabled ==
CTF{TestFlag}
故障排除
修改挑战
为了测试如果挑战失败了会发生什么,我们可以修改任务,然后看看会发生什么。
运行以下命令来代替cat /flag
用echo /flag
在challenge/chal.c
:
sed -i s/cat/echo/ challenge/chal.c && make -C challenge
如果尝试再次连接,您会注意到旧版本仍在运行(您仍然会看到CTF{TestFlag}
)。
这是因为新更新是作为首次发布进行的,而旧挑战仅在新挑战就绪后才停止。
如果您看到挑战的旧版本正在运行,则表明该部署无法正常工作。
检查挑战状态
要查看挑战部署的状态,可以运行:
kctf chal status
前面的命令应该返回这样的内容(注意它说“不健康”,并且最新的POD是READY = 1/2
):
= CHALLENGE RESOURCE =
NAME HEALTH STATUS DEPLOYED PUBLIC
chal-sample unhealthy Running true false
= INSTANCES / PODs =
Challenge execution status
This shows you how many instances of the challenges are running.
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
chal-sample-66cb778c45-stjkq 2/2 Running 0 5m49s 10.244.0.10 kctf-cluster-control-plane <none> <none>
chal-sample-6c86956d78-5md9s 1/2 Running 0 15s 10.244.0.11 kctf-cluster-control-plane <none> <none>
= DEPLOYMENTS =
Challenge deployment status
This shows you if the challenge was deployed to the cluster.
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
chal-sample 1/1 1 1 5m49s challenge,healthcheck kind/challenge:6eba2,kind/healthcheck:dcf4 app=chal-sample
= EXTERNAL SERVICES =
Challenge external status
This shows you if the challenge is exposed externally.
SERVICES:
NAME TYPE EXTERNAL-IP PORT DNS
chal-sample NodePort <none> 1337 <none>
Ingresses:
No resources found in default namespace.
阅读日志
为了解决不健康的挑战,我们需要阅读失败的健康检查日志。我们需要使用来做到这一点kubectl
。
kubectl logs chal-sample-6c86956d78-5md9s -c healthcheck
chal-sample-6c86956d78-5md9s
用显示为READY =的窗格名称替换1/2
。您将看到如下内容:
[Sat Mar 13 12:19:58 UTC 2021] b'== proof-of-work: '
Traceback (most recent call last):
File "/home/user/healthcheck.py", line 33, in <module>
print(r.recvuntil(b'CTF{'))
File "/usr/local/lib/python3.8/dist-packages/pwnlib/tubes/tube.py", line 310, in recvuntil
res = self.recv(timeout=self.timeout)
File "/usr/local/lib/python3.8/dist-packages/pwnlib/tubes/tube.py", line 82, in recv
return self._recv(numb, timeout) or b''
File "/usr/local/lib/python3.8/dist-packages/pwnlib/tubes/tube.py", line 160, in _recv
if not self.buffer and not self._fillbuffer(timeout):
File "/usr/local/lib/python3.8/dist-packages/pwnlib/tubes/tube.py", line 131, in _fillbuffer
data = self.recv_raw(self.buffer.get_fill_size())
File "/usr/local/lib/python3.8/dist-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
raise EOFError
EOFError
1 err
在这里,您可以看到运行状况检查无法CTF{
从质询中读取(我们更改cat /flag
为echo /flag
)。
为了促进开发,您可以通过运行以下命令来禁用运行状况检查:
sed -i s/enabled:\ true/enabled:\ false/ challenge.yaml
然后kctf chal start
再次运行。如果现在尝试再次连接,您将看到:
== proof-of-work: disabled ==
/flag
故障排除
用户报告挑战已失效,作者处于离线状态,您不知道挑战已经解决了多长时间。该挑战没有配置运行状况检查,也没有文档。有人忘记测试任务。噩梦成真。
假设您不知道挑战的工作原理,本指南将向您展示如何对残破的挑战进行故障排除。该指南分为三个部分,可指导您对不同环境中的任务进行故障排除:
- 在Docker中-使用本地测试
kctf chal debug docker
是解决问题的最简便,最快的方法,除了下一节介绍的一些基本Docker命令之外,您不需要了解太多。 - 在本地(Kubernetes)群集中–使用Kubernetes进行故障排除需要一些额外的设置步骤。但是,只有在极少数情况下才需要这样做,因为只有在作者对Kubernetes设置进行了更改或kCTF中存在错误的情况下,才有意义。
- 远程–远程故障排除很简单,尽管这冒着用户将远程状态置于不一致状态的风险,但这是一个很好的解决方法。
注意:本指南中的命令使用kctf-chal-troubleshooting
占位符作为中断的挑战的名称,用您的挑战的实际名称替换此占位符。
source kctf/activate
在运行以下任何命令之前,请记住要在CTF目录上运行。
使用Docker进行故障排除
构建和运行Docker
一个不错的起点是检查Docker映像是否正常工作。作者可能会对Dockerfile进行少量更改,然后中断任务。甚至没有开始的挑战通常表明某些东西已损坏。
要构建Docker映像,请运行:
kctf chal debug docker
这将输出,当任何错误构建的图像,但图像不会实际运行。如果要运行该映像,请运行:
这将在最后输出如下内容:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81d06c71f6b5 kctf-chal-troubleshooting "/bin/sh -c '/usr/bi…" 1 second ago Up Less than a second 0.0.0.0:32780->1337/tcp unruffled_payne
请注意,这里有一个PORTS列,告诉您本地计算机中的端口32780已映射到容器内的端口1337。如果您连接到本地端口(在以下命令中使用本地端口交换“ 32780”),则该挑战应会起作用:
nc localhost 32780
如果挑战非常艰巨,它将仍然无法解决。如果您输入任何内容,将不会发生任何事情。破坏质询的另一种可能方式是,如果运行命令时出错,在这种情况下,连接至质询也将不起作用(它将退出)。
您实际上可以通过运行以下命令来检查挑战是否在运行几秒钟后仍在运行:
docker ps -f ancestor=kctf-chal-troubleshooting
如果您看不到挑战在运行,则说明任务甚至无法启动。如果质询仍在运行,但连接失败,则质询已正确启动,但存在一些运行时错误。
无论哪种方式,下一步都是读取Docker日志。
看日志
如果遇到挑战,但无法运行,最好的办法是阅读Docker日志。要获取Docker日志,请运行:
docker logs $(docker ps -q -f ancestor=kctf-chal-troubleshooting)
如果挑战是可挑战的,您可能会看到以下格式的错误:
[E][2020-02-02T20:20:02+0200][1] void subproc::subprocNewProc(nsjconf_t*, int, int, int, int)():204 execve('/bin/sh') failed: No such file or directory
那是一个nsjail错误。在这种情况下,它抱怨/bin/sh
失败并返回错误No such file or directory。
如果您的挑战基于该apache-php
示例,则您将看到Apache访问/错误日志,但是无论哪种方式,要继续进行调试,您都需要了解挑战内部的情况。
shell到Docker映像中
要继续调试,您可以使用进入容器的“shell” docker exec
。为此,请运行:
docker exec -it $(docker ps -q -f ancestor=kctf-chal-troubleshooting) bash
进入内部后,您可以检查运行挑战的环境。例如,您可以列出当前进程:
ps aux
这样,您可以找出任务上当前正在运行的内容,但是您也可以通过读取Dockerfile并检查在CMD
那里配置的内容来找出问题。
在这里,您还可以检查其他日志(如果有)以及检查文件系统的权限。调试的下一步是直接在Shell上运行命令,看看是否可以获得新信息。
下一个故障排除步骤是找出nsjail的调用位置。对于pwnables,nsjail将直接作为侦听服务运行,而对于Web任务,它将在执行PHP文件时作为CGI脚本运行。
调试nsjail
nsjail的配置通常位于/config中。您将要使用Docker尝试的相同命令再次运行nsjail,但要进行测试。Nsjail支持通过在--config
标志之后添加的命令行参数来覆盖配置。如果将nsjail配置为在“侦听”模式下工作(例如,侦听端口),则可以run_once
通过添加标志来覆盖它以在模式下运行-Mo
:
/usr/bin/nsjail --config /config/nsjail.cfg -Mo
这应该触发与docker logs
上一步中发现的错误相同的错误,但是,现在,如果您运行以下命令,则可以启用更多详细选项:
/usr/bin/nsjail --config /config/nsjail.cfg -Mo -v
这将输出更多错误,并且这些错误应提供有关错误原因的更多详细信息。
使用Kubernetes进行故障排除
如果一切都在Docker中工作,那么问题可能会更高(在Kubernetes中)。调试此问题的第一步是检查挑战是否在本地群集中工作。请按照此处的说明在KIND中运行任务。
基本命令
本地集群运行后,您可以按照上面类似的步骤进行调试。
首先,通过运行以下命令更改为挑战的名称空间:
kubectl config set-context --current --namespace=kctf-chal-troubleshooting
要检索挑战的状态,请运行:
kubectl get deployment/chal
要读取执行日志,请运行:
kubectl logs deployment/chal -c challenge
要将shell放入吊舱中,请运行:
kubectl exec -it deployment/chal -c challenge
但是,还有一些其他命令可用于调试特定于Kubernetes的错误。
检查Kubernetes部署
对Kubernetes的基本了解(如kCTF在8分钟中所述)是一个有用的先决条件,但是本指南应该足够直观,即使没有Kubernetes知识也可以大致了解正在发生的事情。
最常见的故障排除方法是使用kubectl describe
命令。该命令告诉您Kubernetes所了解的有关挑战的一切。您应该首先通过运行以下命令描述“部署”:
kubectl describe deployment/chal
该命令最有趣的部分是:
- 现状与原因
- 大事记
如果部署可行,您应该看到挑战试图在事件(最后)中创建一个或两个“ pod”(您可以将Pod视为副本)。否则,状态会告诉您由于某种原因而无法执行此操作。这通常意味着配置文件是由任务的创建者手动修改的,因此现在是调查k8s
挑战目录下的文件更改历史记录的好时机。
望着豆荚
下一步是研究挑战的副本(豆荚)。您可以通过运行以下命令列出针对特定挑战的广告连播:
kubectl get pods --selector=app=kctf-chal-troubleshooting
这应该告诉您任务的状态;健康的挑战应如下所示:
NAME READY STATUS RESTARTS AGE
apache-php-9877d8b7c-k89zw 2/2 Running 0 24h
apache-php-9877d8b7c-t2p4k 2/2 Running 0 24h
请注意,READY表示2/2,这意味着2个容器中有2个已准备就绪。如果出现错误,您可能会看到RESTARTS的数字大于0,或者READY显示1/2或0/2。
要调试它,请运行:
kubectl describe pods --selector=app=kctf-chal-troubleshooting
这将描述豆荚;与部署类似,这些字段将引起关注:
- 现状与原因
- 大事记
- 状态(每个容器)
一个健康的挑战应该具有“运行状态”和“状态”。其他任何状态/状态将在原因下进行说明。
如果“原因”没有道理,则可以在“事件”下找到更多信息。理想情况下,事件的最后一行应该是:
Normal Created 71m ... Created container challenge
否则,活动将解释问题所在。
远程故障排除
准备好帽子和靴子,因为您现在要进行生产测试!这可能是万不得已的方法,并且最有可能无法解决任何问题(而是引入新问题),但这是不可避免的步骤,因为通常在生产中更容易调试错误。
基本故障排除
可用于kCTF的命令类似于可用于Docker和kubectl的命令。
要获取挑战的状态,请运行:
kctf chal status
要挑战,运行:
kctf chal debug ssh
要运行状况检查,请运行:
kctf chal debug ssh --container=healthcheck
要获取远程日志,请运行:
kctf chal debug logs
另外,您可以使用kctf-kubectl在kCTF集群下运行任何kubectl命令。
同样,请确保通过运行以下命令使用正确的名称空间:
kctf-kubectl config set-context --current --namespace=kctf-chal-troubleshooting
如果您使用Makefile与挑战进行互动(例如通过),则会自动完成此操作kctf chal status
。
重新启动或重新部署
好的第一步是重新开始挑战。为此,请运行:
kctf-kubectl rollout restart deployment/chal
注意:要使Kubernetes自动重启易碎挑战,您应该进行健康检查。
要重新部署挑战(例如,如果挑战在本地群集中本地运行良好),请运行:
kctf chal start
这会将本地挑战部署到远程集群。
要临时撤消不良的部署,请运行:
kubectl rollout undo deployment/chal
其他相关链接
- 本地测试演练–快速入门指南,向您展示如何在本地构建和测试挑战。
- 8分钟内完成kCTF –关于kCTF是什么以及它如何与Kubernetes进行交互的8分钟快速摘要。
- Google Cloud演练–一切正常运行后,请尝试将其部署到Google Cloud。
- 故障排除–帮助解决难题。
- 安全威胁模型–有关kCTF的安全注意事项,包括有关资产,风险和潜在攻击者的信息。
- kCTF VRP设置–根据此页面上展示的挑战,演示针对我们的kCTF演示集群的攻击。
项目地址
https://google.github.io/kctf/
https://github.com/google/kctf