本次比赛时间大概为2019年1月末,当时留校参加ACM的集训,后面几天由于太难听不进去,恰逢此次比赛,于是第一次参加CTF。

第一次做出ctf题,虽然很简单,但我好高兴

(原来只想到拼了命也要做出一道题来,没想到一做做了这么多)

第一道▼逆向签到

下载文件,打开IDA64,把文件脱进来,F5将汇编翻译成c语言

主函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  size_t v4; // rbx
  int v5; // [rsp+0h] [rbp-C0h]
  int v6; // [rsp+4h] [rbp-BCh]
  int v7; // [rsp+8h] [rbp-B8h]
  int v8; // [rsp+Ch] [rbp-B4h]
  int v9; // [rsp+10h] [rbp-B0h]
  int v10; // [rsp+14h] [rbp-ACh]
  int v11; // [rsp+18h] [rbp-A8h]
  int v12; // [rsp+1Ch] [rbp-A4h]
  int v13; // [rsp+20h] [rbp-A0h]
  int v14; // [rsp+24h] [rbp-9Ch]
  int v15; // [rsp+28h] [rbp-98h]
  int v16; // [rsp+2Ch] [rbp-94h]
  int v17; // [rsp+30h] [rbp-90h]
  int v18; // [rsp+34h] [rbp-8Ch]
  int v19; // [rsp+38h] [rbp-88h]
  int v20; // [rsp+3Ch] [rbp-84h]
  int v21; // [rsp+40h] [rbp-80h]
  int v22; // [rsp+44h] [rbp-7Ch]
  int v23; // [rsp+48h] [rbp-78h]
  int v24; // [rsp+4Ch] [rbp-74h]
  int v25; // [rsp+50h] [rbp-70h]
  int v26; // [rsp+54h] [rbp-6Ch]
  int v27; // [rsp+58h] [rbp-68h]
  int v28; // [rsp+5Ch] [rbp-64h]
  int v29; // [rsp+60h] [rbp-60h]
  int v30; // [rsp+64h] [rbp-5Ch]
  int v31; // [rsp+68h] [rbp-58h]
  int v32; // [rsp+6Ch] [rbp-54h]
  int v33; // [rsp+70h] [rbp-50h]
  char s[40]; // [rsp+80h] [rbp-40h]
  int v35; // [rsp+A8h] [rbp-18h]
  int i; // [rsp+ACh] [rbp-14h]

  puts("* please input flag: ");
  __isoc99_scanf("%s", s);
  if ( strlen(s) == 29 )
  {
    v35 = rand() % 100;
    for ( i = 0; ; ++i )
    {
      v4 = i;
      if ( v4 >= strlen(s) )
        break;
      s[i] ^= v35;
    }
    v5 = 53;
    v6 = 63;
    v7 = 50;
    v8 = 52;
    v9 = 40;
    v10 = 1;
    v11 = 50;
    v12 = 61;
    v13 = 55;
    v14 = 99;
    v15 = 62;
    v16 = 118;
    v17 = 98;
    v18 = 60;
    v19 = 60;
    v20 = 12;
    v21 = 106;
    v22 = 58;
    v23 = 37;
    v24 = 54;
    v25 = 12;
    v26 = 38;
    v27 = 12;
    v28 = 102;
    v29 = 48;
    v30 = 60;
    v31 = 33;
    v32 = 54;
    v33 = 46;
    if ( (unsigned int)Check((__int64)s, (__int64)&v5) == 1 )
      puts("* CCCCCCCCCCCongratulation!!!!");
    else
      puts("* try it again");
    result = 0;
  }
  else
  {
    puts("* try it again");
    result = 0;
  }
  return result;
}

check函数:

signed __int64 __fastcall Check(__int64 a1, __int64 a2)
{
  signed int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; i <= 28; ++i )
  {
    if ( *(char *)(i + a1) != *(_DWORD *)(4LL * i + a2) )
      return 0LL;
  }
  return 1LL;
}

主函数中下图所示的代码块将输入的字符串逐个字符地与随机数(0~99)进行异或

(若a^b=c,则a=b^c,按此原理可写脚本)

img

check函数将经过异或处理后的数据和主函数中存储的v5~v33作比较,相同则返回真

第一次接触逆向的题,我没想明白随机数每次都变,怎么保证输入的字符串是正确答案?

后来无意中暴力了一下,得到了100个不同的字符串,这才想明白,有100个字符串是正确答案,一个随机数对应一个,也就是说,当flag的字符串对应的随机数并没有随机到的时候,这是输入flag后也会显示* try it again

我写的暴力脚本:

(python还在学,不熟练,先用c++)

(c++是世界上最好的语言,233)

