2019 上海市大学生网络安全大赛 RE部分WP

这次比赛就做了这一道逆向题,看到队友的WP,下面的对v10的加密方式为RC4,从我提取的v4数组就能够察觉出这是CR4了,自己傻乎乎的用OD调试,跟踪数据半天才做出来,还是见得的少了... ...下面有几篇不错的RC4的文章:

C语言实现https://zhoujianshi.github.io/articles/2016/RC4%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/index.html

Python实现https://specters.top/2019/03/01/Python%E5%AE%9E%E7%8E%B0RC4/

联系逆向解释https://kabeor.cn/RC4%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95%E5%8F%8A%E9%80%86%E5%90%91%E6%96%B9%E6%B3%95%E5%88%9D%E6%8E%A2/

文件下载:https://www.lanzous.com/i76vcne

1.准备

获取信息

  • 32位文件

2.IDA打开

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _DWORD *v4; // [esp+1Ch] [ebp-A0h]
  int v5; // [esp+20h] [ebp-9Ch]
  int v6; // [esp+24h] [ebp-98h]
  int v7; // [esp+28h] [ebp-94h]
  int v8; // [esp+2Ch] [ebp-90h]
  char v9; // [esp+30h] [ebp-8Ch]
  int v10; // [esp+50h] [ebp-6Ch]
  int v11; // [esp+70h] [ebp-4Ch]

  sub_402620();
  v5 = 1701148529;
  v6 = 101;
  v7 = 0;
  v8 = 0;
  v4 = malloc(0x408u);
  puts("Plz solve the puzzle:");
  sub_40ED00("%32s", &v9);
  if ( (unsigned __int8)sub_401C70(&v9)
    && (sub_401B60((int)&v10, &v9),
        sub_401850(v4, (int)&v5, strlen((const char *)&v5)),
        sub_4018D0(v4, &v10, 8),
        (unsigned __int8)sub_401950(&v10)) )
  {
    sub_401BA0(&v9, (int)&v11);
    sub_40ED20("Congrats!\n%s\n", &v11);
  }
  else
  {
    puts("Failed!");
  }
  return 0;
}

通过分析,要得到flag,则需要if中的条件成立

  if ( (unsigned __int8)sub_401C70(&v9)
    && (sub_401B60((int)&v10, &v9),
        sub_401850(v4, (int)&v5, strlen((const char *)&v5)),
        sub_4018D0(v4, &v10, 8),
        (unsigned __int8)sub_401950(&v10)) )

3.代码分析

打开sub_401C70(&v9)函数

int __cdecl sub_401C70(char *a1)
{
  char *v2; // edx

  if ( strlen(a1) != 16 )
    return 0;
  v2 = a1;
  while ( (unsigned __int8)(*v2 - 58) > 38u && (unsigned __int8)(*v2 - 48) <= 54u )// 输入的字符串每个字符都是‘a‘~‘f‘或者‘0‘~‘9‘之间
  {
    if ( ++v2 == a1 + 16 )                      // 遍历到字符串末尾结束,输入字符串长度为16
      return 1;
  }
  return 0;
}

3.1 暴力解v10

因为&&符号后面是( func1, func2, func3)形式的判断条件,因此我先直接看func3即可(func3决定条件是否成立),打开sub_401950(&v10)函数

bool __cdecl sub_401950(_BYTE *a1)
{
  _BYTE *v1; // ecx
  bool result; // al

  v1 = a1;
  while ( 2 )
  {
    switch ( *v1 )
    {
      case 0:
        dword_40F028 &= dword_40F038;
        dword_40F02C *= dword_40F028;
        goto LABEL_4;
      case 1:
        if ( !dword_40F02C )
          goto LABEL_6;
        dword_40F028 /= dword_40F02C;
        dword_40F024 += dword_40F034;
        goto LABEL_4;
      case 2:
        dword_40F030 ^= dword_40F034;
        dword_40F03C += dword_40F020;
        goto LABEL_4;
      case 3:
        dword_40F03C -= dword_40F030;
        dword_40F030 &= dword_40F024;
        goto LABEL_4;
      case 4:
        dword_40F034 *= dword_40F020;
        dword_40F02C -= dword_40F038;
        goto LABEL_4;
      case 5:
        dword_40F020 ^= dword_40F02C;
        dword_40F038 -= dword_40F03C;
        goto LABEL_4;
      case 6:
        if ( !dword_40F03C )
          goto LABEL_6;
        dword_40F034 |= dword_40F024 / dword_40F03C;
        dword_40F024 /= dword_40F03C;
        goto LABEL_4;
      case 7:
        dword_40F038 += dword_40F028;
        dword_40F034 |= dword_40F024;
        goto LABEL_4;
      case 8:
        dword_40F020 *= dword_40F02C;
        dword_40F030 -= dword_40F03C;
        goto LABEL_4;
      case 9:
        dword_40F028 += dword_40F034;
        dword_40F02C ^= dword_40F030;
LABEL_4:
        if ( ++v1 != a1 + 8 )
          continue;
        result = (dword_40F038 == 231)
               + (dword_40F034 == 14456)
               + (dword_40F030 == 14961)
               + (dword_40F02C == -13264)
               + (dword_40F028 == 16)
               + (dword_40F024 == 104)
               + (dword_40F020 == -951) == 7;
        if ( dword_40F03C != -239 )
          goto LABEL_6;
        break;
      default:
LABEL_6:
        result = 0;
        break;
    }
    return result;
  }
}

