Skip to content

Commit 0917068

Browse files
committed
begin of work on pseudo-sandbox mode
1 parent 850e06b commit 0917068

File tree

6 files changed

+129
-3
lines changed

6 files changed

+129
-3
lines changed

internal/runner/options.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ type Options struct {
2929
TCPWithTLS bool
3030
Version bool
3131
Silent bool
32+
Sandbox bool
33+
MaxFileSize int
3234
}
3335

3436
// ParseOptions parses the command line options for application
@@ -49,6 +51,8 @@ func ParseOptions() *Options {
4951
flag.StringVar(&options.Realm, "realm", "Please enter username and password", "Realm")
5052
flag.BoolVar(&options.Version, "version", false, "Show version of the software")
5153
flag.BoolVar(&options.Silent, "silent", false, "Show only results in the output")
54+
flag.BoolVar(&options.Sandbox, "sandbox", false, "Enable sandbox mode")
55+
flag.IntVar(&options.MaxFileSize, "max-file-size", 50, "Max Upload File Size")
5256

5357
flag.Parse()
5458

internal/runner/runner.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ func New(options *Options) (*Runner, error) {
5757
BasicAuthPassword: r.options.password,
5858
BasicAuthReal: r.options.Realm,
5959
Verbose: r.options.Verbose,
60+
Sandbox: r.options.Sandbox,
61+
MaxFileSize: r.options.MaxFileSize,
6062
})
6163
if err != nil {
6264
return nil, err

pkg/httpserver/httpserver.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package httpserver
22

33
import (
4+
"errors"
45
"net/http"
6+
"os"
7+
"path/filepath"
58

69
"github.com/projectdiscovery/sslcert"
710
)
@@ -19,6 +22,8 @@ type Options struct {
1922
BasicAuthPassword string
2023
BasicAuthReal string
2124
Verbose bool
25+
Sandbox bool
26+
MaxFileSize int // 50Mb
2227
}
2328

2429
// HTTPServer instance
@@ -32,9 +37,22 @@ func New(options *Options) (*HTTPServer, error) {
3237
var h HTTPServer
3338
EnableUpload = options.EnableUpload
3439
EnableVerbose = options.Verbose
35-
h.layers = h.loglayer(http.FileServer(http.Dir(options.Folder)))
40+
folder, err := filepath.Abs(options.Folder)
41+
if err != nil {
42+
return nil, err
43+
}
44+
if _, err := os.Stat(folder); os.IsNotExist(err) {
45+
return nil, errors.New("path does not exist")
46+
}
47+
options.Folder = folder
48+
var dir http.FileSystem
49+
dir = http.Dir(options.Folder)
50+
if options.Sandbox {
51+
dir = SandboxFileSystem{fs: http.Dir(options.Folder), RootFolder: options.Folder}
52+
}
53+
h.layers = h.loglayer(http.FileServer(dir))
3654
if options.BasicAuthUsername != "" || options.BasicAuthPassword != "" {
37-
h.layers = h.loglayer(h.basicauthlayer(http.FileServer(http.Dir(options.Folder))))
55+
h.layers = h.loglayer(h.basicauthlayer(http.FileServer(dir)))
3856
}
3957
h.options = options
4058

pkg/httpserver/loglayer.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"net/http/httputil"
88
"path"
9+
"path/filepath"
910

1011
"github.com/projectdiscovery/gologger"
1112
)
@@ -24,13 +25,54 @@ func (t *HTTPServer) loglayer(handler http.Handler) http.Handler {
2425

2526
// Handles file write if enabled
2627
if EnableUpload && r.Method == http.MethodPut {
27-
data, err := ioutil.ReadAll(r.Body)
28+
// sandbox - calcolate absolute path
29+
if t.options.Sandbox {
30+
absPath, err := filepath.Abs(filepath.Join(t.options.Folder, r.URL.Path))
31+
if err != nil {
32+
gologger.Print().Msgf("%s\n", err)
33+
w.WriteHeader(http.StatusBadRequest)
34+
return
35+
}
36+
// check if the path is within the configured folder
37+
pattern := t.options.Folder + string(filepath.Separator) + "*"
38+
matched, err := filepath.Match(pattern, absPath)
39+
if err != nil {
40+
gologger.Print().Msgf("%s\n", err)
41+
w.WriteHeader(http.StatusBadRequest)
42+
return
43+
} else if !matched {
44+
gologger.Print().Msg("pointing to unauthorized directory")
45+
w.WriteHeader(http.StatusBadRequest)
46+
return
47+
}
48+
}
49+
50+
var (
51+
data []byte
52+
err error
53+
)
54+
if t.options.Sandbox {
55+
maxFileSize := toMb(t.options.MaxFileSize)
56+
// check header content length
57+
if r.ContentLength > maxFileSize {
58+
gologger.Print().Msg("request too large")
59+
return
60+
}
61+
// body max length
62+
r.Body = http.MaxBytesReader(w, r.Body, maxFileSize)
63+
}
64+
65+
data, err = ioutil.ReadAll(r.Body)
2866
if err != nil {
2967
gologger.Print().Msgf("%s\n", err)
68+
w.WriteHeader(http.StatusInternalServerError)
69+
return
3070
}
3171
err = handleUpload(path.Base(r.URL.Path), data)
3272
if err != nil {
3373
gologger.Print().Msgf("%s\n", err)
74+
w.WriteHeader(http.StatusInternalServerError)
75+
return
3476
}
3577
}
3678

pkg/httpserver/sandboxfs.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package httpserver
2+
3+
import (
4+
"errors"
5+
"net/http"
6+
"path/filepath"
7+
)
8+
9+
type SandboxFileSystem struct {
10+
fs http.FileSystem
11+
RootFolder string
12+
}
13+
14+
func (sbfs SandboxFileSystem) Open(path string) (http.File, error) {
15+
abspath, err := filepath.Abs(filepath.Join(sbfs.RootFolder, path))
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
filename := filepath.Base(abspath)
21+
// rejects names starting with a dot like .file
22+
dotmatch, err := filepath.Match(".*", filename)
23+
if err != nil {
24+
return nil, err
25+
} else if dotmatch {
26+
return nil, errors.New("invalid file")
27+
}
28+
29+
// reject symlinks
30+
symlinkCheck, err := filepath.EvalSymlinks(abspath)
31+
if err != nil {
32+
return nil, err
33+
}
34+
if symlinkCheck != abspath {
35+
return nil, errors.New("symlinks not allowed")
36+
}
37+
38+
// check if the path is within the configured folder
39+
if sbfs.RootFolder != abspath {
40+
pattern := sbfs.RootFolder + string(filepath.Separator) + "*"
41+
matched, err := filepath.Match(pattern, abspath)
42+
if err != nil {
43+
return nil, err
44+
} else if !matched {
45+
return nil, errors.New("invalid file")
46+
}
47+
}
48+
49+
f, err := sbfs.fs.Open(path)
50+
if err != nil {
51+
return nil, err
52+
}
53+
54+
return f, nil
55+
}

pkg/httpserver/util.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package httpserver
2+
3+
func toMb(n int) int64 {
4+
return int64(n) * 1024 * 1024
5+
}

0 commit comments

Comments
 (0)