diff --git a/Plugins/services/oracle/connector.go b/Plugins/services/oracle/connector.go new file mode 100644 index 0000000..089eb39 --- /dev/null +++ b/Plugins/services/oracle/connector.go @@ -0,0 +1,207 @@ +package oracle + +import ( + "context" + "database/sql" + "fmt" + "strings" + "time" + + _ "github.com/sijms/go-ora/v2" + "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/plugins/base" +) + +// OracleConnection Oracle连接包装器 +type OracleConnection struct { + db *sql.DB + target string + info string + serviceName string +} + +// OracleConnector Oracle连接器实现 +type OracleConnector struct{} + +// NewOracleConnector 创建Oracle连接器 +func NewOracleConnector() *OracleConnector { + return &OracleConnector{} +} + +// Connect 连接到Oracle服务器(不进行认证) +func (c *OracleConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { + target := fmt.Sprintf("%s:%s", info.Host, info.Ports) + + // 尝试建立连接但不进行认证,使用空凭据进行连接尝试 + db, dbInfo, serviceName, err := c.createConnection(ctx, info.Host, info.Ports, "", "", "") + if err != nil { + // 检查是否是Oracle服务相关错误 + if c.isOracleError(err) { + // 即使连接失败,但可以识别为Oracle服务 + return &OracleConnection{ + db: nil, + target: target, + info: "Oracle Database (Service Detected)", + serviceName: "", + }, nil + } + return nil, err + } + + return &OracleConnection{ + db: db, + target: target, + info: dbInfo, + serviceName: serviceName, + }, nil +} + +// Authenticate 使用凭据进行认证 +func (c *OracleConnector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error { + oracleConn, ok := conn.(*OracleConnection) + if !ok { + return fmt.Errorf("invalid connection type") + } + + // 解析目标地址 + parts := strings.Split(oracleConn.target, ":") + if len(parts) != 2 { + return fmt.Errorf("invalid target format") + } + + host := parts[0] + port := parts[1] + + // 使用提供的凭据创建新连接 + db, info, serviceName, err := c.createConnection(ctx, host, port, cred.Username, cred.Password, "") + if err != nil { + return err + } + + // 更新连接信息 + if oracleConn.db != nil { + oracleConn.db.Close() + } + oracleConn.db = db + oracleConn.info = info + oracleConn.serviceName = serviceName + + return nil +} + +// Close 关闭连接 +func (c *OracleConnector) Close(conn interface{}) error { + if oracleConn, ok := conn.(*OracleConnection); ok && oracleConn.db != nil { + return oracleConn.db.Close() + } + return nil +} + +// createConnection 创建Oracle数据库连接 +func (c *OracleConnector) createConnection(ctx context.Context, host, port, username, password, serviceName string) (*sql.DB, string, string, error) { + timeout := time.Duration(common.Timeout) * time.Second + + // 常见Oracle服务名列表 + commonServiceNames := []string{"XE", "ORCL", "ORCLPDB1", "XEPDB1", "PDBORCL"} + + // 如果未指定服务名,尝试所有常见服务名 + serviceNamesToTry := []string{serviceName} + if serviceName == "" { + serviceNamesToTry = commonServiceNames + } + + var lastErr error + for _, svcName := range serviceNamesToTry { + if svcName == "" { + continue + } + + // 构造连接字符串 + connStr := fmt.Sprintf("oracle://%s:%s@%s:%s/%s?connect_timeout=%d", + username, password, host, port, svcName, int(timeout.Seconds())) + + // 对SYS用户使用SYSDBA权限 + if strings.ToUpper(username) == "SYS" { + connStr += "&sysdba=1" + } + + // 建立数据库连接 + db, err := sql.Open("oracle", connStr) + if err != nil { + lastErr = err + continue + } + + // 设置连接参数 + db.SetConnMaxLifetime(timeout) + db.SetConnMaxIdleTime(timeout) + db.SetMaxIdleConns(0) + db.SetMaxOpenConns(1) + + // 创建ping上下文 + pingCtx, pingCancel := context.WithTimeout(ctx, timeout) + + // 测试连接 + err = db.PingContext(pingCtx) + pingCancel() + + if err != nil { + db.Close() + lastErr = err + // 如果是认证错误,继续尝试下一个服务名 + if strings.Contains(err.Error(), "ORA-01017") { + continue + } + continue + } + + // 获取数据库信息 + info := c.getDatabaseInfo(db, ctx) + return db, info, svcName, nil + } + + return nil, "", "", lastErr +} + +// getDatabaseInfo 获取Oracle数据库信息 +func (c *OracleConnector) getDatabaseInfo(db *sql.DB, ctx context.Context) string { + var version string + err := db.QueryRowContext(ctx, "SELECT BANNER FROM V$VERSION WHERE ROWNUM = 1").Scan(&version) + if err != nil { + // 尝试其他查询 + err = db.QueryRowContext(ctx, "SELECT VERSION FROM PRODUCT_COMPONENT_VERSION WHERE PRODUCT LIKE 'Oracle%' AND ROWNUM = 1").Scan(&version) + if err != nil { + return "Oracle Database" + } + } + + return version +} + +// isOracleError 检查是否是Oracle相关错误 +func (c *OracleConnector) isOracleError(err error) bool { + if err == nil { + return false + } + + errorStr := strings.ToLower(err.Error()) + oracleErrorIndicators := []string{ + "ora-", + "oracle", + "tns:", + "listener", + "database", + "connection refused", + "invalid authorization specification", + "service name", + "sid", + } + + for _, indicator := range oracleErrorIndicators { + if strings.Contains(errorStr, indicator) { + return true + } + } + + return false +} \ No newline at end of file diff --git a/Plugins/services/oracle/exploiter.go b/Plugins/services/oracle/exploiter.go new file mode 100644 index 0000000..18b7cbf --- /dev/null +++ b/Plugins/services/oracle/exploiter.go @@ -0,0 +1,42 @@ +package oracle + +import ( + "context" + "fmt" + + "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/plugins/base" +) + +// OracleExploiter Oracle利用器实现 +type OracleExploiter struct{} + +// NewOracleExploiter 创建Oracle利用器 +func NewOracleExploiter() *OracleExploiter { + return &OracleExploiter{} +} + +// Exploit 执行Oracle利用 +func (e *OracleExploiter) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + // Oracle插件主要用于服务识别和认证测试,不进行进一步利用 + return &base.ExploitResult{ + Success: false, + Error: fmt.Errorf("Oracle插件不支持进一步利用"), + }, nil +} + +// GetExploitMethods 获取支持的利用方法 +func (e *OracleExploiter) GetExploitMethods() []base.ExploitMethod { + return []base.ExploitMethod{ + { + Name: "信息收集", + Type: base.ExploitDataExtraction, + Description: "收集Oracle数据库信息", + }, + } +} + +// IsExploitSupported 检查是否支持指定的利用类型 +func (e *OracleExploiter) IsExploitSupported(method base.ExploitType) bool { + return method == base.ExploitDataExtraction +} \ No newline at end of file diff --git a/Plugins/services/oracle/plugin.go b/Plugins/services/oracle/plugin.go new file mode 100644 index 0000000..69f24ff --- /dev/null +++ b/Plugins/services/oracle/plugin.go @@ -0,0 +1,244 @@ +package oracle + +import ( + "context" + "fmt" + "strings" + + "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/i18n" + "github.com/shadow1ng/fscan/plugins/base" +) + +// OraclePlugin Oracle插件实现 +type OraclePlugin struct { + *base.ServicePlugin + exploiter *OracleExploiter +} + +// NewOraclePlugin 创建Oracle插件 +func NewOraclePlugin() *OraclePlugin { + // 插件元数据 + metadata := &base.PluginMetadata{ + Name: "oracle", + Version: "2.0.0", + Author: "fscan-team", + Description: "Oracle数据库扫描和利用插件", + Category: "service", + Ports: []int{1521, 1522, 1525}, // Oracle常用端口 + Protocols: []string{"tcp"}, + Tags: []string{"oracle", "database", "weak-password", "sysdba"}, + } + + // 创建连接器和服务插件 + connector := NewOracleConnector() + servicePlugin := base.NewServicePlugin(metadata, connector) + + // 创建Oracle插件 + plugin := &OraclePlugin{ + ServicePlugin: servicePlugin, + exploiter: NewOracleExploiter(), + } + + // 设置能力 + plugin.SetCapabilities([]base.Capability{ + base.CapWeakPassword, + base.CapDataExtraction, + }) + + return plugin +} + +// Scan 重写扫描方法,进行Oracle服务扫描 +func (p *OraclePlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { + // 如果禁用了暴力破解,只进行服务识别 + if common.DisableBrute { + return p.performServiceIdentification(ctx, info) + } + + target := fmt.Sprintf("%s:%s", info.Host, info.Ports) + + // 先尝试高危凭据 + highRiskCredentials := p.getHighRiskCredentials() + for _, cred := range highRiskCredentials { + result, err := p.ScanCredential(ctx, info, cred) + if err == nil && result.Success { + // 认证成功 + if strings.ToUpper(cred.Username) == "SYS" { + common.LogSuccess(i18n.GetText("oracle_sys_auth_success", target, cred.Username, cred.Password)) + } else { + common.LogSuccess(i18n.GetText("oracle_auth_success", target, cred.Username, cred.Password)) + } + + return &base.ScanResult{ + Success: true, + Service: "Oracle", + Credentials: []*base.Credential{cred}, + Banner: result.Banner, + Extra: map[string]interface{}{ + "service": "Oracle", + "port": info.Ports, + "username": cred.Username, + "password": cred.Password, + "type": "high-risk-credentials", + }, + }, nil + } + } + + // 生成凭据进行暴力破解 + credentials := p.generateCredentials() + + // 遍历凭据进行测试 + for _, cred := range credentials { + result, err := p.ScanCredential(ctx, info, cred) + if err == nil && result.Success { + // 认证成功 + common.LogSuccess(i18n.GetText("oracle_auth_success", target, cred.Username, cred.Password)) + + return &base.ScanResult{ + Success: true, + Service: "Oracle", + Credentials: []*base.Credential{cred}, + Banner: result.Banner, + Extra: map[string]interface{}{ + "service": "Oracle", + "port": info.Ports, + "username": cred.Username, + "password": cred.Password, + "type": "weak-password", + }, + }, nil + } + } + + // 所有凭据都失败,但可能识别到了Oracle服务 + return p.performServiceIdentification(ctx, info) +} + +// getHighRiskCredentials 获取高危凭据列表 +func (p *OraclePlugin) getHighRiskCredentials() []*base.Credential { + return []*base.Credential{ + {Username: "SYS", Password: "123456"}, + {Username: "SYSTEM", Password: "123456"}, + {Username: "SYS", Password: "oracle"}, + {Username: "SYSTEM", Password: "oracle"}, + {Username: "SYS", Password: "password"}, + {Username: "SYSTEM", Password: "password"}, + {Username: "SYS", Password: "sys123"}, + {Username: "SYS", Password: "change_on_install"}, + {Username: "SYSTEM", Password: "manager"}, + } +} + +// generateCredentials 生成Oracle凭据 +func (p *OraclePlugin) generateCredentials() []*base.Credential { + var credentials []*base.Credential + + // 获取Oracle用户名字典 + usernames := common.Userdict["oracle"] + if len(usernames) == 0 { + usernames = []string{"oracle", "sys", "system", "admin", "scott", "hr", "oe"} + } + + // 获取密码字典 + passwords := common.Passwords + if len(passwords) == 0 { + passwords = []string{"", "oracle", "admin", "password", "123456", "manager", "tiger"} + } + + // 生成用户名密码组合 + for _, username := range usernames { + for _, password := range passwords { + // 替换密码中的用户名占位符 + actualPassword := strings.Replace(password, "{user}", username, -1) + + credentials = append(credentials, &base.Credential{ + Username: strings.ToUpper(username), // Oracle用户名通常大写 + Password: actualPassword, + }) + } + } + + return credentials +} + +// Exploit 使用exploiter执行利用 +func (p *OraclePlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + return p.exploiter.Exploit(ctx, info, creds) +} + +// GetExploitMethods 获取利用方法 +func (p *OraclePlugin) GetExploitMethods() []base.ExploitMethod { + return p.exploiter.GetExploitMethods() +} + +// IsExploitSupported 检查利用支持 +func (p *OraclePlugin) IsExploitSupported(method base.ExploitType) bool { + return p.exploiter.IsExploitSupported(method) +} + +// performServiceIdentification 执行Oracle服务识别(-nobr模式) +func (p *OraclePlugin) performServiceIdentification(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { + target := fmt.Sprintf("%s:%s", info.Host, info.Ports) + + // 尝试识别Oracle服务 + connector := NewOracleConnector() + conn, err := connector.Connect(ctx, info) + + if err == nil && conn != nil { + if oracleConn, ok := conn.(*OracleConnection); ok { + // 记录服务识别成功 + common.LogSuccess(i18n.GetText("oracle_service_identified", target, oracleConn.info)) + + connector.Close(conn) + return &base.ScanResult{ + Success: true, + Service: "Oracle", + Banner: oracleConn.info, + Extra: map[string]interface{}{ + "service": "Oracle", + "port": info.Ports, + "info": oracleConn.info, + "service_name": oracleConn.serviceName, + }, + }, nil + } + } + + // 如果无法识别为Oracle,返回失败 + return &base.ScanResult{ + Success: false, + Error: fmt.Errorf("无法识别为Oracle服务"), + }, nil +} + +// ============================================================================= +// 插件注册 +// ============================================================================= + +// RegisterOraclePlugin 注册Oracle插件 +func RegisterOraclePlugin() { + factory := base.NewSimplePluginFactory( + &base.PluginMetadata{ + Name: "oracle", + Version: "2.0.0", + Author: "fscan-team", + Description: "Oracle数据库扫描和利用插件", + Category: "service", + Ports: []int{1521, 1522, 1525}, // Oracle常用端口 + Protocols: []string{"tcp"}, + Tags: []string{"oracle", "database", "weak-password", "sysdba"}, + }, + func() base.Plugin { + return NewOraclePlugin() + }, + ) + + base.GlobalPluginRegistry.Register("oracle", factory) +} + +// 自动注册 +func init() { + RegisterOraclePlugin() +} \ No newline at end of file