Flare-On逆向挑战赛(一)

2019-10-20 43474人围观 ,发现 4 个不明物体 其他

从2013年开始举办的Flare-On逆向挑战赛今年已经是第6届。今年的比赛共有12道题目,涉及Windows,.NET,Linux和Android上x86的各种架构。 此外,这也是Flare-On历史上的第一次比赛中出现NES ROM的题目。 该比赛是仅有的以Windows为中心的CTF竞赛之一。完成Flare-On逆向挑战也是笔者一直以来的目标之一,今年终于有时间去实现这个目标了。

MugatuWare

题目信息

题目下载解压之后共有5个文件,两个加密过的gif文件,best.gif.Mugatu和the_key_to_success_0000.gif.Mugatu。一个恶意软件制作者留下的txt文件

恶意软件Mugatuware.exe。题目下载链接:xxxxxxxxxx

分析

将exe文件拖入ida之后,并同时在x86dbg当中单步跟进调试的时候会发现它将程序的IAT表进行了修改,导致ida进行静态分析的时候看到调用的dll当中的函数与实际不符。当你用PEview查看程序原本的IMPORT Address Table会发现PE文件在执行到start函数的时候内存IAT表当中的kernel32.dll函数顺序已经被完全颠倒了,通过脚本修复ida当中出现的函数调用之后就能查看到程序原本的执行流程。

能正常的查看程序的执行流程了。题目获取了机器hostname,ip地址等信息存到了全局数据段当中

接着访问了http://twitrss.me/twitter_user_to_rss/?user=ACenterForAnts这个域名,将读取页面中的pubdate信息和获得的机器信息进行亦或加密。然后再通过网络方式将加密之后的结果以base64的形式POST到mugatu.flare-on.com这个URL,然后目前这个域名是没有办法被解析的。

如果修改dns解析让其指向自己的服务器IP就能看到和这个域名进行交互的整个过程,但是这里我没有这么做,而是选择继续动态调试,patch返回值以理解程序的执行流程。

如果POST获得了返回值就会调用sub_401894这个函数,函数中对POST访问之后获得的数据进行了base64解码并亦或了0x4d,然后比较了前面25个字节是否为”orange mocha frappuccino”,如果是的话,会将第25个字节开始的内容复制到417100地址中,然后在4015c0函数当中创建了一个新的线程执行sub_4017b7这个函数,这个函数当中会通过SetEvent发送一个信号,并通过WriteFile向”\.\mailslot\Let_me_show_you_Derelicte”这个mailslot当中发送了20个字节的数据。

目前为止还没有看到真正的加密过程在什么位置,看到的只是对网页进行访问发送并接收信息,所以接下来可以继续动态执行。执行到sub_401491函数当中会发现一些端倪,首先检查了当前程序加载的dll内容并复制到了目的地址当中,之后通过LoadLibrary和GetProcAddress函数解析了IAT表中导入函数的地址,然后将这个dll中kernel32.dll所有的导入函数地址颠倒顺序并取反存在了一个数组当中。

在后面的代码当中,程序往v27这个位置当中写入了如上图所示的奇怪内容。这个地方一开始我也没有猜出来是什么,直到之后在反编译的时候才发现这里实际上写的是指令。

push   0xdeadbeef
not    DWORD PTR [esp]
ret

push地址到栈上并对栈顶的内容进行了去反,个人认为之所以加上了取反这个操作应该是为了过杀毒软件的动态查杀。通过动态调试可以发现v27这个地址是后面要执行的dll的IAT当中跳转到的地址,所以是在这里修改了后面dll的IAT。之后在程序40151b位置通过call eax指令跳转到了新加载的dll的main函数执行。

.text:00401517 010                 push    0
.text:00401519 014                 push    edi
.text:0040151A 018                 push    esi
.text:0040151B 01C                 call    eax

我们需要做的是dump出程序重新加载出的dll进行分析,dump的地方应该是在LoadLibrary执行之前。dump完成之后修复这个dll就能够获得实际执行的程序,真正的勒索加密过程应该是在这个dll当中实现的。上图0x2ea0000开始的数据段为程序复制的dll程序代码,0x2f50000位置开始的数据段是被复制的真正的dll代码。接下来要做的是找到这个函数的关键加密代码位置,找这种关键代码比较基本的就是shift+f12找字符串的引用。这里有几个比较关键的字符串,一个是”\.\mailslot\Let_me_show_you_Derelicte”这个mailslot名字的字符串,一个是Event的名字F0a1M。通过这样的字符串我们可以很容易的定位到关键的代码位置。

