看雪.纽盾 KCTF 2019 Q2 | 第六题点评及解题思路

发布者:Editor
发布于:2019-07-01 18:14

今天是很特别的一天呢,七月一日,一周的第一天,一月的第一天,下半年第一天,建党98周年,香港回归的22周年。


小伙伴们周末愉快玩耍时,有没有看我们公布的KCTF英雄榜单呢?你是否榜上有名呢?


一路走来,我们接到神秘来信,唤醒沉睡的敦煌,破解金字塔的诅咒,打开神秘的达芬奇密码,昨天我们一起探索了丛林的秘密,今天让我们一起来解析第六题,寻找消失的岛屿~


题目简介


题目背景:


走出森林,是洁白的沙滩,湛蓝的海水,和晒太阳的螃蟹。远处影影绰绰的有几座小岛。突然,浓墨色的黑烟正在慢慢的向这边的天空渲染。外星人的战舰在黑云中若隐若现,并越发密集。远方的岛屿正在慢慢沉入海水之中,脚下的沙滩也慢慢的被海水覆盖,海水正在吞噬这些岛屿,留给我们的时间不多了。


在这些众多的岛屿之中,究竟哪一个藏了宝石?你又该怎样前往这些岛屿呢?



本题共有2170人围观,最终有153支团队攻破成功。这道题是除第一题外,攻破人数最多的一道题,相对来说,这道题还是比较简单的。


攻破此题的战队排名一览:



接下来我们来对题目进行解析,破解其中的奥秘。


看雪评委crownless点评


本题考查了修改过编码table的base64算法,重点在于单个字符的变换强度。真实的编码table从头到尾不会在内存中显示。所以攻击者需要先将断点设置在charEncrypt处,找出编码的变换规则,然后找到修改过后的编码table,根据变换规则推导出真正的编码table。


出题团队简介


本题出题战队 iret :



iret 团队简介:某安全公司实习生,目前正在学习逆向和木马分析,希望能从各位大佬身上学习并提升自己。



设计思路


该题目为base64魔改的CrackMe。


首先定义了一个自定义的base64编码table:


#defineTABLE1"tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/"


然后定义了一个单个字符加密的方法:


staticcharcharEncrypt(intdata)
{
char*table = TABLE1;
data = table[data];
if(data>=65&& data<=90)
{
data = (155-data) ;
return(char)data;
}
if(data>=97&&data<=122)
{
data = (data-64);
return(char)data;
}
if(data>=48&&data<=57)
{
data = (data +50) ;
return(char)data;
}
if(data==43)
{
data =119;
return(char)data;
}
if(data==47)
data =121;
return(char)data;
}


接下来使用c语言实现了base64编码,不仅使用修改后的编码table,还会在赋值的时候调用单个字符加密方法将字符加密后赋值。

破解思路:


本题的重点在于单个字符的变换强度。真实的编码table从头到尾不会在内存中显示。所以攻击者需要先将断点设置在charEncrypt处,找出编码的变换规则,然后找到修改过后的编码table,根据变换规则推导出真正的编码table。

完整代码:


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<inttypes.h>
#defineTABLE1"tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/"

/*base64编/解码用的基础字符集*/


staticcharcharEncrypt(intdata)
{
char*table = TABLE1;
data = table[data];
if(data>=65&& data<=90)
{
data = (155-data) ;
return(char)data;
}
if(data>=97&&data<=122)
{
data = (data-64);
return(char)data;
}
if(data>=48&&data<=57)
{
data = (data +50) ;
return(char)data;
}
if(data==43)
{
data =119;
return(char)data;
}
if(data==47)
data =121;
return(char)data;
}