通过分析,这就是将v10的每一位进行switch选择,对一组数据进行更改,最后要满足要求,才能返回1。

因此,得到v10的值成为了这道题的关键,又因为v10的每个数都是0~9之间,且长度为8,我直接暴力求解。

#include <bits/stdc++.h>

#pragma warning(disable:4996)
#define _DWORD unsigned int
#define _BYTE char

using namespace std;

bool __cdecl sub_401950(_BYTE* a1)
{
    _BYTE* v1; // ecx
    bool result; // al

    int val_0 = 0x0A;
    int val_1 = 0x8A;
    int val_2 = 0x1A1;
    int val_3 = 0x12A;
    int val_4 = 0x269;
    int val_5 = 0x209;
    int val_6 = 0x68;
    int val_7 = 0x39F;
    int val_8 = 0x2C8;

    v1 = a1;
    while (2)
    {
        switch (*v1-48)
        {
        case 0:
            val_3 &= val_7;
            val_4 *= val_3;
            goto LABEL_4;
        case 1:
            if (!val_4)
                goto LABEL_6;
            val_3 /= val_4;
            val_2 += val_6;
            goto LABEL_4;
        case 2:
            val_5 ^= val_6;
            val_8 += val_1;
            goto LABEL_4;
        case 3:
            val_8 -= val_5;
            val_5 &= val_2;
            goto LABEL_4;
        case 4:
            val_6 *= val_1;
            val_4 -= val_7;
            goto LABEL_4;
        case 5:
            val_1 ^= val_4;
            val_7 -= val_8;
            goto LABEL_4;
        case 6:
            if (!val_8)
                goto LABEL_6;
            val_6 |= val_2 / val_8;
            val_2 /= val_8;
            goto LABEL_4;
        case 7:
            val_7 += val_3;
            val_6 |= val_2;
            goto LABEL_4;
        case 8:
            val_1 *= val_4;
            val_5 -= val_8;
            goto LABEL_4;
        case 9:
            val_3 += val_6;
            val_4 ^= val_5;
        LABEL_4:
            if (++v1 != a1 + 8)
                continue;
            result = (val_7 == 231)
                + (val_6 == 14456)
                + (val_5 == 14961)
                + (val_4 == -13264)
                + (val_3 == 16)
                + (val_2 == 104)
                + (val_1 == -951) == 7;
            if (val_8 != -239)
                goto LABEL_6;
            break;
        default:
        LABEL_6:
            result = 0;
            break;
        }
        return result;
    }
}

int main()
{
    char* s = (char*)malloc(10);

    for (long i = 10000000; i < 100000000; ++i) {
        sprintf(s, "%08ld", i);
        //cout << s << endl;
        if (sub_401950(s)) {
            cout << i << endl;
            system("PAUSE");
        }
    }
    system("PAUSE");
    return 0;
}

得到v10的值

3.2 v9的十六进制加密

得到v10值后,不能获得有关输入字符串的相关信息,打开前面的函数sub_401B60(&v10, &v9)

int __cdecl sub_401B60(int a1, _BYTE *a2)
{
  _BYTE *v2; // ebx
  int i; // esi
  char v4; // ST08_1
  _BYTE *v5; // ST00_4
  int result; // eax

  v2 = a2;
  for ( i = a1; *v2; result = sub_40ED40(v5, "%02X", v4) )
  {
    v4 = i;
    v5 = v2;
    v2 += 2;
    ++i;
  }
  return result;
}

能够猜测到,v9加密为十六进制,存储到v10中,(如果v9="0123456789abcdef",则v10=0x0123456789abcdef。通过后面OD调试,也能够发现。)

3.3 v4数组生成

