首页
论坛
专栏
课程

[原创]看雪CTF2017秋季赛第二题--被套路的新人

2017-10-29 20:12 6471

[原创]看雪CTF2017秋季赛第二题--被套路的新人

2017-10-29 20:12
6471
 

目录

1. 套路

这段时间作业有些多,所以writeup写的有些晚了。这是一道没有反调试、没有加壳的题,用ida的F5大法直接就可以看到“源码”和两个“校验方程的函数”让第一次做CTF题的我认为这就是让我解方程啊!于是就写程序穷举方程了,结果被套路了一天。。。

 

图片描述
图片描述
图片描述

  • PS:穷举的代码就不放上来

2. 发现蹊跷

在穷举半天没有结果之后就感觉被套路了,于是仔细分析

2.1 检查输入

  • 首先想到的就是scanf函数是不安全的所以就去仔细看了一下输入函数:
    图片描述
  • 从ida的反汇编输入函数(我按n键重命名为了call_scanf_401050)中发现了该函数在栈中分配了一块12字节大小的局部空间(var_C = dwrod ptr -0Ch)并交该空间提供给scanf做输入缓冲区:
    lea eax, [esp+0Ch+var_C]
    push eax
    push "%s"
    call _scanf
    
  • 现在套路已经很明显了:作者开辟了12字节的输入缓冲区给了没有限制长度的scanf函数,且前面发现的两个校验函数中都用了相同的两个int类型的变量作为4个方程的未知数,这里太不合理了,作者应该留了溢出在这里。

