首页
社区
课程
招聘
[比赛]看雪.WiFi万能钥匙 CTF 2017第八题 点评及解题思路
发表于: 2017-6-30 17:27 2649

[比赛]看雪.WiFi万能钥匙 CTF 2017第八题 点评及解题思路

2017-6-30 17:27
2649

看雪CTF 2017 比赛进行至第八题

截止至今天中午12点,第八题破解人数为5人!

攻击方排名前十名波动不大,

loudy从第十位上升至第7位,

poyoten再次冲进前十位~

前十名能否保守住自己的位置呢?

是否会有黑马一战成名呢?

期待ing......

接下来我们来回顾一下第八题

看看 看雪评委和出题者是怎么说的ヾ(๑╹◡╹)ノ"。


看雪评委 netwind 点评

该题亮点依然在反调试上,用虚拟机进行代码保护,通过线程动态加解密主要代码,通过校验和检查代码完整性,根据时间来反调试,另外还将实际代码替换为假代码,防止反汇编,在反调试方面让攻方选手举步维艰。算法上采用了RSA算法的思想,解决掉反调试后,参考RSA算法可以求解。

作者简介


作者loudy,原名吴林峰,毕业于北京航空航天大学电子信息工程专业,当前从事工作出于保密规定暂不公开。爱好跑步、篮球、电影;热衷编程、特别是逆向分析。2007年接触看雪,以潜水学习为主,也希望多认识有相同爱好的朋友。


看雪 CTF2017 第八题设计思路


一、答案

UFdVXVRTVVFYWltdXV9XQkFDQEUtLVBTV1BWVVFQUVxeWVxfWENDQkRCQE5CTEBCWFZaVVRTVlxZU1lcXC0tVGhpNV9pc19BX1ZlcnlfU2ltcGxlX1Rlc3Qh

二、基本设计思想

(1)简单虚拟机(使用虚拟寄存器传值)

实现了系统函数调用、CMP、JNZ、XOR等调用

(2)反调试

主要使用了3种

1、  线程动态加密解密主要代码

2、  CheckSum检查主要代码

3、  时间判断

(3)反汇编

主要通过将实际代码替换为假代码,防止反汇编。

(4)算法设计

主要是利用RSA思想,其中(比较小)

P:900F0CA3041C345B

Q:98FCAE63A170C363

N:56172073862A662A8D6BB1A135999031

E:D9C382944A461EE3

D:4A064A97921BDF9E3F354E9020AE054F

首先对输入进行base64解码,然后将解码后注册码分成三段,

1、  第一段解码得到E,再和固定值做大数乘法运算,结果和另一固定值比较,相等则到下一步

2、  第二段解码得到N,再和固定值做大数乘法运算,结果和另一固定值比较,相等则到下一步

3、  通过E和N对第三段加密,和内存固定值比较,相等则成功

三、破解思路

1、先解决反调试和反反汇编,将真正代码还原

2、识别大数乘法运算,通过固定值和大数除法还原E、N,得到第一段和第二段注册码。

3、通过E、N解出D

4、用N和D对固定值解密,得到第三段注册码。

5、组合三段注册码,base64加密,得到最终的注册码。


下面选取攻击者 风间仁 的破解分析        

1. TlsCallback

创建了6个线程, 前3个线程是负责smc解码的, 后3个线程没用 

1
.text:00402D30 TlsCallback_2

线程1: 解码004025DC处的跳转及check1函数

1
2
3
4
5
6
7
.text:00402830 thread1
  
.text:004025DC                 push    offset loc_4025E2
.text:004025E1                 retn
.text:004025E2                 lea     ecx, [ebp+var_110]
.text:004025E8                 push    ecx
.text:004025E9                 call    check1

线程2: 解码0040263C处的跳转及check2函数

1
2
3
4
5
6
7
.text:00402970 thread2
  
.text:0040263C                 push    offset loc_402642
.text:00402641                 retn
.text:00402642                 lea     eax, [ebp+var_110]
.text:00402648                 push    eax
.text:00402649                 call    check2

线程3: 解码0040269C处的跳转及check3函数

1
2
3
4
5
6
7
.text:00402A90 thread3
  
.text:0040269C                 push    offset loc_4026A2
.text:004026A1                 retn
.text:004026A2                 lea     edx, [ebp+var_110]
.text:004026A8                 push    edx
.text:004026A9                 call    check3


2. 主流程

一个自定义的虚拟机


校验函数

1
.text:00402700 fn_check


检测代码改动


1
.text:00402481                 call    check_text_areas


sn=base64_decode(sn)

1
.text:0040251D                 call    base64_decode


