CVE-2022-1509 (Other, published 2022-04-28T10:15Z)
Description: value: Sed Injection Vulnerability in GitHub repository hestiacp/hestiacp prior to 1.5.12. An authenticated remote attacker with low privileges can execute arbitrary code under root context.
Severity: HIGH (CVSS v3 base score: 8.8)
Weakness: CWE-77 \u2014 Improper Neutralization of Special Elements used in a Command ('Command Injection')
The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
Repository: https://github.com/hestiacp/hestiacp
Files changed:
bin/v-add-access-key
bin/v-add-backup-host
bin/v-add-cron-job
bin/v-add-dns-domain
bin/v-add-dns-record
bin/v-add-fastcgi-cache
bin/v-add-firewall-chain
bin/v-add-mail-domain
bin/v-add-mail-domain-ssl
bin/v-add-mail-domain-webmail
bin/v-add-remote-dns-domain
bin/v-add-remote-dns-host
bin/v-change-user-ns
bin/v-change-user-theme
bin/v-change-web-domain-name
bin/v-check-access-key
bin/v-delete-database-temp-user
bin/v-delete-dns-on-web-alias
bin/v-delete-mail-account-forward
bin/v-delete-user-notification
bin/v-delete-user-ssh-key
bin/v-generate-ssl-cert
bin/v-get-user-salt
bin/v-log-action
bin/v-log-user-logout
bin/v-purge-nginx-cache
bin/v-schedule-letsencrypt-domain
bin/v-update-host-certificate
bin/v-update-user-backup-exclusions
func/main.sh
test/checks.bats
web/inc/main.php
Commit message: [Security] Patches multiple security issue  (#2555)

* Add missing check in is_format_valid

* Add check on v-delete-dns-domain

* Validate theme via is_format_valid

* No need for extra code

* Prevent LF command passed trough + Add test to verify

* Add missing checks

* Don't allow exception for localhost

* Add some more missing checks

* Fix to strict error validation

* Add validation for user

* Fix bug in main.sh

* Use -d instead -f

Vulnerable code:
is_format_valid 'user'
if [ -n "$comment" ]; then
    is_format_valid 'comment'
fi
    is_username_format_valid "$username" "username"
is_format_valid 'user' 'domain'
is_format_valid 'chain'
is_format_valid 'user' 'domain' 'antispam' 'antivirus' 'dkim' 'dkim_size'
is_format_valid 'user' 'domain'
is_format_valid 'host' 'port' 'type' 'dns_user'
    ns3=$(echo "$4" | sed -e 's/\.*$//g' -e 's/^\.*//g')
    ns4=$(echo "$5" | sed -e 's/\.*$//g' -e 's/^\.*//g')
    ns5=$(echo "$6" | sed -e 's/\.*$//g' -e 's/^\.*//g')
    ns6=$(echo "$7" | sed -e 's/\.*$//g' -e 's/^\.*//g')
    ns7=$(echo "$8" | sed -e 's/\.*$//g' -e 's/^\.*//g')
    ns8=$(echo "$9" | sed -e 's/\.*$//g' -e 's/^\.*//g')
# Merging values
ns="$ns1,$ns2,$ns3,$ns4,$ns5,$ns6,$ns7,$ns8"
ns=$(echo "$ns" | sed -e "s/,,//g" -e "s/,$//")

is_format_valid 'user'
is_format_valid 'access_key_id'
is_format_valid 'user' 'dbuser'
is_format_valid 'user' 'domain'
nid=$2
is_format_valid 'user' 'nid'
sed -i "/NID='$nid' /d" $USER_DATA/notifications.conf 2>/dev/null
is_format_valid 'user'
check_args '2' "$#" 'USER DOMAIN'
is_format_valid 'user' 'domain'
is_format_valid 'user' 'domain'
is_format_valid 'user' 'vfile'
# removed "" around  \.\. and $(printf '\t'): SC2076: Don't quote right-hand side of =~, it'll match literally rather than as a regex. And we need regex match!

    if ! [[ "$1" =~ ^admin|user$ ]]; then
if ($_SESSION['user_combined_ip'] != $user_combined_ip && $_SERVER['REMOTE_ADDR'] != '127.0.0.1') {

Fixed code:
is_format_valid 'user' 'comment' 'format'
    is_type_valid "$type" 'sftp,ftp,b2'
    is_username_format_valid "$user" "username"
is_format_valid 'restart'
if [ -n "$restart" ]; then 
    is_format_valid 'restart'
fi

if [ -n "$restart" ]; then
    is_format_valid 'restart'
fi
is_format_valid 'user' 'domain' 'restart'
port_ext=$2
is_format_valid 'chain' 'port_ext' 'protocol'
is_format_valid 'user' 'domain' 'antispam' 'antivirus' 'dkim' 'dkim_size' 'restart'
if [ -n "$restart" ]; then 
    is_format_valid "$restart"
fi
is_format_valid 'user' 'domain' 'restart'
if [ -n "$flush" ]; then
    is_type_valid "records yes no" "$flush" 
fi
is_format_valid 'host' 'port' 'dns_user'

ns="$ns1,$ns2"
    ns="$ns,$ns3"
    ns="$ns,$ns4"
    ns="$ns,$ns5"
    ns="$ns,$ns6"
    ns="$ns,$ns7"
    ns="$ns,$ns8"
is_format_valid 'user' 'theme'
is_domain_format_valid "$new_domain"
is_format_valid 'access_key_id' 'ip' 'command'
is_format_valid 'user' 'database' 'dbuser'
alias=$3
is_format_valid 'user' 'domain' 'alias' 'restart'
is_format_valid 'user' 'domain' 'account'
id=$2
is_format_valid 'user' 'id'
sed -i "/NID='$id' /d" $USER_DATA/notifications.conf 2>/dev/null
is_no_new_line_format "$key_id"
is_common_format_valid $country "country"
is_common_format_valid $state "state"
is_common_format_valid $org "org"
is_common_format_valid $unit "unit"

is_format_valid 'user' 'ip'
is_common_format_valid "$event_level" "event level"

    is_format_valid 'user'
is_common_format_valid "$session_id" "Session ID"
rstart=$3
check_args '2' "$#" 'USER DOMAIN [RESTART]'
is_format_valid 'user' 'domain' 'restart'
is_format_valid 'user' 'domain' 'aliases'
is_domain_format_valid "$hostname" "hostname"
is_format_valid 'user'
    is_no_new_line_format "$1";
    is_no_new_line_format "$1";
    is_no_new_line_format "$1";
}

is_no_new_line_format() {
    test=$(echo $1 | sed -e 's/\.*$//g' -e 's/^\.*//g');
    if [[ "$test" != "$1" ]]; then
      check_result "$E_INVALID" "invalid value :: $1"
    fi
  is_no_new_line_format "$1";
    is_no_new_line_format "$1";
    is_no_new_line_format "$1"
    is_no_new_line_format "$1";
    if ! [[ "$1" =~ ^admin$|^user$ ]]; then
                alias)          is_alias_format_valid "$arg" ;;        
                chain)          is_object_format_valid "$arg" 'chain' ;;
                dom_alias)      is_alias_format_valid "$arg" ;;
                format)         is_type_valid 'plain json shell csv' "$arg" ;;
                ssl_dir)        is_folder_exists "$arg" "$arg_name" ;;
                theme)          is_common_format_valid "$arg" "$arg_name" ;;
