美文网首页
攻防世界进阶Re(二)

攻防世界进阶Re(二)

作者: Adam_0 | 来源:发表于2019-07-07 13:33 被阅读0次

    感觉难度越来越大了,把进阶题分为几篇来写,以便查看。

    0x00 reverse-box

    感觉都是没有看到过的题型,看了大佬WP,还是不懂。。。先留着以后再看。
    要用GDB命令脚本先放个链接如何写gdb命令脚本

    0x01 IgniteMe

    这道题就非常的友好了,拖进IDA。


    image.png

    检查输入的前四个字符为 ' EIS{ ' 最后一个为 ‘ } ’,然后进入sub_4011c0() 关键函数。
    这个函数就是交换大小写,然后异或操作之后看与最后的字符串一不一样。


    image.png
    首先双击unk_4420B0选中,按shift+e 把它提取出来(新学的操作,好舒服啊!终于不用手动码了)。
    然后就是写脚本异或回去得到v4了,在转换大小写加上 EIS{ 和 } 就是flag了。
    image.png

    脚本如下:

    a = "GONDPHyGjPEKruv{{pj]X@rF"
    xor_string = [ 0x0D, 0x13, 0x17, 0x11, 0x02, 0x01, 0x20, 0x1D, 0x0C, 0x02, 
                   0x19, 0x2F, 0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16, 0x09, 0x0F, 
                   0x15, 0x27, 0x13, 0x26, 0x0A, 0x2F, 0x1E, 0x1A, 0x2D, 0x0C, 
                   0x22, 0x04]
    s = []
    flag = ''
    for i in range(len(a)):
        s.append(ord(a[i])^ xor_string[i])
    for i in range(len(s)):
        s[i] -= 72
        s[i] = s[i]^0x55
    for i in s:
        flag += chr(i)
    print(flag.lower())
    

    0x02 srm-50

    这道题感觉我是非预期解啊,都不分析什么,正解应该是用OD破解找到注册码的。我直接拖进IDA,F12找到报错的字符串,看到if判断语句,将v11[0],v11[1],v11[2],v11[3],v12,v13…………连续字符得到就是flag了。。。。。。


    image.png

    0x03 ReverseMe-120

    拖进IDA,看到主函数感觉很简单的亚子。我们看到最后只判断了v13是否与“you_know_how_to_remove_junk_code” 一样,往上分析v13经过了sub_401000()然后与0x25异或。


    image.png

    跟进sub_401000(),分析不出来是什么,,,看了大佬的WP ,这是base64的解密,还是太菜了,没有看出特征。所以将“you_know_how_to_remove_junk_code” 与0x25异或后再base64加密得到flag。


    image.png

    0x04 CRACKME

    运行exe,要调入注册码,随便输入有弹窗错误。拖进IDA,发现是MFC写的程序。参考大佬文章,MessageBoxA函数是于创建、显示并操作一个消息对话框的,所以我们找到调用MessageBoxA的函数。


    image.png

    跟进sub_401720 和sub_4016E0发现不了什么,继续查看他们的引用。在这里就可以猜测这两个MessageBoxA一个是显示错误弹窗一个是正确弹窗。输入的数据经过sub_401630()之后才判断对错。所以跟进sub_401630()分析。


    image.png
    这里看了WP有点不懂,为什么伪随机数种子一直都是1 ,伪随机数不变。继续分析 if 里面,就是每隔10个字符判断是否相等。
    image.png

    这里将一个很长的字符串复制给v2+96,看WP说v2就是上面的v3,这一点也有些疑惑,没有看出来。也许是this指针的原因吧。接下来这个长字符串从第二个字符开始到330位,每隔10位取出一个字符。


    image.png
    脚本如下:
    a = ";f1K3{c5:efl21t4;1t1zaxpim9}5+?gtux;=vc9v{v7+buhU{bT=-am2q}=fh[xk{y?xrqe{?}l5-sd2-Mo+:j{9=sY[dalvpx?z3{?no{[k5ll{zjsu5[kfla+r6Zg72o0skq6cGl5cw[=d?3v9q5-vkjSv{4sqtg=f0cz{+jurjfl[tb]lrfF1;2}udhb?0g8{om:T4dh;z:oz-Dn=m=ux;o[gs9{+zqx+sq-dsxctcvykUs2oddrt43pwv:f0;njkrb9los6g0{ih?rqantfx$sslqd:rvqixr;j{?o:sn+[i[yA11;gsmr8lm0?3};+iv+Tf:4Gtv2:-20upi0]7?77=;qzx{m-W;0vtueh]ko8d?=w:fbhd{E:;19?p=k:b+}doht6wpEq-z]2qbV1}dh416qw9:xm[;ed;:ecb-0:ni-s4u2kf6]2wn45amzjrun=ofkx-=hmgo-lz;j909=rmo7xcj4le0hxs[i]-vjl[?o12:sv4upio7ma1hRy7556+57krev:hLQ+1cx65z5v5];6n=[p83;n={zm{k2p"
    for i in range(1,330,10):
       print(a[i],end='')
    
    

    0x05 tt3441810

    这道题看不懂,以为是手动脱壳。搜了官方WP,也是一脸懵逼,这是杂项题吗?
    用IDA或者notepad++打开看到十六进制数,在这里看到 ‘fl’ ,{ } 等字样,然后把一些混淆字符去掉得到flag,还得提交括号里面的内容。又看了里面自带的WP,还是学到了xxd使用。
    flag{poppopret}

    image.png

    0x06 zorropub

    先拖进IDA,分析下程序流程,题的意思很简单,主要是输入饮料的数量和饮料ID经过下面函数在计算MD5,判断其MD5是否相等。

    
      v15 = __readfsqword(0x28u);
      seed = 0;
      puts("Welcome to Pub Zorro!!");
      printf("Straight to the point. How many drinks you want?", a2);
      __isoc99_scanf("%d", &v5);
      if ( v5 <= 0 )
      {
        printf("You are too drunk!! Get Out!!", &v5);
        exit(-1);
      }
      printf("OK. I need details of all the drinks. Give me %d drink ids:", (unsigned int)v5);
      for ( i = 0; i < v5; ++i )
      {
        __isoc99_scanf("%d", &v6);   //循环输入饮料ID
        if ( v6 <= 16 || v6 > 0xFFFF )  //饮料ID在 [16,65535]范围内。
        {
          puts("Invalid Drink Id.");
          printf("Get Out!!", &v6);
          exit(-1);
        }
        seed ^= v6;   //随机种子
      }
      i = seed;
      v9 = 0;
      while ( i )
      {
        ++v9;
        i &= i - 1;
      }
      if ( v9 != 10 )
      {
        puts("Looks like its a dangerous combination of drinks right there.");
        puts("Get Out, you will get yourself killed");
        exit(-1);
      }
      srand(seed);
      MD5_Init((__int64)&v10);
      for ( i = 0; i <= 29; ++i )
      {
        v9 = rand() % 1000;
        sprintf(&s, "%d", v9);
        v3 = strlen(&s);
        MD5_Update((__int64)&v10, (__int64)&s, v3);
        v12[i] = v9 ^ LOBYTE(dword_6020C0[i]);   //LOBYTE()得到一个16bit数最低(最右边)那个字节
      }
      v12[i] = 0;
      MD5_Final(v11, &v10);
      for ( i = 0; i <= 15; ++i )
        sprintf(&s1[2 * i], "%02x", (unsigned __int8)v11[i]);
      if ( strcmp(s1, "5eba99aff105c9ff6a1a913e343fec67") )
      {
        puts("Try different mix, This mix is too sloppy");
        exit(-1);
      }
      return printf("\nYou choose right mix and here is your reward: The flag is nullcon{%s}\n", v12);
    }
    

    这里我们有两种思路,1.爆破MD5,2.逆向算法。
    1.爆破MD5:
    先根据饮料ID范围在 [16,65535],和下面check来从中筛选出符合ID的值。

     while ( i )
      {
        ++v9;
        i &= i - 1;
      }
      if ( v9 != 10 )
    

    在使用subprocess库,(想使用pwntools的,结果没有搜到pwntools如何杀死进程的函数,看了大佬文章才知道还有subprocess的),将满足ID的数使用subprocess里的communicate()带进去试一试,看能否返回flag。
    脚本如下:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    from subprocess import *
    
    #这里是找出符合条件的数
    a=[]
    for i in range(16,65535):
        v9 = 0
        s=i
        while i:
            v9 +=1
            i &= i-1
        if v9 == 10:
            a.append(s)
    #循环输入符合条件的数,爆破flag
    for i in a:
        proc = Popen(['./zorro_bin'],stdin=PIPE,stdout=PIPE)
        out = proc.communicate(('1\n%s\n' % i).encode('utf-8'))[0]#这里communicate返回的是一个元组,但是元组只有一个元素,所以要加上偏移0。
        if "nullcon".encode('utf-8') in out:
          print(out)
          print(i)
    
    image.png

    2.逆向算法
    这是在官方WP复制的。利用libc.so.6动态链接库得到生成的伪随机数。然后对着IDA的函数复现一遍。得到结果也是输入59306的时候正确。

    import ctypes
    import os
    import sys
    import hashlib
    libsystem = ctypes.CDLL('libc.so.6')
    # Extracted by hand
    encryption_key = [
        0x03C8, 0x0032, 0x02CE, 0x0302, 0x007F,
        0x01B8, 0x037E, 0x0188, 0x0349, 0x027F,
        0x005E, 0x0234, 0x0354, 0x01A3, 0x0096,
        0x0340, 0x0128, 0x02FC, 0x0300, 0x028E,
        0x0126, 0x001B, 0x032A, 0x02F5, 0x015F,
        0x0368, 0x01EB, 0x0079, 0x011D, 0x024E
    ]
    need_md5 = '5eba99aff105c9ff6a1a913e343fec67'
    mc = 0
    while True:
        for drink_id_count in range(17, 0xFFFE):
            mc += 1
            if mc % 1000 == 0:
                sys.stdout.write('.')
                sys.stdout.flush()
            for counter in range(5):
                input_1 = counter
                seed = 0
                drink_ids = []
                for x in range(input_1):
                    drink_id = drink_id_count
                    drink_ids.append(drink_id)
                    if drink_id <= 16 or drink_id > 0xFFFF:
                        continue
                    else:
                        seed ^= drink_id
                count = seed
                some_num = 0
                while count > 1:
                    some_num += 1
                    count &= (count - 1)
                if some_num != 10:
                    continue
                else:
                    pass
                libsystem.srand(seed)
                flag = ""
                h = hashlib.md5()
                for x in range(30):
                    ran = libsystem.rand()
                    rand_number = ran % 1000
                    h.update("%d" % rand_number)
                    flag += chr((rand_number ^ encryption_key[x])&0xFF)#LOBYTE()得到一个16bit数最低(最右边)那个字节,所以   & 0xff
                if h.hexdigest() == need_md5:
                    print "\nHash -> %s" % h.hexdigest()
                    print "Found it! Drinks:%d, Drink IDs:%s" % (counter, drink_ids)
                    raw_input()
    
    

    0x07 Reversing-x64Elf-100

    IDA打开,主函数。分析得到主要逻辑函数为sub_4006FD(),非常简单的逻辑,写脚本得到flag。

    signed __int64 __fastcall sub_4006FD(__int64 a1)
    {
      signed int i; // [rsp+14h] [rbp-24h]
      const char *v3; // [rsp+18h] [rbp-20h]
      const char *v4; // [rsp+20h] [rbp-18h]
      const char *v5; // [rsp+28h] [rbp-10h]
    
      v3 = "Dufhbmf";
      v4 = "pG`imos";
      v5 = "ewUglpt";
      for ( i = 0; i <= 11; ++i )
      {
        if ( (&v3)[i % 3][2 * (i / 3)] - *(char *)(i + a1) != 1 )
          return 1LL;
      }
      return 0LL;
    }
    
    脚本如下:
    arr = [
           ['D','u','f','h','b','m','f'],
           ['p','G','`','i','m','o','s'],
           ['e','w','U','g','l','p','t']
          ]
    for i in range(12):
        key = arr[i%3][2*int(i/3)] 
        print(chr(ord(key)-1),end='')
        
    

    0x08 gametime

    这道题主要靠细心,先运行一下看看,在拖进IDA,分析下逻辑。这个游戏就是靠手快,刚好出现规定字符时,按一下对应字符进入下一关,越来越快,这单身一辈子也没有这手速啊。。所以拖进OD,找到些关键跳转下断点,耐心的调试耐心的调试耐心的调试耐心的调试,就出来key了。

    这里总结下OD的使用:

    • 找到关键字符,在其上方第一个跳转一般为关键跳转,越过正确字符的跳转为关键跳转。
    • 在关键跳转处下断点,运行到断点时看将要跳转到哪里去,在判断是否跳转,比如有几个跳转都将向同一个地址跳转,那么这个跳转就改成不跳转,因为游戏出错有一点错误就退出,所以这里一定是指向错误的输出函数,正确的只有一条路,不可能有多个跳转指向它。
    • 遇到JMP跳转一般不要改,因为这个是无条件跳转,无论你输入的正确与否,它都将跳转。
    • 出现需要循环运行时,不用一个个的按F8,在跳转的下面一行下断点,然后F9直接运行,将会断在刚刚下断点处。
    • 边调试边看运行结果,看是否正确输出。
    image.png

    0x09 easyre-153

    首先UPX脱壳。IDA打开,看到pipe()和fork()。
    pipe函数可用于创建一个管道,以实现进程间的通信。
    fork函数通过系统调用创建一个与原来进程几乎完全相同的进程,其返回值是进程号。

    这里的逻辑就是输入一个进程号要与之前的进程号相等。就显示正确。 image.png
      v8 = __readgsdword(0x14u);
      pipe(pipedes);
      v5 = fork();
      if ( !v5 )
      {
        puts("\nOMG!!!! I forgot kid's id");
        write(pipedes[1], "69800876143568214356928753", 0x1Du);
        puts("Ready to exit     ");
        exit(0);
      }
      read(pipedes[0], &buf, 0x1Du);
      __isoc99_scanf("%d", &v6);
      if ( v6 == v5 )
      {
        if ( (*(_DWORD *)((_BYTE *)lol + 3) & 0xFF) == 204 )
        {
          puts(":D");
          exit(1);
        }
        printf("\nYou got the key\n ");
        lol(&buf);
      }
      wait(0);
      return 0;
    }
    

    但是这里就算输入正确,lol函数也会返回 'flag_is_not_here',所以我们需要使用IDA动态调试
    去修改汇编改变跳转得到flag,之前的进程号也可以随便输然后改变后面的关键跳转即可。

    image.png
    得到flag,要加上RCTF。太坑了,我以为我的不是flag,看了WP才知道。
    image.png

    相关文章

      网友评论

          本文标题:攻防世界进阶Re(二)

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