Coordinated Disclosure Timeline

Summary

Plenti’s serve command creates a local server to view one’s website. This server is vulnerable to arbitrary file write and arbitrary file deletion, which may lead to remote code execution.

Project

plenti

Tested Version

v0.7.1

Details

Issue 1: Arbitrary File Write in serve.go (GHSL-2024-297)

The /postLocal endpoint is vulnerable to an arbitrary file write vulnerability when a plenti user serves their website.

func postLocal(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			fmt.Printf("Could not read 'body' from local edit: %v", err)
		}
		var localChanges []localChange
		err = json.Unmarshal(b, &localChanges)
		if err != nil {
			fmt.Printf("Could not unmarshal JSON data: %v", err)
		}
		var contents []byte
		for _, change := range localChanges {
			if change.Action == "create" || change.Action == "update" {
				contents = []byte(change.Contents)
				if change.Encoding == "base64" {
					contents, err = base64.StdEncoding.DecodeString(change.Contents)
					if err != nil {
						fmt.Printf("Could not decode base64 asset: %v", err)
					}
				}
				if len(change.File) > 0 && change.File[0:1] == "/" {
					// Make sure path is relative to project
					change.File = "." + change.File
				}
				err = os.WriteFile(change.File, contents, os.ModePerm)

Impact

This issue may lead to Remote Code Execution.

Issue 2: Arbitrary File Deletion in serve.go (GHSL-2024-298)

The /postLocal endpoint is vulnerable to an arbitrary file write deletion when a plenti user serves their website.

func postLocal(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			fmt.Printf("Could not read 'body' from local edit: %v", err)
		}
		var localChanges []localChange
		err = json.Unmarshal(b, &localChanges)
		if err != nil {
			fmt.Printf("Could not unmarshal JSON data: %v", err)
		}
		var contents []byte
		for _, change := range localChanges {
			if change.Action == "delete" {
				currentDir, _ := os.Getwd()
				err = os.Remove(filepath.Join(currentDir, change.File))
				if err != nil {
					fmt.Printf("Unable to delete local file: %v", err)
				}
			}

Impact

This issue may lead to Information Loss.

CVE

Credit

These issues were discovered and reported by GHSL team member @Kwstubbs (Kevin Stubbings).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2024-297 or GHSL-2024-298 in any communication regarding these issues.