mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
207 lines
5.0 KiB
Go
207 lines
5.0 KiB
Go
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
|
||
} |