CTF密码学之Base64,Base32,Base16

Base64

介绍

Base64可以将ASCII字符串或者是二进制编码成只包含A—Z,a—z,0—9,+,/ 这64个字符( 26个大写字母,26个小写字母,10个数字,1个+,一个 / 刚好64个字符)。这64个字符用6个bit位就可以全部表示出来,一个字节有8个bit 位,那么还剩下两个bit位,这两个bit位用0来补充。其实,一个Base64字符仍然是8个bit位,但是有效部分只有右边的6个 bit,左边两个永远是0。Base64的编码规则是将3个8位字节(3×8=24位)编码成4个6位的字节(4×6=24位),之后在每个6位字节前面,补充两个0,形成4个8位字节的形式,那么取值范围就变成了0~63。又因为2的6次方等于64,所以每6个位组成一个单元。一般在CTF逆向题目中base64的加密过程主要是用自定义的索引表,所以如果能一眼能看出是base64加密就会节约很多时间。

加密过程

  • base64的编码都是按字符串长度,以每3个8bit的字符为一组,
  • 然后针对每组,首先获取每个字符的ASCII编码,
  • 然后将ASCII编码转换成8bit的二进制,得到一组3*8=24bit的字节
  • 然后再将这24bit划分为4个6bit的字节,并在每个6bit的字节前面都填两个高位0,得到4个8bit的字节
  • 然后将这4个8bit的字节转换成10进制,对照Base64编码表 ,得到对应编码后的字符。

索引表如下

索引对应字符索引对应字符索引对应字符索引对应字符
0A17R34i51z
1B18S35j520
2C19T36k531
3D20U37l542
4E21V38m553
5F22W39n564
6G23X40o575
7H24Y41p586
8I25Z42q597
9J26a43r608
10K27b44s619
11L28c45t62+
12M29d46u63/
13N30e47v
14O31f48w
15P32g49x
16Q33h50y

例子

第一个例子以base64加密SLF为例子,过程如下

字符串      S       L        F
ASCII      83      80       76
二进制   01010011‬  01001100  01000110
合并       01010011‬0100110001000110
6位      010100     110100	 110001    000110
补零	 00010100   00110100   00110001	 00000110
进制       20        52         49         6
对照       U         0          x          G

SLF -> U0xG

第二个例子以base64加密M为例子,过程如下

字符串      M
ASCII      77
二进进   01001101
合并     01001101
6位      010011     01
补零	 00010011   00010000
进制       19        16
对照       T         Q         =         =

M -> TQ==

实现

最上面的base64char索引表可以自定义,这里用c实现

#include <stdio.h>
#include <string.h>

// 全局常量定义
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const char padding_char = '=';

/*编码代码
* const unsigned char * sourcedata, 源数组
* char * base64 ,码字保存
*/
int base64_encode(const unsigned char * sourcedata, char * base64)
{
	int i = 0, j = 0;
	unsigned char trans_index = 0;    // 索引是8位,但是高两位都为0
	const int datalength = strlen((const char*)sourcedata);
	for (; i < datalength; i += 3){
		// 每三个一组,进行编码
		// 要编码的数字的第一个
		trans_index = ((sourcedata[i] >> 2) & 0x3f);
		base64[j++] = base64char[(int)trans_index];
		// 第二个
		trans_index = ((sourcedata[i] << 4) & 0x30);
		if (i + 1 < datalength){
			trans_index |= ((sourcedata[i + 1] >> 4) & 0x0f);
			base64[j++] = base64char[(int)trans_index];
		}
		else{
			base64[j++] = base64char[(int)trans_index];

			base64[j++] = padding_char;

			base64[j++] = padding_char;

			break;   // 超出总长度,可以直接break
		}
		// 第三个
		trans_index = ((sourcedata[i + 1] << 2) & 0x3c);
		if (i + 2 < datalength){ // 有的话需要编码2个
			trans_index |= ((sourcedata[i + 2] >> 6) & 0x03);
			base64[j++] = base64char[(int)trans_index];

			trans_index = sourcedata[i + 2] & 0x3f;
			base64[j++] = base64char[(int)trans_index];
		}
		else{
			base64[j++] = base64char[(int)trans_index];

			base64[j++] = padding_char;

			break;
		}
	}

	base64[j] = '\0';

	return 0;
}

