极客wp总结

队伍名:int 晓 队员:一打七QAQ Akuma 分数:4200

本wp全由一打七QAQ写完,一个开校真小白,真菜鸡,如果有什么问题和错误,请谅解。

官方wp:https://mp.weixin.qq.com/s/8R4lGzToBxjK1lC9B_SFBg

web题

1.朋友的学妹

打开网址,右键鼠标,打开源文件,发现

<!--flag=U1lDe0YxQF80c19oNExwZnVsbGxsbGx9-->

用网站base64解码得到flag
SYC{F1@_4s_h4Lpfullllll}

2.EZwww

打开网址,说这个网站已经备份了,直接在网址后面加上/www.zip,发现开始下载一个压缩包,解压,有两个文件,一个是假的flag,一个是index.php,把index.php的后缀改为txt。打开发现

<html>
 <head>
  <title>Lola's website1.0</title>
 </head>
 <body>
 <?php echo '<h1>welcome to my website</h1>'; ?>
 <?php echo '<p>i will never forget to backup my website......</p>'; ?>
 <?php echo '<img src="img/lola.gif" alt="welcome~"/>'; ?>
 </body>
</html>
<?php
$key1 = $_POST['a'];
$key2 = base64_decode('c3ljbDB2ZXI=');
if($key1 === $key2)
{
    //this is a true flag
echo '<p>SYC{xxxxxxxxxxxxxxxxxx}</p>';
}
?>

说以post提交a,然后要key1等于key2,key2还base64编码了,解码得到sycl0ver,然后用火狐浏览器(注意要下载一个插件Hackbar)以post提交a=sycl0ver,得到flag SYC{Backup_1s_4_good_h4bit_l0l}

3.刘壮的黑页

这道题主要考post和get两个提交方式,不知道怎么回事,网站打不开了QAQ,反正就是用get方式提交一个东西,一个用post提交一个东西,就会得到flag

4.Welcome

打开网站,显示405错误,只不过不是题错了,而是要改用post提交一个东西才会显示一些东西,比如a=’1’,得到
<?php
error_reporting(0);
if ($_SERVER[‘REQUEST_METHOD’] !== ‘POST’) {
header(“HTTP/1.1 405 Method Not Allowed”);
exit();
} else {

    if (!isset($_POST['roam1']) || !isset($_POST['roam2'])){
        show_source(__FILE__);
    }
    else if ($_POST['roam1'] !== $_POST['roam2'] && sha1($_POST['roam1']) === sha1($_POST['roam2'])){
        phpinfo();  // collect information from phpinfo!
    }
} 

再次以post提交roam1[]=1&roam2[]=2,一个绕过方式。会出现一个界面,从中找到一个可以位置 /f1444aagggg.php,在网址后面加上去,得到

Not Found

The requested URL was not found on this server.
Apache/2.4.38 (Debian) Server at 49.234.224.119 Port 8000

改为post请求发送,在消息头里面看到flag

SYC{w31c0m3_t0_5yc_r0@m_php1}

5.我是大黑客

这道题考的应该是一句话木马,打开网站,有提示说留下了一个备份文件,在网址后面加上/liuzhuang.php.bak。开始下载文件,把文件后缀名改为txt,打开,得到

<?php
eval($_POST['liuzhuang']);

//谁是大恶人 那必定是我liuzhuang
//当你的服务器看到 0xliuzhuang 就知道要买台新机器了
?>

用蚁剑

输入URL,密码为上面的liuzhuang。得到flag

SYC{1iuzHuang_yyd_G0d!}

6.ezbypass

Please use a GET request to pass in the variables a and b, compare them with strcmp and let strcmp return a value of NULL.

Note that a and b cannot be equal. 

请使用GET请求传入变量a和b,将它们与strcmp进行比较,并让strcmp返回NULL值。
注意a和b不能相等。用strcmp函数的漏洞绕过

以get方式提交?a[ ]=2&b[ ]=4,然后得到

OKOK,You got the first step.
Please POST a variable c that is not a number to make it equal to 123 

好吧,你迈出了第一步。请发布一个不是数字的变量c,使其等于123。开始我以为用ASCLL码就可以了,不行,用位运算使c=123,也不行。最后发现是php弱类型绕过。post提交c=123a,得到flag

nice!!!You got it
SYC{php_4s_so_funny} 

re题目

1.No RE no gain

下载文件,直接拖到IDA里面,shift+f12查看字符串,有很多假的flag,例如

.rdata:0040400C    0000001F    C    SYC{5ay_He1lo_tH3_Re_W0rld!!!}
.rdata:0040402C    0000001F    C    SYC{Say_h3llo_th3_rE_World!!!}
.rdata:0040404C    0000001F    C    SYC{s4y_he110_The_RE_w0r1d!!!}
.rdata:0040406C    0000001F    C    SYC{S4y_He11o_th3_RE_World!!!}
.rdata:0040408C    0000001F    C    SYC{s4y_He110_The_rE_w0r1d~~~}
.rdata:004040AC    0000001F    C    SYC{say_h3ll0_Th3_RE_w0rld!!!}
.rdata:004040CC    0000001F    C    SYC{s4y_he110_th3_rE_w0rld~~~}
.rdata:004040EC    0000001F    C    SYC{s4y_he11o_The_r3_wor1d!!!}
.rdata:0040410C    0000001F    C    SYC{Say_He110_th3_r3_w0r1d!!!}
.rdata:0040412C    0000001F    C    SYC{say_He11o_The_rE_wor1d!!!}
.rdata:0040414C    0000001F    C    SYC{say_he11o_the_r3_Wor1d~~~}
.rdata:0040416C    0000001F    C    SYC{Say_He110_The_rE_w0r1d~~~}
.rdata:0040418C    0000001F    C    SYC{S4y_H3110_th3_RE_World!!~}