打开sub_401850(v4, &v5, strlen((const char *)&v5))函数

int __cdecl sub_401850(_DWORD *a1, int a2, int a3)
{
  int v3; // eax
  int v4; // edi
  unsigned int *v5; // ebx
  int v6; // ecx
  int result; // eax
  unsigned int v8; // esi
  char v9; // dl
  unsigned int *v10; // edx

  v3 = 0;
  v4 = (int)(a1 + 2);
  *a1 = 0;
  a1[1] = 0;
  do
  {
    *(_DWORD *)(v4 + 4 * v3) = v3;
    ++v3;
  }
  while ( v3 != 256 );
  v5 = a1 + 2;
  v6 = 0;
  LOBYTE(result) = 0;
  do
  {
    v8 = *v5;
    v9 = *(_BYTE *)(a2 + v6++) + *v5;
    result = (unsigned __int8)(v9 + result);
    v10 = (unsigned int *)(v4 + 4 * result);
    *v5 = *v10;
    *v10 = v8;
    if ( v6 >= a3 )
      v6 = 0;
    ++v5;
  }
  while ( v5 != a1 + 258 );
  return result;
}

这里在生成V4数组,我从OD中提取出来

        0x00, 0x00, 0x71, 0x12,
        0x62, 0x31, 0x4D, 0x97,
        0x14, 0x0D, 0xED, 0xA3,
        0xD6, 0xFC, 0xF1, 0x3B,
        0x3C, 0x33, 0xB5, 0x22,
        0xA2, 0x1A, 0x17, 0x1D,
        0x98, 0x91, 0x06, 0x2A,
        0x8B, 0x23, 0xE6, 0x55,
        0x46, 0x3A, 0x65, 0x28,
        0x30, 0x39, 0xD4, 0x0C,
        0x01, 0x2D, 0x25, 0x10,
        0x09, 0x8F, 0x6A, 0x3F,
        0x44, 0xD8, 0x6D, 0xC5,
        0xA6, 0x72, 0x07, 0x83,
        0x40, 0xC6, 0x8E, 0x1F,
        0x77, 0x61, 0x96, 0x4A,
        0x08, 0xFE, 0x53, 0x5A,
        0xA1, 0xDF, 0xB6, 0x67,
        0x66, 0x5C, 0x57, 0xB8,
        0xD3, 0x11, 0x52, 0x21,
        0xCC, 0x56, 0x2E, 0xC2,
        0x88, 0xAA, 0xF9, 0x20,
        0x7A, 0x6F, 0x4E, 0x76,
        0xE8, 0xC1, 0xD5, 0xBD,
        0xCE, 0x9E, 0x38, 0x95,
        0x50, 0xF2, 0x9F, 0xB2,
        0x9A, 0x0B, 0x47, 0x16,
        0x60, 0xBF, 0xFD, 0x92,
        0x35, 0x89, 0xDA, 0xFF,
        0x9B, 0xBA, 0x13, 0xAB,
        0xF4, 0x79, 0x87, 0xAC,
        0x8C, 0x73, 0x84, 0xB3,
        0x0E, 0xC8, 0x26, 0xA5,
        0xE7, 0x15, 0xE9, 0xC3,
        0x69, 0x70, 0xE0, 0x68,
        0x42, 0x81, 0xCD, 0xEB,
        0xDE, 0x7D, 0xEF, 0xD0,
        0x24, 0x00, 0xF0, 0x41,
        0xA0, 0xEE, 0x05, 0x94,
        0x85, 0xBB, 0x43, 0x02,
        0xF7, 0xC0, 0xD1, 0x1B,
        0x7F, 0x5B, 0xEC, 0xF6,
        0x2B, 0x1E, 0xE2, 0x27,
        0xFB, 0x78, 0x54, 0x58,
        0xE4, 0x32, 0xDB, 0xB7,
        0xC7, 0x90, 0x7C, 0xF8,
        0x5D, 0x5F, 0x63, 0xBE,
        0x2C, 0x0A, 0xDD, 0x9C,
        0x75, 0x19, 0xC4, 0xA8,
        0x86, 0x36, 0xBC, 0x8D,
        0xD7, 0x7B, 0xB4, 0x5E,
        0x3E, 0xA7, 0xB1, 0xE1,
        0x59, 0x82, 0xB9, 0xAE,
        0xD9, 0x7E, 0xAF, 0xCF,
        0x9D, 0xF5, 0xFA, 0x48,
        0x4F, 0xA9, 0x6C, 0x64,
        0x6E, 0x49, 0x4B, 0x6B,
        0x29, 0x45, 0xE5, 0x04,
        0xA4, 0x4C, 0x34, 0x80,
        0xD2, 0x3D, 0xE3, 0x99,
        0x37, 0xDC, 0x93, 0xC9,
        0xCA, 0xCB, 0xEA, 0xB0,
        0x0F, 0x03, 0x8A, 0xF3,
        0x51, 0x1C, 0xAD, 0x74,
        0x18, 0x2F