#include<iostream>
#include<cstdlib>
#include<ctime> 
using namespace std;
int main()
{
    srand(time(0));//不写这个的话,随机数并不随机
    int v[29]={53,63,50,52,40,1,50,61,55,99,62,118,98,60,60,12,106,58,37,54,12,38,12,102,48,60,33,54,46};
    int v35=rand()%100;
    for (int i=0;i<=99;i++)
    {
        v35=i;
        cout<<v35<<endl;
        for (int i=0;i<=28;i++)
            cout<<(char(v35^v[i]));
        cout<<endl;
    }
    return 0;
}

结果

img

第二道▼base全家桶了解一下??

base64解码为base32再解码到base16再解码到明文

密文:R1kzRE1RWldHRTNET04yQ0dVM1RNTkpXSU0zREdNWlFHWkNETU5KVklZM1RJTVpRR01ZREtSUldHTTNUS05TRUc0MkRNTVpYR1EzRE1OMkU=

64转32:

GY3DMQZWGE3DON2CGU3TMNJWIM3DGMZQGZCDMNJVIY3TIMZQGMYDKRRWGM3TKNSEG42DMMZXGQ3DMN2E

32转16

666C61677B57656C63306D655F7430305F63756D746374667D

16解码:

flag{Welc0me_t00_cumtctf}

Base64编码是使用64个可打印ASCII字符(A-Z、a-z、0-9、+、/)将任意字节序列数据编码成ASCII字符串,另有“=”符号用作后缀用途。

Base32编码是使用32个可打印字符(字母A-Z和数字2-7)对任意字节数据进行编码的方案

Base16编码使用16个ASCII可打印字符(数字0-9和字母A-F)对任意字节数据进行编码

第三道▼现代密码签到

两次DES解码

密文:

U2FsdGVkX1+p43JX7+KrdUBXg/UTw+ejas2dbmiVanvVSxOuhSdp3JLc+7G4zK5p hHvL/5MHRKFV/L2THW1XCylB3U+pxCxbmnpQ2RB2ZTU=U2FsdGVkX1+p43JX7+KrdUBXg/UTw+ejas2dbmiVanvVSxOuhSdp3JLc+7G4zK5p hHvL/5MHRKFV/L2THW1XCylB3U+pxCxbmnpQ2RB2ZTU=

第一次解码:

U2FsdGVkX18968C+7acWUzWtYyuQd2MFLMh0HnGGnMlmYlemknPnfg==

第二次解码:

cumtctf{double_D3s_HHH}

img

img

第四道▼Misc签到

对照盲文翻译即可,翻译为BAIND,根据提示,改为B1IND,正好和BLIND(盲的)相似

img

img

第五道▼Easy_Math

用IDA64打开

主函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  char v4; // [rsp+0h] [rbp-160h]
  int v5; // [rsp+100h] [rbp-60h]
  int v6; // [rsp+104h] [rbp-5Ch]
  int v7; // [rsp+108h] [rbp-58h]
  int v8; // [rsp+10Ch] [rbp-54h]
  int v9; // [rsp+110h] [rbp-50h]
  int v10; // [rsp+114h] [rbp-4Ch]
  int v11; // [rsp+118h] [rbp-48h]
  int v12; // [rsp+11Ch] [rbp-44h]
  int v13; // [rsp+120h] [rbp-40h]
  __int64 v14; // [rsp+130h] [rbp-30h]
  __int64 v15; // [rsp+138h] [rbp-28h]
  __int64 v16; // [rsp+140h] [rbp-20h]
  __int64 v17; // [rsp+148h] [rbp-18h]
  int v18; // [rsp+150h] [rbp-10h]
  char s[8]; // [rsp+157h] [rbp-9h]
  char v20; // [rsp+15Fh] [rbp-1h]

  *(_QWORD *)s = 0LL;
  v20 = 0;
  v14 = 0LL;
  v15 = 0LL;
  v16 = 0LL;
  v17 = 0LL;
  v18 = 0;
  v5 = 1;
  v6 = 2;
  v7 = 1;
  v8 = 2;
  v9 = 1;
  v10 = 1;
  v11 = 1;
  v12 = 1;
  v13 = 2;
  memset(&v4, 0, 0x100uLL);
  puts("* please input flag: ");
  __isoc99_scanf("%s", s);
  if ( strlen(s) == 9 )
  {
    String2Int(s, (__int64)&v14);
    Change((__int64)&v14, (__int64)&v5, (__int64)&v4);
    if ( (unsigned int)Check((__int64)&v4) == 1 )
      puts("CCCCCCCCCCCongratulation!!!!");
    else
      puts("try it again");
    result = 0;
  }
  else
  {
    puts("* try it again");
    result = 0;
  }
  return result;
}

