当前位置: 首页 > news >正文

KCTF-遗世独立

最近在看雪玩KCTF,其中有一个Crypto叫遗世独立的,题面如下

$ ./cryptooo SECCON{ } Encrypted(44): waUqjjDGnYxVyvUOLN8HquEO0J5Dqkh/zr/3KXJCEnw= what's the key?

下载cryptooo,运行发现,结果不是提示的内容,答案应该是要找到正确的入参。

程序拖入IDA,里面有些红色报错,但不影响主逻辑,无视,主函数如下:

int __fastcall main(int a1, char **a2, char **a3)
{unsigned int v4; // [rsp+2Ch] [rbp-814h]char v5[2056]; // [rsp+30h] [rbp-810h] BYREFunsigned __int64 v6; // [rsp+838h] [rbp-8h]v6 = __readfsqword(0x28u);if ( a1 <= 1 )return 1;memset(v5, 0, 0x800uLL);v4 = sub_401373(a2[1], (unsigned int)strlen(a2[1]), v5, 2048LL);return printf("Encrypted(%d): %s\n", v4, v5);
}

核心逻辑sub_401373如下:

__int64 __fastcall sub_401373(__int64 a1, int a2, __int64 a3, int a4)
{int v5; // [rsp+2Ch] [rbp-4h]if ( a2 <= 0 )return 0xFFFFFFFFLL;v5 = 4 * (a2 / 3 + (a2 % 3 != 0));if ( v5 > a4 )return 0xFFFFFFFFLL;sub_400FCE(a1, (unsigned int)a2, a3);return (unsigned int)v5;
}

先计算输出字符串长度,明显的base64特征,逆向前已经试过,直接base64解码不是字符串。

再看sub_400FCE

unsigned __int64 __fastcall sub_400FCE(__int64 a1, signed int a2, __int64 a3)
{unsigned __int64 result; // raxunsigned __int64 v5; // [rsp+28h] [rbp-68h]unsigned __int64 v6; // [rsp+28h] [rbp-68h]unsigned __int64 v7; // [rsp+28h] [rbp-68h]int v8; // [rsp+30h] [rbp-60h]int i; // [rsp+34h] [rbp-5Ch]int v10; // [rsp+3Ch] [rbp-54h]int v11[18]; // [rsp+40h] [rbp-50h] BYREFunsigned __int64 v12; // [rsp+88h] [rbp-8h]v12 = __readfsqword(0x28u);qmemcpy(v11, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64);sub_400F3A(v11, 64LL, a1, a1, (unsigned int)a2);v10 = a2 % 3;v8 = 0;for ( i = 0; ; ++i ){result = (unsigned int)v8;if ( v8 >= a2 / 3 )break;v5 = *(unsigned __int8 *)(a1 + 3 * v8 + 2LL) | ((*(unsigned __int8 *)(a1 + 3 * v8 + 1LL) | ((unsigned __int64)*(unsigned __int8 *)(a1 + 3 * v8) << 8)) << 8);*(_BYTE *)(a3 + 4 * i) = *((_BYTE *)v11 + ((v5 >> 18) & 0x3F));*(_BYTE *)(a3 + 4 * i + 1LL) = *((_BYTE *)v11 + ((v5 >> 12) & 0x3F));*(_BYTE *)(a3 + 4 * i + 2LL) = *((_BYTE *)v11 + ((v5 >> 6) & 0x3F));*(_BYTE *)(a3 + 4 * i + 3LL) = *((_BYTE *)v11 + (v5 & 0x3F));++v8;}if ( v10 > 0 ){if ( v10 == 1 ){v6 = (unsigned __int64)*(unsigned __int8 *)(a1 + 3 * v8) << 16;*(_BYTE *)(a3 + 4 * i) = *((_BYTE *)v11 + ((v6 >> 18) & 0x3F));*(_BYTE *)(a3 + 4 * i + 1LL) = *((_BYTE *)v11 + ((v6 >> 12) & 0x3F));*(_BYTE *)(a3 + 4 * i + 2LL) = 61;result = a3 + 4 * i + 3LL;*(_BYTE *)result = 61;}if ( v10 == 2 ){v7 = (*(unsigned __int8 *)(a1 + 3 * v8 + 1LL) | ((unsigned __int64)*(unsigned __int8 *)(a1 + 3 * v8) << 8)) << 8;*(_BYTE *)(a3 + 4 * i) = *((_BYTE *)v11 + ((v7 >> 18) & 0x3F));*(_BYTE *)(a3 + 4 * i + 1LL) = *((_BYTE *)v11 + ((v7 >> 12) & 0x3F));*(_BYTE *)(a3 + 4 * i + 2LL) = *((_BYTE *)v11 + ((v7 >> 6) & 0x3F));result = a3 + 4 * i + 3LL;*(_BYTE *)result = 61;}}return result;
}

