String concatenation is the cause of SQL Injection. This is avoided using parametrisation.
Stored procedures add an additional layer of security by enforcing invalid syntax when you concatenate, but are not "safer" if you use, say, dynamic SQL in them.
So, your code above is caused by concatenation of these strings
exec sp_GetUser '
x' AND 1=(SELECT COUNT(*) FROM Client); --
' , '
monkey
'
This gives invalid syntax, luckily
Parametrising it would give
exec sp_GetUser 'x'' AND 1=(SELECT COUNT(*) FROM Client); --' , 'monkey'
This means
@UserName = x' AND 1=(SELECT COUNT(*) FROM Client); --
@Password = monkey
Now, in the code above you'll get no rows because I assume you have no user x' AND 1=(SELECT COUNT(*) FROM Client); --
If the stored proc looked like this (using concatenated dynamic SQL), then your parametrised stored proc call will still allow SQL Injection
...
SET @sql = 'SELECT userName from users where userName = ''' +
@UserName +
''' and userPass = ''' +
@Password +
''''
EXEC (@sql)
....
So, as demonstrated, string concatenation is the main enemy for SQL injection
Stored procedures do add encapsulation, transaction handling, reduced permissions etc, but they can still be abused for SQL injection.
You can look on Stack Overflow for more about parametrisation