/** 在字符串中查询特定字符位置索引
* const char *str ,字符串
* char c,要查找的字符
*/
int num_strchr(const char *str, char c) // 
{
	const char *pindex = strchr(str, c);
	if (NULL == pindex){
		return -1;
	}
	return pindex - str;
}
/* 解码
* const char * base64 码字
* unsigned char * dedata, 解码恢复的数据
*/
int base64_decode(const char * base64, unsigned char * dedata)
{
	int i = 0, j = 0;
	int trans[4] = { 0, 0, 0, 0 };
	for (; base64[i] != '\0'; i += 4){
		// 每四个一组,译码成三个字符
		trans[0] = num_strchr(base64char, base64[i]);
		trans[1] = num_strchr(base64char, base64[i + 1]);
		// 1/3
		dedata[j++] = ((trans[0] << 2) & 0xfc) | ((trans[1] >> 4) & 0x03);

		if (base64[i + 2] == '='){
			continue;
		}
		else{
			trans[2] = num_strchr(base64char, base64[i + 2]);
		}
		// 2/3
		dedata[j++] = ((trans[1] << 4) & 0xf0) | ((trans[2] >> 2) & 0x0f);

		if (base64[i + 3] == '='){
			continue;
		}
		else{
			trans[3] = num_strchr(base64char, base64[i + 3]);
		}

		// 3/3
		dedata[j++] = ((trans[2] << 6) & 0xc0) | (trans[3] & 0x3f);
	}

	dedata[j] = '\0';

	return 0;
}

// 测试
int main()
{
	const unsigned char str[] = "a45rbcd";
	const unsigned char *sourcedata = str;
	char base64[128];
	base64_encode(sourcedata, base64);

	printf("编码:%s\n", base64);

	char dedata[128];

	base64_decode(base64, (unsigned char*)dedata);

	printf("译码:%s", dedata);

	getchar();
	getchar();
	return 0;
}

输出如下

C:\Users\thunder>"D:\AlgorithmTest.exe"
编码:YTQ1cmJjZA==
译码:a45rbcd

C:\Users\thunder>

上面的代码是base64加密和解密字符串a45rbcd我们用IDA查看,base64char即是我们的索引表

int __cdecl base64_encode(const char *sourcedata, char *base64)
{
  int v2; // STEC_4
  int v3; // STEC_4
  int v4; // STEC_4
  signed int datalength; // [esp+D0h] [ebp-2Ch]
  unsigned __int8 trans_index; // [esp+DFh] [ebp-1Dh]
  unsigned __int8 trans_indexa; // [esp+DFh] [ebp-1Dh]
  int j; // [esp+E8h] [ebp-14h]
  int ja; // [esp+E8h] [ebp-14h]
  int jb; // [esp+E8h] [ebp-14h]
  int i; // [esp+F4h] [ebp-8h]

  i = 0;
  j = 0;
  datalength = j__strlen(sourcedata);
  while ( i < datalength )
  {
    base64[j] = base64char[((signed int)(unsigned __int8)sourcedata[i] >> 2) & 0x3F]; # 右移
    ja = j + 1;
    trans_index = 16 * sourcedata[i] & 0x30;
    if ( i + 1 >= datalength )
    {
      base64[ja] = base64char[trans_index];
      v2 = ja + 1;
      base64[v2++] = padding_char;
      base64[v2] = padding_char;
      j = v2 + 1;
      break;
    }
    base64[ja] = base64char[((signed int)(unsigned __int8)sourcedata[i + 1] >> 4) & 0xF | trans_index]; # 右移
    jb = ja + 1;
    trans_indexa = 4 * sourcedata[i + 1] & 0x3C;
    if ( i + 2 >= datalength )
    {
      base64[jb] = base64char[trans_indexa];
      v4 = jb + 1;
      base64[v4] = padding_char;
      j = v4 + 1;
      break;
    }
    base64[jb] = base64char[((signed int)(unsigned __int8)sourcedata[i + 2] >> 6) & 3 | trans_indexa]; # 右移
    v3 = jb + 1;
    base64[v3] = base64char[sourcedata[i + 2] & 0x3F];
    j = v3 + 1;
    i += 3;
  }
  base64[j] = 0;
  return 0;
}

辨别

其实辨别很简单,有很多的方法,最简单的方法就是动态调试,直接用OD或者IDA动态调试,多输入几组数据,观察加密后的字符串,存在=这种字符串多半都有base64加密。 如果不能动态调试那就用IDA静态观察,观察索引表,观察对输入的操作,比如上面很明显的三次右移操作。

解密

一般解密用python来实现

import base64
s = 'key' # 要加密的字符串
a = base64.b64encode(s) # 加密

print a

print base64.b64decode(a) # 解密

在线解密网站 : https://www.qqxiuzi.cn/bianma/base.php

Base32

原理

Base32编码是使用32个可打印字符(字母A-Z和数字2-7)对任意字节数据进行编码的方案,编码后的字符串不用区分大小写并排除了容易混淆的字符,可以方便地由人类使用并由计算机处理。

符号符号符号符号
0A8I16Q24Y
1B9J17R25Z
2C10K18S262
3D11L19T273
4E12M20U284
5F13N21V295
6G14O22W306
7H15P23X317
填充=

Base32将任意字符串按照字节进行切分,并将每个字节对应的二进制值(不足8比特高位补0)串联起来,按照5比特一组进行切分,并将每组二进制值转换成十进制来对应32个可打印字符中的一个。

