mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-07 09:46:03 -10:00
gate mysql allowAllFiles behind -lfa
Route every mysql DSN through a sandbox that drops allowAllFiles unless -lfa is set, matching the fs.ReadFile restriction.
This commit is contained in:
3 files changed
+72
-3
No files matched your search
@@ -2,7 +2,6 @@ package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -227,7 +226,7 @@ func (c *MySQLClient) ExecuteQueryWithOpts(ctx context.Context, opts MySQLOption
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db, err := sql.Open("mysql", dsn)
|
||||
db, err := openDB(executionId, dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -72,9 +76,33 @@ func BuildDSN(opts MySQLOptions) (string, error) {
|
||||
return dsn.String(), nil
|
||||
}
|
||||
|
||||
// sandboxDSN enforces the local file access sandbox on a MySQL DSN. The
|
||||
// driver's allowAllFiles option lets a malicious server read any local file
|
||||
// off the host via LOAD DATA LOCAL INFILE, so it is only honored when -lfa is
|
||||
// enabled, mirroring the fs.ReadFile restriction.
|
||||
func sandboxDSN(dsn string, lfaAllowed bool) (string, error) {
|
||||
cfg, err := mysql.ParseDSN(dsn)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if cfg.AllowAllFiles && !lfaAllowed {
|
||||
cfg.AllowAllFiles = false
|
||||
}
|
||||
return cfg.FormatDSN(), nil
|
||||
}
|
||||
|
||||
// openDB opens a sandboxed MySQL connection from dsn.
|
||||
func openDB(executionId, dsn string) (*sql.DB, error) {
|
||||
dsn, err := sandboxDSN(dsn, protocolstate.IsLfaAllowed(&types.Options{ExecutionId: executionId}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sql.Open("mysql", dsn)
|
||||
}
|
||||
|
||||
// @memo
|
||||
func connectWithDSN(ctx context.Context, executionId string, dsn string) (bool, error) {
|
||||
db, err := sql.Open("mysql", dsn)
|
||||
db, err := openDB(executionId, dsn)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSandboxDSN(t *testing.T) {
|
||||
t.Run("strips allowAllFiles when lfa disabled", func(t *testing.T) {
|
||||
got, err := sandboxDSN("root:x@nucleitcp(127.0.0.1:3306)/?allowAllFiles=true", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg, err := mysql.ParseDSN(got)
|
||||
require.NoError(t, err)
|
||||
require.False(t, cfg.AllowAllFiles)
|
||||
})
|
||||
|
||||
t.Run("keeps allowAllFiles when lfa enabled", func(t *testing.T) {
|
||||
got, err := sandboxDSN("root:x@nucleitcp(127.0.0.1:3306)/?allowAllFiles=true", true)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg, err := mysql.ParseDSN(got)
|
||||
require.NoError(t, err)
|
||||
require.True(t, cfg.AllowAllFiles)
|
||||
})
|
||||
|
||||
t.Run("leaves dsn without allowAllFiles untouched", func(t *testing.T) {
|
||||
got, err := sandboxDSN("root:x@nucleitcp(127.0.0.1:3306)/", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg, err := mysql.ParseDSN(got)
|
||||
require.NoError(t, err)
|
||||
require.False(t, cfg.AllowAllFiles)
|
||||
})
|
||||
|
||||
t.Run("errors on invalid dsn", func(t *testing.T) {
|
||||
_, err := sandboxDSN("::not-a-dsn::", false)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user