N1CTF2018 APFS&Lipstick题解

APFS题目描述

Apple released the brand new APFS on WWDC 2017 with a bunch of new features. With curiousity, Ben tried it out at once. Also, he left some surprise for you :)

提示

  1. This challenge is not related to the version of your MacOS
  2. look CAREFULLY at the challenge description apfs_snapshot
  3. HFS+: 1 sec APFS: 10^-9 sec
  4. A ^ B = F
  5. L1: 16bytes-aligned L2: hint “apfs_snapshot” L3: hint “HFS+: 1 sec APFS: 10^-9 sec” L4: mtime LSB, A ^ B = zip

附件信息

  • 文件名:497e2194-d11f-4dfd-9246-b9746f289bc7.dmg
  • 文件哈希:5fcfa02736ea8bf82c01f37a0309369a(MD5)
  • 文件大小:8,543,242 Byte

题解

下载得到一个dmg文件,dmg为macOS中的磁盘镜像格式。所以直接双击打开,发现需要密码

一开始以为是macOS的密码提示那个漏洞,尝试使用了低版本macOS(10.12.6)发现并没有密码提示,然后这时候提示1也出来了,所以明白点不在这。于是拖到010editor中查看,在文件尾发现了密码N1CTF_APFS

输入密码后成功挂载。 挂在之后发现只有一个名为ctf的文件夹,里面存放了531个0 Byte的txt

然后在这里思路卡壳,队里一个朋友发现这个dmg里还有一层快照

于是使用tmutil listlocalsnapshots /Volumes/N1CTF_APFS查看快照信息,发现快照名为ctf

然后尝试恢复快照,这里遇到了一个巨大的坑,在新版的macOS(10.13.3)上没有快照恢复的相关指令。于是又去py出题人,出题人表示没有问题。让我再去看看,网上找找相关介绍,找了一圈发现有个文章上说可以用apfs_snapshot来恢复,然而我cd到 /System/Library/Filesystems/apfs.fs/Contents/Resources发现并没有apfs_snapshot

不得已,找朋友借了一个低版本的macOS(10.12.6)进行恢复的 使用./apfs_snapshot -b ctf /Volumes/N1CTF_APFS来恢复快照

然后在磁盘管理当中卸载然后重新挂载该磁盘即可 后来讨论也可以使用如下指令直接把这层快照挂载出来。

cd Volumessudo mkdir N1CTF_APFS_snapshotsudo mount_apfs -s ctf ./N1CTF_APFS ./N1CTF_APFS_snapshot

于是得到了镜像前后的磁盘

在这里为了获取快照前后的情况,花了好几个小时。。然后去py出题人,出题人表示提没问题,也和 macOS 版本无关,当时真的想吐血。 在我搞定了快照前后的磁盘之后。。。提示(提示2)又恰到好处的放出来了。。 接下来在这里思路卡壳了好久。尝试进行了数据恢复,找出来一大堆没用的东西,也浪费了很多时间。然后去py出题人,出题人表示让我认真读题,好好看看加粗的with a bunch of new features。然后我滚去把 WWDC 2017 中关于 APFS 所有的的新特性都看过去了。看来看去就一个快照,快照已经用过了。这时候提示3出来了。讲道理这提示我一时摸不着头脑,做了如下猜想:

  1. APFS 比 HFS+ 读写快,但是不可能快9个数量级啊,划掉
  2. APFS 比 HFS+ 索引快,但是依然不可能快9个数量级啊,而且 APFS 里实际的数据只存一份,有点像Linux的硬链接,不对不对,划掉划掉
  3. 肯定与什么东西是1秒(s)和1纳秒(ns)的差距,找找就行,Bingo

于是Google it,终于在Apple的开发者中心(https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/APFS_Guide/VolumeFormatComparison/VolumeFormatComparison.html)找到了这个详情。

原来 APFS 中记录的时间戳精确到纳秒,而 HFS+ 只精确到秒。看来问题在时间戳了 然后写 python 脚本提取时间戳,这里又遇到一个坑。我的pythonos.path.getmtime()只能精确到小数点后两位时间,我对着这时间发了一小时呆。。。不得已又去py出题人,出题人骂了我一顿,并表示爱莫能助。后来跟出题人进行了深入交流之后,出题人表示要不你换个工具试试。然后我搜索到coreutils, 于是brew 启动!coreutils 安装! 终于可以看到精确的时间戳了

