比赛中遇到的一些简单的逆向题目
2022-10-8 17:5:57 Author: Tide安全团队(查看原文) 阅读量:16 收藏

0x01 easycrackme

昨天小伙伴发来一个逆向题目让帮忙做一下,拿到程序是个elf文件,先放到kali里面运行一下,题目提示要输入一个key

image-20220810134953307

可以看到程序一共有6关,通过这6关拿到flag

先来看下第一关

size_t __fastcall check1(const char *a1)
{
  size_t result; // [email protected]
  size_t v2; // [sp+18h] [bp-8h]@1

  puts("=== 关卡 1 ===");
  v2 = strlen(a1);
  if ( a1[v2 - 1] == 10 )
    a1[v2 - 1] = 0;
  result = strlen(a1);
  if ( result != 34 )
  {
    puts("-- 通关失败");
    exit(1);
  }
  return result;
}

简单分析一下,第一关只是对长度进行检查,长度应该为34才对,输入一个长度为34的字符串试试。

第一关通关成功,再来看看第二关。

int __fastcall check2(const char *a1)
{
  int result; // [email protected]

  puts("=== 关卡 2 ===");
  result = strncmp(a1, "flag{", 5uLL);
  if ( result )
  {
    puts("-- 通关失败");
    exit(1);
  }
  return result;
}

第二关是对输入字符串的前5个字符进行校验,如果前5个字符为flag{就会通关

来试一下

第二关通关成功,来看下第三关

__int64 __fastcall check3(const char *a1)
{
  __int64 result; // [email protected]

  puts("=== 关卡 3 ===");
  result = a1[strlen(a1) - 1];
  if ( (_BYTE)result != 125 )
  {
    puts("-- 通关失败");
    exit(1);
  }
  return result;
}

第三关是取最后一位字符串和125进行比较,125是}的ascii码,所以第三关是判断最后一位字符串是不是}

来试一下

第三关通关成功,继续看下第四关

void __fastcall check4(const char *a1)
{
  const char v1; // [sp+16h] [bp-2Ah]@2
  const char v2; // [sp+17h] [bp-29h]@8
  unsigned __int64 i; // [sp+18h] [bp-28h]@1
  char *v4; // [sp+28h] [bp-18h]@1
  char *s1; // [sp+38h] [bp-8h]@1

  puts("=== 关卡 4 ===");
  v4 = (char *)(strchr(a1, 95) - a1);           // 95 == _
  s1 = (char *)malloc(((unsigned __int64)(v4 - 5) >> 1) + 1);
  for ( i = 0LL; i < (unsigned __int64)(v4 - 5) >> 1; ++i )
  {
    v1 = a1[2 * i + 5];
    if ( v1 <= 47 || v1 > 57 )                  
    {
      if ( v1 > 96 && v1 <= 102 )               
        v1 -= 87;                               
    }
    else
    {                                           
      v1 -= 48;                                 
    }
    v2 = a1[2 * i + 6];
    if ( v2 <= 47 || v2 > 57 )
    {
      if ( v2 > 96 && v2 <= 102 )
        v2 -= 87;
    }
    else
    {
      v2 -= 48;
    }
    s1[i] = v2 | 16 * v1;
  }
  s1[(unsigned __int64)(v4 - 5) >> 1] = 0;
  if ( strcmp(s1, "olympics") )
  {
    puts("-- 通关失败");
    exit(1);
  }
  free(s1);
}

第四关稍微复杂一点,整体程序前三关是对输入字符串的格式进行判断,后三关是对输入的字符串内容进行判断,后三关分别对应着三串字符串,通过_连接,拼接起来得到flag

第四关就是第一个字符串,首先程序会找到字符串中_的位置,然后根据_的位置作为循环的长度进行处理,最终经过处理的字符串和olympics进行比较,如果相等就通关,如果不相等就输出通关失败。

对字符串处理的关键代码在for循环内,简单分析一下for循环是对字符串中第5位到第20位进行处理,s1就是要比较的字符串,分别取单数字符的ascii和双数字符的ascii乘16再进行位运算,写个脚本爆破一下

