diff --git a/src/client/client.c b/src/client/client.c index 61fcf117..4bdcf3fd 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -34,6 +34,8 @@ #endif #include +#include + #include #include @@ -147,6 +149,21 @@ static WC_INLINE void clu_build_addr(SOCKADDR_IN4_T* addr, SOCKADDR_IN6_T* ipv6, FILE* fp; char host_out[100]; char cmd[100]; + const char* cp; + + /* Validate hostname: only allow characters valid in DNS names + * (RFC 1123) to prevent shell injection via popen(). + * Use explicit ASCII ranges instead of isalnum() to avoid + * locale-dependent behavior. */ + for (cp = peer; *cp != '\0'; cp++) { + if (!((*cp >= 'A' && *cp <= 'Z') || + (*cp >= 'a' && *cp <= 'z') || + (*cp >= '0' && *cp <= '9') || + *cp == '.' || *cp == '-')) { + err_sys("invalid character in hostname"); + return; + } + } XSTRNCPY(cmd, "host ", 6); XSTRNCAT(cmd, peer, 99 - XSTRLEN(cmd)); diff --git a/tests/client/client-test.sh b/tests/client/client-test.sh index 88297c2d..00e6a329 100755 --- a/tests/client/client-test.sh +++ b/tests/client/client-test.sh @@ -25,5 +25,39 @@ fi rm tmp.crt +# Regression tests: shell injection via hostname must not execute injected command. +# Applies to the WOLFSSL_USE_POPEN_HOST path where peer is concatenated into a +# popen() shell command. On other builds, getaddrinfo/gethostbyname reject +# these hostnames before any shell is involved, so the tests pass either way. +INJFILE="clu_injection_probe.txt" +rm -f "$INJFILE" + +# Semicolon: "evil.com;touch clu_injection_probe.txt" passed as peer +./wolfssl s_client -connect 'evil.com;touch clu_injection_probe.txt:443' \ + 2>/dev/null +if [ -f "$INJFILE" ]; then + echo "SECURITY FAILURE: command injection via hostname (semicolon)" + rm -f "$INJFILE" + exit 99 +fi + +# Command substitution: "$(touch clu_injection_probe.txt)" passed as peer +./wolfssl s_client -connect 'evil$(touch clu_injection_probe.txt).com:443' \ + 2>/dev/null +if [ -f "$INJFILE" ]; then + echo "SECURITY FAILURE: command injection via hostname (command substitution)" + rm -f "$INJFILE" + exit 99 +fi + +# Pipe: "evil.com|touch clu_injection_probe.txt" passed as peer +./wolfssl s_client -connect 'evil.com|touch clu_injection_probe.txt:443' \ + 2>/dev/null +if [ -f "$INJFILE" ]; then + echo "SECURITY FAILURE: command injection via hostname (pipe)" + rm -f "$INJFILE" + exit 99 +fi + echo "Done" exit 0