all repos — nirvash @ 2ed7370c2cfeeab6158174c97723d0584a1600db

modular CMS using the quartzgun library

more robust path traversal prevention
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iHUEABYKAB0WIQT/foVVmI9pK13hPWFohAcXSWbK8wUCZjRQIwAKCRBohAcXSWbK
84EVAQC9t72GsjzqPA/z35zRqHYeDB+PNJ5nbnxJkeaGoOem3gD+OP0MSbF5xvUC
Y4gNzouAQ1l7wXLCob2lGPvryac3qwU=
=aRaP
-----END PGP SIGNATURE-----
commit

2ed7370c2cfeeab6158174c97723d0584a1600db

parent

5b506f06d0595c7bb4906890554e004b5fda20ce

2 files changed, 44 insertions(+), 24 deletions(-)

jump to
M archetype/eureka.goarchetype/eureka.go

@@ -89,12 +89,12 @@ return pages

} func (self *EurekaAdapter) GetPage(filename string) Page { - if strings.Contains(filename, "../") || strings.Contains(filename, "..\\") { + fullPath := filepath.Join(self.Root, "inc", filename) + if !strings.HasPrefix(filepath.Clean(fullPath), self.Root) { return Page{ Error: "You cannot escape!", } } - fullPath := filepath.Join(self.Root, "inc", filename) f, err := os.ReadFile(fullPath) if err != nil {

@@ -199,8 +199,8 @@ // eureka creates titles from slugs, so we transform the title into the slug

slug = strings.ToLower(strings.ReplaceAll(title, " ", "_")) + ".htm" path := filepath.Join(self.Root, "inc", slug) - if strings.Contains(slug, "../") || strings.Contains(slug, "..\\") { - return errors.New("You cannot escape!") + if !strings.HasPrefix(filepath.Clean(path), self.Root) { + errors.New("You cannot escape!") } _, err := os.Stat(path)

@@ -220,8 +220,11 @@ func (self *EurekaAdapter) SavePage(oldSlug, newSlug, title, content string) error {

// eureka creates titles from slugs, so we transform the title into the slug newSlug = strings.ToLower(strings.ReplaceAll(title, " ", "_")) + ".htm" - if strings.Contains(newSlug, "../") || strings.Contains(newSlug, "..\\") || - strings.Contains(oldSlug, "../") || strings.Contains(oldSlug, "..\\") { + oldPath := filepath.Join(self.Root, "inc", oldSlug) + newPath := filepath.Join(self.Root, "inc", newSlug) + + if !strings.HasPrefix(filepath.Clean(oldPath), filepath.Join(self.Root, "inc")) || + !strings.HasPrefix(filepath.Clean(newPath), filepath.Join(self.Root, "inc")) { return errors.New("You cannot escape!") }

@@ -249,15 +252,16 @@ return nil

} func (self *EurekaAdapter) DeletePage(slug string) error { - if strings.Contains(slug, "../") || strings.Contains(slug, "..\\") { - return errors.New("You cannot escape!") - } - siteRoot := self.Config[ConfigOption{ Name: "SITEROOT", Type: "string", }] htmlFile := filepath.Join(self.Root, siteRoot, slug+"l") + + if !strings.HasPrefix(filepath.Clean(htmlFile), filepath.Join(self.Root, siteRoot)) { + return errors.New("You cannot escape!") + } + _, err := os.Stat(htmlFile) if !os.IsNotExist(err) { os.Remove(htmlFile)
M archetype/fileManager.goarchetype/fileManager.go

@@ -54,12 +54,12 @@

func (self *SimpleFileManager) ListSubTree(root string) FileListing { list := FileListing{} - if strings.Contains(root, "../") || strings.Contains(root, "..\\") { + fullPath := filepath.Join(self.Root, root) + + if !strings.HasPrefix(filepath.Clean(fullPath), self.Root) { list.Error = "You cannot escape!" return list } - - fullPath := filepath.Join(self.Root, root) files, err := ioutil.ReadDir(fullPath)

@@ -114,7 +114,7 @@ return FileData{

Error: err.Error(), } } - if !strings.HasPrefix(fullPath, self.Root) { + if !strings.HasPrefix(filepath.Clean(fullPath), self.Root) { return FileData{ Error: "You cannot escape!", }

@@ -133,29 +133,31 @@ }

func (self *SimpleFileManager) Remove(slug string) error { fullPath := filepath.Join(self.Root, slug) - _, err := os.Stat(fullPath) + if !strings.HasPrefix(filepath.Clean(fullPath), self.Root) { + return errors.New("You cannot escape!") + } + + _, err := os.Stat(fullPath) if err != nil { return err } - if !strings.HasPrefix(fullPath, self.Root) { - return errors.New("You cannot escape!") - } return os.RemoveAll(fullPath) } func (self *SimpleFileManager) AddFile(path string, req *http.Request) error { fullPath := filepath.Join(self.Root, path) + + if !strings.HasPrefix(filepath.Clean(fullPath), filepath.Clean(self.Root)) { + return errors.New("You cannot escape!") + } + _, err := os.Stat(fullPath) if err != nil { return err } - if !strings.HasPrefix(fullPath, filepath.Clean(self.Root)) { - return errors.New("You cannot escape!") - } - req.ParseMultipartForm(self.maxUploadMB << 20) file, header, err := req.FormFile("file") if err != nil {

@@ -180,7 +182,7 @@ }

func (self *SimpleFileManager) MkDir(path, newDir string) error { fullPath := filepath.Join(self.Root, path) - if !strings.HasPrefix(fullPath, self.Root) { + if !strings.HasPrefix(filepath.Clean(fullPath), self.Root) { return errors.New("You cannot escape!") }

@@ -204,12 +206,21 @@ }

func (self *SimpleFileManager) Rename(oldFullPath, newPath, newName string) error { fullPath := filepath.Join(self.Root, oldFullPath) + + if !strings.HasPrefix(filepath.Clean(fullPath), self.Root) { + return errors.New("You cannot escape!") + } + _, err := os.Stat(fullPath) if err != nil { return err } - newParent := filepath.Join(self.Root, newPath) + newParent := filepath.Clean(filepath.Join(self.Root, newPath)) + if !strings.HasPrefix(newParent, self.Root) { + return errors.New("You cannot escape!") + } + _, err = os.Stat(newParent) if err != nil { return err

@@ -218,6 +229,11 @@

if newName == "" { _, oldName := filepath.Split(oldFullPath) newName = oldName + } + + newFullPath := filepath.Join(newParent, newName) + if !strings.HasPrefix(filepath.Clean(newFullPath), newParent) { + return errors.New("You cannot escape!") } return os.Rename(fullPath, filepath.Join(newParent, newName))