真正的flag在,.rdata:00404277 0000001A C Great! Here is your flag:,点进去,找到位置ctrl+x,到真正的flag处。得到

SYC{S4y_He11o_th3_RE_World!!!}

2.我真不会写驱动!

不知道是不是题有问题,直接拖到IDA里面,shift+f12查看字符串,就有了flag

SYC{First_Win64_DRIVER}

3.WhatsApk

APK实际上就是一个备份文件,解压后,得到一些文件,网上搜一搜apk文件结构,了解了解。

取巧做法将全部文件后缀名改为txt,然后一个一个文件的搜索SYC,得到flag

SYC{WelC0me_to_android!}

实际上估计应该是要用jed软件,然后在jeb里面搜索,下一道题就会用到。

4.HelloAndroid

首先安装jed,挺难找软件的,安好了还要安个Java。

安装好了,把class.dex文件直接拖到jeb里面,然后点字符串,搜索SYC,得到

SYC{Android_in_Java_is_easy!}

5.re00

考点:异或
要先到IDA里面看,shift+12,点进去可以的地方,ctrl+x,然后f5,看伪代码,关键点为

if ( (char)(buf[i] ^ 0x44) != byte_4060[i] )//buf[i]就是输入的flag,然后和0x44进行了一个异或运算,得到了byte_4060[i]。

点开byte_4060[i],其值为

byte_4060       db 17h, 1Dh, 7, 3Fh, 37h, 2Dh, 29h, 34h, 28h, 21h, 1Bh
.data:0000000000004060                                         ; DATA XREF: main+93↑o
.data:0000000000004060                 db 37h, 2Dh, 29h, 34h, 28h, 21h, 1Bh, 3Ch, 2Bh, 3 dup(36h)
.data:0000000000004060                 db 1Bh, 36h, 2Dh, 23h, 2Ch, 30h, 2 dup(7Bh), 39h

需要知道dup代表的是什么,然后异或回去就行
脚本如下

#include <stdio.h>
#include <stdlib.h>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char *argv[]) {
    signed int i; // [rsp+8h] [rbp-48h]
    int buf[32]; // [rsp+10h] [rbp-40h]
    int byte_4060[32]={0x17,0x1D,0x07,0x3F,0x37,0x2D,0x29,0x34,0x28,0x21,0x1B,0x37,0x2D,0x29,0x34,0x28,0x21,0x1B,0x3C,0x2B,0x36,0x36,0x36,0x1B,0x36,0x2D,0x23,0x2C,0x30,0x7B,0x7B,0x39};
//从IDA中得到byte_4060的16进制数串 后面有一串字符 但是不像flag,发现if里面用异或运算编码了,写脚本来解码。
  for ( i = 0; i <= 31; ++i )
  {
     buf[i]=byte_4060[i] ^0x44;//解码

  }

  for(i=0;i<=31;i++)
  {
      printf("%x ",buf[i]);//可改为printf("%c",buf[i]);直接得到字符串
  }
  printf("\n");
  //得到解码后的16进制数串,将其转换为字符串,得到flag。
  puts("yes! you get it!");
  return 0LL;
}

得到flag

SYC{simple_simple_xorrr_right??}

6.maze

首先还是用IDA打开,shift+12搜索字符串,发现tttttttttttttttql!!!十分可疑,点进去,Ctrl+x进入主函数,F5生成伪代码,如下

int sub_401A10()
{
  int v0; // ecx
  int v1; // eax
  int v2; // esi
  char *v3; // ecx
  unsigned int v4; // kr00_4
  int v5; // eax
  char v6; // cl
  char v8[108]; // [esp+8h] [ebp-70h]
  char v9[108]; // [esp+9h] [ebp-6Fh]

  sub_402400(dword_431974);
  sub_401CB0(v0, "give me your way:");
  sub_406FD0(v8, 0, 100);
  v1 = sub_409A97(0);
  sub_409E54(v1);
  sub_401100("%64s", (unsigned int)v8);
  v2 = 0;
  v3 = v9;
  v4 = strlen(v8);
  if ( v4 )
  {
v5 = dword_431960;
while ( 1 )
{
  v6 = v8[v2];
  if ( v6 == 97 )
break;
  switch ( v6 )
  {
case 119:
  v5 -= 31;
  goto LABEL_11;
case 115:
  v5 += 31;
  goto LABEL_11;
case 100:
  ++v5;
  goto LABEL_11;
  }
LABEL_12:
  LOBYTE(v3) = byte_42E820[v5];
  if ( (_BYTE)v3 == 111 )
  {
sub_401CB0(v3, "Ur not on the way!!");
sub_4099B9(0);
LABEL_17:
sub_401CB0(v3, "ttttttttttttttttttttql!!");
sub_4099B9(0);
JUMPOUT(*(_DWORD *)algn_401B14);
  }
  if ( (_BYTE)v3 == 69 )
goto LABEL_17;
  if ( ++v2 >= v4 )
return 0;
}
--v5;
LABEL_11:
dword_431960 = v5;
goto LABEL_12;
  }
  return 0;
}

百度搜索:ctf 迷宫问题,https://wiki.x10sec.org/reverse/maze/maze/发现与这道题有相识之处,从而知道