上面给出了码表,通用码表,sub_400F3A处理之后,base64编码,自己也写了base64逻辑,结果是一致的。

然后就是进入了一个大坑,sub_400F3A的加密过程还是很复杂的,并且经过分析发现,字符串前面的内容会影响后面的加密结果,也就是逆推答案是相当难的。

本来打算放弃,但考虑到base64的特点,还是可以找到突破口的,base64是把3个字节,转换为4个字节的字符串,也就是说,我们已知了前7位和最后1位,需要找的是中间的24位(输出长度是44,输入是必是31-33位,结尾一个=,说明是32位)。

逆向思考不行,干脆正面硬刚,从前往后爆破。先通过前7个字节,找到第8、9字节,然后3个字节3个字节计算,最后2个字节中,最后一个字节是固定的'}',只需要计算一个字节就可以了。

想到这里,直接写爆破exp

import subprocessm = "CRYPTOcrypto1_"
target="waUqjjDGnYxVyvUOLN8HquEO0J5Dqkh/zr/3KXJCEnw="
flag="SECCON{"
idx=0
endidx=11
flaglen =  len(flag)
def GetFlag(calcflag, targetLen):global flagret = Falseif len(calcflag) < targetLen:for c in m:tmpFlag = calcflag + cret = GetFlag(tmpFlag, targetLen)if ret:break;else:tmpFlag = calcflag + 'a' * (32 - len(calcflag) - 1) + '}'result = subprocess.run(['./cryptooo', tmpFlag], capture_output=True, text=True)outPut = result.stdout[15:]headLen = 4 * (len(calcflag)//3)if targetLen % 3 != 0:headLen = headLen + 4checkvalue = outPut[:headLen]targetValue = target[:headLen]if checkvalue == targetValue:flag = calcflagret = Truereturn ret
while flaglen + 1 < 32:calcFlag = flagtargetLen = 3 * (len(calcFlag) // 3) + 3if targetLen > 31:targetLen = 31if GetFlag(calcFlag, targetLen):flaglen = len(flag)else:print('get failed')break
if  flaglen == 31:flag  += '}'print(flag)

这里的m是为了缩短程序运行时间简化版,因为已知flag里只有CryptoO1_,实际爆破时字符集远多于这个,时间大概要1小时左右。

相关文章:

  • SpringBoot外部化配置
  • 前端开发遇到 Bug,怎么办?如何利用 AI 高效解决问题
  • 29.第二阶段x64游戏实战-技能冷却
  • 认知计算:迈向人类级智能的 AI 新范式
  • 《易经》的数学表达:初级版和高级版
  • labelme进行关键点标注并转换为yolo格式
  • 模型量化与保存
  • 防火墙高可靠性
  • 支持向量存储:PostgresSQL及pgvector扩展详细安装步骤!老工程接入RAG功能必备!
  • C# AOP编程
  • Elasticsearch常用命令
  • 信息学奥赛一本通 1853:【08NOIP提高组】传纸条 | 洛谷 P1006 [NOIP 2008 提高组] 传纸条
  • 宝塔安装的 MySQL 无法连接的情况及解决方案
  • ARM64虚拟地址到物理地址转换页表映射过程--基于crash
  • [测试_3] 生命周期 | Bug级别 | 测试流程 | 思考
  • 《国家高等教育智慧平台:重塑学习新时代》
  • MySQL Host 被封锁解决方案(全版本适用 + Java 后端优化)
  • 机器学习之随机森林(五)
  • vue3 el-table 行号
  • 鸿蒙进阶——驱动框架UHDF 机制核心源码解读(一)
  • 军事新闻最新消息11/新网站应该怎么做seo
  • 个人做网站接装修活哪个网站好/长春网络优化哪个公司在做
  • 美橙做过网站案例/会计培训班需要学多长时间