美文网首页
[suctf_2018] Enigma

[suctf_2018] Enigma

作者: pu1p | 来源:发表于2018-05-29 23:44 被阅读32次

    0x00 背景

    这次比赛因为撞上了运动会, 加上周日还有一些别的事情, 所以其实也就打了半天, 就做出来了这个题目(虽然可能打两天也只能做出来这一个题目QAQ), 算是xctf中第一个独立做出来的题目, 竟然又是逆向......pwn是真的太难了..........多看师傅们的wp吧.... 因为刚好选了门课, 讲的就是图灵奖得主的生平, 当然首先要讲的就是图灵了, 讲图灵自然就绕不过他二战期间帮助英国军方破解德国的密码机--Enigma 的故事了.....这也是我没有做pwn, 而是选择先做这个题的原因.
    贴上官方wp

    0x01 程序分析

    看到这个题目我首先想到两种解法: 爆破或者逆算法. 因为enigma是每个字母单独加密的, 所以完全可以每个字母单独爆破. 并不会很慢. 应该要比逆算法快不少(对于一名逆向新手而言)
    所以我就决定先自己用c重新实现一下程序中的算法, 然后爆破之. 这个过程就是体力活.......没有太多技术含量..........(可能有骚操作但是我不会吧......), 个人感觉了解enigma加密原理对理解程序有很大的帮助. 总之最后自己用c复现出来代码如下(对比官方wp中的官方实现, 我意识到了巨大的差距...........):

    #include <stdio.h>
    #include <stdlib.h>
    
    char target[36] = {'\xa8', '\x1c', '\xaf', '\xd9', '\x00', '\x6c', '\xac', '\x02',
                       '\x9b', '\x05', '\xe3', '\x68', '\x2f', '\xc7', '\x78', '\x3a',
                       '\x02', '\xbc', '\xbf', '\xb9', '\x4d', '\x1c', '\x7d', '\x6e',
                       '\x31', '\x1b', '\x9b', '\x84', '\xd4', '\x84', '\x00', '\x76',
                       '\x5a', '\x4d', '\x06', '\x75'};
    
    char key0[4] = {'\x31', '\x62', '\x93', '\xc4'};
    char key1[4] = {'\x21', '\x42', '\x63', '\x84'};
    char key2[4] = {'\x3d', '\x7a', '\xb7', '\xf4'};
    
    int key0_idx = 0;
    int key1_idx = 0;
    int key2_idx = 0;
    
    char v17=0;
    char v18=0;
    
    int encode3[9] = {0x2f9bacef, 0x97CDD677, 0x4BE6EB3B, 0x0A5F3759D,
                      0x0D2F9BACE, 0x697CDD67, 0x0B4BE6EB3, 0x5A5F3759, 0x2D2F9BAC};
    char input[36] = {'c'};
    char input2[36];
    char encoded[36];
    
    int g_val = 0x5f3759df;
    
    char encode00(char key,  int idx){
        void * v1[2];
        char input_i = input[idx];
        for(int i=0; i<8; ++i){
            char v3 = v17;
            int v4 = ((key & (1 << (i&0x3f))) != 0);
            int v5 = ((input_i & (1 << (i&0x3f))) != 0);
            v18 = v3 ^ v4 ^ v5;
            v17 = v4 & v5 | v3 & (v4 | v5);
            if(v18 == 0){
                input[idx] = input[idx] & ~(1 << (i&0x3f));
            }else{
                input[idx] = input[idx] | (1 << (i&0x3f));
            }
        }
        return input[idx];
    }
    
    int keyset[3] = {0, 0, 0};
    
    char encode0(int idx){
        v17 = 0;
        v18 = 0;
        char key = key0[key0_idx];
        char ret = encode00(key, idx);
    
        key = key1[key1_idx];
        ret = encode00(key, idx);
    
        key = key2[key2_idx];
        ret = encode00(key, idx);
    
        encoded[idx] = ret;
        if ( ++key0_idx == 4 )
        {
          key0_idx = 0;
          ++key1_idx;
        }
        if ( key1_idx == 4 )
        {
          key1_idx = 0;
          ++key2_idx;
        }
        if ( key2_idx == 4 )
          key2_idx = 0;
        return ret;
    }
    
    int func(int a1, int a2){
        return (((a1&0xff) & (1 << (a2 & 0x3f))) != 0);
    }
    
    char encode11(int idx, int j){
        char encoded_i = encoded[idx];
        int v0 = func(encoded_i, j);
        int v1 = (v0 != func(encoded_i, 7-j));
        if(v1){
            encoded_i |= (1 << (j & 0x3f));
        }else{
            encoded_i &= ~(1 << (j & 0x3f));
        }
        encoded[idx] = encoded_i;
        return encoded_i;
    }
    
    char encode12(int idx, int j){
        char encoded_i = encoded[idx];
        int v0 = func(encoded_i, 7-j);
        int v1 = (v0 != func(encoded_i, j));
        if(v1){
            encoded_i |= (1 << ((7-j)&0x3f));
        }else{
            encoded_i &= ~(1 << ((7-j)&0x3f));
        }
        encoded[idx] = encoded_i;
        return encoded_i;
    }
    
    int encode1(int i){
        for(int j=0; j<=2; ++j){
            encode11(i, j);
            encode12(i, j);
            encode11(i, j);
        }
    }
    
    
    int encode22(){
        int v0 = func(g_val, 31);
        int v1 = (func(g_val, 7) ^ v0);
        int v2 = (func(g_val, 5) ^ v1);
        int v3 = (func(g_val, 3) ^ v2);
        int v4 = (func(g_val, 2) ^ v3);
        int v16 = (v4 ^ func(g_val, 0));
        g_val = g_val>>1;
    
        int v6 = (v16!=0);
    
        if(v6){
            g_val |= (1 << (31 & 0x3f));
        }else{
            g_val &= ~(1 << (31 & 0x3f));
        }
        return g_val;
    }
    
    char encode2(int i){
        char *e3_ptr = (char *) encode3;
        char ret = encoded[i] ^ e3_ptr[i];
        encoded[i] = ret;
        return ret;
    }
    
    void show(){
        printf("output:\n" );
        for(int i=0; i<36; ++i){
            printf("%d, %2x, %c\n", i, encoded[i], encoded[i]);
        }
        printf("input:\n" );
        for(int i=0; i<36; ++i){
            printf("%d: %2x, %c\n", i, input2[i], input2[i]);
        }
        for(int i=0; i<36; ++i){
            printf("%c", input2[i]);
        }
    }
    
    void idx2key(int idx){
        key0_idx = idx % 4;
        key1_idx = (idx >> 2) % 4;
        key2_idx = (idx >> 4) % 4;
    }
    
    int decode(){
        char *e3_ptr = (char *)encode3;
        for(int i=0; i<36; ++i){
            for(char possible = 0; ; ++possible){
                idx2key(i);
                input[i] = possible;
                input2[i] = possible;
                encode0(i);
                encode1(i);
                char ret = encode2(i);
                if(ret == target[i]){
                    int a = i;
                    break;
                }
            }
        }
        show();
        return 0;
    }
    
    
    int test(){
        for(int i=0; i<36; ++i){
            input[i] = 'a';
            encode0(i);
            encode1(i);
            encode2(i);
        }
        show();
    }
    
    int main(){
        decode();
        // test();
        // printf("hello" );
        return 0;
    }
    

    然后其实也就没啥了, 爆破的算法很无脑.......不过期间遇到了一个坑, 就是enigma是有种东西叫转子, 每加密一个字母转子就会转一个单位, 不同状态的转子对应的加密映射是不同的. 所以爆破的时候一定需要将每一位对应的转子的状态设置好, 否则就会出现奇怪的东西. 还卡在一个地方是因为我把用来存贮输入的flag的数组input在爆破的过程中修改了.......进而导致最后输出input的时候得到的是一串包含一堆不可见字符的怎么看都不像flag的乱码........所以我们要牢记软件构造的教导!养成良好的编程习惯!不要修改参数!!!

    0x04 exp

    参考上面代码的decode()函数

    0x05 收获

    逆向真好玩.
    pwn是不可能pwn的, 这辈子都不可能pwn的, 漏洞又找不到, 找到了漏洞又不会利用, 只有靠看wp勉强明白题目什么意思这样子


    [suctf_2018] Enigma

    相关文章

      网友评论

          本文标题:[suctf_2018] Enigma

          本文链接:https://www.haomeiwen.com/subject/dhmzjftx.html