.rdata:0042E821'__________ooooo_____o________o_ooooooooo__ooo__ooo_oooooooooo_ooo'
.rdata:0042E821 db 'ooooooo__o__oooo_oooooooooo________oooo___ooooo_ooooooooooooooooo'
.rdata:0042E821 db '_oooooooooooo_ooooooooooooooooo_ooooo_oooooo_ooooooooooooooooo_oo'
.rdata:0042E821 db 'ooo_oooooo_oooooooooos_______ooooo_oooooo__________Eooooooooooooo'
.rdata:0042E821 db 'oooooooooooooooooo',0

代表的是迷宫,用shift+e调出迷宫,发现迷宫数据,但是需要知道迷宫的长宽,我的做法是先调为16进制,通过拉扯边框的大小使里面数据成为一个矩形,发现变成9*31时恰好是个矩形!!!!,并且按照5F走迷宫的确有一条路。

再看主函数,发现

v5 -= 31;
v5 += 31;

中的31相当之可疑,发现这原来是1维数组变2维数组,每31个就换行。16进制的图十分不好看,换一下
#include<stdio.h>

int main(void)
{
    int i,j;
    int a[9][31]={   95,  95,  95,  95,  95,  95,  95,  95,  95,  95, 
  111, 111, 111, 111, 111,  95,  95,  95,  95,  95, 
  111,  95,  95,  95,  95,  95,  95,  95,  95, 111, 
   95, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
   95,  95, 111, 111, 111,  95,  95, 111, 111, 111, 
   95, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111,  95, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111,  95,  95, 111,  95,  95, 111, 111, 111, 
  111,  95, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111,  95,  95,  95,  95,  95,  95,  95,  95, 
  111, 111, 111, 111,  95,  95,  95, 111, 111, 111, 
  111, 111,  95, 111, 111, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
   95, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111, 111,  95, 111, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111,  95, 111, 111, 111, 111, 111,  95, 111, 111, 
  111, 111, 111, 111,  95, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111,  95, 111, 111, 111, 111, 111,  95, 111, 
  111, 111, 111, 111, 111,  95, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 115,  95,  95,  95, 
   95,  95,  95,  95, 111, 111, 111, 111, 111,  95, 
  111, 111, 111, 111, 111, 111,  95,  95,  95,  95, 
   95,  95,  95,  95,  95,  95,  69, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111,0 };

  for(i=0;i<9;i++)
  {
      for(j=0;j<31;j++)
      {
          printf("%c",a[i][j]);
    }
    printf("\n");
  }


} 

发现运行后不对

__________ooooo_____o________o_
ooooooooo__ooo__ooo_oooooooooo_
oooooooooo__o__oooo_oooooooooo_
_______oooo___ooooo_ooooooooooo
oooooo_oooooooooooo_ooooooooooo
oooooo_ooooo_oooooo_ooooooooooo
oooooo_ooooo_oooooo_oooooooooos
_______ooooo_oooooo__________Eo
oooooooooooooooooooooooooooooo

--------------------------------
Process exited after 0.1652 seconds with return value 0
请按任意键继续. . .

迷宫怎么会缺了一个角。观察地图,最右边那一列如果移到左边不就感觉更好了,感觉应该是少了一个,添上去,c代码如下
#include<stdio.h>

int main(void)
{
    int i,j;
    int a[9][31]={   95,95,  95,  95,  95,  95,  95,  95,  95,  95,  95, 
  111, 111, 111, 111, 111,  95,  95,  95,  95,  95, 
  111,  95,  95,  95,  95,  95,  95,  95,  95, 111, 
   95, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
   95,  95, 111, 111, 111,  95,  95, 111, 111, 111, 
   95, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111,  95, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111,  95,  95, 111,  95,  95, 111, 111, 111, 
  111,  95, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111,  95,  95,  95,  95,  95,  95,  95,  95, 
  111, 111, 111, 111,  95,  95,  95, 111, 111, 111, 
  111, 111,  95, 111, 111, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
   95, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111, 111,  95, 111, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111,  95, 111, 111, 111, 111, 111,  95, 111, 111, 
  111, 111, 111, 111,  95, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111,  95, 111, 111, 111, 111, 111,  95, 111, 
  111, 111, 111, 111, 111,  95, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 115,  95,  95,  95, 
   95,  95,  95,  95, 111, 111, 111, 111, 111,  95, 
  111, 111, 111, 111, 111, 111,  95,  95,  95,  95, 
   95,  95,  95,  95,  95,  95,  69, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 
  111, 111, 111, 111, 111, 111, 111, 111,};

  for(i=0;i<9;i++)
  {
      for(j=0;j<31;j++)
      {
          printf("%c",a[i][j]);
    }
    printf("\n");
  }


}//(注意要把数组中最后的0去掉)

运行

___________ooooo_____o________o
_ooooooooo__ooo__ooo_oooooooooo
_oooooooooo__o__oooo_oooooooooo
________oooo___ooooo_oooooooooo
ooooooo_oooooooooooo_oooooooooo
ooooooo_ooooo_oooooo_oooooooooo
ooooooo_ooooo_oooooo_oooooooooo
s_______ooooo_oooooo__________E
ooooooooooooooooooooooooooooooo

--------------------------------
Process exited after 0.07231 seconds with return value 0
请按任意键继续. . .

迷宫准备就绪。感觉出题人相当有意思,SYC;迷宫中的s应该代表的是start起点,E应该代表的是end终点。实际上看主函数也可看出终点

LABEL_17:
sub_401CB0(v3, "ttttttttttttttttttttql!!");
sub_4099B9(0);
JUMPOUT(*(_DWORD *)algn_401B14);
  }
  if ( (_BYTE)v3 == 69 )//如果v3=69,也就是E,跳到LABEL_17,说ttttttttttttttql!!!说明是正确的终点
