写在前面:首先感谢出题人和运维的辛苦付出,让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网站解密
或者使用前面所提及的博文中的脚本进行爆破
#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
最后贴上脚本
来源:oschina
链接:https://my.oschina.net/u/4413809/blog/4807708