String2Int函数:

作用很简单,把输入的字符串的每一个字符都分别赋值给从v14开始的9个变量

size_t __fastcall String2Int(const char *a1, __int64 a2)
{
  size_t result; // rax
  int i; // [rsp+1Ch] [rbp-14h]

  for ( i = 0; ; ++i )
  {
    result = strlen(a1);
    if ( i >= result )
      break;
    *(_DWORD *)(4LL * i + a2) = a1[i];
  }
  return result;
}

change函数:(就你这破函数事儿多,变啥变啊,看了我好几个小时)

这个太难看了,萌新不会写算法,所以就手动模拟了(模拟图见本题最后)

_DWORD *__fastcall Change(__int64 a1, __int64 a2, __int64 a3)
{
  _DWORD *result; // rax
  signed int m; // [rsp+24h] [rbp-14h]
  signed int l; // [rsp+28h] [rbp-10h]
  signed int k; // [rsp+2Ch] [rbp-Ch]
  signed int j; // [rsp+30h] [rbp-8h]
  signed int i; // [rsp+34h] [rbp-4h]

  for ( i = 0; i <= 2; ++i )
  {
    for ( j = 0; j <= 2; ++j )
    {
      result = (_DWORD *)(4 * (3 * i + (signed __int64)j) + a3);
      *result = 0;
    }
  }
  for ( k = 0; k <= 2; ++k )
  {
    for ( l = 0; l <= 2; ++l )
    {
      for ( m = 0; m <= 2; ++m )
      {
        result = (_DWORD *)(4 * (3 * k + (signed __int64)l) + a3);
        *result += *(_DWORD *)(4 * (3 * m + (signed __int64)l) + a2) * *(_DWORD *)(4 * (3 * k + (signed __int64)m) + a1);
      }
    }
  }
  return result;
}

Check函数:

经过change变换后的字符与check内的局部变量v2~v10相比较

signed __int64 __fastcall Check(__int64 a1)
{
  int v2; // [rsp+8h] [rbp-30h]
  int v3; // [rsp+Ch] [rbp-2Ch]
  int v4; // [rsp+10h] [rbp-28h]
  int v5; // [rsp+14h] [rbp-24h]
  int v6; // [rsp+18h] [rbp-20h]
  int v7; // [rsp+1Ch] [rbp-1Ch]
  int v8; // [rsp+20h] [rbp-18h]
  int v9; // [rsp+24h] [rbp-14h]
  int v10; // [rsp+28h] [rbp-10h]
  int i; // [rsp+34h] [rbp-4h]

  v2 = 274;
  v3 = 294;
  v4 = 316;
  v5 = 262;
  v6 = 274;
  v7 = 252;
  v8 = 380;
  v9 = 421;
  v10 = 427;
  for ( i = 0; i <= 8; ++i )
  {
    if ( *(_DWORD *)(4LL * i + a1) != *(&v2 + i) )
      return 0LL;
  }
  return 1LL;
}

手动模拟,解出方程,转换成char类型的,得到flag

img

img

第六道▼BXS图标真好看

(这是我们队(c家家)的第七道,我队第六道是张龙做的web)

后缀改为png,得到密文fgookwnl{_un_gaDy_0p},刚开始以为fgookwnl对应flag,两个字母对应一个字母,后来无意中发现flag中的f到l和l到a都相隔7位,就以为所有的都是往后找7个数,但到了flag中的g时,发现从a到g相隔7位(不算已经取出的f),但从g到{才隔着6位(除非算上取出的l才相隔7位),然后就以为前3此移位是7位,后面的移位都是6位,但这样取,}居然提前取出来了。然后他突然灵机一动,共21个字符,7移动了3次,然后移动6,3*7=21,即7三次,6三次,一直到1三次(虽然最后发现1只有两次,因为21个字符有20个间隔)

img

第七道▼古典密码签到

先base32,根据提示得到^pho^oav`
tZnj`
tZZZcccx

一点基础也没有,根本无从下手,中间想试试移位,但死活和flag对不上(忘了还有cumtctf这个格式)。最后想试试凯撒,但凯撒只能搞纯字母,过了一会,突然想到,可能是凯撒的思想,不一定在26个字符中移位,也可能在Z(ascll最小,为90)和x(ascll最大,为120)间移位。编了个程序,虽然最后发现有点小错误,但还是发现出现了cumtctf的字样,于是肯定思路没错,找错误吧。错误还没找到的,先发现了其实就是向后移动5位。。。重新编了个往后移5位的程序,答案就跑出来了

img

进前十啦

img

img

web的签到题是张龙做的哦

第八道▼起床改error啦!

解压,得到一张图片

img