str = 'olympics'

for i in str:
    for v1 in range(33, 127):
        for v2 in range(33, 127):
            count = v1
            if count <= 47 or count > 57:
                if count > 96 and count <= 102:
                    count -= 87
            else:
                count -= 48

            if v2 <= 47 or v2 > 57:
                if v2 > 96 and v2 <= 102:
                    v2 -= 87
            else:
                v2 -= 48

            if (v2 | 16 * count) == ord(i):
                print(i,ord(i),'v1=', chr(v1), v1, 'v2=', chr(v2), v2)

爆破得到符合条件的解不止一个,所以这道题目应该都多个flag,随便找一个符合条件的解运行一下看看

可以看到已经通关,继续看下第五关

void __fastcall check5(const char *a1)
{
  char *v1; // [email protected]
  char v2; // [email protected]
  unsigned __int64 i; // [sp+18h] [bp-28h]@1
  signed __int64 v4; // [sp+20h] [bp-20h]@1
  char *v5; // [sp+28h] [bp-18h]@1
  char *s1; // [sp+38h] [bp-8h]@1

  puts("=== 关卡 5 ===");
  v1 = strchr(a1, 95);
  v4 = v1 + 1 - a1;
  v5 = (char *)(strchr(v1 + 1, 95) - a1);
  s1 = (char *)malloc((size_t)&v5[-v4 + 1]);
  for ( i = 0LL; i < (unsigned __int64)&v5[-v4]; ++i )
  {
    if ( i & 1 )
      v2 = 33;
    else
      v2 = 32;
    s1[i] = *(&a1[i] + v4) ^ v2;
  }
  s1[2 * (_QWORD)&v5[-v4]] = 0;
  if ( strcmp(s1, "in") )
  {
    puts("-- 通关失败");
    exit(1);
  }
  free(s1);
}

第五关比较简单,就是两个字符串分别与32和33进行异或得到in,所以正确的字符串应该是IO,在这里就出现问题了,不知道我的电脑什么原因,输入正确的字符串无法通关第五关

这时我以为是我做错了,要到了别人的wp,仔细看了一下没什么问题,只是第四关解的方式不一样,可是我心想第四关也和第五关没关系啊,让朋友在他的电脑上试一下,发现是可以的,后来我又换了Ubuntu试了一下发现是可以的,不知道为啥

那继续第六关