然后卡壳。这特么能看出个鬼。然后继续py出题人,出题人把我从头鄙视到脚,鄙视进每一个细胞。每一个粒子。在我诚心诚念我是渣之后,出题人给出了有用的信息,去看看修改时间(mtime),好的,然后把快照前后的修改时间全部提取出来。

cd /Volumes/N1CTF_APFS/ctfgstat -c "%n %y" * > ~/qian.txtcd /Volumes/N1CTF_APFS_snapshot/ctfgstat -c "%n %y" * > ~/hou.txt

取出来的数据如下图

此时提示4也上了,那A B 就很明确了,A表示快照前,B表示快照后,二者异或就行了 好的,异或,异或完了,我对着这数据又发呆了,这能看出来个鬼啊。不管了,继续py出题人,出题人问我你知道 LSB 么,我说我不知道(说的同时 Google 了一下),大概看了一下,最低有效位,用来做一些标志,比如0为正1为负之类的。 原来如此,那我把最后一位取出来,看下是奇还是偶不就拿到二进制数据了么,还没弄完就想了一下,不对啊,这才531个字符,够干嘛啊。继续问出题人,出题人表示让我仔细观察。那行,我先拿出来再看看 写了个py来提取最低位,最低位拿出来的数据如下:

然后仔细观察。发现数据的范围都是0-7,没有8和9,然后联想到八进制,每个数字就是3个bit,所以最终能得到一个531 * 3bit = 1593 bit的数据,然后此时提示4页很明白了,A为快照前,B为快照后,异或就行了,然后写程序处理一下。获得了所有数的二进制

#!/usr/bin/env python# coding: utf-8def oct2bin(number): return str(bin(number)).replace("0b", "").zfill(3)def main(): qian = "2 4 0 4 5 4 0 3 0 1 0 0 5 0 0 0 0 0 2 0 4 0 0 0 0 0 0 6 7 5 6 3 3 1 4 4 6 1 7 2 4 3 3 4 6 6 1 6 1 4 2 0 0 0 0 0 0 0 0 2 2 4 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 3 1 4 6 6 1 4 1 3 1 6 2 7 1 6 4 3 6 0 7 2 3 7 4 6 6 4 5 6 2 3 2 3 3 2 1 2 6 6 5 3 6 6 5 6 2 2 6 3 0 7 4 4 3 0 4 7 5 6 2 6 1 6 5 6 7 4 7 2 7 1 4 5 6 3 5 3 1 5 7 5 3 5 6 6 7 2 2 5 1 5 5 1 4 5 0 6 3 2 1 4 3 1 3 1 2 1 5 3 7 5 6 2 4 5 1 5 5 0 6 4 5 3 7 4 3 2 4 4 5 1 3 0 5 4 4 6 5 7 3 3 0 6 2 5 6 3 3 0 0 0 6 2 4 0 4 5 4 0 1 0 0 4 3 7 4 0 0 0 2 4 0 0 0 0 1 0 2 0 0 0 0 0 0 3 3 6 7 1 5 4 6 2 3 0 7 5 2 1 5 6 3 3 0 7 0 6 1 0 0 0 0 0 0 0 0 1 1 2 0 0 0 0 0 0 0 0 0 4 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 3 1 5 4 3 0 2 6 3 4 5 6 3 5 0 7 4 1 6 4 0 2 4 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 4 0 0 0 1 2 3 7 4 7 6 4 1 4 6 1 2 2 6 5 6 4 6 0 0 4 6 2 3 7 3 5 4 1 7 2 0 4 5 3 2 3 2 3 0 0 2 6 7 3 3 6 2 0 2 5 0 3 0 3 5 4 7 5 1 4 0 1 2 4 0 4 5 4 0 5 0 1 4 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 4 0 0 2 6 4 0 0 0 0 0 0 0 0 5 3 4 0 0 0 0 0 0 0 0 0 0 0 0" qian = qian.split(" ") hou = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 3 0 1 0 0 4 0 0 0 0 7 3 6 5 5 3 5 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 5 1 1 0 5 6 0 6 7 2 4 6 0 6 7 1 0 3 2 2 5 2 5 6 2 6 1 2 2 5 1 0 2 3 0 7 2 6 0 3 5 7 0 6 1 3 1 2 3 5 7 3 2 7 3 2 0 2 2 3 4 5 6 6 1 7 4 2 4 5 1 2 5 3 2 0 5 6 5 1 1 2 4 7 4 0 2 1 1 1 6 2 1 6 5 6 7 4 7 3 2 6 7 6 7 3 0 4 5 6 3 7 4 1 6 3 1 2 5 6 4 0 1 1 7 4 4 7 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 6 5 4 0 4 0 2 0 0 0 0 3 5 7 2 6 5 6 4 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 4 3 1 5 0 3 5 7 3 5 3 4 0 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" hou = hou.split(" ") r = "" for i in range(0, len(qian)):  a = int(qian[i])  b = int(hou[i])  f = a ^ b  r += oct2bin(f) print rif __name__ == '__main__': main()