sn的格式(共90位): (20位)--(39位)--(27位)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:00402573                 cmp     [ebp+var_130], 90
.text:0040257A                 jnz     short loc_4025AC
.text:0040257C                 movsx   edx, [ebp+Dst+14h]
.text:00402583                 cmp     edx, '-'
.text:00402586                 jnz     short loc_4025AC
.text:00402588                 movsx   eax, [ebp+Dst+15h]
.text:0040258F                 cmp     eax, '-'
.text:00402592                 jnz     short loc_4025AC
.text:00402594                 movsx   ecx, [ebp+Dst+3Dh]
.text:0040259B                 cmp     ecx, '-'
.text:0040259E                 jnz     short loc_4025AC
.text:004025A0                 movsx   edx, [ebp+Dst+3Eh]
.text:004025A7                 cmp     edx, '-'
.text:004025AA                 jz      short loc_4025B2


校验前20位

(sn1 xor abcdefg...) * 98765432109876543210123 == 1549780652036258484424751705102781884386113

1
.text:004025E9                 call    check1

校验中间39位

(sn2 xor abcdefg...) ^ 2 == 13095069099216326605010245808779535277211541324456558063162414338128147458401

1
.text:00402649                 call    check2

检验后27位, sn3视为16进制

sn3 ^ 15691529100101820131 mod 114433688655117320765854989491151409201 == 71639176673360967005214790689576394595

1
.text:004026A9                 call    check3

sn3作为16进制转换的时候是有符号转换的, 所以导致>=80的值无法正常转换

这里只需要关心能产生0x00的值即可(有挺多个, 如0x89) 

1
2
3
4
5
6
7
8
9
10
11
12
13
for (int i = 8; i < 16; i++)
{
    for (int k = 0; k < 16; k++)
    {
        int v = (char)((i << 4) | k);
        char v1 = v / 16;
        char v2 = v % 16;
        v1 += v1 < 0 ? 0x37 : 0x30;
        v2 += v2 < 0 ? 0x37 : 0x30;
        // 这里非数字字母的字符, 后面都会被0替代
        printf("%02X: %c%c\n", (BYTE)v, v1, v2);
    }
}


3. 后27位计算


已知:

e = 15691529100101820131

n = 114433688655117320765854989491151409201

m = 71639176673360967005214790689576394595


分解得到

d = 98395538376216701876091105738065053007


c = m ^ d mod n = 55986991232018409201158808992848352475 (2A1EB3C9579DFA307CF5B6C8730114DB) 


c中>=0x80的是无法通过转换得到的(如B3, C9, ...)


sn3(27字节) > c (16字节)

c ^ e mod n = m, 根据(a * b) mod n = (a mod n) * (b mod n) mod n

得 (c mod n) * (c mod n) * ... mod n = m

再由c mod n = (c + k * n) mod n, 代入上式得到

(c + k * n) ^ e mod n = m


根据下面这两个条件可以立刻得到很多解(如: 0208674855670d0560353c3a1d3e242e217766)

sn3 = c + k * n

sn3中的字节必须小于0x80


得到的解, 在前面填充可以转换成00的值:  89898989898989890208674855670d0560353c3a1d3e242e217766

与前面的串连接起来base64得到最终结果 

UFdVXVRTVVFYWltdXV9XQkFDQEUtLVBTV1BWVVFQUVxeWVxfWENDQkRCQE5CTEBCWFZaVVRTVlxZU1lcXC0tiYmJiYmJiYkCCGdIVWcNBWA1PDodPiQuIXdm


4. 穷举脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import itertools
n=114433688655117320765854989491151409201
e=15691529100101820131
c=55986991232018409201158808992848352475
def valid_result(v):
    sv=hex(v)[2:]
    sv_len=len(sv)
    if (sv_len&1) != 0:
        sv='0'+sv
        sv_len=sv_len+1
    for i in itertools.count(0,2):
        if (i>=sv_len):
            break
        if (ord(sv[i])>=0x38):
            return False
    return True
def solve(ibegin,iend):
    g=0
    nc=c
    k=0
    for i in itertools.count(0):
        g=g+1
        nc=nc+n
        if ((ibegin+i)>=iend):
            break;
        #print(nc)
        #print(pow(nc,e,n))
        if (g>=1000000):
            print(ibegin+i)
            g=0        
        if (valid_result(nc)):
            print('---')
            print(i)
            print(nc)
            print(hex(nc))
            #break
    return
solve(0,4294967296)
'''
---
396154
45333533916159234226406484520676079360374630
0x208674855670d0560353c3a1d3e242e217766
---
456935
52288927946305920099875916636937753163020611
0x2583f59392b7266031a47246a1e672b013943
..


最后感谢 WiFi 万能钥匙安全应急响应中心的赞助支持, 接下来的比赛大家一定要使出洪荒之力哦!↖(^ω^)↗ 比心 ❤ 


 赞助商 


上海连尚网络科技有限公司成立于 2013 年,是一家专注于提供免费上网和内容服务的移动互联网企业。连尚网络自主研发的核心产品 WiFi 万能钥匙,以分享经济的模式,通过云计算和大数据技术,利用热点主人分享的闲置WiFi资源,为用户提供免费、稳定、安全的上网服务,以帮助更多的人上网,找到属于他们的机会,改变自己的命运。



[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//