[suctf_2018] Enigma

2018-05-29  本文已影响32人  pu1p

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
上一篇下一篇

猜你喜欢

热点阅读