『CTF』Base家族编码

admin 2025年1月21日00:03:52评论6 views字数 2975阅读9分55秒阅读模式

点击蓝字

关注我们

日期: 2025-01-20

作者: Mr-hello

介绍: Base家族编码一些变形分析。

0x00 前言知识

Base64编码是一种将二进制数据转换为文本字符串的机制,其基本原理是将输入的二进制数据按每24位一组进行处理,然后将每组24位的数据分成46位的块。简而言之,Base64编码将3个字节的二进制数据转换为4Base64字符。通过使用一个特定的编码表,Base64将明文数据替换为对应的Base64字符。

需要注意的是,Base64并不是一种加密方法。它仅仅是一种将二进制数据转换为可打印字符的编码方式,因此不具备加密特性。与加密算法不同,加密算法旨在保护数据的机密性,并涉及密钥的使用,而Base64只是一个简单的无密钥编解码过程。

Base64编码在CTF比赛中经常出现。由于常规的Base64编码相对简单,一些中等偏上难度的题目通常会对Base64进行变形处理。这类题目可能会涉及使用不同的编码表或进行多次编码,以增加难度。对于更高难度的题目,还可能涉及对编码逻辑的进一步变动。

0x01比赛实例

曾经自己出过一道三层换表Base64的题目,就先拿这个题目来讲,程序拖入反编译工具,找到关键函数进行分析如下。

『CTF』Base家族编码

本题增加了一个难点,在编译时调用了很多层嵌套函数,增加了分析难度,但是最终可以获得的信息就是,标准Base64算法,换了表,进行三次编码。

『CTF』Base家族编码

后面将三个写定的编码表来解码,发现无法正常解密,可能对编码表还有变化,于是使用动态调试,下断点在for循环中,从而每次截取到最终的编码表。

『CTF』Base家族编码
『CTF』Base家族编码
『CTF』Base家族编码

使用CyberChef解密即可得到flag

『CTF』Base家族编码

0x02 一些思考

近年来,Base64的考察频繁出现在各种大大小小的比赛中。从最初的标准 Base64,到后来的各种“魔改”版本,题目设计也逐渐变得更加灵活多样。较为简单的魔改通常是直接修改编码表,并将其硬编码在程序中,这种情况可以通过静态分析轻松得出答案。而更复杂一些的变种则会动态生成编码表,再基于生成的表进行编码。例如,前面提到的某道题目便是如此。这类题目通常可以通过动态调试获取编码表,替换后即可顺利解出答案。

难度更高的变种是对Base64的编码逻辑进行修改。在这种情况下,通常需要深入研究编码的变动逻辑,然后对标准解码脚本进行相应的修改,以解出答案。然而,这种方法的难度较大,因为它要求对算法逻辑有深入的理解,并具备相当高的编程能力,才能研究出相应的逆算法。

在撰写这篇文章时,作者有一个小想法:既然通过改写标准解码脚本来解题需要耗费大量时间和高超的编程能力,我们是否可以考虑通过原文爆破的方式来解决这个问题呢?这种方法可能提供一种更直接的途径,帮助我们在复杂编码逻辑下找到答案。

『CTF』Base家族编码

前文提到,Base64编码的原理是将 3个字节的二进制数据转换为4个 Base64 字符。也就是说,输入三个字符,会生成四个编码字符;输入六个字符,则会生成八个编码字符。并且,前四个编码字符的生成仅与原文的前三个字符相关。因此,我们可以考虑采用每3个字符进行循环爆破的方式,从而逐步推导出全部的原文字符。

这种方法的核心在于,针对每一组3个字符,尝试各种可能的组合,直到生成的编码字符与给定的编码匹配。通过这种逐步破解的方式,可以在不完全了解编码逻辑的情况下,逐步还原出原文字符。这种方法虽然可能需要一定的计算资源,但为解决复杂编码逻辑提供了一种可行的思路。

0x03 回到例题

这道题目源自某次比赛,涉及一种仿照Base64的魔改编码,具体关键函数如下。

『CTF』Base家族编码

编码关键函数内容如下。

『CTF』Base家族编码

类比 Base64,我们可以发现某些编码方式虽然表面相似,但在关键函数中对位运算的数值进行了更改。如果尝试通过修改标准 Base64 解码脚本来适应这种变化,可能会耗费大量精力和时间。然而,值得注意的是,这类算法通常具有一个特点:前面的加密结果不会影响后续字符的加密运算。在这种情况下,我们可以考虑使用爆破的方法来进行解码。

#include<stdio.h>#include<stdint.h>#include<stdlib.h>#include<Windows.h>int i = 0;char aAbcdefghijklmn[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";void __cdecl sub_B548B0(unsigned char * a1, int a2, char * a3){    unsigned char  v3[64]; // [esp+14h] [ebp-40h] BYREF    char enc[] = "ZaYcebbYdMLQJcNlMMPVLZegOJcJZaZROMNYcMIaOKfUNPeX";    memset(v3, 0xCCu, sizeof(v3));    a3[0] = aAbcdefghijklmn[((4 * (a1[a2 + 2] & 3)) | a1[a2 + 1] & 0x30 | a1[a2] & 0xC0) >> 2];    a3[1] = aAbcdefghijklmn[((4 * (a1[a2] & 3)) | a1[a2 + 2] & 0x30 | a1[a2 + 1] & 0xC0) >> 2];    a3[2] = aAbcdefghijklmn[((4 * (a1[a2 + 1] & 3)) | a1[a2] & 0x30 | a1[a2 + 2] & 0xC0) >> 2];    a3[3] = aAbcdefghijklmn[(a1[a2 + 2] & 0xC | (4 * a1[a2 + 1]) & 0x30 | (16 * a1[a2]) & 0xC0) >> 2];    if (a3[0] == enc[i] && a3[1] == enc[i + 1] && a3[2] == enc[i + 2] && a3[3] == enc[i + 3]) {        printf("%s", a1);        i += 4;    }}int main() {    unsigned char str[4] = { 0 };    char a3[10] = { 0 };    for (int j = 0; j < 10; j++)    {        for (int t1 = 32; t1 < 126; t1++)        {            for (int t2 = 32; t2 < 126; t2++)            {                for (int t3 = 32; t3 < 126; t3++)                {                    str[0] = t1;                    str[1] = t2;                    str[2] = t3;                    sub_B548B0(str, 0, a3);                }            }        }    }    return 0;}

0x04 最后总结

当然,上述例题的改动相对来说还是比较简单的。然而,在某些题目中,关键函数可能被拆分到不同的函数中,分为多个步骤进行,这无疑增加了伪代码汇总和逻辑还原的难度。在这种情况下,将原算法整合为一个可行的 C 代码可能会成为一大挑战。不过,纵观 Base 家族的编码逻辑,只要是类似这种“前面的加密结果不会影响后续字符加密运算”的算法,通常都可以通过爆破的方式来进行解题。这种方法能够绕过复杂的逻辑,还原出最终的原文。

『CTF』Base家族编码

免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。

点此亲启

原文始发于微信公众号(宸极实验室):『CTF』Base家族编码

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月21日00:03:52
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   『CTF』Base家族编码https://cn-sec.com/archives/3651223.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息