diff --git a/cmd/integration-test/javascript.go b/cmd/integration-test/javascript.go index aad685344..ccee6a6b9 100644 --- a/cmd/integration-test/javascript.go +++ b/cmd/integration-test/javascript.go @@ -17,17 +17,19 @@ var jsTestcases = []TestCaseInfo{ {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() }}, {Path: "protocols/javascript/vnc-pass-brute.yaml", TestCase: &javascriptVncPassBrute{}}, + {Path: "protocols/javascript/postgres-pass-brute.yaml", TestCase: &javascriptPostgresPassBrute{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }}, {Path: "protocols/javascript/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}}, {Path: "protocols/javascript/no-port-args.yaml", TestCase: &javascriptNoPortArgs{}}, } var ( - redisResource *dockertest.Resource - sshResource *dockertest.Resource - oracleResource *dockertest.Resource - vncResource *dockertest.Resource - pool *dockertest.Pool - defaultRetry = 3 + redisResource *dockertest.Resource + sshResource *dockertest.Resource + oracleResource *dockertest.Resource + vncResource *dockertest.Resource + postgresResource *dockertest.Resource + pool *dockertest.Pool + defaultRetry = 3 ) type javascriptNetHttps struct{} @@ -169,6 +171,38 @@ func (j *javascriptVncPassBrute) Execute(filePath string) error { return multierr.Combine(errs...) } +type javascriptPostgresPassBrute struct{} + +func (j *javascriptPostgresPassBrute) Execute(filePath string) error { + if postgresResource == nil || pool == nil { + // skip test as postgres is not running + return nil + } + tempPort := postgresResource.GetPort("5432/tcp") + finalURL := "localhost:" + tempPort + defer purge(postgresResource) + errs := []error{} + for i := 0; i < defaultRetry; i++ { + results := []string{} + var err error + _ = pool.Retry(func() error { + //let postgres 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...) +} + type javascriptMultiPortsSSH struct{} func (j *javascriptMultiPortsSSH) Execute(filePath string) error { @@ -292,4 +326,23 @@ func init() { if err := vncResource.Expire(30); err != nil { log.Printf("Could not expire resource: %s", err) } + + // setup a temporary postgres instance + postgresResource, err = pool.RunWithOptions(&dockertest.RunOptions{ + Repository: "postgres", + Tag: "latest", + Env: []string{ + "POSTGRES_PASSWORD=postgres", + "POSTGRES_USER=postgres", + }, + Platform: "linux/amd64", + }) + if err != nil { + log.Printf("Could not start postgres resource: %s", err) + return + } + // by default expire after 30 sec + if err := postgresResource.Expire(30); err != nil { + log.Printf("Could not expire postgres resource: %s", err) + } } diff --git a/integration_tests/protocols/javascript/postgres-pass-brute.yaml b/integration_tests/protocols/javascript/postgres-pass-brute.yaml new file mode 100644 index 000000000..6545d4f78 --- /dev/null +++ b/integration_tests/protocols/javascript/postgres-pass-brute.yaml @@ -0,0 +1,47 @@ +id: postgres-pass-brute + +info: + name: PostgreSQL Password Bruteforce + author: pdteam + severity: high + description: | + This template bruteforces passwords for protected PostgreSQL instances. + If PostgreSQL is not protected with password, it is also matched. + metadata: + shodan-query: product:"PostgreSQL" + tags: js,network,postgresql,authentication + +javascript: + - pre-condition: | + isPortOpen(Host,Port) + + code: | + const postgres = require('nuclei/postgres'); + const client = new postgres.PGClient; + success = client.Connect(Host, Port, User, Pass); + + args: + Host: "{{Host}}" + Port: "5432" + User: "{{usernames}}" + Pass: "{{passwords}}" + + attack: clusterbomb + payloads: + usernames: + - postgres + - admin + - root + passwords: + - "" + - postgres + - password + - admin + - root + stop-at-first-match: true + + matchers: + - type: dsl + dsl: + - "success == true" + diff --git a/pkg/js/libs/postgres/postgres.go b/pkg/js/libs/postgres/postgres.go index 5280e3cdc..1504cb43e 100644 --- a/pkg/js/libs/postgres/postgres.go +++ b/pkg/js/libs/postgres/postgres.go @@ -82,7 +82,7 @@ func isPostgres(executionId string, host string, port int) (bool, error) { // const client = new postgres.PGClient; // const connected = client.Connect('acme.com', 5432, 'username', 'password'); // ``` -func (c *PGClient) Connect(ctx context.Context, host string, port int, username, password string) (bool, error) { +func (c *PGClient) Connect(ctx context.Context, host string, port int, username string, password string) (bool, error) { ok, err := c.IsPostgres(ctx, host, port) if err != nil { return false, err @@ -104,7 +104,7 @@ func (c *PGClient) Connect(ctx context.Context, host string, port int, username, // const result = client.ExecuteQuery('acme.com', 5432, 'username', 'password', 'dbname', 'select * from users'); // log(to_json(result)); // ``` -func (c *PGClient) ExecuteQuery(ctx context.Context, host string, port int, username, password, dbName, query string) (*utils.SQLResult, error) { +func (c *PGClient) ExecuteQuery(ctx context.Context, host string, port int, username string, password string, dbName string, query string) (*utils.SQLResult, error) { ok, err := c.IsPostgres(ctx, host, port) if err != nil { return nil, err @@ -157,7 +157,7 @@ func executeQuery(executionId string, host string, port int, username string, pa // const client = new postgres.PGClient; // const connected = client.ConnectWithDB('acme.com', 5432, 'username', 'password', 'dbname'); // ``` -func (c *PGClient) ConnectWithDB(ctx context.Context, host string, port int, username, password, dbName string) (bool, error) { +func (c *PGClient) ConnectWithDB(ctx context.Context, host string, port int, username string, password string, dbName string) (bool, error) { ok, err := c.IsPostgres(ctx, host, port) if err != nil { return false, err @@ -207,7 +207,7 @@ func connect(executionId string, host string, port int, username string, passwor _ = db.Close() }() - _, err := db.Exec(ctx, "select 1") + _, err := db.ExecContext(ctx, "select 1") if err != nil { switch true { case strings.Contains(err.Error(), "connect: connection refused"):