除此以外在dump出的dll当中还有一个没有去掉符号的函数,名字叫做BlueSteel。直接查看这个函数也能很快定位到这个关键位置。逻辑大概是这样的,当接收到LAtt3这个事件发出的信号量之后会调用ReadFile从”\.\mailslot\Let_me_show_you_Derelicte”这个mailslot名字的mailslot当中读取20个字节。读取完成了之后调用sub_1229这个函数,函数当中遍历所有的Driver和里面的文件,当发现文件名是以gif结尾的时候会调用sub_1000函数进行加密。

加密使用的函数是传入的参数a3,动态调试的时候能够发现实际上调用的是sub_16b9地址的函数。

int __cdecl xtea_encrypt(int count, unsigned int *a2, int a3)
{
  [...]
  v0 = *a2;
  v1 = a2[1];
  sum = 0;
  do
  {
    v6 = sum + *(unsigned __int8 *)((sum & 3) + a3);
    sum -= 0x61C88647;
    v0 += v6 ^ (v1 + ((v1 >> 5) ^ 16 * v1));
    result = sum + *(unsigned __int8 *)(((sum >> 11) & 3) + a3);
    v1 += result ^ (v0 + ((v0 >> 5) ^ 16 * v0));
    --count;
  }
  while ( count );
  *a2 = v0;
  a2[1] = v1;
  return result;
}

解决

这个加密函数第一眼看上去就是一个xtea加密,所以我直接重命名为xtea_encrypt了(然后之后却被这个重命名给坑了)。因此,当执行到这里的时候我们可以断定这个位置使用的是xtea进行加密,但是并不知道加密使用的key是什么。重新查看题目文件夹当中的文件可以发现其中一个加密过的gif名称为the_key_to_success_0000.gif.Mugatu,那么可以尝试一下让key为[0, 0, 0, 0]。发现能够成功的解密出图片

接下来就是真正坑的地方。一直以为这个题目应该是正常的xtea加密,然后知道了第一个key的byte是0×31。所以我爆破了一天一夜剩下的15个字节,但是并没有爆破出来。直到两天后重新查看这个加密函数的时候才发现这个加密算法当中存在问题,并不是标准的xtea加密,进行加法操作的时候把k强制转换成为了 unsigned __int8 *类型,这就意味着key实际上只有4个字节。由于告诉了我们第一个字节,所以只需要爆破剩下的3个字节就好。

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t key[4]) {  
    unsigned int i;  
    uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;  
    for (i=0; i < num_rounds; i++) {  
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);  
        sum -= delta;  
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);  
    }  
    v[0]=v0; v[1]=v1;  
}  
int main()  
{
    char *filename = "C:\\Users\\test\\Documents\\Visual Studio 2012\\Projects\\Project1\\Debug\\best.gif.Mugatu";
    char output[] = "output.gif";
    uint32_t v[2] = {};  
    uint32_t k[4] = {0x31, 0x73, 0x35, 0xb1};  
    unsigned int r=32;//num_rounds建议取值为32  
    int size, count;
    // v为要加密的数据是两个32位无符号整数  
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位  
    FILE *fp, *fp1;
    fp = fopen(filename, "rb+");
    fp1 = fopen(output, "wb+");
    HANDLE handle = CreateFile(filename, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
    if(handle != INVALID_HANDLE_VALUE){
        size = GetFileSize(handle, NULL);
        printf("%d\n", size);
        CloseHandle(handle);
    }
    count = size / 8;
    for(int i = 0; i < count; i++){
        fread(v, 1, 8, fp);
        decipher(0x20, v, k);
        fwrite(v, 1, 8, fp1);
    }
    fclose(fp);
    fclose(fp1);
    return 0;
}

get the flag!

*本文原创作者:Kriston,本文属于FreeBuf原创奖励计划,未经许可禁止转载

发表评论

已有 4 条评论

取消
Loading...

这家伙太懒,还未填写个人描述!

63 文章数 6 评论数

特别推荐

推荐关注

活动预告

填写个人信息

姓名
电话
邮箱
公司
行业
职位
css.php