由于数据的二进制传输是按照8比特一组进行(即一个字节),因此Base32按5比特切分的二进制数据必须是40比特的倍数(5和8的最小公倍数)。例如输入单字节字符“%”,它对应的二进制值是“100101”,前面补两个0变成“00100101”(二进制值不足8比特的都要在高位加0直到8比特),从左侧开始按照5比特切分成两组:“00100”和“101”,后一组不足5比特,则在末尾填充0直到5比特,变成“00100”和“10100”,这两组二进制数分别转换成十进制数,通过上述表格即可找到其对应的可打印字符“E”和“U”,但是这里只用到两组共10比特,还差30比特达到40比特,按照5比特一组还需6组,则在末尾填充6个“=”。填充“=”符号的作用是方便一些程序的标准化运行,大多数情况下不添加也无关紧要,而且,在URL中使用时必须去掉“=”符号。

与Base64相比,Base32具有许多优点:

  • 适合不区分大小写的文件系统,更利于人类口语交流或记忆。
  • 结果可以用作文件名,因为它不包含路径分隔符 “/”等符号。
  • 排除了视觉上容易混淆的字符,因此可以准确的人工录入。(例如,RFC4648符号集忽略了数字“1”、“8”和“0”,因为它们可能与字母“I”,“B”和“O”混淆)。
  • 排除填充符号“=”的结果可以包含在URL中,而不编码任何字符。

Base32也比Base16有优势:

  • Base32比Base16占用的空间更小。(1000比特数据Base32需要200个字符,而Base16则为250个字符)

Base32的缺点:

  • Base32比Base64多占用大约20%的空间。因为Base32使用8个ASCII字符去编码原数据中的5个字节数据,而Base64是使用4个ASCII字符去编码原数据中的3个字节数据。

解密

import base64
s = 'key' # 要加密的字符串
a = base64.b32encode(s) # 加密

print a

print base64.b32decode(a) # 解密

在线网站 : https://www.qqxiuzi.cn/bianma/base.php

Base16

原理

Base16编码使用16个ASCII可打印字符(数字0-9和字母A-F)对任意字节数据进行编码。Base16先获取输入字符串每个字节的二进制值(不足8比特在高位补0),然后将其串联进来,再按照4比特一组进行切分,将每组二进制数分别转换成十进制,在下述表格中找到对应的编码串接起来就是Base16编码。可以看到8比特数据按照4比特切分刚好是两组,所以Base16不可能用到填充符号“=”。

Base16编码后的数据量是原数据的两倍:1000比特数据需要250个字符(即 250*8=2000 比特)。换句话说:Base16使用两个ASCII字符去编码原数据中的一个字节数据。

编码编码
0088
1199
2210A
3311B
4412C
5513D
6614E
7715F

Base16编码是一个标准的十六进制字符串(注意是字符串而不是数值),更易被人类和计算机使用,因为它并不包含任何控制字符,以及Base64和Base32中的“=”符号。输入的非ASCII字符,使用UTF-8字符集。

解密

import base64
s = 'key' # 要加密的字符串
a = base64.b16encode(s) # 加密

print a

print base64.b16decode(a) # 解密

在线网站 : https://www.qqxiuzi.cn/bianma/base.php

参考链接:

http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html

https://blog.csdn.net/u011491972/article/details/52800177

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CTF (Capture The Flag) 是一种网络安全竞技赛,旨在通过解决各种与安全相关的难题,以寻找答案或获得指定目标的旗帜来获取得分。因此,CTF Base全家桶就是提供给CTF参赛者使用的一套工具和资源。 CTF Base全家桶通常包括以下几个方面的内容: 1. 编程技能:在CTF比赛中,编写脚本或程序来解决难题非常重要。CTF Base全家桶提供各种编程语言的学习资源、训练题目和工具,以帮助参赛者提高编程技能。 2. 漏洞利用和逆向工程:CTF比赛中常常需要攻破系统的漏洞和进行逆向工程,以获取隐藏的信息或漏洞利用。CTF Base全家桶提供有关常见漏洞类型和攻击技术的学习资料、实验环境和工具。 3. 密码学和加密算法:CTF比赛中,密码学知识和加密算法非常重要。CTF Base全家桶提供密码学基础知识、常见加密算法的分析工具和解密技术的学习资源。 4. 网络和Web安全:由于CTF比赛的主要平台是网络和Web,了解网络协议、网络安全原理以及Web漏洞是非常重要的。CTF Base全家桶提供网络安全基础知识、常见Web漏洞的学习资料和漏洞扫描工具等。 CTF Base全家桶为参赛者提供了一站式的学习和实践平台,以提高他们的网络安全技能和解决问题的能力。通过充分利用CTF Base全家桶中提供的资源和工具,参赛者能够更好地理解和攻克CTF比赛中的难题,从而在比赛中取得更好的成绩。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值