__int64 __fastcall check6(const char *a1)
{
  char *v1; // [email protected]
  char *v2; // [email protected]
  char *v3; // [email protected]
  signed __int64 v4; // [email protected]
  signed __int64 v5; // [email protected]
  __int64 v6; // [email protected]
  __int64 v7; // [email protected]
  int v8; // [email protected]
  int v9; // [email protected]
  char *s; // [sp+8h] [bp-1A8h]@1
  int v12; // [sp+1Ch] [bp-194h]@12
  signed __int64 v13; // [sp+20h] [bp-190h]@4
  signed __int64 v14; // [sp+28h] [bp-188h]@4
  unsigned __int64 v15; // [sp+30h] [bp-180h]@11
  __int64 v16; // [sp+38h] [bp-178h]@11
  signed __int64 v17; // [sp+40h] [bp-170h]@1
  signed __int64 v18; // [sp+48h] [bp-168h]@1
  char *s1; // [sp+58h] [bp-158h]@8
  int v20[82]; // [sp+60h] [bp-150h]@8
  __int64 v21; // [sp+1A8h] [bp-8h]@1

  s = (char *)a1;
  v21 = *MK_FP(__FS__, 40LL);
  puts("=== 关卡 6 ===");
  v1 = strchr(a1, 95);
  v2 = strchr(v1 + 1, 95);
  v17 = v2 + 1 - a1;
  v3 = strchr(v2 + 1, 125);
  v18 = v3 - a1;
  if ( ((_BYTE)v3 - (_BYTE)a1 - (_BYTE)v17) & 3 )
  {
    puts("-- 通关失败");
    exit(1);
  }
  v13 = 3 * ((unsigned __int64)(v18 - v17) >> 2);
  v14 = v18 - v17;
  while ( 1 )
  {
    v4 = v14--;
    if ( !v4 || *(&a1[v14] + v17) != 61 )
      break;
    --v13;
  }
  s1 = (char *)malloc(v13 + 1);
  v5 = 40LL;
  v6 = (__int64)v20;
  v7 = (__int64)">";
  while ( v5 )
  {
    *(_QWORD *)v6 = *(_QWORD *)v7;
    v7 += 8LL;
    v6 += 8LL;
    --v5;
  }
  v15 = 0LL;
  v16 = 0LL;
  while ( v15 < v13 )
  {
    v12 = (v20[*(&s[v15] + v17) - 43] << 6) | v20[*(&s[v17 + 1] + v15) - 43];
    if ( *(&s[v17 + 2] + v15) == 61 )
      v8 = v12 << 6;
    else
      v8 = (v12 << 6) | v20[*(&s[v17 + 2] + v15) - 43];
    if ( *(&s[v17 + 3] + v15) == 61 )
      v9 = v8 << 6;
    else
      v9 = (v8 << 6) | v20[*(&s[v17 + 3] + v15) - 43];
    s1[v16] = v9 >> 16;
    if ( *(&s[v17 + 2] + v15) != 61 )
      s1[v16 + 1] = BYTE1(v9);
    if ( *(&s[v17 + 3] + v15) != 61 )
      s1[v16 + 2] = v9;
    v15 += 4LL;
    v16 += 3LL;
  }
  if ( strcmp(s1, "china") )
  {
    puts("-- 通关失败");
    exit(1);
  }
  free(s1);
  return *MK_FP(__FS__, 40LL) ^ v21;
}

第六关看到字符串china,有了前两关的经验猜测是处理后的字符串和china进行比较,但是看这个代码比较复杂啊,这里说实话一开始没看到,看到别人的wp说这里有左移6和等号等字符,判断是base64的解码(看来自己的知识储备还是不够,这段代码要是猜不出来是base64解码再去分析要浪费很多时间了),所以将china进行base64编码后得到flag

题目到这已经得到了flag,应该已经结束了,但是看了其他人的wp发现第四关检查的应该是olympics的十六进制6f6c796d70696373,在上边用脚本爆破的时候就说过了第四关应该有很多个解,所以说这个题目出的很有问题,虽然不是一道很好的题目,但是还是记录一下。

0x02 freestyle

第二届网刃杯网络安全大赛题目

查看伪代码,主函数中发现两个功能函数

__int64 fun1()
{
  char s[24]; // [rsp+0h] [rbp-20h] BYREF
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Welcome to Alaska!!!");
  puts("please input key: ");
  fgets(s, 20, stdin);
  if ( 4 * (3 * atoi(s) / 9 - 9) != 4400 )
    exit(0);
  puts("ok,level_1 over!\n\n");
  return 1LL;
}

__int64 fun2()
{
  char s[24]; // [rsp+0h] [rbp-20h] BYREF
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Welcome to Paradise Lost!!!");
  puts("The code value is the smallest divisible");
  puts("please input key: ");
  fgets(s, 20, stdin);
  if ( 2 * (atoi(s) % 56) != 98 )
    exit(0);
  puts("ok,level_2 over!");
  return 1LL;
}

分析代码得到,只是简单的数学运算,得到fun1的值为3327,fun2的值为105

题目提示flag是md5格式,也就是3327105的md5值

flag{31a364d51abd0c8304106c16779d83b1}

0x03 Re_function

第二届网刃杯网络安全大赛题目

题目比赛的时候没做出来,拿到手是个压缩吧,有密码,一开始爆破没成功,他这个其实是压缩包后面跟着一串十六进制数据,里面是压缩包的密码,这个也是后来看别人的wp才知道的,一开始也用十六进制编辑器打开看了,也发现了后面的十六进制数据,当时没多想。

image-20220425165501189

png的文件头

把数据复制出来,在线解密网站