v4数组

3.4 v10的加密与解密

打开 sub_4018D0(v4, &v10, 8)函数

_DWORD *__cdecl sub_4018D0(_DWORD *a1, _BYTE *a2, int a3)
{
  int v3; // edx
  int v4; // ecx
  int v5; // esi
  _BYTE *v6; // ebx
  int v7; // edi
  unsigned int *v8; // eax
  int v9; // edx
  _DWORD *v10; // ebp
  _DWORD *v11; // ST00_4
  unsigned int v12; // ebp
  _DWORD *result; // eax

  v3 = *a1;
  v4 = a1[1];
  v5 = (int)(a1 + 2);
  if ( a3 > 0 )
  {
    v6 = a2;
    v7 = *a1;
    do
    {
      v7 = (unsigned __int8)(v7 + 1);
      v8 = (unsigned int *)(v5 + 4 * v7);
      v9 = *v8;
      v4 = (unsigned __int8)(*v8 + v4);
      v10 = (_DWORD *)(v5 + 4 * v4);
      v11 = v10;
      v12 = *v10;
      *v8 = v12;
      *v11 = v9;
      *v6++ ^= *(_DWORD *)(v5 + 4 * (unsigned __int8)(v9 + v12));
    }
    while ( v6 != &a2[a3] );
    v3 = v7;
  }
  result = a1;
  *a1 = v3;
  a1[1] = v4;
  return result;
}

这里利用v4数组,对v10的每一位进行异或运算。

这个函数执行后,就是最后一个函数,刚刚我们暴力解出了61495072,v10 = ‘\x06\x01\x04\x09\x05\x00\x07\x02‘,因此我们能够对v10的每一位进行异或,得到v10加密前的值。

本来是准备利用提取出的v4,代入函数计算出v10的值,但是没成功,哈哈哈!后面发现v4的值实际上是固定的,不会受到输入的影响并且我只需要知道*v6异或的值即可。因此,我在OD中调试,记录异或的值。

0x7c, 0x0AB, 0x2D, 0x91, 0x2F, 0x98, 0x0ED, 0xA9

我们能够写出脚本

num = [0x7c, 0x0AB, 0x2D, 0x91, 0x2F, 0x98, 0x0ED, 0xA9]
v10 = ‘\x06\x01\x04\x09\x05\x00\x07\x02‘

flag = []
n = 0

for i in v10:
    flag.append(hex(ord(i) ^ num[n]))
    n = n + 1
print(flag)

[‘0x7a‘, ‘0xaa‘, ‘0x29‘, ‘0x98‘, ‘0x2a‘, ‘0x98‘, ‘0xea‘, ‘0xab‘]

组合起来,得到v9经过十六进制加密后的v10=0x7aaaa29982a98eaab

因此我们能够得到v9="7aaa29982a98eaab",输入到程序中得到flag。

4.get flag!

flag{5cb92582-66a8-e5b7-d3bf-3b99df8ac7f0}

原文地址:https://www.cnblogs.com/Mayfly-nymph/p/11802313.html

时间: 2024-08-30 15:10:30

2019 上海市大学生网络安全大赛 RE部分WP的相关文章

第三届上海市大学生网络安全大赛 签到题

题目内容: https://www.ichunqiu.com/default/apppys 扫码下载APP你想要的flag就在CTF竞赛圈里~加入CTF竞赛圈,大表姐的buff在等着你~ flag:flag{7heR3_i5_a_Lif3_4b0ut_tO_sT4rt_WHen_T0morROw_coMe5} 原文地址:https://www.cnblogs.com/zaixialongaotian/p/10355512.html

2019全国大学生信息安全大赛两道web

简单小结 菜鸟第一次打国赛,这次题目质量很高,学到了许多姿势. Web Justsoso 打开题目,源代码出存在提示: 使用LFI读取index.php与hint.php http://d4dc224926cd47bca560b0ec2f84bad155efe5b747574b89.changame.ichunqiu.com/?file=php://filter/read=convert.base64-encode/resource=index.php http://d4dc224926cd47

2019-上海市大学生网络安全大赛-writeup-REVERSE-Satan