goto LABEL_17;

现在需要做的就是走迷宫,迷宫肯定需要方向,是什么呢?再看主函数

  v6 = v8[v2];
  if ( v6 == 97 )//‘a’=97,--v5不就代表向左移动吗
       --v5;//做了一点改动
  switch ( v6 )
  {
case 119://'199'=w,v5 -= 31不就是向上移动吗
  v5 -= 31;
  goto LABEL_11;
case 115://‘115’=s,  v5 += 31不就是向下移动吗
  v5 += 31;
  goto LABEL_11;
case 100://'100'=d,++5不就表示向右移动吗
  ++v5;
  goto LABEL_11;
  }

感觉出题人,非常人性化,知道打游戏这几个键最常用QAQ。tqltqltql
开始走迷宫,得到flag(注意s和E不算)

得到flag:SYC{dddddddwwwwaaaaaaawwwddddddddddsdsdsddwdwdwddddsssssssdddddddddd}

7.Hello .NET

一.反编译

文件下载下来是个可执行文件,需要反编译来看代码

首先下载反编译软件
https://www.cnblogs.com/heibai-ma/p/12893654.html

然后看如何反编译
https://blog.csdn.net/ajwqb06628/article/details/102071245?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.compare&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.compare

二.分析代码

反编译后得到的关键代码如下(需要自己去在反编译出来的文件里面找找)
public class MainWindow : Window, IComponentConnector
{
internal TextBox InputBox;
internal Button Check_Btn;
internal TextBlock Status;
private bool _contentLoaded;

    public MainWindow()
    {
        this.InitializeComponent();
    }

