2020 JUSTCTF F@k3 0ff1c@l REVERSE WP

谁说胖子不能爱 提交于 2020-12-16 03:29:46

写在前面:首先感谢出题人和运维的辛苦付出,让jkd有了第一届CTF,祝比赛越办越好

       第一次写wp,同时由于自己太菜,赛中很多是参考了类似题的wp才能做出来,大部分直接手撕。为了让这篇wp显得不那么拉跨,于是参考了某究极卷王黄爷爷的脚本和出题人原汁原味的两道wp。

1.

下载附件,点开一看

丢进exeinfope,是个64位的

打开ida64,shift+f12,搜索字符串

2.

应该是非预期解(我大概也没有哪个题是预期解

做题参考博文:

https://blog.csdn.net/xiangshangbashaonian/article/details/83476084?utm_source=app

(此处感谢安卓出题人,同样也是这道题的出题人,让孩子去你的博客找base64解法,没找到,却找到了这题的解法)

以下是反编译的代码

关键代码是第23行,需要将数组md5s的四组十进制数转回十六进制,再每组进行md5解密,最后拼在一起,就是flag

然后对着博文里的抄转十六进制的代码

第三组少了一位,应该是转十六进制的时候出了问题,赛后看了黄爷爷的wp后才知道最前面少了一个0,分组去在线md5网站解密

网站:https://pmd5.com/

或者使用前面所提及的博文中的脚本进行爆破

#937b8b318c5691f3 = JUST{
#b9ed7cb8a2f0bafe = you_a
#0e29cc9171a49daf = re_ri
#a99e9ee21f22d4d7 = ght_}
#flag=JUST{you_are_right_}

3.

题目提示使用uncompyle6的库进行反编译,没管(直接去了在线反编译网站

然鹅md5那题不能这么干,使用网站反编译出来的会少代码

这里放一下安装及使用的代码

pip install uncompyle6    #安装前记得安装pip

uncompyle6 models.pyc > models.py   #将models.pyc文件反编译为py文件

就一个异或,a^b=c,由于异或的特性,可以知道a^c=b,b^c=a,然后写脚本

4.

看题目就知道要脱壳

这里讲一下三类脱upx壳方式

①kali直接敲命令(小部分适用)

②工具脱壳(来自黄爷爷のwp)同样是小部分适用

脱壳程序:https://www.cr173.com/soft/10562.html

③手动脱壳(所有都适用)

自行谷歌百度(我还没学会/理直气壮

 

拖进exeinfo查壳(写wp才发现提示了脱壳方法)

脱壳完再次进行查壳,确保脱干净,脱不干净就只能手撸了(

丢ida,搜索字符串,找到核心代码

然后写脚本,也可以手撸,一个个异或过去(我这个菜鸡就是这么干的)

脚本来自黄爷爷

5.

培训讲过同类题,凭着一丢丢印象和wp做出来了

一通胡乱分析,知道是用aswd控制上下左右,I是入口,E是出口

根据最下面两行长一点的代码分析可知,迷宫为20x20,然后用记事本排列

此处是ida十六进制处找到的迷宫图

贴一下黄爷爷输出迷宫的代码 

#coding=utf-8
migong = [0x49, 0x5F, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x5F, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x2A, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x2A, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x5F, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x2A, 0x5F, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x2A, 0x2A, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x2A, 0x2A, 0x2A, 0x5F, 0x2A, 0x2A, 0x5F, 0x5F, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x5F, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x45, 0x2A, 0x2A]
print(len(migong))
for i in range(0,len(migong)):
	print(chr(migong[i]),end = '')
	if((i+1)%20==0): 
		print('\n')

6.

下载附件,丢进ida,进入主函数

进入getflag函数进行分析

定位v6,往上看可知v6为我们所需要输入的,即flag值

将v7设置为指定值,可知v7可从data_start_得到

最右边的一个个抄下来

最终真正有用的就这两行

求v6

写脚本(直接爆破)

贴一下出题源代码

#include <iostream>
#include <stdlib.h>
#include <cstring>
#include <exception>

using namespace std;

template <class T>
void getflag(T *key,int len) noexcept
{
    cout<<"Have a Guess of flag ?: ";
    char a[100]; memset(a, NULL, sizeof a);
    int i = 0;
    while(cin>>a[i] && cin.peek() != '\n')
    {
        i++;
        if(i==100)
        {
            cout<<"whatDoYouWantToDo?"<<flush<<endl;
            system("pause");
            exit(1);
        }
    }

    int flag[100]; memset(flag, NULL, sizeof flag);

    for(int j = 0 ; j < i+1 ; j++)
    {
        flag[j] = (int)a[j] >> 4 ^ (int)a[j];
    }

    for(int k = 0 ; k < len; k++)
    {
        if(flag[i] != key[i] || i != len - 1)
        {
            cout<<"TRY AGAIN!"<< flush << endl;
            system("pause");
            exit(1);
        }
    }

    cout<<"Good Job ! flag is:";
    for(char k:a)
    {
        cout<<k;
    }

    cout<<flush<<endl;
    system("pause");
    exit(0);
}


int main()
{
    int key[] = {78, 80, 86, 81, 124, 87, 99, 90, 82, 77, 81, 76, 90, 93, 75, 87, 90, 86, 65, 65, 73, 86, 90, 67, 75, 75, 64, 35, 122} ;
    try{
        getflag<int>(key,sizeof(key)/ sizeof(key[0]));

    }catch(exception &e){
        cout<<e.what()<<flush<<endl;
        return 1;
    }
    return 0;
}

以上是虚假的wp,下面是真正的wp(来自出题人)

7, rand

这道题的灵感是在之前做题的时候发现的,需要初始化随机数发生器,srand()里面的参数一般用时间(毕竟时间不可逆)达到真正的初始化,然而,如果没有达到初始化(或者是每次的初始化的参数是固定的,那么接下来生成的随机数是相同的)比如

 

这是初始化参数恒为0的情况

这个是利用时间生成一个参数,

 

其实这个题就是利用参数固定,会生成一个固定的数组

 

代码逻辑很简单,可以动态调试,也可以写个demo

 

利用注解解出字符,然后逆向分析就行了(a^b=c,那么a=c^b,b=c^a,这个需要说一下吧,别啥不啥就手撕了)顺便说一下,打开ida直接 shift+f12会查出一个假flag(这种题也就我这种菜逼出了)

8,压轴题

这道题是标准的c++代码写的,所以对于没见过的比较陌生

找到主函数

 

注意看有一个输入flag,然后v3=function1(flag);这个是主要逻辑,进函数看看

 

看这里,首先一个循环对输入的数据按位进行了异或,

然后把flag放入function2函数进行处理

打开function2

 

可以看到,把flag的每一位传入然后进行的操作,我们知道A的ascii是65,Z的ascii是90,

可以看到对大写字母的处理是转化成相应的小写字母(^0x20可以进行大小写转化,不信可以自己试试),然后-92,小写a的ascii为97,但是减去92之后%26,说明把字母向前移动了5位(凯撒加密)然后加上97(小写a的ascii),其实function2的函数逻辑很明显了,就是先大小写转换,然后字母向前移动5位

 

Function3的函数更简单,判断传入的参数是否相等

结合调用的function1函数可以看出,返回的就是加密的flag和s比较相同的个数

 

然后返回主函数

找到function4

就是计算flag的长度,

判断function1的返回数据是否是flag的长度

逻辑分析完毕

总结

就是把输入的flag按位异或他的下标+1

然后大小写转换,向前移动5位

然后等于s

最后贴上脚本

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!