引言:
上篇文章我们讲了AirPlane的第一关和第二关,作者以奇特的姿势找出flag,其思路应该是值得各位CTF入门选手借鉴参考的。实验文件可下载,过程详细,适合新手上手训练。难度系数:两颗星
书接上文,上次我们说到Task1和2的解决方法,不难看出这三个挑战的难度是递增的,所以今天来看看3有什么清奇的脑洞~
地址:http://10100110110100001100001011000100110000101101011.com/Airplane/3_with_the_best.php
密码:“Challenge”
下载后会有一个提示文件:
通过这个提示文件,让我想起了某些恶意软件所使用的类似的技术,只准对特定的国家。一般情况下它是通过两种方式实现的:
1. 向一些提供基于外部IP地理位置数据的服务器发送请求
2.通过检查软件安装语言/键盘排布方式判断其国家
我们可以运行一下这个实例然后观察它的行为,如果它有一些网络连接之类的东西出现的话…(我们还可以使用ie进程监测工具来查看)
运行结果如下:
…超时后终止。没有网络间接的迹象,所以我猜它可能是通过监测系统安装语言来判断使用者所在地。
这个是我把它拖到了IDA中开始静态分析。拖进去后变成这样:
这里有一个GetLocaleinfoEx。我怀疑它可能会涉及到验证,所以我们来关注一下这个值:
输出结果被保存在一个WORD大小的变量中,我顺着这条线继续找线索,结果并没个卵用。。。
然而,我发下它的 上一个字节似乎在别处被引用了。
如果这个标记匹配的话,一些其他的函数将会被复制到这个函数空间里打印出最初程序运行的那个信息:
所以我们这里有个自修改程序代码,看看这个函数都搞了什么事情:
[AppleScript] 纯文本查看 复制代码
?
1
2
3
4
5
fs:30h -> PEB
PEB + 0XC -> _PEB_LDR_DATA Ldr
Ldr + 0x14 -> _LIST_ENTRY InMemoryOrderModuleList
看起来不像是个能够打印密码的函数,相反,它从加载的DLL文件中搜索Kernel32.dll这个文件:
我们也可以从这看atypical NOPinstruction。这可能会混淆一些调试器。
OllyDbg之类的解析不出来:
如果我们想在OllyDbg下分析它,我们需要用一个典型的NOP(0x90)来替换这个片段,以便得到一个清晰的视图
现在我将在OllyDbg中进行一些动态分析,我在标记检查上设置了断点(决定是否覆盖该函数的那个)
当它被击中时,我在注册表中修改了Z标志。在函数被覆盖后,我强制执行了OllyDbg来重新分析代码,然后在程序开始处设置断点。
当执行到断点时,我们跳到函数执行的地方看一眼它搞了什么飞机。
蓝后…我发现了有趣的东西—内存中的一个新的PE文件
我们可以看到,之前存储的指向Kerne32.dll的指针被指向这个模块的指针所覆盖
为了更好地分析这个东东,我把它搞下来丢到PE_unmapper打开,然后将它命名为stub.dll并且它引出一个函数:GetComputerNameW:
在IDA中打开之后,我们可以发现,这个模块在标记检查之前就已经打开了。
它是在内存中手动加载的(不需要在磁盘中删除,也不使用LoadLibrary)。这个技巧在恶意软件上也经常被使用。
不管怎么说,我们现在需要找出使用了stub.dll的地方,所以我在这个模块上设置了断点:
然后..
现在我在主模块的文本部分设置了断点。
这是stubll.dll在主模块中被引用的地址:
所以,此时应用程序获得了函数的地址:”GetComputerNameW”.它可以从Kernel32.dll(如果没有设置地区标志)或者从stub.dll(如果设置了地区标志)来获取此函数。
看起来我们距离成功只有一个小目标的距离了,因为一些格式化的打印(‘%s’)是在这行之后完成的(可能这是打印的标志)、那么关键之处就是GetComputerNameW这里了。我们来搞一波:
GetComputerNameW:
OllyDbg这次也无法解析某些指令。所以我在IDA中代开了转储版本的stub.dll,并作为参考。
这个是函数开始时的样子:
[AppleScript] 纯文本查看 复制代码
?
1
2
3
fs:30h -> PEB
PEB + 0x10 -> _RTL_USER_PROCESS_PARAMETERS ProcessParameters
ProcessParameters + 0x44 -> _UNICODE_STRING CommandLine.Buffer
该函数获取主进程的命令行,然后处理到缓冲区。同样,无规则的NOP指令已经被使用(图中红标)
我们可以看到两个被比较的缓冲区,其中一个是存储内存的命令行缓冲区,另一个是在stub.dll中硬编码的。比较了四个连续的dword,如果这两个缓冲区没有匹配,那么该函数将设置一个错误代码并退出:
硬编码的缓冲区从RVA 0x2000开始,这是rdata的开始部分)。
29.png (45.25 KB, 下载次数: 48)
下载附件 保存到相册
2017-7-12 19:21 上传
当然了,我们需要让上面的函数没有错误退出,然后我们的flag就会被打印出来。
很容易可以得出这样的结论:为了获得flag,我们必须要在内存缓冲区中拥有与硬编码缓冲区相同的值。
在开始比较这个之前,我设置了断点。我只是复制了硬编码的缓冲区的内容,并且把这些内容覆盖到了内存中的缓冲区中。
然后再运行一下我们的程序,将将将将~~
密码就粗来了
还!有!谁!
AirPlane Task 3
原文:https://hshrzd.wordpress.com/2017/06/25/shabak-airplane-challenge-task-3/
由prison翻译整理,首发i春秋。