简介
数学是现代加密的基础,而加密,是现代一切认证实现的基础。
在web中,很多场景登陆的账号密码请求都是经过 js 加密之后再发送,如图
通过burp抓包可以看到加密信息. 很明显可以看到 password 参数的值是经过前端加密之后再进行传输的,遇到这种情况,普通发包的爆破脚本就很难爆破成功。所以我们需要明白基础的加密概念,与常见的加密方式。
下面总结几种常见的加密,与真实案例中的加密对抗。
几种加密类型
ASCII 编码
ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号,以及在美式英语中使用的特殊控制字符。
JS代码示例
代码语言:txt复制document.write(escape("password") )
var code=unescape("112 97 115 115 119 111 114 100");
eval(code)
PHP代码示例
代码语言:txt复制$urlencode =_GET[passwd]
// urlencode 加密
$urlencode = urlencode($str);
echo $urlencode . '</br>';
// urldecode 解密
$urldecode = urldecode($urlencode);
echo $urldecode; // 要加密的字符串
JAVA代码示例
代码语言:txt复制public String EnCode(String a,String s)
{
s = CheckSelectItem(s);//分隔符获取
StringBuilder ASCIIL = new StringBuilder();//结果保存
StringBuilder ssvb = new StringBuilder();//输入值
String[] ss = a.split("");
for (String ik : ss) {
ssvb.append(ik);
}
char[] chars = ssvb.toString().toCharArray();//输入值保存为char数组
for (int i = 0; i < chars.length; i ) {
if(i != chars.length - 1)
{
ASCIIL.append(Integer.valueOf(chars[i])).append(s);
}
else {
ASCIIL.append(Integer.valueOf(chars[i]));
}
}//ASCII转换
return ASCIIL.toString();
}
public String DeCode(String a,String s)
{
s = CheckSelectItem(s);//分隔符获取
String out;//输出
if(s.equals(""))
{
out = "解密请选择空格分割或换行分割";
}
else
{
StringBuilder sbu = new StringBuilder();
String[] chars = a.split(s);//输入值切片
for (String aChar : chars) {
sbu.append((char) Integer.parseInt(aChar));
}//ASCII转换
out = sbu.toString();
}
return out;
}
private String CheckSelectItem(String i)
{
String delc;
switch (i)
{
case "NULL": delc = "";break;
case " Space": delc = " ";break;
case "\n Newline": delc = "n";break;
default: delc = i;
}
return delc;
}//返回分隔符
Hash算法
Hash 算法是一个广义的算法,也可以认为是一种思想,使用Hash算法可以提高存储空间的利用率,可以提高数据的查询效率,也可以做数字签名 来保障数据传递的安全性。所以Hash算法被广泛地应用在互联网应用中。
MD5 和 SHA1 是曾经被广泛应用的Hash函数,而它们都是以 MD4 为基础设计的。
MD5(Message Digest Algorithm MD5),将输入以512位分组,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,函数的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
JS代码示例
代码语言:txt复制var b =$("#logPassword");$.md5(b.val());
PHP代码示例
代码语言:txt复制// md5 加密
$md5 = md5($str);
echo $md5 . '</br>';
JAVA代码示例
代码语言:txt复制public String HashEncode(String message,String check)
{
MessageDigest mc = null;//实例化MessageDigest方法用来作MD5转换
String checking;
checking = check;
try {
mc = MessageDigest.getInstance(checking);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
assert mc != null;
mc.update(message.getBytes());//hash摘要
System.out.println(java.util.Base64.getEncoder().encodeToString(mc.digest()));
}
public static void main(String[] args) {
Hash hash = new Hash();
hash.HashEncode("加密前文本","MD5");
}
Base64
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。
编码规则: 是将3个8位字节(3×8=24位)编码成4个6位的字节(4×6=24位),之后在每个6位字节前面,补充两个0,形成4个8位字节的形式,那么取值范围就变成了0~63。又因为2的6次方等于64,所以每6个位组成一个单元。
base64既可以加密也可以解密,导入base64.js然后在js脚本中
JS代码示例
代码语言:txt复制var b =$("#logPassword");var code = $.encode64(b.val());$.unencode64(code); //加密和解密
PHP代码示例
代码语言:txt复制<?php
$cany = $_GET[password];#定义要加密的字符串
echo base64_encode($cany); #输出加密后的字符串
echo base64_decode($cany); #输出解密后的字符串
?>
JAVA代码示例
代码语言:txt复制package All_Tool_List.Coding;
import java.nio.charset.StandardCharsets;
public class Base64 {
public String Base64Decode(String in)
{
byte[] bs64 = java.util.Base64.getDecoder().decode(in);//获取用户输入字符通过base64加密,输出byte数组型值
return new String(bs64, StandardCharsets.UTF_8);//将byte数组转换成String输出
}
public String Base64Encode(String in)
{
byte[] bytes = in.getBytes(StandardCharsets.UTF_8);//将用户输入字符转换成byte(utf-8编码)
return java.util.Base64.getEncoder().encodeToString(bytes);//输出值
}
}
下面配合实战案例来看一看
实战案例
前端加密示例
在一次实战中,登录页面输入admin admin,抓包如下
乍一看,不知道它使用了什么加密方式。
代码语言:txt复制admin admin
["=96=98=106=101=105","=96=98=106=101=105","0"]
仔细分析,尝试修改密码字段为abcde,12345,继续抓包
代码语言:txt复制admin abcde
["=96=98=106=101=105","=96=96=96=96=96","0"]
admin 12345
["=96=98=106=101=105","=48=48=48=48=48","0"]
这个时候,对编码比较熟悉的人已经发现了,其实就是简单的ASCII码减去其所在位数,即第一位字符 a 的 ascii 减一为96,第二位字符 d 的 ascii 减二为98,以此类推,最后以 = 分隔每个字符即可。知道了加密规则后,代码就容易写出了。
JAVA代码示例
只需将密码本放在C盘下命名为pass.txt,程序会自动生成加密后的结果在控制台输出。
代码语言:txt复制import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
public class ASCIIM1EC {
public static void main(String[] args) {
FileReader("C:pass.txt");
}
public static String ASCIIEnCode(String a)
{
String s = "=";
String out;
StringBuilder ASCIIL = new StringBuilder("=");
StringBuilder ssvb = new StringBuilder();
String[] ss = a.split(s);
for (String ik : ss) {
ssvb.append(ik);
}
String ika = ssvb.toString();
char[] chars = ika.toCharArray();
int k =1;
for (int i = 0; i < chars.length; i ) {
if(i != chars.length - 1)
{
ASCIIL.append(((int)chars[i])-k).append(s);
}
else {
ASCIIL.append(((int)chars[i])-k);
}
k ;
}
k =1;
out = ASCIIL.toString();
return out;
}
public static int[] FileReader(String file) {
Charset charset = Charset.forName("US-ASCII");
try (BufferedReader reader = Files.newBufferedReader(Paths.get(file), charset)) {
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
System.out.println(ASCIIEnCode(line));
}
} catch (IOException x) {
System.err.format("IOException: %s%n", x);
}
return null;
}
}
示例密码表
输出结果
代码语言:txt复制=48=48=48=48=48=48
=96=98=106=101=105
=111=95=112=111=114=105=107=92
=111=95=112=111
=48=48=48=48
=48=48=48=48=48=48=48=48=48
=106=95=112=108=106
=110=98=102=111=105=108=96
=48=48=48=48=117=114=92=107=91
可见,加密后的值和与案例中的基本吻合,剩下的就是配合代码与burp交互进行爆破
同时,我们以一个后端的反推案例为例
后端加密示例
在一次对 BlogEngine.NET 的站点进行渗透时,发现其存在CVE-2019-10718 XML实体注入漏洞。查找相应的exp,成功转储其存储用户信息的 users.xml
得到一串加密后的字符串: 91wzdr6ew 0xclxg1xsauvog6qeijwznsp1g/7ifm u=
在看到这串字符时第一眼也依旧看不出是什么加密,查找其托管在github上的后端源码后发现使用的是 SHA-256 BASE64 Hash加密。
后端源码链接:https://github.com/rxtur/BlogEngine.NET/blob/master/BlogEngine/
分析后尝试本地实现这个加密方法,java代码示例如下。
JAVA代码示例
代码语言:txt复制public String HashEncode(String message,String check)
{
MessageDigest mc = null;//实例化MessageDigest方法用来作MD5转换
String checking;
checking = check;
try {
mc = MessageDigest.getInstance(checking);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
assert mc != null;
mc.update(message.getBytes());//hash摘要
System.out.println(java.util.Base64.getEncoder().encodeToString(mc.digest()));
}
public static void main(String[] args) {
Hash hash = new Hash();
hash.HashEncode("Password1","SHA-256");
}
输出结果
代码语言:txt复制Password1 //明文
GVE/3J2k 3KkoF62aRdUjTyQ/5TVQZ4fI2PuqJ3 4d0= //密文
接下来就看你的明文字典够不够强大了。
总结
本文从几种常见的加密方式切入,对目前web前端加密的几种情况进行了概括。并结合真实案例给出了两种加密的对抗方法。对于web安全来说,前端的加密是事倍功半,应该真正把重心着眼于后端,当然前后端同时加固必然是最优解。由于篇幅有限,本文无法展开描述,后续会慢慢补充关于web加密的这部分内容。
本文作者 r0fus0d、RyuZU、the-fog