2.2 溢出验证

  • 打开od断点下到0x00401077 call scanf的位置,输入123456789012(刚好撑满12字节的临时变量空间,溢出了字符串结尾处自动补的0x00
    • 首先观察输入前的栈环境:
      图片描述
    • 再观察输入后的栈环境:
      图片描述
    • 根据上图中的栈环境再结合scanf是cdcall调用约定发现返回地址在我们溢出一个字节0x00后变成了入口地址,按F9跑起来后程序又来到了要求输入的地方:
      图片描述
    • 瞬间感觉这里就是作者给的提示!当时立马想到如果溢出到提示"You get it!"的地址就破解了此题,但是又发现这里的地址是0x0040102F,地址中的0x10是无法输入的字符\^p(但是可以粘贴进去):
      图片描述
    • 如果真的可以溢出到该地址的话这道题又是多解了,所以答案肯定不在这里

3. 寻找目标

3.1 拨开迷雾

  • 如果解题方法真的是溢出的话,溢出覆盖的地址肯定是比赛规则中的可输入字符。

  • 首先猜想溢出后的地址的代码会jmp0x0040102F这个地址,于是全局搜索0x0040102F,没有匹配到。

  • 于是手动在ida中查看符合要求的地址,于是发现了下面的符合要求数据:
    图片描述

  • 这一堆数据比较蹊跷,仔细观察里面出现了很多0x75、0x71等含有7的数据,然后再猜想不是直接jmp到固定地址,应该是相对跳,对应intel手册中jxx查的跳转指令的二进制代码(因为作者不一定要用jmp):
    图片描述

  • 此时我还天真的以为这段数据里面有一个指令会跳转到0x0040102F那里,幸好一个机灵反应到目前除了覆盖返回地址的那几个字符有用其他字符都是没用的,如果真是这样的话这道题就多解了,所以立马想到应该还有验证前面几个字符的代码要执行,所以这堆数据应该是真正的校验代码!

  • 幸好我在科锐刚学习完汇编老师有讲到花指令的的相关知识,就是通过加无用的数据使得代码在静态分析和od没有走到目标代码是这些代码看起来是乱的:
    图片描述

3.2 直达目标

  • 这段数据/代码的首地址是0x00413131,刚好是ASCII码的11A,先输入12345678901211A溢出到目标地址:

    图片描述

  • 首先F9跑起来看看发生了什么:

    图片描述

  • 执行了验证代码!!!
  • 然后我使用了od的指令跟踪功能,在'0x40103F'的位置(提示验证失败的代码)下断点,重新调试断到0x00413131的位置,打开od->查看->run跟踪窗口,然后点击od->调试->跟踪步入,来看看结果(后来才了解到有去花指令的插件),所有执行过的代码都记录在了Run trace窗口中:

    图片描述

  • 同时也发现了里面有很多无用的跳转,看来作者也是通过这些跳转来使得这部分指令不能被ida和od识别

  • 但是因为是验证失败了,所以肯定有部分指令没有被执行,所以有部分没有记录下来。这时上网搜了去花指令插件的使用方法,才发现花指令这么好去

4. 解题

4.1 还原公式

本来懒得手动还原,准备使用RadAsm编译后用ida辅助还原,结果一值搞不对,后来就手动还原公式了

  • 抄到RadAsm中的校验的汇编指令:
.386
.model flat, stdcall  ;32 bit memory model
option casemap :none  ;case sensitive

includelib TestRad.Inc

.const

.data?
    ; 构造条件开始
    g_input    dd 0
    g_EAF917E2  dd EAF917E2h
    g_E8F508C8  dd E8F508C8h
    ; 构造条件结束

.code

CheckFun proc C
    ; 构造条件开始
    mov  eax, 11Ah
    push eax
    mov  eax, 3333h
    push eax
    mov  eax, 2222h
    push eax
    mov  eax, 1111h
    push eax
    ; 构造条件结束

    xor  eax, eax
    mov  dword ptr [g_input], eax
    pop  eax
    mov  ecx, eax
    pop  eax
    mov  ebx, eax
    pop  eax
    mov  edx, eax
    mov  eax, ecx

    sub eax, ebx
    shl eax, 2
    add eax, ecx
    add eax, edx
    sub eax, dword ptr [g_EAF917E2]
    je  TWO
    ret

TWO:
    add eax, ecx
    sub eax, ebx  
    mov ebx, eax    
    shl eax, 1
    add eax, ebx
    add eax, ecx      
    mov ecx,eax
    add eax, edx
    sub eax, dword ptr [g_E8F508C8]
    je  THREE
    ret

THREE:
    mov eax, ecx
    sub eax, edx
    ret
CheckFun endp

start:
    invoke CheckFun
    ret

end start
  • 手动还原:
    (x - y) << 2 + x + z         == 0xEAF917E2
    (x - y) << 1 + x - y + x + z == 0xE8F508C8
    (x - y) << 1 + x - y + x - z == 0x0C0A3C68
    
  • 这次就没有写程序爆破了,而是在网上找的在线解方程组页面解开的:
    图片描述
    • 得到的结果转化成16进制为:x=0x7473754A; y=0x726F6630; z=6E756630,转化为ASCII码:
      图片描述

4.3 解题密钥

  • 所以解题密钥就是解方程组之后的结果加上11A为:Just0For0Fun11A

这次Fpc大佬的出题给我这样的新人上了很好的一堂课,感谢Fpc、感谢看雪



[公告]安全测试和项目外包请将项目需求发到看雪企服平台:https://qifu.kanxue.com

上传的附件:
最新回复 (9)
雪衫 2017-10-30 09:18
2
0
科锐大佬就是厉害。-  -这么多writeup感觉你写得比较接地气
大只狼 2017-10-30 09:24
3
0
很棒!顺便问问那个IntellOpcodes能否来一份
KevinsBobo 6 2017-10-30 09:37
4
0
雪衫 科锐大佬就是厉害。- -这么多writeup感觉你写得比较接地气
我其实是个小菜鸟
KevinsBobo 6 2017-10-30 09:37
5
0
大只狼 很棒!顺便问问那个IntellOpcodes能否来一份[em_16]
上传到附件了
BlackTrace 2017-10-30 10:16
6
0
掉入坑,+1
幻想TK 2017-10-30 10:39
7
0
同意1楼,
Fpc 4 2017-10-30 19:52
8
0
病毒小子 2017-11-9 14:32
9
0
点赞,不仅解出了题,还能授人以渔
alphc 2019-9-27 16:42
10
0
游客
登录 | 注册 方可回帖
返回