得到一半图片,密码为3CF8

解压后得到一个exe和一个elf文件

先分析下exe运行一下是要输入一个flag,看了看伪代码,没看明白

image-20220810135105777

经过分析发现一个main函数,但是没法反编译,后来看了很多的wp直说是换表的base64,但是还不是很理解。

去OD进行动调看看

image-20220505135822643

搜索字符串直接断到输入的位置,并且根据OD给出的提示发现字符串长度为28位,F8继续调试

image-20220505135512928

找到对输入字符串进行处理的位置,这里是每隔2位,把输入的字符串和0x37进行xor,F8继续调试

image-20220505135940440

在这里可以看到处理后输入的字符串和要进行对比的28位字符串,前边判断是每隔2位和0x37进行xor,写脚本还原之前的字符串

str = [0x64, 0x71, 0x54, 0x54, 0x64, 0x78, 0x74, 0x78, 0x64, 0x41, 0x40, 0x48, 0x70, 0x6D, 0x18, 0x4A, 0x41, 0x78, 0x66, 0x72, 0x41,0x78, 0x5E, 0x4E, 0x5D, 0x52, 0x0E]

for i in range(0,len(str),2):
    str[i] ^= 0x37
print(bytes(str))
# SqcTSxCxSAwHGm/JvxQrvxiNjR9

其实到这里这个exe就已经分析完了,打开elf文件,找到换的解密表进行解密就可以了

image-20220810135152058

这里是更换的表,在线解密

也可以通过python脚本进行解密

import base64
a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'    #标准表
b = 'FeVYKw6a0lDIOsnZQ5EAf2MvjS1GUiLWPTtH4JqRgu3dbC8hrcNo9/mxzpXBky7+'    #新表
c = 'SqcTSxCxSAwHGm/JvxQrvxiNjR9='
trantab = c.maketrans(b, a)
print(base64.b64decode(c.translate(trantab)))

flag{we1come_t0_wrb}

0x04 眼力大考验

2022年蓝贝国际创新创业大赛“数字技术+信息安全领域赛道网络攻防大赛”

拿到程序,无壳,主函数伪代码。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v3; // eax
  char v5; // dl
  char v6; // cl
  char v7; // bl

  __main();
  if ( argc == 2 )
  {
    v3 = argv[1];
    if ( *v3 == 51 && v3[1] == 54 && v3[2] == byte_403005 )// 62h
    {
      v5 = v3[3];
      if ( v5 == byte_403008 && v3[4] == 50 && v3[5] == 52 && v3[6] == 52 && v3[7] == 50 )
      {
        v6 = v3[8];
        if ( v6 == table )
        {
          v7 = v3[9];
          if ( v7 == byte_403007
            && v7 == v3[10]
            && v6 == v3[11]
            && v3[12] == 52
            && v3[13] == 57
            && v3[14] == 49
            && v3[15] == 48
            && v3[16] == 53
            && v3[17] == 51
            && v3[18] == 48
            && v3[19] == 50
            && v7 == v3[20]
            && v6 == v3[21]
            && v5 == v3[22]
            && v3[23] == 56
            && v5 == v3[24]
            && v7 == v3[25]
            && v3[26] == byte_403006
            && v3[27] == byte_403009
            && v3[28] == 50
            && v3[29] == 49
            && v6 == v3[30]
            && v3[31] == 48 )
          {
            printf("flag{%s}\n", v3);
          }
        }
      }
    }
  }
  else
  {
    printf("Usage: %s pass", *argv);
  }
  return 0;
}

很简单的代码逻辑,v3就是flag字符串,32位,根据主函数分析出字符串内容,但是v3[10]、v3[11]、v3[20]、v3[21]、v3[22]、v3[24]、v3[25]、v3[30]的值不知道。

分析出来字符串:36be2442ad 49105302 8 cf21 0

剩下的字符串只能去内存中找了,祭出OD,动调一下

image-20220715161808477

在内存中挨个找出缺少的字符串,然后拼接成flag:36be2442adda49105302dae8edcf21a0