按照提示,图文无关,拖进C32Asm

文件末尾出现了flag.doc的字样,怀疑是两个文件结合而来

img

打开kali,将图片拖进虚拟机ctf文件夹

img

将新生成的flag.doc拖回win10,居然打不开

img

这时发现之前用binwalk时出现的Zip archive data这句话,百度一下,原来是种压缩方式

img

unzip解压这个doc

img

拖回win10,打开

img

那就接着找吧。点击文件再点击选项

img

然后点击显示,勾上隐藏文字

img

答案就出来了

img

第九道▼矿大校歌认真听听吧?

一个带密码的压缩包,尝试1~6位数字暴力破解密码,没出来,拖进winhex(或C32Asm),最后一行写着cumtctf2019,猜测就是密码,果然。

img

里面只有一个cumt.mp3,是矿大校歌。打开听了,正常的校歌。

拖进audacity(在这个软件上耗费了我三个小时以上的时间,没想到这道题用不到这个软件)

img

img

频谱图也看不出来flag(最初怀疑过紫线和白线,放大后一点一点找,耗费了好几个小时,也听了好几个小时的校歌)

img

关闭,打开kali,用binwalk分析

结果并不是多个文件的结合

img

回到win10,用MP3Stego试一下

双击MP3Stego目录下的mp3.bat(该文件作用是在cmd中进入MP3Stego目录),键入

decode -X -P cumtctf cumt.mp3

-X是文件名

-P是密码

(至于为啥后面的参数的顺序是反着的我也不清楚)

img

生成了两个文件

img

打开cumt.mp3.txt,得到flag{cumtctf_1s_v3ry_g00d!}

(最初不知道密码,猜了个题目名,即cmut,结果提示错误,没想到cumtctf2019既是压缩包密码,又是MP3隐写的密码)

img

第十题▼SimpleUpload签到

img

只能上传图片,但要求是上传.php

使用burpsuite抓包再修改数据上传https://www.sohu.com/a/236194259_658302

安装了jdk后,我仍然无法打开burpsuite.jar,所以我将burpsuite.jar放入

img

打开cmd,键入java -jar burpsuite.jar,通过这样打开burpsuite,但弊端是每次都要输入一次,而且使用burpsuite过程中不能关闭输入命令的那个cmd窗口,否则burpsuite也会关闭。

img

img

依次点close(不更新),next,然后点start burp

img

打开360极速浏览器(chrome我还不会设置代理服务器),搜索“代理”

img

点击代理服务器设置

img

在代理服务器列表中添加127.0.0.1:8080

img

点击代理服务器,勾选127.0.0.1:8080

浏览器地址栏键入题目网址,发现打不开,那就先把上图的不使用代理服务器勾选,打开网页后再勾选127.0.0.1:8080

再桌面新建一个空白文件,文件名为1.php .png

注意php和.png之间有一个空格。

打开burpsuite,开启抓包(Intercept is on)

img

这时上传1.php .png

抓到包

img

点击Proxy中的Intercept中的Hex

img

找到1.php .png所在的一行

img

将20改为00(空格的hex是20)

img

原理是:提交的是.png,但服务器端读到00后就截断了,所以服务器端认为是.php

点击Forward,上传修改后的包

img

得到flag

img

(做出来后没几分钟,张龙告诉我他也做出来了,好像他是直接F12查看的代码,假期结束后再问问他)

(之前他说这道题有头绪了,我以为这100分到手了,没想到两天了他还不提交flag,我以为他懒得不做了,气的我现学了好几个小时,才搞到flag。不靠谱的男人(。・∀・)ノ)

第十一题▼

参考了这个的第一题https://www.cnblogs.com/baifan2618/p/7762666.html

题目网址http://bxs.cumt.edu.cn:30007/test/index.php

(sqlmap的各种参数详见另一篇笔记)

打开kali的终端,键入

sqlmap -u http://bxs.cumt.edu.cn:30007/test/index.php?id=1 --dbs

结果如下图

img

得到可访问数据库

img

猜测flag在security中

sqlmap -u http://bxs.cumt.edu.cn:30007/test/index.php?id=1 --tables -D security

img

上下两图无缝衔接(太长了,截不到一起)

img

看到flagishere就知道稳了

sqlmap -u http://bxs.cumt.edu.cn:30007/test/index.php?id=1 --columns -T flagishere -D security

img

img

我这个萌新还以为flag就是non-numeric呢,还以为要在网址后面加?id=numeric

都错了。接着键入

sqlmap -u http://bxs.cumt.edu.cn:30007/test/index.php?id=1 --dump -C flag -T flagishere -D security

img

img

得到flag

最终成绩

img

img

Last modification:June 10th, 2020 at 12:00 pm