Satan 这次比赛只看了这道逆向题,下午快调出来的时候被 rjj 拉过去唱歌,晚上回来已经结束了,调了几个脚本的bug出了这道题. 题目给了一个 64 位 ELF,丢进 IDA 中打开,主要逻辑都在 main 函数内,用 C++/STL 编写了很多字符串操作. 先看前几行的操作,第 183 行初始化了一个字符串.第 184 行"operator >>" 就是C++的输入流,而"unk_6072A0"可以通过交叉引用发现是 cin,于是这一行把用户输入读

2015年网络空间安全高端论坛暨第二届“世安杯”广东省大学生网络安全竞赛

<ignore_js_op> 远离了那个枪林弹雨的时代,却仍然嗅到字里行间的硝烟. 9月3日,抗战70周年纪念日.驻足在五星红旗下,我思考着,我们需要做的,不仅是纪念一场战争的结束,更应该注意到,另一场战争已悄然开始. 一句程序,可以打开网站,也可以注入SQL:一台电脑,可以看电影,也可以DDOS攻击:一群码农,可以写游戏,也可以使整个网络瘫痪.当我们调侃程序猿.攻城狮时,殊不知,在保卫着我们国家最脆弱的第五空间——网络空间的安全,也正是我们的攻城狮. 我们诚邀你参与这次活动.与来自各大高校.

【翻译贴】上海市第八届星光计划(2019年)技能大赛网站设计(中职组)决赛题目

两年一度的上海市星光计划比赛被誉为职业技能大赛的奥林匹克,比赛分为中职组和高职组 随着若干年的发展,比赛的题目强度逐年提高,逐步向世界职业技能大赛的比赛强度靠拢 这次翻译的题目是上海市第八届星光计划(2019年)技能大赛网站设计(中职教师组)的决赛题目.全文是英文的,学生组的题目和教师组差不多,个别地方降低了要求.故这次翻译了教师组的题目. 决赛要求 模块 A:站点设计 一.竞赛时间 150 min 二.竞赛任务及要求 1. INTRODUCTION: Frankfurt Internation

2019年华北五省(市、自治区)及港澳台大学生计算机应用大赛 - 河北赛区

2019年华北五省(市.自治区)及港澳台大学生计算机应用大赛 移动互联网应用创新 http://bjcac.buu.edu.cn/a/1.aspx 河北省 一二等奖(答辩后分出一.二等): 河北省 三等奖: 原文地址:https://www.cnblogs.com/hbuwyg/p/11723852.html

第十一届GPCT杯大学生程序设计大赛完美闭幕

刚刚过去的周六(6月7号)是今年高考的第一天,同一时候也是GPCT杯大学生程序设计大赛颁奖的日子,以下我们用图文再回想一下本次大赛颁奖的过程. 评审过程的一些花絮<感谢各位评审这些天的付出!>: 感谢葡萄城给我的这次的锻炼的机会,让我学会了非常多,也明确了非常多!最重要的是感谢评委们!你们辛苦了! 感谢MTV-我在上嵌入式课-- 永远别忘了为你付出的人,成就永远不属于一个人.就是装也得怀有一颗谦卑感恩之心.版主大人受小弟一拜! 再次感谢品审大人,我们组还非常多要改进的,到时候一定要跟各路大神好

2019西湖论剑网络安全技能大赛(大学生组)部分WriteUp

这次比赛是我参加以来成绩最好的一次,这离不开我们的小团队中任何一个人的努力,熬了一整天才答完题,差点饿死在工作室(门卫大爷出去散步,把大门锁了出不去,还好学弟提了几个盒饭用网线从窗户钓上来才吃到了午饭).写好WP回到宿舍的时候已经快十二点了,随便吃了点面包倒头就睡...... 接下来大概写写我们的解题思路,由于做题的时候没想到可以进名次,而且赛后比赛平台也关了,所以很多实现过程的截图就没法弄了,只下了除web以外的题目. CRYPTO 第一题 HardGame 这道题我们并没有做出来,可以看看大

科来杯”第七届山东省大学生 网络安全技能大赛

[Reserve] [re-elf] [babyLogin] v10和v11差一个字节,在内存中.所以 v11[0-1] 就是v10的地址 最后结果是求v1,所以逆向算法就是(byte_40925C[i*2] ^ byte_409034[dword_40924C[i % 4]] ^ 0x68) - 2 [Moblie] 加密:对字符串的每位的ascii+1 解密:对字符串的每位的ascii-1 [web] [有趣的PHP与JS ] 打不开了没法截图了 XFF第一层绕过 修改UA第二层绕过 md5