然后得到拼接在一起的二进制数据

然后就简单了,每8 bit转一字节,然后写到文件,依然用python处理

#/usr/bin/env python#coding: utf-8def bin2hex(string): return chr(int(string, 2))def main(): data = "010100000100101100000011000001000000101000000000000000010000100000000000000000000011010110110010011001000100110001111101111101110001101011001010001100010000000000000000000000000010010100000000000000000000000000001000000000000000000000000000011001100110110001100001011001110010111001110100011110000111010000100001111111100000000010101101001111100001100001111101000100100000100111100100101001101110001010000110100101001000000001101000001111010110001110011111000001000111100010111111111001110010011001100011010101111001100101111101101001010100010110000010011110110001011101100111011010111000011000110001000010000101010100100011111101101110110110011000111011100010001101000110101111010100100100111010010100000100101100000001000000100011111100000000000010100000000000000001000010000000000000000000001101011011001001100100010011000111110111110111000110101100101000110001000000000000000000000000001001010000000000000000000000000000100000000000001001000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000110011001101100011000010110011100101110011101000111100001110100000010100000000000100000000000000000000000000000000000000000000000000001000000000001100000000000001101111100101010110111100011101100001110110011110100110000000100110010011111011101100001111010000100101011010011010011000000010110111011011110010000010101000011000011101100111101001100000001010100000100101100000101000001100000000000000000000000000000000000000001000000000000000100000000010110100000000000000000000000000101011100000000000000000000000000000000000000" tmp = "" r = "" for i in data:  if len(tmp) == 8:   r += bin2hex(tmp)   tmp = ""  tmp += i f = open("result", "w") f.write(r) f.close()if __name__ == '__main__': main()

得到了一个result文件,file一下发现是个zip包

直接解压,发现需要密码。

一开始以为是伪加密,但是修改了加密位之后发现还是如此,所以猜测是真加密,尝试使用之前的密码N1CTF_APFS,一次成功,得到flag

个人感悟

在做题的过程中始终与出题人保持联系,中间也有聊到一些有的没的,出题人表示这个题目是取材于真实的取证工作当中的,完完全全的还原了当时取证的详细过程。 做题过程中不断地在吐槽出题人脑洞,在做完这个题之后返回来看看,出题人并不脑洞,只能说自己当时的心态确实出现了问题。 自己认认真真的思考了一下比赛环境与真实环境的差异,以及什么是取证,怎么才算取证等等。 感觉到自己之前的眼界还是太局限,仅仅停留在比赛是远远不够的,在真实的取证工作中,不会有任何的提示,细心才是唯一的法宝,数据可能藏在任何地方,不漏掉任何蛛丝马迹,不放过一分一毫才是取证的真谛。 同时也感觉到自己的底子实在是太弱了,在了解到出题人的一些经历之后觉得自己还差得太远,除了基础性知识缺失,相关经验也十分匮乏。 在CTF中能够见到这种十分贴近于实战的题目是非常开心的,希望未来能够遇见更多这样的题目,CTF不是为了比赛而比赛,而是信安爱好者交流技术,切磋技能的平台。


Lipstick

lipstick为口红的意思,这次题目是一张图片

首先使用隐写神器Stegsolve,

在中间地带发现了YSL(杨树林,b( ̄▽ ̄)d)这个口红品牌的字样。再继续深入,Analyse→Data Extract

Save Bin保存为一个zip包

这里用winrar打开会报错,得用7z等压缩工具打开才可以。

尝试了下伪加密,无果。于是整个过程就剩下一个密码。一般来说图片隐写的话,要么是二进制里藏了东西,要么就是图形藏了东西。这里二进制里藏了zip包,剩下的密码就只能从图形里入手。图形里是21个颜色格,我分别取色

BC0B28D04179D47A6FC2696FEB8262CF1A77C0083EBC0B28BC0B28D132746A1319BC0B28BC0B28D4121DD75B59DD8885CE0A4AD4121D7E453AD75B59DD8885

