@@ -3,21 +3,96 @@ package httpserver
33import (
44 "errors"
55 "io/ioutil"
6+ "net/http"
7+ "os"
8+ "path"
69 "path/filepath"
710 "strings"
11+
12+ "github.com/projectdiscovery/gologger"
813)
914
15+ // uploadlayer handles PUT requests and save the file to disk
16+ func (t * HTTPServer ) uploadlayer (handler http.Handler ) http.Handler {
17+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
18+ // Handles file write if enabled
19+ if EnableUpload && r .Method == http .MethodPut {
20+ // sandbox - calcolate absolute path
21+ if t .options .Sandbox {
22+ absPath , err := filepath .Abs (filepath .Join (t .options .Folder , r .URL .Path ))
23+ if err != nil {
24+ gologger .Print ().Msgf ("%s\n " , err )
25+ w .WriteHeader (http .StatusBadRequest )
26+ return
27+ }
28+ // check if the path is within the configured folder
29+ pattern := t .options .Folder + string (filepath .Separator ) + "*"
30+ matched , err := filepath .Match (pattern , absPath )
31+ if err != nil {
32+ gologger .Print ().Msgf ("%s\n " , err )
33+ w .WriteHeader (http .StatusBadRequest )
34+ return
35+ } else if ! matched {
36+ gologger .Print ().Msg ("pointing to unauthorized directory" )
37+ w .WriteHeader (http .StatusBadRequest )
38+ return
39+ }
40+ }
41+
42+ var (
43+ data []byte
44+ err error
45+ )
46+ if t .options .Sandbox {
47+ maxFileSize := toMb (t .options .MaxFileSize )
48+ // check header content length
49+ if r .ContentLength > maxFileSize {
50+ gologger .Print ().Msg ("request too large" )
51+ return
52+ }
53+ // body max length
54+ r .Body = http .MaxBytesReader (w , r .Body , maxFileSize )
55+ }
56+
57+ data , err = ioutil .ReadAll (r .Body )
58+ if err != nil {
59+ gologger .Print ().Msgf ("%s\n " , err )
60+ w .WriteHeader (http .StatusInternalServerError )
61+ return
62+ }
63+
64+ sanitizedPath := filepath .FromSlash (path .Clean ("/" + strings .Trim (r .URL .Path , "/" )))
65+
66+ err = handleUpload (t .options .Folder , sanitizedPath , data )
67+ if err != nil {
68+ gologger .Print ().Msgf ("%s\n " , err )
69+ w .WriteHeader (http .StatusInternalServerError )
70+ return
71+ } else {
72+ w .WriteHeader (http .StatusCreated )
73+ return
74+ }
75+ }
76+
77+ handler .ServeHTTP (w , r )
78+ })
79+ }
80+
1081func handleUpload (base , file string , data []byte ) error {
1182 // rejects all paths containing a non exhaustive list of invalid characters - This is only a best effort as the tool is meant for development
1283 if strings .ContainsAny (file , "\\ `\" ':" ) {
1384 return errors .New ("invalid character" )
1485 }
1586
16- // allow upload only in subfolders
17- rel , err := filepath .Rel (base , file )
18- if rel == "" || err != nil {
19- return err
87+ untrustedPath := filepath .Clean (filepath .Join (base , file ))
88+ if ! strings .HasPrefix (untrustedPath , filepath .Clean (base )) {
89+ return errors .New ("invalid path" )
90+ }
91+ trustedPath := untrustedPath
92+
93+ if _ , err := os .Stat (path .Dir (trustedPath )); os .IsNotExist (err ) {
94+ return errors .New ("invalid path" )
2095 }
2196
22- return ioutil .WriteFile (file , data , 0655 )
97+ return ioutil .WriteFile (trustedPath , data , 0655 )
2398}
0 commit comments