is_folder_exists () {
  if [ ! -d "$1" ]; then 
    check_result "$E_NOTEXIST" "folder $1 does not exist"
  fi
}

is_command_valid_format () {
  if [[ ! "$1" =~ ^v-[[:alnum:]][-|\.|_[:alnum:]]{0,64}[[:alnum:]]$ ]]; then 
    check_result "$E_INVALID" "Invalid command format"
  fi
  if [[ -n $( echo "$1" | grep -e '\-\-' ) ]]; then 
    check_result "$E_INVALID" "Invalid command format"
  fi
}
    is_no_new_line_format "$1"
#!/usr/bin/env bats

if [ "${PATH#*/usr/local/hestia/bin*}" = "$PATH" ]; then
    . /etc/profile.d/hestia.sh
fi

load 'test_helper/bats-support/load'
load 'test_helper/bats-assert/load'
load 'test_helper/bats-file/load'

function random() {
head /dev/urandom | tr -dc 0-9 | head -c$1
}

function setup() {
    # echo "# Setup_file" > &3
    if [ $BATS_TEST_NUMBER = 1 ]; then
        echo 'user=test-5285' > /tmp/hestia-test-env.sh
        echo 'user2=test-5286' >> /tmp/hestia-test-env.sh
        echo 'userbk=testbk-5285' >> /tmp/hestia-test-env.sh
        echo 'userpass1=test-5285' >> /tmp/hestia-test-env.sh
        echo 'userpass2=t3st-p4ssw0rd' >> /tmp/hestia-test-env.sh
        echo 'HESTIA=/usr/local/hestia' >> /tmp/hestia-test-env.sh
        echo 'domain=test-5285.hestiacp.com' >> /tmp/hestia-test-env.sh
        echo 'domainuk=test-5285.hestiacp.com.uk' >> /tmp/hestia-test-env.sh
        echo 'rootdomain=testhestiacp.com' >> /tmp/hestia-test-env.sh
        echo 'subdomain=cdn.testhestiacp.com' >> /tmp/hestia-test-env.sh
        echo 'database=test-5285_database' >> /tmp/hestia-test-env.sh
        echo 'dbuser=test-5285_dbuser' >> /tmp/hestia-test-env.sh
    fi
    
    source /tmp/hestia-test-env.sh
    source $HESTIA/func/main.sh
    source $HESTIA/conf/hestia.conf
    source $HESTIA/func/ip.sh
}