flag{36be2442adda49105302dae8edcf21a0}

0x05 隐秘的角落

DASCTF2022.07赋能赛

拿到程序还是先运行一下

image-20220726233736856

程序是go写的,找到主函数main_main

void __cdecl main_main()
{
  __int64 v0; // rdi
  __int64 v1; // rsi
  __int64 v2; // r8
  __int64 v3; // r9
  __int64 v4; // [rsp+8h] [rbp-88h]
  _QWORD *v5; // [rsp+8h] [rbp-88h]
  _QWORD *v6; // [rsp+50h] [rbp-40h]
  _QWORD v7[2]; // [rsp+58h] [rbp-38h] BYREF
  _QWORD v8[2]; // [rsp+68h] [rbp-28h] BYREF
  __int64 v9[2]; // [rsp+78h] [rbp-18h] BYREF

  sync___ptr_WaitGroup__Add((__int64)&main_wg, 1LL);
  runtime_newobject((__int64)&unk_4B0DA0, v4);
  v6 = v5;
  v9[0] = (__int64)&unk_4B0DA0;
  v9[1] = (__int64)&off_4E9BB0;                 // hi,ctfer. give me a flag:
  fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v9, 1LL);
  v8[0] = &unk_4AE9C0;
  v8[1] = v6;
  fmt_Fscanf(
    v0,
    v1,
    (const char *)&go_itab__os_File_io_Reader,
    (__int64)v8,
    v2,
    v3,
    (__int64)&go_itab__os_File_io_Reader,
    os_Stdin,
    (__int64)"%s",
    2LL,
    (__int64)v8,
    1LL,
    1);
  runtime_newproc(0x10u, (char)&checkflag, *v6);
  v7[0] = &unk_4B0DA0;
  v7[1] = &off_4E9BC0;                          // Who am I? where am I? what am I doing?
  fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v7, 1LL);
  sync___ptr_WaitGroup__Wait((__int64)&main_wg);
}

针对go中的一些函数不太清楚,但是这个程序的主函数并不复杂,通过刚才运行程序时所出现的字符串加上主函数进行分析,应该关键点就在第36行checkflag函数中,跟进看一下

void __golang main_checkflag(__int64 a1, __int64 a2)
{
  char v2; // al
  __int64 v3; // [rsp+18h] [rbp-70h]
  char v4; // [rsp+18h] [rbp-70h]
  __int64 v5; // [rsp+20h] [rbp-68h]
  __int64 v6; // [rsp+28h] [rbp-60h]
  __int64 v7; // [rsp+30h] [rbp-58h]
  char v8[32]; // [rsp+40h] [rbp-48h] BYREF
  _QWORD v9[2]; // [rsp+60h] [rbp-28h] BYREF
  _QWORD v10[2]; // [rsp+70h] [rbp-18h] BYREF

  v3 = runtime_stringtoslicebyte((__int64)v8, a1, a2);
  main_Myencode(v3);
  if ( v5 == byte_55EA78 )
  {
    runtime_memequal((__int64)main_enc, v3, byte_55EA78, v3);
    v2 = v4;
  }
  else
  {
    v2 = 0;
  }
  if ( v2 )
  {
    v10[0] = &unk_4B0DA0;
    v10[1] = &off_4E9B90;                       // Yes,flag is: DASCTF{md5(Input)}
    fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v10, 1LL, 1LL, v6, v7);
  }
  else
  {
    v9[0] = &unk_4B0DA0;
    v9[1] = &off_4E9BA0;                        // No,Did you find me?
    fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v9, 1LL, 1LL, v6, v7);
  }
  sync___ptr_WaitGroup__Add((__int64)&main_wg, -1LL);
}

跟进继续分析发现,flag的值为输入值的md5

checkflag函数的内容也比较简单,关键点在于第14行main_Myencode函数,这个函数当时我在做这道题目的时候就简单看了看,没太注意,导致漏掉了关键的地方,跟进main_Myencode函数