    private void Check(object sender, RoutedEventArgs e)
    {
        int num;
        string text = this.InputBox.Text;   //输入一个flag,也是要求的目标
        List<int> list = new List<int>();   //  https://zhidao.baidu.com/question/539039145.html
        int[] numArray = new int[] { 
            0x12, 14, 40, -14, -2, 30, 10, 0x2a, 0x23, 0x30, 0x2b, 0x31, 0x34, 0x48, 0x39, 0x44,
            0x56, 0x91, 0x73, 0x80, 0x73, 0x56
        };//长度为22
        for (num = 0x63; list.Count < text.Length; num += 2)        //0x63=99,   list.Count < text.Length,意思是要list[num]这个数组的长度和list.text[]数组长度一样,实际上list[num],text[num],numArray[num]的长度都要一样,都为22.         
        {
            bool flag2 = true;  //布尔变量
            for (int i = 3; i < num; i += 2)//这个循环就是为了求list[num]这个数组。和求素数基本差不多,有点不一样。
            {
                if ((num % i) == 0)
                {
                    flag2 = false;
                    break;
                }
            }
            if (flag2)
            {
                list.Add(num);   // 得到list[num]这个下面要用的数组
            }
        }
        bool flag = true;
        for (num = 0; (num < text.Length) && (num < numArray.Length); num++)
        {
            if ((list[num] - text[num]) != numArray[num])  //  不等于的话,flag=false,就说明错了,说明等于的时候才是对的,意思是flag  text[num]=list[num]-numArray[num]
            {
                flag = false;
                break;
            }
        }
        if ((text.Length == numArray.Length) & flag)
        {
            this.Status.Foreground = new SolidColorBrush(Colors.Green);
            this.Status.Text = "Flag is corrent";
        }
        else
        {
            this.Status.Foreground = new SolidColorBrush(Colors.Red);
            this.Status.Text = "Flag is incorrent";
        }
    }

三.写脚本

脚本1

#include<stdio.h>

int main(void)
{
    int num;
    char a[22]={0,};
    int b[22]={18,14,40,-14,-2,30,10,42,35,48,43,49,52,72,57,68,86,145,115,128,115,86};
    int c[22]={0,};
    int len=100;
    int flag2,i,j,n; 

    for(num=99,j=0;j<60;num+=2,j++)
    {
        n=0;
        flag2=1;
        for(i=3;i<num;i+=2)
        {
            if(num%i==0)
            {
            flag2=0;
            break;
            }    
        }

        if(flag2)
        {    c[n]=num;
            printf("%d ",c[n]);
            n++;//得到c[]={101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211} 
        }
//                b[]={18,14,40,-14,-2,30,10,42,35,48,43,49,52,72,57,68,86,145,115,128,115,86};
    }

//    printf("\n");
//    for(num=0;num<22;num++)
//    {
//        a[num]=c[num]-b[num];
//        printf("%d ",a[num]);
//     } 
//    
//    
}

脚本2

#include<stdio.h>
int main(void) 
{
    int a[22]={18,14,40,-14,-2,30,10,42,35,48,43,49,52,72,57,68,86,145,115,128,115,86};
    int b[22]={101 ,103 ,107 ,109 ,113 ,127 ,131 ,137 ,139 ,149 ,151 ,157 ,163, 167 ,173 ,179 ,181 ,191 ,193 ,197 ,199 ,211};
    int c[22]={0};
    int i;

    for(i=0;i<22;i++)
    {
        c[i]=b[i]-a[i];
        printf("%c",c[i]);
    }
}

得到flag
SYC{say_hello_to_.NET}

8.刘壮的BaseXX

一.调试与代码分析

拿到exe文件,直接拖到IDA里面看伪代码,如下

__int64 sub_1400014E0()
{
  void *v1; // [rsp+28h] [rbp-B0h]//定义了一个数组
  signed __int64 v2; // [rsp+30h] [rbp-A8h] //一个整型变量,应该是数组的长度
  char v3[128]; // [rsp+40h] [rbp-98h]//又定义了一个数组

  memset(v3, 0, sizeof(v3));
  v1 = (void *)sub_140004EF4(256i64);
  sub_140003498(aYouMightKnowBa);
  while ( 1 )
  {
    while ( 1 )
    {
      sub_140001060((__int64)aInputYourFlag);//叫你输入flag
      sub_140004ED8((__int64)v3, 128i64);//将flag赋给v3
      v2 = -1i64;
      do
        ++v2;
      while ( v3[v2] );//应该算v2的值,计算数组长度
      sub_140001420((__int64)v3, v2);//第一步加密,异或和位运算,需要进一步看子函数。
      memset(v1, 0, 0x100ui64);
      sub_1400010E0((__int64)v3, (__int64)v1, v2);//改了base64编码索引表的加密方式,将v3再次加密,需要进一步看子函数。
      if ( v1 )
        break;
      sub_140003498(aUnExpectedErro);
    }
    if ( (unsigned int)sub_140001490((unsigned __int8 *)v1) )//将最后的v1和设定的字符串进行比较,如果对了就会跳出循环,说明正确,设定的字符串为 maj7TmztjquUN8Xm-hKplvaYfEAxrUnIc51qxlKOwCKN4XsdzBmjOd_-  ,长度为56,也可通过这个来计算v3长度为42。
      break;
    sub_140001060((__int64)aWrongFlagTryAg);
  }
  sub_140001060((__int64)aCorrectYouAreR);
  sub_140004EE0(v1);
  sub_140004C24(aPause);
  return 0i64;
}

重要子函数1

__int64 __fastcall sub_140001420(__int64 a1, unsigned int a2)//a1是v3,也就是加密后的,v2是数组长度。
{
  __int64 result; // rax
  signed int i; // [rsp+4h] [rbp-14h]

  for ( i = 0; ; ++i )
  {
    result = a2;
    if ( i >= (signed int)a2 )
      break;
    *(_BYTE *)(a1 + i) = i ^ ((*(_BYTE *)(a1 + i) >> 4) | 16 * *(_BYTE *)(a1 + i));//加密方式,注意其中的16*,意思是<<4,如果是8*的话,是<<3。
  }
  return result;
}

重要子函数2,整体的加密过程不做过多分析

__int64 __fastcall sub_1400010E0(__int64 a1, __int64 a2, int a3)//a1是v3,是加密前的,a2是v2是加密后的,a3是原来的长度。
{
  int v3; // ST00_4
  int v4; // ST00_4
  int v5; // ST00_4
  int v7; // [rsp+0h] [rbp-18h]
  int v8; // [rsp+4h] [rbp-14h]
  int v9; // [rsp+8h] [rbp-10h]

  v9 = a3 % 3;
  v8 = 0;
  v7 = 0;
  while ( v8 < a3 - v9 )
  {
    *(_BYTE *)(a2 + v7) = aZyxwvutsrqponm[(signed int)*(unsigned __int8 *)(a1 + v8) >> 2];
    v3 = v7 + 1;
    *(_BYTE *)(a2 + v3++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 1) & 0xF0) >> 4) | 16 * (*(_BYTE *)(a1 + v8) & 3)];
    *(_BYTE *)(a2 + v3++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 2) & 0xC0) >> 6) | 4
                                                                                    * (*(_BYTE *)(a1 + v8 + 1) & 0xF)];  //4*是<<2。
    *(_BYTE *)(a2 + v3) = aZyxwvutsrqponm[*(_BYTE *)(a1 + v8 + 2) & 0x3F];
    v7 = v3 + 1;
    v8 += 3;
  }
  if ( v9 == 1 )
  {
    *(_BYTE *)(a2 + v7) = aZyxwvutsrqponm[(signed int)*(unsigned __int8 *)(a1 + v8) >> 2];
    v4 = v7 + 1;
    *(_BYTE *)(a2 + v4++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 1) & 0xF0) >> 4) | 16 * (*(_BYTE *)(a1 + v8) & 3)];
    *(_BYTE *)(a2 + v4++) = 61;
    *(_BYTE *)(a2 + v4) = 61;//两个61代表加两个==
    v7 = v4 + 1;
  }
  else if ( v9 == 2 )
  {
    *(_BYTE *)(a2 + v7) = aZyxwvutsrqponm[(signed int)*(unsigned __int8 *)(a1 + v8) >> 2];
    v5 = v7 + 1;
    *(_BYTE *)(a2 + v5++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 1) & 0xF0) >> 4) | 16 * (*(_BYTE *)(a1 + v8) & 3)];
    *(_BYTE *)(a2 + v5++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 2) & 0xC0) >> 6) | 4
                                                                                    * (*(_BYTE *)(a1 + v8 + 1) & 0xF)];
    *(_BYTE *)(a2 + v5) = 61;
    v7 = v5 + 1;
  }
  return (unsigned int)v7;
}

其中最关键的还是那个 zyxwvutsrqponmlkjihgfedcbaABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-_ ,这是将编码的索引表改了。

// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE *v4; // [rsp+20h] [rbp-30h]
  int v5; // [rsp+2Ch] [rbp-24h]
  int v6; // [rsp+44h] [rbp-Ch]
  int v7; // [rsp+48h] [rbp-8h]
  int i; // [rsp+48h] [rbp-8h]
  int v9; // [rsp+4Ch] [rbp-4h]

  _main(*(_QWORD *)&argc, argv, envp);
  v5 = strlen("Happy birthday! yangtianci!");
  if ( v5 % 3 )
    v9 = 4 * (v5 / 3 + 1);
  else
    v9 = 4 * (v5 / 3);
  v4 = malloc(v9 + 1i64);
  v4[v9] = 0;
  v7 = 0;
  v6 = 0;
  while ( v7 < v5 )
  {
    v4[v6] = aAbcdefghijklmn[(char)(aHappyBirthdayY[v7] >> 2)];
    v4[v6 + 1] = aAbcdefghijklmn[16 * (aHappyBirthdayY[v7] & 3) | (char)(aHappyBirthdayY[v7 + 1] >> 4)];
    v4[v6 + 2] = aAbcdefghijklmn[4 * (aHappyBirthdayY[v7 + 1] & 0xF) | (char)(aHappyBirthdayY[v7 + 2] >> 6)];
    v4[v6 + 3] = aAbcdefghijklmn[aHappyBirthdayY[v7 + 2] & 0x3F];
    v7 += 3;
    v6 += 4;
  }
  if ( v5 % 3 == 1 )
  {
    v4[v9 - 2] = 61;
    v4[v9 - 1] = 61;
  }
  else if ( v5 % 3 == 2 )
  {
    v4[v9 - 1] = 61;
  }
  for ( i = 0; i < v9; ++i )
    putchar((unsigned __int8)v4[i]);
  return 0;
}

可以将自己写的base编码放到IDA里面看,与题中对比,大同小异。

总结:看伪代码需要明确知道定义的各个变量的含义,以及放到子函数总哪个变量对应哪个变量,慢慢分析。

写脚本

写脚本需要掌握base64解码

#include <stdio.h>
#include <stdlib.h>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char *argv[]) {
        char *str1="maj7TmztjquUN8Xm-hKplvaYfEAxrUnIc51qxlKOwCKN4XsdzBmjOd_-";
        unsigned char *str2;
        int table[]={0,0,0,0,0,0,0,0,0,0,0,0,
             0,0,0,0,0,0,0,0,0,0,0,0,
             0,0,0,0,0,0,0,0,0,0,0,
             0,0,0,0,0,0,0,0,0,0,62,0,
             0,61,60,59,58,57,56,55,
             54,53,52,0,0,0,0,0,0,0,26,
             27,28,29,30,31,32,33,34,35,36,37,38,
             39,40,41,42,43,44,45,46,47,
             48,49,50,51,0,0,0,0,63,0,25,
             24,23,22,21,20,19,18,17,16,
             15,14,13,12,11,10,9,8,7,
             6,5,4,3,2,1,0
               }; //换了之后的索引表
        int len1;
        int len2;
        int i,j; 

        len1=strlen(str1);
        if(strstr(str1,"=="))
        {
            len2=(len1/4)*3-2;
        }    
        else if(strstr(str1,"="))
        {
            len2=(len1/4)*3-1;
        }
        else
        {
            len2=(len1/4)*3;
        }

        str2=malloc(sizeof(unsigned char)*len2+1);  
        str2[len2]='\0';

        for(i=0,j=0;i<len1-2;i+=4,j+=3)
        {
            str2[j]=((table[str1[i]])<<2)|((table[str1[i+1]])>>4);
            str2[j+1]=(table[str1[i+1]]<<4)|((table[str1[i+2]])>>2);
            str2[j+2]=((table[str1[i+2]])<<6)|(table[str1[i+3]]);
        }

    //    printf("%d",len2); 
        for(i=0;i<len2;i++)
        {
              printf("%d ",str2[i]);
           }

           printf("\n") ;

        //第一步base解码

           for(i=0;i<len2;i++)
           {
               str2[i]=((str2[i]^i)<<4)|((str2[i]^i)>>4); //第二步解密。
               printf("%c",str2[i]);
           }

    return 0;
}

三.得到flag

SYC{M0dified_B@se64_is_Sti11_S1mpl3_Right}

9.un_snake

一.反汇编

首先下载下来发现是pyc的文件,网上一搜需要反编译,用网上在线编译,发现少了很多代码。然后用uncompyle6进行反编译

uncompyle6 -o  C:\Users\hp\Downloads\un_snake.cpython-38.py C:\Users\hp\Downloads\un_snake.cpython-38.pyc

在cmd中输入这串代码如果这样有

# Successfully decompiled file

代表成功了

二.分析代码

将反编译出来的.py文件后缀名改为.c。为了好看一点,打开如下

# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
# Embedded file name: ./un_snake.py
# Compiled at: 2020-08-05 16:20:40
# Size of source mod 2**32: 1238 bytes
import this
from base64 import *