@test "is_access_key_id_format_valid valid" {
    run is_access_key_id_format_valid 'M0ocDoIKbsoXSqtk1mgc' "key"
    assert_success
}
@test "is_access_key_id_format_valid short" {
    run is_access_key_id_format_valid 'M0ocDoIKbsoXSqtk1mg' "key"
    assert_failure $E_INVALID
}
@test "is_access_key_id_format_valid long" {
    run is_access_key_id_format_valid 'M0ocDoIKbsoXSqtk1mgca' "key"
    assert_failure $E_INVALID
}
@test "is_access_key_id_format_valid non alpha" {
    run is_access_key_id_format_valid 'M0ocDoIKbsoX$qtk1mgc' "key"
    assert_failure $E_INVALID
}

@test "is_access_key_id_format_valid LHF" {
    run is_access_key_id_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_user_format_valid valid" {
    run is_user_format_valid 'hxh54SKbALne4s69VsqJRMbMd8Br' "key"
    assert_success
}
@test "is_user_format_valid short" {
    run is_user_format_valid 'hxh54SKbALne4s69VsqJR' "key"
    assert_success
}
@test "is_user_format_valid long" {
    run is_user_format_valid 'hxh54SKbALne4s69VsqJRMbMd8Braaa' "key"
    assert_failure $E_INVALID
}
@test "is_user_format_valid dash" {
    run is_user_format_valid 'hxh54SKbALne4-s6-VsqJRMbMd8Br' "key"
    assert_success
}

@test "is_user_format_valid dash repeat" {
    run is_user_format_valid 'hxh54SKbALne4s6--VsqJRMbMd8Br' "key"
    assert_success
}
@test "is_user_format_valid dash start" {
    run is_user_format_valid '-hxh54SKbALne4s6VsqJRMbMd8Br' "key"
    assert_failure $E_INVALID
}
@test "is_user_format_valid dash end" {
    run is_user_format_valid 'hxh54SKbALne4s6VsqJRMbMd8Br-' "key"
    assert_failure $E_INVALID
}
@test "is_user_format_valid LHF" {
    run is_user_format_valid 'M0ocDoIK
    soXSqtk1mgc' "key"
    assert_failure $E_INVALID
}