这里折腾了好久,发现是要找颜色所对应的YSL口红的色号(lll¬ω¬)

搜到一个网址:

https://www.yslbeautyus.com/on/demandware.store/Sites-ysl-us-Site/en_US/Product-Variation?pid=194YSL

这里颜色值可以对应上色号,于是写脚本收集颜色值对应的色号,并把色号转换为二进制,再组合,再bin2text

# -*- coding:utf8 -*-__author__='pcat@chamd5.org'import requestsimport reimport libnumdef foo(): url=r'https://www.yslbeautyus.com/on/demandware.store/Sites-ysl-us-Site/en_US/Product-Variation?pid=194YSL' cont=requests.get(url).content # print cont pattern=r'YSL_color=(.*?)%20[\s\S]*?background-color: #(.*?)"' rst=re.findall(pattern,cont) dYSL={} for num,color in rst:  dYSL[color]=int(num.lstrip('0')) lst=['BC0B28','D04179','D47A6F','C2696F','EB8262', 'CF1A77','C0083E','BC0B28','BC0B28','D13274', '6A1319','BC0B28','BC0B28','D4121D','D75B59', 'DD8885','CE0A4A','D4121D','7E453A','D75B59', 'DD8885'] flag=''.join('{:b}'.format(dYSL[i]) for i in lst) print libnum.b2s(flag) passif __name__ == '__main__': foo() print 'ok'

打印出来是“白学家”,用7z进行解压缩

解压后打开flag.txt即可。

ps,两个题目附件的下载链接:

https://pan.baidu.com/s/12J01OX2o81Tsq8Y9fMJX7w

原文发布于微信公众号 - ChaMd5安全团队(chamd5sec)

原文发表时间:2018-03-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏转载gongluck的CSDN博客

波形音频(WAVE)底层接口的学习与使用

在WINDOWS下,音频函数有多种类型,如MCI、多媒体OLE控制、高级音频等,使用方法都比较简单。 但如果想编写一个功能较强大的音频处理程序,那...

1.1K5
来自专栏SAP最佳业务实践

SAP最佳业务实践:MM–组件收费的委外加工(251)-6开销售发票

4.7 创建出具发票凭证 创建出具发票凭证给委外加工商。 完成了对委外加工商的发货。 SAP ECC菜单Processes -Create Invoices f...

3788
来自专栏施炯的IoT开发专栏

Windows 10 IoT Serials 5 - 如何为树莓派应用程序添加语音识别与交互功能

    都说语音是人机交互的重要手段,虽然个人觉得在大庭广众之下,对着手机发号施令会显得有些尴尬。但是在资源受限的物联网应用场景下(无法外接鼠标键盘显示器),如...

20410
来自专栏木子昭的博客

<技术贴>当图虫遇到爬虫...根据”分类名称”,获取json数据根据json数据,获取图集url与title爬虫架构:运行界面:最终效果

首先,图虫网是一个很棒的图片网站,这里的爬虫只是为了研究技术,请读者朋友们,不要大量采集网站信息,爬取的图片,请取得版权后再使用... ? 图虫网 根据”分类...

3499
来自专栏iOSDevLog

用Kotlin破解Android版微信小游戏-跳一跳成果跳一跳思路源码使用方法参考来源Android 插件 免PC

3736
来自专栏Crossin的编程教室

【Python 第4课】输入

Hi~Crossin又来了。 可以用编程语言让计算机按你说的指令做事情之后,大家是不是有些跃跃欲试呢?别着急,先回顾一下我们之前几节课。我们到现在一共提到了三种...

3167
来自专栏Android 开发学习

音频开发ijkplayer小结 android

2142
来自专栏JadePeng的技术博客

HTML5录音控件

最近的项目又需要用到录音,年前有过调研,再次翻出来使用,这里做一个记录。 HTML5提供了录音支持,因此可以方便使用HTML5来录音,来实现录音、语音识别等功能...

1.2K5
来自专栏web编程技术分享

【Java框架型项目从入门到装逼】第一节 - Spring框架 IOC的丧心病狂解说,来一波神的视角

3096
来自专栏编程之旅

iOS开发 —— Swift版地址选择器

已经有二十多天没有更新自己的博客了,这段时间经历了很多事情,离开了生活了六七年的杭州,从离职再入职,忙的是一塌糊涂。

1092

扫码关注云+社区