def pre(data):
    th1s = 'TBESCFSRSAEUITANAIIN'.encode()
    if (data_len := len(data)) > (th1s_len := len(th1s)):
        th1s = th1s * (data_len // th1s_len) + th1s[:data_len - th1s_len]     #这个if主要是将date的长度弄的和th1s一样
    return bytes(map(lambda x, y: x ^ y, data, th1s))  #再算stuff.encode()


def enc(plain):
    plain = list(plain)
    plain = plain[::-1]  #将字符串反着排序 
    for i in range(len(plain)):
        c = plain[i]
        plain[i] = (c << 3 | c >> 5) & 255   #先执行for循环,再执行else里面的
    else:
        for i in range(len(plain) - 1):
            plain[i] ^= plain[(i + 1)]#异或运算,将plain加密了
        else:
            return bytes(plain)  #再算stuff_ready 


def check(a):
    return b64encode(a) == b'mEiQCAjJoXJy2NiZQGGQyRm6IgHYQZAICKgowHHo4Dg='  //base64解码,先算enc(stuff_ready) 


if __name__ == '__main__':
    print()
    while True:
        stuff = input('Now input you flag:')   //输入的flag,最终目标 
        stuff_ready = pre(stuff.encode())
        result = check(enc(stuff_ready))
#其实就是3个逆向的问题,一步一步往上推,每一个函数都代表一个加密,其中第2个加密最复杂
        if result:
            print('You get it! Python is so charming right?')
            break
        else:
            print('Failed, try again!')

    print('[馃悕] Commit you flag, see you next time!')

看到这个Python代码,开始什么感觉有些地方看不懂,网上查了一部分,Python自学了一部分(也就学到了列表),然后进行分析个大概思路,简略分析在上面的代码后面,下面是查找的一些内容。

1.plain = plain[::-1]

b = a[i:j]   表示复制a[i]到a[j-1],以生成新的list对象

a = [0,1,2,3,4,5,6,7,8,9]
b = a[1:3]   # [1,2]
当i缺省时,默认为0,即 a[:3]相当于 a[0:3]
当j缺省时,默认为len(alist), 即a[1:]相当于a[1:10]
当i,j都缺省时,a[:]就相当于完整复制一份a

b = a[i:j:s]表示:i,j与上面的一样,但s表示步进,缺省为1.
所以a[i:j:1]相当于a[i:j]
当s<0时,i缺省时,默认为-1. j缺省时,默认为-len(a)-1
所以a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍,即倒序。

2.map(lambda x, y: x ^ y, data, th1s)

https://blog.csdn.net/landing_guy_/article/details/109331685

3.for循环后面直接接else(主要是学Python没见过,不知道怎么走)

https://blog.csdn.net/wj1066/article/details/81913696

三.写脚本

然后开始写脚本,由于本人很菜所以,将base64解码分开写的。

由于网上直接base64解码,会出现乱码,所以在Python中解码,代码如下

import base64
url = "mEiQCAjJoXJy2NiZQGGQyRm6IgHYQZAICKgowHHo4Dg="
str_url = base64.b64decode(url)
print(str_url)

得到结果

b'\x98H\x90\x08\x08\xc9\xa1rr\xd8\xd8\x99@a\x90\xc9\x19\xba"\x01\xd8A\x90\x08\x08\xa8(\xc0q\xe8\xe08'

是16进制和一些字符的组合,全部换为10进制(因为我比较喜欢10进制),注意最后的那个8,它转换为10进制是56,就是这一个数,会让你得不到flag(本人深有体会)。转出来如下

a[]={152,72,144,8,8,201,161,114,114,216,216,153,64,97,144,201,25,186,34,1,216,65,144,8,8,168,40,192,113,232,224,56}

然后进行第二,三步解密

#include<stdio.h>
#include <string.h>

int main(void)
{
int a[]={152,72,144,8,8,201,161,114,114,216,216,153,64,97,144,201,25,186,34,1,216,65,144,8,8,168,40,192,113,232,224,56};//base64解码后的10进制。

int b[]={84,66,69,83,67,70,83,82,83,65,69,85,73,84,65,78,65,73,73,78,84,66,69,83,67,70,83,82,83,65,69,85};//这是上面Python中的th1s换成了10进制,可以另外写一个脚本吧上面th1s用整型输出,就不用一个一个写。因为base64解码后的长度为64,故b[]的长度也必须要是32,也就是原来的基础上加上前12个字符。
int i,j;
int c,t,len;

len=sizeof(a)/sizeof(int);//求出解码后的长度,32

printf("%d",len);
printf("\n");

for(i=0;i!=len;i++)
{
    printf("%d,",a[i]);
}
printf("\n");
printf("%d",a[31]);
printf("\n");

for(i=len-2;i!=-1;i--)
{
    a[i]=a[i]^a[i+1];//反向异或回去,可以自己举4个数来看看是不是真的可以反向异或回去。
}

for(i=0;i!=len;i++)
{
    printf("%d,",a[i]);
}
printf("\n");

for(i=0;i!=len;i++)
{
    a[i]=((a[i]<<5)|(a[i]>>3))&255;//进行位移运算,也可以举例看看怎么弄回去。注意的是其中的&,作用为:主要是限定范围。不会得到大于255的数。
    printf("%d,",a[i]);
}//输出位移后的数组 

printf("\n");
for(i = 0; i < (len-1)/2; i++)
{
    t = a[i];
    a[i] = a[len-i-1];
    a[len-i-1] = t;
}//将字符串倒序 

for(i=0;i!=len;i++)
{
    printf("%d,",a[i]);
}

printf("\n");
for(i=0;i!=len;i++)
{
    a[i]=a[i]^b[i];
    printf("%c",a[i]);
}//输出最后的flag

附加脚本

16进制转10进制
#include <stdio.h>

int main(void)

 {

    int i;
    int a[]={0x98,0x48,0x90,0x08,0x08,0xc9,0xa1,0x72,0x72,0xd8,0xd8,0x99,0x40,0x61,0x90,0xc9,0x19,0xba,0x22,0x01,0xd8,0x41,0x90,0x08,0x08,0xa8,0x28,0xc0,0x71,0xe8,0xe0,0x38};
    for(i=0;i!=32;i++)
    {
        printf("%d,",a[i]);
    }
    printf("\n");
    return 0;
}

th1s转10进制

#include<stdio.h>

int main(void)
{
    char a[]="TBESCFSRSAEUITANAIINTBESCFSRSAEU";
    int i;
    for(i=0;a[i]!='\0';i++)
    {
        printf("%d,",a[i]);
    }
    return 0;
}

四.得到flag

SYC{ssssss_Th1sfPs_Pyth0n_ssss~}#我得到的是这个,可能上面某个位置数据错了,看了下flag,直接改一下得到下面的flag。

SYC{ssssss_Th1s_is_Pyth0n_ssss~}

Misc题目

1.一“页”障目

直接把宣传单对折,得到flag

2.壮言壮语

直接网上佛曰解密,得到flag

我刘壮就是np,给你flag吧,SYC{i_l0ve_Japanese_wife}

3.秘技·反复横跳

网上根据提示搜索binwalk。
https://www.jianshu.com/p/03f15e6fedf3
一道类似的题,在ubuntu中执行网站中的操作就可以了,最后分解图片会得到一个二维码,但是是左右交换了的,也算点题把,交换回来,微信扫一扫,就可以得到flag了

SYC{L3ft~Le4t~R1gHt~RiGhT}

4.飞翔的刘壮

题目提示: Welcome “into” 11th Geek Challeng.这里的into就应该代表的是10。
一个游戏,下载到手机上,在得到10分的时候死了,就可以得到flag

SYC{fl4ppyb1rd_1s_s0_inter3tin9!}

5.来拼图

就是从众多图片中选出有flag信息的拼在一起,拼出来有点不好看

SYC{d60fca51c7e61259191f0}

6.吉普赛的歌姬

不知道这道题考什么,考信息收集能力?去QQ空间看看,然后知道flag在相册里面,然后根据说说前两条,先到贴吧看看,然后搜索DJ南方,然后叫你去网易找伪·Nightcore这个电台,(中间还会遇到两个base64加密的假flag,人心险恶)找一首叫吉普赛歌姬的歌(歌里面什么也没有),但是歌下面有评论说,密码是名字+生日,然后进入相册,得到一张图片,上面有flag

SYC{Fr1endsh1p_1s_w1tchcr@ft}

pwn题目

1.数学咋样?

用ubuntu打开端口

luckyboy@luckyboy-virtual-machine:~$ nc 81.69.0.47 1111
------------------------------------------
Can you help me to solve my math problem?
------------------------------------------
I have 20 tests
![0]  num_1 = 822, num_2 = 701
I can't calculate the expression 'num_1 + num_2'.
input your answer:

进行20次计算,最后得到flag

2.runcode

考点是读取一个文件并输出,百度搜索一下怎么读取一个文件
https://blog.csdn.net/qq_26853817/article/details/79756617,根据这个来写脚本。如下

#include <stdio.h> 

int main(void)
{
    char a[200] = {0};
    FILE *fp = fopen("/home/ctf/flag", "r");

    fgets(a, 100, fp);
    puts(a);

    return 0; 
}

注意数组长度要足够长,不然读不完。放到网站里面run,run两次,得到flag

SYC{C0din9_ls_E4sy_T0_You}

密码学题目

1.二战情报员刘壮

摩氏密码,直接网上在线解密,得到L1UZHU4NG_I_Z1Y1,得到flag

SYC{L1UZHU4NG_I_Z1Y1}

2.铠甲与萨满

凯撒密码,直接网上在线解密,先根据前三个字母确定位移为6,也可以一个一个试,解密出来的flag为

SYC{liuzhuangliuzhuang_bangbangbang}

3.跳跃的指尖

跳跃的指尖,需要脑洞大一点,就是键盘啦,每一段字母都会围一个单词,写出所有单词就是flag

SYC{easyCrypto}

4.成都养猪二厂

猪圈密码和栅栏密码,网上在线解密,先把图片中的猪圈密码解出来得到ssyirceehsagiulisolbhy,然后根据那个txt里面的内容确定栅栏密码的位移量为7,网上在线解密,得到flag

SYC{his_house_is_really_big}

5.规规矩矩的工作

看题目和提示,应该是考矩阵和希尔密码,直接在网上解矩阵,https://matrix.reshish.com/,得到

x1 = 10
x2 = 4
x3 = -2

A=0,B=1,C=2……然后换为字母,为KEY,用cmd打开decode_machine.exe(要不然直接打开好像会闪退),输入KEY,得到flag

SYC{linear_algebra_make_ctf_great_again}

6.Simple calculation

这道题部分靠运气,直接解矩阵,得到

x1 = 77/2
x2 = -48
x3 = -21
x4 = 65
x5 = -63/2

有分数,x1和x5分数求余的话好像网上找了很多也没有解出来,然后试着将,3 7 1 1 20换为29 7 1 1 -6(实际上试了很多数,加上一点点LOL基础),解出来得到

x1 = 19
x2 = 30
x3 = 18
x4 = -65
x5 = 27

全部换到0~26范围,得到flag

SYC{TESNB}

7.韡髻猊岈

Vigenere解密,网上自己找资料,了解原理,Vigenere解密第一步要确定密钥长度,第二步要算出密钥是什么,这个要知道原理和对照那个Vigenere表来算密钥。
首先根据相同的单词算出密钥长度为6,在根据加密内容中的TGP(解密出来肯定是SYC),算出半个密钥为bin,然后开始在bin前面加aaa变成aaabin,去解密,发现SYC后面的英文为

vhxre iu nh teats bn viiegere,

那个iu,肯定是is,通过这个又算出一个密钥的字母caabin,在用这个来解密,得到

thxre is nh tears bn vigegere

这个thxre肯定是there,从而算出密钥为catbin,得到flag

SYC{there_is_no_tears_in_vigenere}