@test "is_fw_action_format_valid ACCEPT" {
    run is_fw_action_format_valid 'ACCEPT' "key"
    assert_success
}
@test "is_fw_action_format_valid DROP" {
    run is_fw_action_format_valid 'DROP' "key"
    assert_success
}
@test "is_fw_action_format_valid TEST" {
    run is_fw_action_format_valid 'TEST' "key"
    assert_failure $E_INVALID
}
@test "is_fw_action_format_valid LHF" {
    run is_fw_protocol_format_valid 'M0ocDoIK
    soXSqtk1mgc' "key"
    assert_failure $E_INVALID
}

@test "is_fw_protocol_format_valid ICMP" {
    run is_fw_protocol_format_valid 'ICMP' "key"
    assert_success
}

@test "is_fw_protocol_format_valid UDP" {
    run is_fw_protocol_format_valid 'UDP' "key"
    assert_success
}
@test "is_fw_protocol_format_valid TCP" {
    run is_fw_protocol_format_valid 'TCP' "key"
    assert_success
}
@test "is_fw_protocol_format_valid TEST" {
    run is_fw_protocol_format_valid 'TEST' "key"
    assert_failure $E_INVALID
}


@test "is_domain_format_valid success" {
     run is_domain_format_valid 'hestiacp.com' "key"
    assert_success
}

@test "is_domain_format_valid www" {
     run is_domain_format_valid 'www' "key"
    assert_failure $E_INVALID
}
@test "is_domain_format_valid number" {
     run is_domain_format_valid '12345' "key"
    assert_failure $E_INVALID
}

@test "is_domain_format_valid .." {
     run is_domain_format_valid '..' "key"
    assert_failure $E_INVALID
}
@test "is_domain_format_valid LF." {
     run is_domain_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_alias_format_valid success" {
     run is_domain_format_valid 'hestiacp.com' "key"
    assert_success
}

@test "is_alias_format_valid www" {
     run is_domain_format_valid 'www' "key"
    assert_failure $E_INVALID
}
@test "is_alias_format_valid number" {
     run is_domain_format_valid '12345' "key"
    assert_failure $E_INVALID
}

@test "is_alias_format_valid .." {
     run is_domain_format_valid '..' "key"
    assert_failure $E_INVALID
}
@test "is_alias_format_valid LF." {
     run is_domain_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_extention_format_valid test" {
     run is_extention_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_string_format_valid test" {
     run is_string_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_database_format_valid test" {
     run is_database_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_date_format_valid test" {
     run is_date_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_dbuser_format_valid test" {
     run is_dbuser_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_dns_type_format_valid test" {
     run is_dns_type_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_dns_record_format_valid test" {
     run is_dns_record_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_email_format_valid test" {
     run is_email_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_fw_port_format_valid test" {
     run is_fw_port_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_int_format_valid test" {
     run is_int_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_interface_format_valid test" {
     run is_interface_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_ip_status_format_valid test" {
     run is_ip_status_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_cron_format_valid test" {
     run is_cron_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_name_format_valid test" {
     run is_name_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}


@test "is_role_valid test" {
     run is_role_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_object_format_valid test" {
     run is_object_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}


@test "is_common_format_valid test" {
     run is_common_format_valid 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "format_no_quotes test" {
     run format_no_quotes 'c
1eshutdown
r' "key"
    assert_failure $E_INVALID
}

@test "is_type_valid" {
    run is_type_valid 'c
    1eshutdown
    r' "test,key"
    assert_failure $E_INVALID
}

@test "is_command_valid_format v-list-users" {
    run is_command_valid_format 'v-list-users'
    assert_success
}

@test "is_command_valid_format v-list--users (Fail)" {
    run is_command_valid_format 'v-list--users'
    assert_failure $E_INVALID
}

@test "is_command_valid_format h-list-users (Fail)" {
    run is_command_valid_format 'h-list-users'
    assert_failure $E_INVALID
}

@test "is_command_valid_format list-users (Fail)" {
    run is_command_valid_format 'list-users'
    assert_failure $E_INVALID
}

@test "is_command_valid_format vlist-users (Fail)" {
    run is_command_valid_format 'vlist-users'
    assert_failure $E_INVALID
}

@test "is_command_valid_format LF (Fail)" {
     run is_command_valid_format 'v-
1eshutdown
r' "key"
    assert_failure $E_INVALID
}
if ($_SESSION['user_combined_ip'] != $user_combined_ip) {