2019 KCTF 晋级赛Q1 | 第六题点评及解题思路
Happy Fool's Day~愚人节快乐!今天你有被套路吗?
今天我们不开玩笑,来认认真真看KCTF 第六题《REPWN》不知道这道题有没有让你怀疑人生呢?
本题围观人数为2553人,共有94支队伍攻破成功,第一血由“pizzatql”用时13786s摘得。
出题战队
战队成员:findreamwang
个人主页:https://bbs.pediy.com/user-739734.htm
个人简介:HaCky,目前就读于西安某高校网络工程专业,现在主要的研究方向是病毒分析和内核研究(即将要学),大一开始学习安全技术。第一次参加KCTF,编码方面多有不足,请各位大佬轻打!
看雪CTF crownless 评委 点评
此题使用了pwn题和re(逆向)题相结合的方式,还涉及了DES加密的识别,考察了DES的理解。总的来说,是一道考察选手综合能力的好题目。
题目设计思路
题目信息
参赛题目:Repwn
题目答案:
20101001X1Y0uN3tG00dHaCkWel1C0me
题目设计
本题使用的Pwn题和Re题相结合的方式,考察选手的综合能力。
首先,编码不加任何混淆和反调试,题目友好,在入口处不对字符串设置加密。
其次main流程简单,易于理解,设置两层check
逆向思路简单,难度小。
同时也有计算题,此处不设置麻烦的运算,这样无趣。
最后需要构造溢出点,这样更加贴近实战,写exp等。
然后是溢出的地方,这里涉及到DES的识别问题,可以人工识别,最好是软件识别。本人菜鸡就不想搞变形的DES,这样难度会挺大的,能力也有限。
另外,对关键的字符串进行加密防止直接找到入口点,例如恺撒,异或。
同时也考察了DES基础理解,因为有个比较cmphex是40个,但是des只有32个字节。哈哈,小心机一把。
格式为第一次输入+第二次输入
破解思路
0x01 查壳
不存在任何壳。
0x02
拖入IDA中看到,定位到主函数,可以看到程序经过了两层验证check1和check。
0x03 过check1
得到大概key为xxxxxxxx(8位)X1Y0uN3tG00dxxx(x为未知数据,长度未知)
0x04 过check2
首先长度为24,进入check2_1
首先将输入转化为Input,然后初始化数字X,Y,Z,我们可以知道x,y,z的生成关系如下:x是input的前四个构成一个4位数,y是input[4-5]构成的2位数,z是input[6-7]构成的2位数,然后解方程,很简单。
得到Input为20101001X1Y0uN3tG00dXXX
然后到达溢出点,目标是寻找溢出点,这个有点坑,方法不唯一,我的想法是这样的:首先查壳的时候发现有DES加密函数,一直交叉引用到溢出点:401C1
现在的问题是如何达到溢出点。00401C10,又由于小端序的问题,顺序应该是101C4000,也就是说input[23]-47=00,也就是说input[23]为47的ASCII,同理,溢出点设计为#$@/,Key为20101001X1Y0uN3tG00d#$@/
0x5 溢出后
之前,我们知道了DES加密。得到加密过程如下
末尾有个比较
引用查看一下:unk_406010,整理出来是40位:但是我们知道DES加密可能会生成32位,不可能生成40位的,所以我们去其32位。秘钥为XiyouNet,DES解密得到:Wel1C0m
解密
最后也提示了flag格式,所以flag为20101001X1Y0uN3tG00dHaCkWel1C0m
解题思路
本题解题思路由看雪论坛 scpczc 提供
1、运行
Please Input Your Key_ Now!
123456789
Ansome_Is_Wrong
2、IDA载入,快速定位
可以看到获取输入,对输入判断后,错误输出错误信息,否则进入另外的流程。
int sub_4014C0()
{
unsigned int v0; // ebx
char Str[4]; // [esp+10h] [ebp-58h]
CHAR v3[32]; // [esp+20h] [ebp-48h]
char a1; // [esp+40h] [ebp-28h]
sub_404930();
sub_4044B0();
v0 = 0;
strcpy(v3, "^ty\x7FcLp+GS+aGQ-GV(lGJ}y))AGS+ae");
strcpy(Str, "Ansome_Is_Wrong");
while ( v0 < strlen(v3) )
{
v3[v0] ^= 0x18u;
++v0;
}
puts("Please Input Your Key_ Now!");
scanf("%s", &a1);
if ( sub_4012F0(&a1) )
{
sub_401460(&a1);
system("pause");
}
else
{
puts(Str);
}
return 0;
}
3、先用X64DBG修改跳转爆破试试。
输出长度错误,说明sub_401460内部还有判断。
004015B1 74 14 je repwn.4015C7 75->74
4、进入第一个判断函数
发现第9位开始后的12位为X1Y0uN3tG00d。第21位为H。
signed int __cdecl sub_4012F0(char *a1)
{
signed int v1; // ecx
signed int v2; // edx
signed int result; // eax
char v4[12]; // [esp+0h] [ebp-38h]
CHAR v5[20]; // [esp+10h] [ebp-28h]
v1 = 8;
v2 = 0;
strcpy(v5, "Your_Input_Is_Wrong");
strcpy(v4, "X1Y0uN3tG00d");
while ( v4[v2] == a1[v1] )
{
++v2;
++v1;
if ( v2 > 11 )
{
result = 1;
if ( a1[20] == 'H' )
return result;
return 0;
}
}
return 0;
}
5、进入另一个判断函数
发现长度必须为24.后四位分别减去 88, 70,3,‘k’,另有玄机,暂不表。
int __cdecl sub_401460(char *Str)
{
char Dest; // [esp+8h] [ebp-10h]
if ( strlen(Str) == 24 )
{
if ( sub_4013B0((int)Str) )
{
Str[20] -= 88;
Str[21] -= 70;
Str[22] -= 3;
Str[23] -= 'k';
strcpy(&Dest, Str);
}
}
else
{
printf("String Length is Wrong");
}
return 0;
}
6、进入sub_4013B0
前8位需要解方程。这是为了保护小学生不搞破解?
2(a+b)= 4040
3b/2+100c=115
a-110c=1900
a=2010
b=10
c=1
所以前8位为20101001。
signed int __cdecl sub_4013B0(int a1)
{
int v1; // ebx
int v2; // ecx
int v3; // esi
signed int result; // eax
sub_401380(a1);
v1 = dword_40802C + 1000 * dword_408020 + 100 * dword_408024 + 10 * dword_408028;
v2 = dword_408034 + 10 * dword_408030;
v3 = dword_40803C + 10 * dword_408038;
if ( 2 * (v1 + v2) != 4040 || 3 * v2 / 2 + 100 * v3 != 115 )
goto LABEL_2;
result = 1;
if ( v1 - 110 * v3 != 1900 )
{
printf("Key_Is_Wrong,Please_Input_Again!");
LABEL_2:
result = 0;
}
return result;
}
20101001X1Y0uN3tG00dH???
随机输入一个,发现程序异常了,多次调试发现在sub_401460返回时错误。Strcpy会溢出,覆盖掉原来的地址。
要想不异常,返回的地址必须在程序空间内,那么大致为004XXXXX,第一个X最大可能为0,而这个值加上 B6 03 46 58,就是最后4位。那么结果就H?Ck。‘H’-58为F0,就是说返回地址为0040??F0.检测测试发现输入HaCk正确。
所以结果为:20101001X1Y0uN3tG00dHaCk
结果显示:Success_Please_Input_The_Flag
请按任意键继续. . .
看雪CTF晋级赛Q1 题解列表
- End -
公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com