staticintbase64_encode(constuint8_t*bindata,char*base64,intbinlength)
{
inti, j;
uint8_tcurrent;
for( i =0, j =0; i < binlength ; i +=3) {
current = (bindata[i] >>2) ;
current &= (uint8_t)0x3F;
base64[j++] = charEncrypt((int)current);
current = ( (uint8_t)(bindata[i] <<4) ) & ( (uint8_t)0x30) ;
if( i +1>= binlength ) {

base64[j++] = charEncrypt((int)current);
base64[j++] ='=';
base64[j++] ='=';
break;
}
current |= ( (uint8_t)(bindata[i+1] >>4) ) & ( (uint8_t)0x0F);

base64[j++] = charEncrypt((int)current);
current = ( (uint8_t)(bindata[i+1] <<2) ) & ( (uint8_t)0x3C) ;
if( i +2>= binlength ) {

base64[j++] = charEncrypt((int)current);
base64[j++] ='=';
break;
}
current |= ( (uint8_t)(bindata[i+2] >>6) ) & ( (uint8_t)0x03);
base64[j++] = charEncrypt((int)current);
current = ( (uint8_t)bindata[i+2] ) & ( (uint8_t)0x3F) ;

base64[j++] = charEncrypt((int)current);
}
base64[j] ='\0';
returnj;
}
intmain(intargc,char**argv)
{
charstr1[55];
printf("please enter Serial:");
scanf(" %s",str1);
if(strlen(str1)>=50)
{
printf("error\n");
exit;
}
char*base64_str =calloc(1,1024);
base64_encode(str1, base64_str,strlen(str1));
char*str ="!NGV%,$h1f4S3%2P(hkQ94==";
if(!strcmp(str,base64_str))
{
printf("Success\n");
}else{
printf("Please Try Again\n");
}

free(base64_str);
system("pause");
return0;
}


解题思路


本题解题思路由看雪论坛qwertyaa提供:



可以看到这个程序主要是 BASE64 的变体,它里面的 charEncrypt 如下:


charfrom[]="tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/";
charcharEncrypt(chardata)
{
chardataa;
dataa =from[data];
if( dataa >'@'&& dataa <='Z')
return-101- dataa;
if( dataa >'`'&& dataa <='z')
returndataa -64;
if( dataa >'/'&& dataa <='9')
returndataa +50;
if( dataa =='+')
return'w';
if( dataa =='/')
dataa ='y';
returndataa;
}


而普通的 BASE64 的 charEncrypt 如下:


charreb[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
charcharEncrypt(chardata)
{
chardataa;
dataa = reb[data];
returndataa;
}


两个 charEncrypt 都是置换,对每个字符求第一个置换的逆置换后进行第二个置换的结果即可,代码如下:


#include<bits/stdc++.h>
usingnamespacestd;
charreb[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
charfrom[]="tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/";
charresult[]="!NGV%,$h1f4S3%2P(hkQ94==";
charcharEncrypt(chardata)
{
chardataa;
dataa = from[data];
if( dataa >'@'&& dataa <='Z')
return-101- dataa;
if( dataa >'`'&& dataa <='z')
returndataa -64;
if( dataa >'/'&& dataa <='9')
returndataa +50;
if( dataa =='+')
return'w';
if( dataa =='/')
dataa ='y';
returndataa;
}
charre[256];
intmain(){
for(inti=0;i<strlen(reb);i++){
re[charEncrypt(i)]=i;
}
intlen=strlen(result);
for(inti=0;i<len;i++)if(result[i]!='='){
result[i]=reb[re[result[i]]];
}
puts(result);
}


将结果用 javascript 的 atob 函数解码得FLAG: KanXue2019ctf_st。


END


1、【英雄榜单】看雪.纽盾 KCTF 晋级赛Q2 排行榜出炉!

2、看雪.纽盾 KCTF 2019 Q2 | 第一题点评及解题思路

3、看雪.纽盾 KCTF 2019 Q2 | 第二题点评及解题思路

4、看雪.纽盾 KCTF 2019 Q2 | 第三题点评及解题思路

5、看雪.纽盾 KCTF 2019 Q2 | 第四题点评及解题思路

6、看雪.纽盾 KCTF 2019 Q2 | 第五题点评及解题思路


主办方


看雪学院(www.kanxue.com)是一个专注于PC、移动、智能设备安全研究及逆向工程的开发者社区!创建于2000年,历经19年的发展,受到业内的广泛认同,在行业中树立了令人尊敬的专业形象。平台为会员提供安全知识的在线课程教学,同时为企业提供智能设备安全相关产品和服务。 



合作伙伴



上海纽盾科技股份有限公司(www.newdon.net)成立于2009年,是一家以“网络安全”为主轴,以“科技源自生活,纽盾服务社会”为核心经营理念,以网络安全产品的研发、生产、销售、售后服务与相关安全服务为一体的专业安全公司,致力于为数字化时代背景下的用户提供安全产品、安全服务以及等级保护等安全解决方案。




小手一戳,了解更多





声明:该文观点仅代表作者本人,转载请注明来自看雪