From e368f58e56420981c60732e16fd8b2e3988dc697 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Tue, 30 Jun 2026 17:23:03 +0530 Subject: [PATCH] fix(ssl): prevent shell injection via site name in self-signed cert generation --- src/helper/Site_Self_Signed.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/helper/Site_Self_Signed.php b/src/helper/Site_Self_Signed.php index 57ea6451..206c3a9a 100644 --- a/src/helper/Site_Self_Signed.php +++ b/src/helper/Site_Self_Signed.php @@ -90,8 +90,10 @@ private function maybe_gen_root_cert() { $this->build_certificate_conf( $root_conf, 'EasyEngine' ); - EE::exec( sprintf( 'openssl genrsa -des3 -passout pass:%s -out %s 2048', $this->password, $this->root_key ) ); - EE::exec( sprintf( 'openssl req -x509 -new -nodes -key %s -sha256 -days 36500 -passin pass:%s -out %s -config %s', $this->root_key, $this->password, $this->root_pem, $root_conf ) ); + // Escape every interpolated value: these strings run through /bin/sh -c, so an + // unescaped password (or path) could inject an attacker subshell as root. + EE::exec( sprintf( 'openssl genrsa -des3 -passout pass:%s -out %s 2048', escapeshellarg( $this->password ), escapeshellarg( $this->root_key ) ) ); + EE::exec( sprintf( 'openssl req -x509 -new -nodes -key %s -sha256 -days 36500 -passin pass:%s -out %s -config %s', escapeshellarg( $this->root_key ), escapeshellarg( $this->password ), escapeshellarg( $this->root_pem ), escapeshellarg( $root_conf ) ) ); $this->trust_certificate( $this->root_pem ); @@ -133,10 +135,12 @@ private function build_v3_ext_conf( $path, $url ) { */ private function generate_certificate( $key_path, $crt_path, $csr_path, $conf_path, $v3_path ) { - EE::exec( sprintf( 'openssl req -new -sha256 -nodes -passout pass:%s -out %s -newkey rsa:2048 -keyout %s -config %s', $this->password, $csr_path, $key_path, $conf_path ) ); + // Paths here derive from the (unvalidated) site name and run through /bin/sh -c, + // so escape every interpolated value to prevent shell injection via the site name. + EE::exec( sprintf( 'openssl req -new -sha256 -nodes -passout pass:%s -out %s -newkey rsa:2048 -keyout %s -config %s', escapeshellarg( $this->password ), escapeshellarg( $csr_path ), escapeshellarg( $key_path ), escapeshellarg( $conf_path ) ) ); - $serial_string = $this->fs->exists( $this->root_srl ) ? sprintf( '-CAserial %s', $this->root_srl ) : sprintf( '-CAcreateserial -CAserial %s', $this->root_srl ); - EE::exec( sprintf( 'openssl x509 -req -in %s -CA %s -CAkey %s %s -passin pass:%s -out %s -days 36500 -sha256 -extfile %s', $csr_path, $this->root_pem, $this->root_key, $serial_string, $this->password, $crt_path, $v3_path ) ); + $serial_string = $this->fs->exists( $this->root_srl ) ? sprintf( '-CAserial %s', escapeshellarg( $this->root_srl ) ) : sprintf( '-CAcreateserial -CAserial %s', escapeshellarg( $this->root_srl ) ); + EE::exec( sprintf( 'openssl x509 -req -in %s -CA %s -CAkey %s %s -passin pass:%s -out %s -days 36500 -sha256 -extfile %s', escapeshellarg( $csr_path ), escapeshellarg( $this->root_pem ), escapeshellarg( $this->root_key ), $serial_string, escapeshellarg( $this->password ), escapeshellarg( $crt_path ), escapeshellarg( $v3_path ) ) ); } /** @@ -151,7 +155,7 @@ private function trust_certificate( $crt_path ) { if ( IS_DARWIN ) { EE::log( 'You may need to enter password once for adding root certificate as trusted on your system.' ); EE::exec( sprintf( - 'sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain %s', $crt_path + 'sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain %s', escapeshellarg( $crt_path ) ) ); } else { $cert_path = '/usr/local/share/ca-certificates';