Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/js/libs/postgres/postgres.go
2070 views
1
package postgres
2
3
import (
4
"context"
5
"database/sql"
6
"fmt"
7
"net"
8
"strings"
9
"time"
10
11
"github.com/go-pg/pg"
12
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
13
postgres "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/postgresql"
14
utils "github.com/projectdiscovery/nuclei/v3/pkg/js/utils"
15
"github.com/projectdiscovery/nuclei/v3/pkg/js/utils/pgwrap" //nolint:staticcheck // need to call init
16
_ "github.com/projectdiscovery/nuclei/v3/pkg/js/utils/pgwrap" //nolint:staticcheck
17
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
18
)
19
20
type (
21
// PGClient is a client for Postgres database.
22
// Internally client uses go-pg/pg driver.
23
// @example
24
// ```javascript
25
// const postgres = require('nuclei/postgres');
26
// const client = new postgres.PGClient;
27
// ```
28
PGClient struct{}
29
)
30
31
// IsPostgres checks if the given host and port are running Postgres database.
32
// If connection is successful, it returns true.
33
// If connection is unsuccessful, it returns false and error.
34
// @example
35
// ```javascript
36
// const postgres = require('nuclei/postgres');
37
// const isPostgres = postgres.IsPostgres('acme.com', 5432);
38
// ```
39
func (c *PGClient) IsPostgres(ctx context.Context, host string, port int) (bool, error) {
40
executionId := ctx.Value("executionId").(string)
41
// todo: why this is exposed? Service fingerprint should be automatic
42
return memoizedisPostgres(executionId, host, port)
43
}
44
45
// @memo
46
func isPostgres(executionId string, host string, port int) (bool, error) {
47
timeout := 10 * time.Second
48
49
dialer := protocolstate.GetDialersWithId(executionId)
50
if dialer == nil {
51
return false, fmt.Errorf("dialers not initialized for %s", executionId)
52
}
53
54
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", fmt.Sprintf("%s:%d", host, port))
55
if err != nil {
56
return false, err
57
}
58
defer func() {
59
_ = conn.Close()
60
}()
61
62
_ = conn.SetDeadline(time.Now().Add(timeout))
63
64
plugin := &postgres.POSTGRESPlugin{}
65
service, err := plugin.Run(conn, timeout, plugins.Target{Host: host})
66
if err != nil {
67
return false, err
68
}
69
if service == nil {
70
return false, nil
71
}
72
return true, nil
73
}
74
75
// Connect connects to Postgres database using given credentials.
76
// If connection is successful, it returns true.
77
// If connection is unsuccessful, it returns false and error.
78
// The connection is closed after the function returns.
79
// @example
80
// ```javascript
81
// const postgres = require('nuclei/postgres');
82
// const client = new postgres.PGClient;
83
// const connected = client.Connect('acme.com', 5432, 'username', 'password');
84
// ```
85
func (c *PGClient) Connect(ctx context.Context, host string, port int, username, password string) (bool, error) {
86
ok, err := c.IsPostgres(ctx, host, port)
87
if err != nil {
88
return false, err
89
}
90
if !ok {
91
return false, fmt.Errorf("not a postgres service")
92
}
93
executionId := ctx.Value("executionId").(string)
94
return memoizedconnect(executionId, host, port, username, password, "postgres")
95
}
96
97
// ExecuteQuery connects to Postgres database using given credentials and database name.
98
// and executes a query on the db.
99
// If connection is successful, it returns the result of the query.
100
// @example
101
// ```javascript
102
// const postgres = require('nuclei/postgres');
103
// const client = new postgres.PGClient;
104
// const result = client.ExecuteQuery('acme.com', 5432, 'username', 'password', 'dbname', 'select * from users');
105
// log(to_json(result));
106
// ```
107
func (c *PGClient) ExecuteQuery(ctx context.Context, host string, port int, username, password, dbName, query string) (*utils.SQLResult, error) {
108
ok, err := c.IsPostgres(ctx, host, port)
109
if err != nil {
110
return nil, err
111
}
112
if !ok {
113
return nil, fmt.Errorf("not a postgres service")
114
}
115
116
executionId := ctx.Value("executionId").(string)
117
118
return memoizedexecuteQuery(executionId, host, port, username, password, dbName, query)
119
}
120
121
// @memo
122
func executeQuery(executionId string, host string, port int, username string, password string, dbName string, query string) (*utils.SQLResult, error) {
123
if !protocolstate.IsHostAllowed(executionId, host) {
124
// host is not valid according to network policy
125
return nil, protocolstate.ErrHostDenied.Msgf(host)
126
}
127
128
target := net.JoinHostPort(host, fmt.Sprintf("%d", port))
129
130
connStr := fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=disable&executionId=%s", username, password, target, dbName, executionId)
131
db, err := sql.Open(pgwrap.PGWrapDriver, connStr)
132
if err != nil {
133
return nil, err
134
}
135
defer func() {
136
_ = db.Close()
137
}()
138
139
rows, err := db.Query(query)
140
if err != nil {
141
return nil, err
142
}
143
resp, err := utils.UnmarshalSQLRows(rows)
144
if err != nil {
145
return nil, err
146
}
147
return resp, nil
148
}
149
150
// ConnectWithDB connects to Postgres database using given credentials and database name.
151
// If connection is successful, it returns true.
152
// If connection is unsuccessful, it returns false and error.
153
// The connection is closed after the function returns.
154
// @example
155
// ```javascript
156
// const postgres = require('nuclei/postgres');
157
// const client = new postgres.PGClient;
158
// const connected = client.ConnectWithDB('acme.com', 5432, 'username', 'password', 'dbname');
159
// ```
160
func (c *PGClient) ConnectWithDB(ctx context.Context, host string, port int, username, password, dbName string) (bool, error) {
161
ok, err := c.IsPostgres(ctx, host, port)
162
if err != nil {
163
return false, err
164
}
165
if !ok {
166
return false, fmt.Errorf("not a postgres service")
167
}
168
169
executionId := ctx.Value("executionId").(string)
170
171
return memoizedconnect(executionId, host, port, username, password, dbName)
172
}
173
174
// @memo
175
func connect(executionId string, host string, port int, username string, password string, dbName string) (bool, error) {
176
if host == "" || port <= 0 {
177
return false, fmt.Errorf("invalid host or port")
178
}
179
180
if !protocolstate.IsHostAllowed(executionId, host) {
181
// host is not valid according to network policy
182
return false, protocolstate.ErrHostDenied.Msgf(host)
183
}
184
185
target := net.JoinHostPort(host, fmt.Sprintf("%d", port))
186
187
ctx, cancel := context.WithCancel(context.Background())
188
defer cancel()
189
190
dialer := protocolstate.GetDialersWithId(executionId)
191
if dialer == nil {
192
return false, fmt.Errorf("dialers not initialized for %s", executionId)
193
}
194
195
db := pg.Connect(&pg.Options{
196
Addr: target,
197
User: username,
198
Password: password,
199
Database: dbName,
200
Dialer: func(network, addr string) (net.Conn, error) {
201
return dialer.Fastdialer.Dial(context.Background(), network, addr)
202
},
203
IdleCheckFrequency: -1,
204
}).WithContext(ctx).WithTimeout(10 * time.Second)
205
defer func() {
206
_ = db.Close()
207
}()
208
209
_, err := db.Exec("select 1")
210
if err != nil {
211
switch true {
212
case strings.Contains(err.Error(), "connect: connection refused"):
213
fallthrough
214
case strings.Contains(err.Error(), "no pg_hba.conf entry for host"):
215
fallthrough
216
case strings.Contains(err.Error(), "network unreachable"):
217
fallthrough
218
case strings.Contains(err.Error(), "reset"):
219
fallthrough
220
case strings.Contains(err.Error(), "i/o timeout"):
221
return false, err
222
}
223
return false, nil
224
}
225
return true, nil
226
}
227
228