mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 21:15:26 +00:00
Merge pull request #6471 from projectdiscovery/feat-4872-oracle-atp2
code from https://github.com/projectdiscovery/nuclei/pull/6427
This commit is contained in:
commit
09c2ca540a
@ -15,13 +15,15 @@ var jsTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/javascript/ssh-server-fingerprint.yaml", TestCase: &javascriptSSHServerFingerprint{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
|
||||
{Path: "protocols/javascript/net-multi-step.yaml", TestCase: &networkMultiStep{}},
|
||||
{Path: "protocols/javascript/net-https.yaml", TestCase: &javascriptNetHttps{}},
|
||||
{Path: "protocols/javascript/oracle-auth-test.yaml", TestCase: &javascriptOracleAuthTest{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
|
||||
}
|
||||
|
||||
var (
|
||||
redisResource *dockertest.Resource
|
||||
sshResource *dockertest.Resource
|
||||
pool *dockertest.Pool
|
||||
defaultRetry = 3
|
||||
redisResource *dockertest.Resource
|
||||
sshResource *dockertest.Resource
|
||||
oracleResource *dockertest.Resource
|
||||
pool *dockertest.Pool
|
||||
defaultRetry = 3
|
||||
)
|
||||
|
||||
type javascriptNetHttps struct{}
|
||||
@ -98,6 +100,38 @@ func (j *javascriptSSHServerFingerprint) Execute(filePath string) error {
|
||||
return multierr.Combine(errs...)
|
||||
}
|
||||
|
||||
type javascriptOracleAuthTest struct{}
|
||||
|
||||
func (j *javascriptOracleAuthTest) Execute(filePath string) error {
|
||||
if oracleResource == nil || pool == nil {
|
||||
// skip test as oracle is not running
|
||||
return nil
|
||||
}
|
||||
tempPort := oracleResource.GetPort("1521/tcp")
|
||||
finalURL := "localhost:" + tempPort
|
||||
defer purge(oracleResource)
|
||||
errs := []error{}
|
||||
for i := 0; i < defaultRetry; i++ {
|
||||
results := []string{}
|
||||
var err error
|
||||
_ = pool.Retry(func() error {
|
||||
//let ssh server start
|
||||
time.Sleep(3 * time.Second)
|
||||
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := expectResultsCount(results, 1); err == nil {
|
||||
return nil
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return multierr.Combine(errs...)
|
||||
}
|
||||
|
||||
// purge any given resource if it is not nil
|
||||
func purge(resource *dockertest.Resource) {
|
||||
if resource != nil && pool != nil {
|
||||
@ -163,4 +197,23 @@ func init() {
|
||||
if err := sshResource.Expire(30); err != nil {
|
||||
log.Printf("Could not expire resource: %s", err)
|
||||
}
|
||||
|
||||
// setup a temporary oracle instance
|
||||
oracleResource, err = pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Repository: "gvenzl/oracle-xe",
|
||||
Tag: "latest",
|
||||
Env: []string{
|
||||
"ORACLE_PASSWORD=mysecret",
|
||||
},
|
||||
Platform: "linux/amd64",
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Could not start Oracle resource: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// by default expire after 30 sec
|
||||
if err := oracleResource.Expire(30); err != nil {
|
||||
log.Printf("Could not expire Oracle resource: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
1
go.mod
1
go.mod
@ -113,6 +113,7 @@ require (
|
||||
github.com/redis/go-redis/v9 v9.11.0
|
||||
github.com/seh-msft/burpxml v1.0.1
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
|
||||
github.com/sijms/go-ora/v2 v2.9.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9
|
||||
github.com/testcontainers/testcontainers-go v0.38.0
|
||||
|
||||
2
go.sum
2
go.sum
@ -1381,6 +1381,8 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
|
||||
github.com/sijms/go-ora/v2 v2.9.0 h1:+iQbUeTeCOFMb5BsOMgUhV8KWyrv9yjKpcK4x7+MFrg=
|
||||
github.com/sijms/go-ora/v2 v2.9.0/go.mod h1:QgFInVi3ZWyqAiJwzBQA+nbKYKH77tdp1PYoCqhR2dU=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
|
||||
@ -15,12 +15,12 @@ func init() {
|
||||
module.Set(
|
||||
gojs.Objects{
|
||||
// Functions
|
||||
"IsOracle": lib_oracle.IsOracle,
|
||||
|
||||
// Var and consts
|
||||
|
||||
// Objects / Classes
|
||||
"IsOracleResponse": gojs.GetClassConstructor[lib_oracle.IsOracleResponse](&lib_oracle.IsOracleResponse{}),
|
||||
"OracleClient": gojs.GetClassConstructor[lib_oracle.OracleClient](&lib_oracle.OracleClient{}),
|
||||
},
|
||||
).Register()
|
||||
}
|
||||
|
||||
@ -1,33 +1,106 @@
|
||||
|
||||
|
||||
/**
|
||||
* IsOracle checks if a host is running an Oracle server
|
||||
* @example
|
||||
* ```javascript
|
||||
* const oracle = require('nuclei/oracle');
|
||||
* const isOracle = oracle.IsOracle('acme.com', 1521);
|
||||
* log(toJSON(isOracle));
|
||||
* ```
|
||||
*/
|
||||
export function IsOracle(host: string, port: number): IsOracleResponse | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* IsOracleResponse is the response from the IsOracle function.
|
||||
* this is returned by IsOracle function.
|
||||
* @example
|
||||
* ```javascript
|
||||
* const oracle = require('nuclei/oracle');
|
||||
* const isOracle = oracle.IsOracle('acme.com', 1521);
|
||||
* const client = new oracle.OracleClient();
|
||||
* const isOracle = client.IsOracle('acme.com', 1521);
|
||||
* ```
|
||||
*/
|
||||
export interface IsOracleResponse {
|
||||
|
||||
IsOracle?: boolean,
|
||||
|
||||
Banner?: string,
|
||||
}
|
||||
|
||||
/**
|
||||
* Client is a client for Oracle database.
|
||||
* Internally client uses go-ora driver.
|
||||
* @example
|
||||
* ```javascript
|
||||
* const oracle = require('nuclei/oracle');
|
||||
* const client = new oracle.OracleClient();
|
||||
* ```
|
||||
*/
|
||||
export class OracleClient {
|
||||
// Constructor of OracleClient
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Connect connects to an Oracle database
|
||||
* @example
|
||||
* ```javascript
|
||||
* const oracle = require('nuclei/oracle');
|
||||
* const client = new oracle.OracleClient();
|
||||
* client.Connect('acme.com', 1521, 'XE', 'user', 'password');
|
||||
* ```
|
||||
*/
|
||||
public Connect(host: string, port: number, serviceName: string, username: string, password: string): boolean | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ConnectWithDSN connects to an Oracle database using a DSN string
|
||||
* @example
|
||||
* ```javascript
|
||||
* const oracle = require('nuclei/oracle');
|
||||
* const client = new oracle.OracleClient();
|
||||
* client.ConnectWithDSN('oracle://user:password@host:port/service', 'SELECT @@version');
|
||||
* ```
|
||||
*/
|
||||
public ConnectWithDSN(dsn: string): boolean | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* IsOracle checks if a host is running an Oracle server
|
||||
* @example
|
||||
* ```javascript
|
||||
* const oracle = require('nuclei/oracle');
|
||||
* const isOracle = oracle.IsOracle('acme.com', 1521);
|
||||
* ```
|
||||
*/
|
||||
public IsOracle(host: string, port: number): IsOracleResponse | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ExecuteQuery connects to Oracle database using given credentials and executes a query.
|
||||
* It returns the results of the query or an error if something goes wrong.
|
||||
* @example
|
||||
* ```javascript
|
||||
* const oracle = require('nuclei/oracle');
|
||||
* const client = new oracle.OracleClient();
|
||||
* const result = client.ExecuteQuery('acme.com', 1521, 'username', 'password', 'XE', 'SELECT * FROM dual');
|
||||
* log(to_json(result));
|
||||
* ```
|
||||
*/
|
||||
public ExecuteQuery(host: string, port: number, username: string, password: string, dbName: string, query: string): SQLResult | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ExecuteQueryWithDSN executes a query on an Oracle database using a DSN
|
||||
* @example
|
||||
* ```javascript
|
||||
* const oracle = require('nuclei/oracle');
|
||||
* const client = new oracle.OracleClient();
|
||||
* const result = client.ExecuteQueryWithDSN('oracle://user:password@host:port/service', 'SELECT * FROM dual');
|
||||
* log(to_json(result));
|
||||
* ```
|
||||
*/
|
||||
public ExecuteQueryWithDSN(dsn: string, query: string): SQLResult | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SQLResult Interface
|
||||
*/
|
||||
export interface SQLResult {
|
||||
Count?: number,
|
||||
Columns?: string[],
|
||||
Rows?: any[],
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package oracle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
@ -9,7 +10,9 @@ import (
|
||||
|
||||
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
|
||||
"github.com/praetorian-inc/fingerprintx/pkg/plugins/services/oracledb"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/js/utils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
goora "github.com/sijms/go-ora/v2"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -24,6 +27,16 @@ type (
|
||||
IsOracle bool
|
||||
Banner string
|
||||
}
|
||||
// Client is a client for Oracle database.
|
||||
// Internally client uses oracle/godror driver.
|
||||
// @example
|
||||
// ```javascript
|
||||
// const oracle = require('nuclei/oracle');
|
||||
// const client = new oracle.OracleClient();
|
||||
// ```
|
||||
OracleClient struct {
|
||||
connector *goora.OracleConnector
|
||||
}
|
||||
)
|
||||
|
||||
// IsOracle checks if a host is running an Oracle server
|
||||
@ -33,7 +46,7 @@ type (
|
||||
// const isOracle = oracle.IsOracle('acme.com', 1521);
|
||||
// log(toJSON(isOracle));
|
||||
// ```
|
||||
func IsOracle(ctx context.Context, host string, port int) (IsOracleResponse, error) {
|
||||
func (c *OracleClient) IsOracle(ctx context.Context, host string, port int) (IsOracleResponse, error) {
|
||||
executionId := ctx.Value("executionId").(string)
|
||||
return memoizedisOracle(executionId, host, port)
|
||||
}
|
||||
@ -69,3 +82,129 @@ func isOracle(executionId string, host string, port int) (IsOracleResponse, erro
|
||||
resp.IsOracle = true
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *OracleClient) oracleDbInstance(connStr string, executionId string) (*goora.OracleConnector, error) {
|
||||
if c.connector != nil {
|
||||
return c.connector, nil
|
||||
}
|
||||
|
||||
connector := goora.NewConnector(connStr)
|
||||
oraConnector, ok := connector.(*goora.OracleConnector)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to cast connector to OracleConnector")
|
||||
}
|
||||
|
||||
// Create custom dialer wrapper
|
||||
customDialer := &oracleCustomDialer{
|
||||
executionId: executionId,
|
||||
}
|
||||
|
||||
oraConnector.Dialer(customDialer)
|
||||
|
||||
c.connector = oraConnector
|
||||
|
||||
return oraConnector, nil
|
||||
}
|
||||
|
||||
// Connect connects to an Oracle database
|
||||
// @example
|
||||
// ```javascript
|
||||
// const oracle = require('nuclei/oracle');
|
||||
// const client = new oracle.OracleClient;
|
||||
// client.Connect('acme.com', 1521, 'XE', 'user', 'password');
|
||||
// ```
|
||||
func (c *OracleClient) Connect(ctx context.Context, host string, port int, serviceName string, username string, password string) (bool, error) {
|
||||
connStr := goora.BuildUrl(host, port, serviceName, username, password, nil)
|
||||
|
||||
return c.ConnectWithDSN(ctx, connStr)
|
||||
}
|
||||
|
||||
func (c *OracleClient) ConnectWithDSN(ctx context.Context, dsn string) (bool, error) {
|
||||
executionId := ctx.Value("executionId").(string)
|
||||
|
||||
connector, err := c.oracleDbInstance(dsn, executionId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
db := sql.OpenDB(connector)
|
||||
defer func() {
|
||||
_ = db.Close()
|
||||
}()
|
||||
|
||||
db.SetMaxOpenConns(1)
|
||||
db.SetMaxIdleConns(0)
|
||||
|
||||
// Test the connection
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ExecuteQuery connects to MS SQL database using given credentials and executes a query.
|
||||
// It returns the results of the query or an error if something goes wrong.
|
||||
// @example
|
||||
// ```javascript
|
||||
// const oracle = require('nuclei/oracle');
|
||||
// const client = new oracle.OracleClient;
|
||||
// const result = client.ExecuteQuery('acme.com', 1521, 'username', 'password', 'XE', 'SELECT @@version');
|
||||
// log(to_json(result));
|
||||
// ```
|
||||
func (c *OracleClient) ExecuteQuery(ctx context.Context, host string, port int, username, password, dbName, query string) (*utils.SQLResult, error) {
|
||||
if host == "" || port <= 0 {
|
||||
return nil, fmt.Errorf("invalid host or port")
|
||||
}
|
||||
|
||||
isOracleResp, err := c.IsOracle(ctx, host, port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isOracleResp.IsOracle {
|
||||
return nil, fmt.Errorf("not a oracle service")
|
||||
}
|
||||
|
||||
connStr := goora.BuildUrl(host, port, dbName, username, password, nil)
|
||||
|
||||
return c.ExecuteQueryWithDSN(ctx, connStr, query)
|
||||
}
|
||||
|
||||
// ExecuteQueryWithDSN executes a query on an Oracle database using a DSN
|
||||
// @example
|
||||
// ```javascript
|
||||
// const oracle = require('nuclei/oracle');
|
||||
// const client = new oracle.OracleClient;
|
||||
// const result = client.ExecuteQueryWithDSN('oracle://user:password@host:port/service', 'SELECT @@version');
|
||||
// log(to_json(result));
|
||||
// ```
|
||||
func (c *OracleClient) ExecuteQueryWithDSN(ctx context.Context, dsn string, query string) (*utils.SQLResult, error) {
|
||||
executionId := ctx.Value("executionId").(string)
|
||||
|
||||
connector, err := c.oracleDbInstance(dsn, executionId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db := sql.OpenDB(connector)
|
||||
defer func() {
|
||||
_ = db.Close()
|
||||
}()
|
||||
|
||||
db.SetMaxOpenConns(1)
|
||||
db.SetMaxIdleConns(0)
|
||||
|
||||
rows, err := db.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := utils.UnmarshalSQLRows(rows)
|
||||
if err != nil {
|
||||
if data != nil && len(data.Rows) > 0 {
|
||||
return data, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
42
pkg/js/libs/oracle/oracledialer.go
Normal file
42
pkg/js/libs/oracle/oracledialer.go
Normal file
@ -0,0 +1,42 @@
|
||||
package oracle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
)
|
||||
|
||||
// oracleCustomDialer implements the dialer interface expected by go-ora
|
||||
type oracleCustomDialer struct {
|
||||
executionId string
|
||||
}
|
||||
|
||||
func (o *oracleCustomDialer) dialWithCtx(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
dialers := protocolstate.GetDialersWithId(o.executionId)
|
||||
if dialers == nil {
|
||||
return nil, fmt.Errorf("dialers not initialized for %s", o.executionId)
|
||||
}
|
||||
if !protocolstate.IsHostAllowed(o.executionId, address) {
|
||||
// host is not valid according to network policy
|
||||
return nil, protocolstate.ErrHostDenied.Msgf(address)
|
||||
}
|
||||
return dialers.Fastdialer.Dial(ctx, network, address)
|
||||
}
|
||||
|
||||
func (o *oracleCustomDialer) Dial(network, address string) (net.Conn, error) {
|
||||
return o.dialWithCtx(context.TODO(), network, address)
|
||||
}
|
||||
|
||||
func (o *oracleCustomDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
return o.dialWithCtx(ctx, network, address)
|
||||
}
|
||||
|
||||
func (o *oracleCustomDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return o.dialWithCtx(ctx, network, address)
|
||||
}
|
||||
29
pkg/protocols/javascript/testcases/oracle-auth-test.yaml
Normal file
29
pkg/protocols/javascript/testcases/oracle-auth-test.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
id: oracle-auth-test
|
||||
|
||||
info:
|
||||
name: Oracle - Authentication Test
|
||||
author: pdteam
|
||||
severity: info
|
||||
tags: js,oracle,network,auth
|
||||
|
||||
javascript:
|
||||
- pre-condition: |
|
||||
isPortOpen(Host,Port);
|
||||
code: |
|
||||
let o = require('nuclei/oracle');
|
||||
let c = o.OracleClient();
|
||||
c.Connect(Host, Port, ServiceName, User, Pass);
|
||||
|
||||
args:
|
||||
ServiceName: "XE"
|
||||
Host: "{{Host}}"
|
||||
Port: "1521"
|
||||
User: "system"
|
||||
Pass: "{{passwords}}"
|
||||
payloads:
|
||||
passwords:
|
||||
- mysecret
|
||||
matchers:
|
||||
- type: dsl
|
||||
dsl:
|
||||
- "response == true"
|
||||
Loading…
x
Reference in New Issue
Block a user