mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00

- 实现POP3协议连接器,支持普通和TLS连接(端口110/995) - 支持弱密码检测和邮件服务识别功能 - 简化实现,不提供利用功能,专注于安全扫描 - 集成国际化消息系统 - 测试通过:服务识别和弱密码检测功能
269 lines
6.4 KiB
Go
269 lines
6.4 KiB
Go
package pop3
|
||
|
||
import (
|
||
"bufio"
|
||
"context"
|
||
"crypto/tls"
|
||
"fmt"
|
||
"net"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/shadow1ng/fscan/common"
|
||
"github.com/shadow1ng/fscan/common/i18n"
|
||
"github.com/shadow1ng/fscan/plugins/base"
|
||
)
|
||
|
||
// POP3Connector POP3连接器实现
|
||
type POP3Connector struct {
|
||
host string
|
||
port int
|
||
}
|
||
|
||
// POP3Connection POP3连接结构
|
||
type POP3Connection struct {
|
||
conn net.Conn
|
||
reader *bufio.Reader
|
||
isTLS bool
|
||
banner string
|
||
username string
|
||
password string
|
||
}
|
||
|
||
// NewPOP3Connector 创建POP3连接器
|
||
func NewPOP3Connector() *POP3Connector {
|
||
return &POP3Connector{}
|
||
}
|
||
|
||
// Connect 建立POP3连接
|
||
func (c *POP3Connector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
|
||
timeout := time.Duration(common.Timeout) * time.Second
|
||
address := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||
|
||
// 结果通道
|
||
type connResult struct {
|
||
conn *POP3Connection
|
||
isTLS bool
|
||
banner string
|
||
err error
|
||
}
|
||
resultChan := make(chan connResult, 1)
|
||
|
||
// 在协程中尝试连接
|
||
go func() {
|
||
// 首先尝试普通连接
|
||
conn, err := common.WrapperTcpWithTimeout("tcp", address, timeout)
|
||
if err == nil {
|
||
banner, authErr := c.readBanner(conn, timeout)
|
||
if authErr == nil {
|
||
pop3Conn := &POP3Connection{
|
||
conn: conn,
|
||
reader: bufio.NewReader(conn),
|
||
isTLS: false,
|
||
banner: banner,
|
||
}
|
||
select {
|
||
case <-ctx.Done():
|
||
conn.Close()
|
||
case resultChan <- connResult{pop3Conn, false, banner, nil}:
|
||
}
|
||
return
|
||
}
|
||
conn.Close()
|
||
}
|
||
|
||
// 如果普通连接失败,尝试TLS连接(端口995)
|
||
if info.Ports == "995" {
|
||
tlsConfig := &tls.Config{
|
||
InsecureSkipVerify: true,
|
||
}
|
||
dialer := &net.Dialer{Timeout: timeout}
|
||
tlsConn, tlsErr := tls.DialWithDialer(dialer, "tcp", address, tlsConfig)
|
||
if tlsErr == nil {
|
||
banner, authErr := c.readBanner(tlsConn, timeout)
|
||
if authErr == nil {
|
||
pop3Conn := &POP3Connection{
|
||
conn: tlsConn,
|
||
reader: bufio.NewReader(tlsConn),
|
||
isTLS: true,
|
||
banner: banner,
|
||
}
|
||
select {
|
||
case <-ctx.Done():
|
||
tlsConn.Close()
|
||
case resultChan <- connResult{pop3Conn, true, banner, nil}:
|
||
}
|
||
return
|
||
}
|
||
tlsConn.Close()
|
||
}
|
||
}
|
||
|
||
select {
|
||
case <-ctx.Done():
|
||
case resultChan <- connResult{nil, false, "", fmt.Errorf(i18n.GetText("pop3_connection_failed"), err)}:
|
||
}
|
||
}()
|
||
|
||
// 等待连接结果
|
||
select {
|
||
case result := <-resultChan:
|
||
if result.err != nil {
|
||
return nil, result.err
|
||
}
|
||
return result.conn, nil
|
||
case <-ctx.Done():
|
||
return nil, ctx.Err()
|
||
}
|
||
}
|
||
|
||
// Authenticate 进行POP3认证
|
||
func (c *POP3Connector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error {
|
||
pop3Conn, ok := conn.(*POP3Connection)
|
||
if !ok {
|
||
return fmt.Errorf("无效的POP3连接类型")
|
||
}
|
||
|
||
timeout := time.Duration(common.Timeout) * time.Second
|
||
|
||
// 发送用户名
|
||
pop3Conn.conn.SetDeadline(time.Now().Add(timeout))
|
||
_, err := pop3Conn.conn.Write([]byte(fmt.Sprintf("USER %s\r\n", cred.Username)))
|
||
if err != nil {
|
||
return fmt.Errorf(i18n.GetText("pop3_auth_failed"), err)
|
||
}
|
||
|
||
// 读取用户名响应
|
||
pop3Conn.conn.SetDeadline(time.Now().Add(timeout))
|
||
response, err := pop3Conn.reader.ReadString('\n')
|
||
if err != nil {
|
||
return fmt.Errorf(i18n.GetText("pop3_auth_failed"), err)
|
||
}
|
||
if !strings.Contains(response, "+OK") {
|
||
return fmt.Errorf("用户名无效: %s", strings.TrimSpace(response))
|
||
}
|
||
|
||
// 短暂延迟
|
||
time.Sleep(300 * time.Millisecond)
|
||
|
||
// 发送密码
|
||
pop3Conn.conn.SetDeadline(time.Now().Add(timeout))
|
||
_, err = pop3Conn.conn.Write([]byte(fmt.Sprintf("PASS %s\r\n", cred.Password)))
|
||
if err != nil {
|
||
return fmt.Errorf(i18n.GetText("pop3_auth_failed"), err)
|
||
}
|
||
|
||
// 读取密码响应
|
||
pop3Conn.conn.SetDeadline(time.Now().Add(timeout))
|
||
response, err = pop3Conn.reader.ReadString('\n')
|
||
if err != nil {
|
||
return fmt.Errorf(i18n.GetText("pop3_auth_failed"), err)
|
||
}
|
||
|
||
if strings.Contains(response, "+OK") {
|
||
// 认证成功,保存凭据信息
|
||
pop3Conn.username = cred.Username
|
||
pop3Conn.password = cred.Password
|
||
return nil
|
||
}
|
||
|
||
return fmt.Errorf("认证失败: %s", strings.TrimSpace(response))
|
||
}
|
||
|
||
// Close 关闭POP3连接
|
||
func (c *POP3Connector) Close(conn interface{}) error {
|
||
if pop3Conn, ok := conn.(*POP3Connection); ok {
|
||
// 尝试优雅退出
|
||
if pop3Conn.conn != nil {
|
||
pop3Conn.conn.Write([]byte("QUIT\r\n"))
|
||
pop3Conn.conn.Close()
|
||
}
|
||
return nil
|
||
}
|
||
return fmt.Errorf("无效的POP3连接类型")
|
||
}
|
||
|
||
// readBanner 读取POP3服务器横幅信息
|
||
func (c *POP3Connector) readBanner(conn net.Conn, timeout time.Duration) (string, error) {
|
||
reader := bufio.NewReader(conn)
|
||
conn.SetDeadline(time.Now().Add(timeout))
|
||
|
||
response, err := reader.ReadString('\n')
|
||
if err != nil {
|
||
return "", fmt.Errorf("读取横幅失败: %v", err)
|
||
}
|
||
|
||
// 检查是否为正常的POP3响应
|
||
if strings.Contains(response, "+OK") {
|
||
return strings.TrimSpace(response), nil
|
||
}
|
||
|
||
// 检查错误响应
|
||
if strings.Contains(strings.ToLower(response), "error") ||
|
||
strings.Contains(strings.ToLower(response), "too many") {
|
||
return "", fmt.Errorf("服务器拒绝连接: %s", strings.TrimSpace(response))
|
||
}
|
||
|
||
return strings.TrimSpace(response), nil
|
||
}
|
||
|
||
// GetConnectionInfo 获取连接信息
|
||
func (conn *POP3Connection) GetConnectionInfo() map[string]interface{} {
|
||
info := map[string]interface{}{
|
||
"protocol": "POP3",
|
||
"tls": conn.isTLS,
|
||
"banner": conn.banner,
|
||
}
|
||
|
||
if conn.username != "" {
|
||
info["username"] = conn.username
|
||
info["authenticated"] = true
|
||
}
|
||
|
||
return info
|
||
}
|
||
|
||
// IsAlive 检查连接是否仍然有效
|
||
func (conn *POP3Connection) IsAlive() bool {
|
||
if conn.conn == nil {
|
||
return false
|
||
}
|
||
|
||
// 尝试发送NOOP命令检查连接
|
||
conn.conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||
_, err := conn.conn.Write([]byte("NOOP\r\n"))
|
||
if err != nil {
|
||
return false
|
||
}
|
||
|
||
response, err := conn.reader.ReadString('\n')
|
||
if err != nil {
|
||
return false
|
||
}
|
||
|
||
return strings.Contains(response, "+OK")
|
||
}
|
||
|
||
// ExecuteCommand 执行POP3命令
|
||
func (conn *POP3Connection) ExecuteCommand(command string) (string, error) {
|
||
if conn.conn == nil {
|
||
return "", fmt.Errorf("连接未建立")
|
||
}
|
||
|
||
timeout := time.Duration(common.Timeout) * time.Second
|
||
conn.conn.SetDeadline(time.Now().Add(timeout))
|
||
|
||
// 发送命令
|
||
_, err := conn.conn.Write([]byte(command + "\r\n"))
|
||
if err != nil {
|
||
return "", fmt.Errorf("发送命令失败: %v", err)
|
||
}
|
||
|
||
// 读取响应
|
||
response, err := conn.reader.ReadString('\n')
|
||
if err != nil {
|
||
return "", fmt.Errorf("读取响应失败: %v", err)
|
||
}
|
||
|
||
return strings.TrimSpace(response), nil
|
||
} |