__int64 __usercall [email protected]<rax>(__int64 a1, __int64 a2)
{
  __int64 v3; // [rsp+18h] [rbp-50h]
  __int64 v4; // [rsp+20h] [rbp-48h]
  char v5[32]; // [rsp+38h] [rbp-30h] BYREF
  __int64 v6; // [rsp+58h] [rbp-10h]

  v6 = runtime_makeslice((__int64)&unk_4B0EE0, a2, a2);
  v3 = runtime_stringtoslicebyte((__int64)v5, (__int64)main_enc_key, qword_55E898);
  crypto_rc4_NewCipher(v3, v4);
  crypto_rc4___ptr_Cipher__XORKeyStream(v3, v6, a2, a2, a1, a2);
  return a2;
}

从里面调用的函数名可以知道这是RC4算法,那么我们就需要找到密文和key,key在main_Myencode函数中第六行main_enc_key,找到key的值为thisiskkk

image-20220810135312238

这里有两种方式找到密文,第一种就是通过静态分析:

可以通过checkflag函数找到main_enc

image-20220810135344191

跟进unk_54df80,但是这里的数据还不是真正的密文,是加密前的数据

真正的密文要跟进main_inti_0函数,查看加密算法

signed __int64 __usercall [email protected]<rax>()
{
  _BYTE *v0; // rdx
  signed __int64 v1; // rbx
  signed __int64 result; // rax

  v0 = main_enc;
  v1 = *(_QWORD *)&byte_55EA78;
  for ( result = 0LL; result < v1; ++result )
  {
    if ( (unsigned __int64)result >= *(_QWORD *)&byte_55EA78 )
      runtime_panicIndex();
    *((_BYTE *)main_enc + result) = v0[result] ^ 0x23;
  }
  return result;
}

可以看到密文是和0x23进行xor后的数据,直接写脚本xor一下得到密文。

第二种方式是直接通过动调得到密文,密文所在位置是0x54df80

可以直接用gdb,在下一条命令的位置下断点,然后跳到0x54df80的位置查看内存信息

image-20220810105504148

运行到断点处,jump一下

image-20220810105558348

得到密文

image-20220810105616179

这里知道了密文和key,网上找了个解密脚本改了改

key = 'thisiskkk'
data = [0xFB, 0xC6, 0xA6, 0x9D, 0xC4, 0xDB, 0x7B, 0x56, 0xB6, 0x46,
0xA6, 0xC0, 0x85, 0x64, 0x7A, 0x9A, 0x37, 0x4C, 0x10, 0x96,
0xE9, 0xA7, 0x28, 0xC4, 0xB1, 0x2D, 0xF1, 0xDE, 0x47, 0x3B,
0xB5, 0xF3, 0x2C, 0x7D, 0x67, 0x1D]

s = [0] * 256
for i in range(256) :
    s[i] = i
print(s)
j = 0
for i in range(256) :
    j = (j + s[i] + ord(key[i % len(key)])) % 256
    print(j)
    s[i], s[j] = s[j], s[i]
i = 0
j = 0
res = ""
for c in data :
    i = (i + 1) % 256
    j = (j + s[i]) % 256
    s[i], s[j] = s[j], s[i]
    res = res + chr(c ^ s[(s[i] + s[j]) % 256])
print(res)

#56e83694-f976-11eb-b343-faffc201c8e0

image-20220810111540060

后开大佬给说了一下在线解密网站也可以解出

image-20220810132128630

DASCTF{9e1963bbbb1285b993c862a5a6f12604}

E

N

D

Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室,近三年来在网络安全技术方面开展研发项目60余项,获得各类自主知识产权30余项,省市级科技项目立项20余项,研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。对安全感兴趣的小伙伴可以加入或关注我们。



文章来源: http://mp.weixin.qq.com/s?__biz=Mzg2NTA4OTI5NA==&mid=2247500695&idx=1&sn=e10b03a841d5815f039f6914c81367c0&chksm=ce5de1f6f92a68e0f0c3648e0b075f17ac37f7a65dd1189d09b6e37436d873ca4aaa47bab2d0#rd
如有侵权请联系:admin#unsafe.sh