I somewhere did a wrong escape of wmi query, can someone help fixing it? Because it shows Invalid query
string deviceid = "Disk #0, Partition #0";
string antecedent = #"\\" + Environment.MachineName + "\\root\\cimv2:Win32_DiskPartition.DeviceID=\"" + deviceid + "\"";
ManagementObjectSearcher assosiaciation_query2 = new ManagementObjectSearcher("select Dependent from Win32_LogicalDiskToPartition where Antecedent = \"" + antecedent + "\"");
foreach (ManagementObject assosiaciation_query_data2 in assosiaciation_query2.Get())
{
Console.WriteLine("Dependent: " + assosiaciation_query_data2["Dependent"]);
}
Thank you.
Ok since in mysql you could use LIKE to find simillar tables, also I read that is possible https://msdn.microsoft.com/en-us/library/aa392263%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 to use LIKE in wmi query
string deviceid = "Disk #0, Partition #0"; ManagementObjectSearcher assosiaciation_query2 = new ManagementObjectSearcher("select Dependent from Win32_LogicalDiskToPartition where Antecedent LIKE '%" + deviceid + "%'");
So ant still it show that query is invalid
You put 2 doublequote inside 2 other doublequote, and that broke your code. You should use WMI escape code inside your antecedent string:
string antecedent = #"\\" + Environment.MachineName + "\\root\\cimv2:Win32_DiskPartition.DeviceID=\\\"" + deviceid + "\\\"";
When WMI receive the command it should be something like that:
\"yourdeviceid\"
that inserted into 2 apex shoud work correctly :) Example:
Mycommand"myOtherCommand\"myvalue\""
EDIT:
I completely misunderstant your goal, sorry, there is an error in your query, because '#' is MySQL Wildcard that mess your query (is MySql inline comment),
Nope, it doesn't works even if i use where Ascendent like 'aaa', idk why your where condition mess up the code :/.
If you use the following piece of code it works:
string query = "select * from Win32_LogicalDiskToPartition";
string antecedent = #"\\" + Environment.MachineName + "\\root\\cimv2:Win32_DiskPartition.DeviceID=\"" + deviceid + "\"";
ManagementObjectSearcher moSearch = new ManagementObjectSearcher(query);
ManagementObjectCollection moCollection = moSearch.Get();
foreach (ManagementObject mo in moCollection)
{
if (string.Equals(mo["Antecedent"].ToString(), antecedent))
Console.WriteLine("Dependent: " + mo["Dependent"]);
}
Related
Currently I am using this statement:
"SELECT categoryDB, number FROM " + dbName+ " WHERE titleDBColumn ='" + titleInput+ "'";
Which helps me find strings that are similar to titleInput (which is a variable coming from the outside).
However, the values in titleDBColumn are almost always shorter strings than those coming in through titleInput.
Example:
titleDBColumn: Streetcar
titleInput: TheStreetCarIOwn
Now it's obvious that I need to use the LIKE operator in the other direction to get the results I want but I cant get the format right. Any ideas?
Sorry if I'm unclear.
This worked for me:
"SELECT categoryDB, number FROM " + dbName + " WHERE '" +
titleInput + "' like '%' + titleDBColumn + '%'";
The resulting SQL must be
SELECT categoryDB, number
FROM tableName
WHERE 'input' LIKE '%' + titleDBColumn + '%'
The % wildcard means "any number of any characters". I.e, 'input' LIKE '%' + titleDBColumn + '%' means that the input text may contain characters before and after the column text.
Also, you should use command parameters, whenever possible. This is not possible for the table name you called dbName. If this name is defined in the code and is not a user input, then it is safe to concatenate it as you did. But otherwise take measures to prevent SQL-Injection.
string sql = "SELECT categoryDB, number FROM `" + dbName +
"` WHERE #input LIKE '%' + titleDBColumn + '%'";
using (var conn = new MySqlConnection(connStr)) {
var cmd = new MySqlCommand(sql, conn);
cmd.Parameters.AddWithValue("#input", titleInput);
conn.Open();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
...
}
}
I have a utility that generates query strings, but the static code analyzers (and my coworkers) are complaining because of risk of "SQL Injection Attack".
Here is my C# code
public static string[] GenerateQueries(string TableName, string ColumnName)
{
return new string[] {
"SELECT * FROM " + TableName,
"SELECT * FROM " + TableName + " WHERE 1=2",
"SELECT * FROM " + TableName + " WHERE [" + TableName + "Id] = #id",
"SELECT * FROM " + TableName + " WHERE [" + TableName + "Id] = IDENT_CURRENT('" + TableName + "')",
"SELECT * FROM " + TableName + " WHERE [" + ColumnName + "] = #value"
};
}
In the code I always call it only with constant strings, such as
var queryList = GenerateQueries("Person", "Name");
Is there any way to rewrite this function so that it is "safe"? For example, if I were using C instead of C#, I could write a macro to generate the strings safely.
At the moment, the only choice I have is to copy/paste this block of SELECT statements for every single table, which is ugly and a maintenance burden.
Is copy/paste really my only option?
EDIT:
Thank you for the replies, esp William Leader. Now I see that my question is wrong-headed. It isn't just the fact that I am concatenating query strings, but also storing them in a variable. The only proper way to do this is to construct the SqlDataAdapter using a constant such as,
var adapter = new SqlDataAdapter("SELECT * FROM PERSON");
There is no other choice. So yes, there will be a lot of copy/paste. I'm starting to regret not using EF.
I was shocked at first, but on reflection this is no different than having an SQL statement already in your code that looks like this:
"SELECT * FROM Person"
We do that kind of thing all the time.
IF
There's an important caveat here. That only remains true if you can control how the function is called. So if this method is a private member of a data layer class somewhere, you might be okay. But I also wonder how useful this really is. It seems like you're not saving much over what you'd get from just writing the queries.
Additionally, it's not good to be in the habit of ignoring your static analysis tools. Sometimes they give you stuff you just know is wrong, but you change it anyway so that when they do find something important you're not conditioned to ignore it.
What your Code analyser is telling you is that you should most likely be calling a procedure with some parameters instead of sending SQL across the wire.
It does not mater a single bit whether or not you use a macro to generate your SQL statements, if you are sending raw SQL across the wire you are open to SQL Injection Attacks
Sending SQL commands to an endpoint making a non sanctioned call. If we fire up a network packet sniffer, we can see that you have a database configured to allow SQL commands to be sent, so we can inject illegal SQL into the system
You could still rely on a single procedure for calling your updates, but if you elect to move to procedures, why would you want to do that?
EDITED to provide an example
create PROC sp_CommonSelectFromTableProc #tableName varchar(32)
AS
-- code to check the tableName parameter does not contain SQL and/or is a valid tableName
-- your procedure code here will probable use
-- exec mydynamicSQLString
-- where mydynamicSQLString is constructed using #tableName
END;
or maybe a table specific procedure
create PROC sp_SelectFromSpecificTableProc
AS
SELECT * FROM SpecificTable
END;
What is important to remember is that SQL injection is independent of the technology used for the underlying application.
It is just overt when the application contains such constructs as
return new string[] {
"SELECT * FROM " + TableName,
"SELECT * FROM " + TableName + " WHERE 1=2",
"SELECT * FROM " + TableName + " WHERE [" + TableName + "Id] = #id",
"SELECT * FROM " + TableName + " WHERE [" + TableName + "Id] = IDENT_CURRENT('" + TableName + "')",
"SELECT * FROM " + TableName + " WHERE [" + ColumnName + "] = #value"
SQL Injection must be addressed at both ends of the data channel.
Here is a pretty good starting point for understanding how to mitigate for SQL Injection attacks
I'm trying to build a query, to list all the known computers in SCCM with a specific name.
The query looks like this:
string query = string.Format("Select Name From SMS_R_System Where Name like '" + "%" + computerName + "%" + "'");
If results are found, it puts the result(s) in a dropdown box.
My problem in these case, the output looks like this:
"instance of SMS_R_System{Name = "DC01";};"
But of course, for our use case we only need DC01 as output.
Any tips?
The full Code for the ButtonEvent:
private void ChkBtn_Click(object sender, EventArgs e)
{
string computerName = PCDropDown.Text;
lBox.Items.Clear();
SmsNamedValuesDictionary namedValues = new SmsNamedValuesDictionary();
WqlConnectionManager connection = new WqlConnectionManager(namedValues);
// Connect to remote computer.
try
{
connection.Connect(PrimarySiteServer.ToString());
// Set the query.
string query1 = string.Format("Select Name From SMS_R_System Where Name like '" + "%" + computerName + "%" + "'");
string query2 = string.Format("Select * From SMS_UserMachineRelationship WHERE ResourceName like '" + "%" + computerName + "%" + "' AND IsActive = '1' AND Types = '1'");
// Get the query results
IResultObject queryResults = connection.QueryProcessor.ExecuteQuery(query1);
// Check for results and display in infobox
bool resultsFound = false;
foreach (IResultObject queryResult in queryResults)
{
resultsFound = true;
lBox.Items.Add("Rechner ist vorhanden");
PCDropDown.Items.Add(queryResult.ToString());
}
if (resultsFound == false)
{
lBox.Items.Add("Rechnername nicht gefunden");
}
}
catch
{
MessageBox.Show("No Connection to Config-Manager - Als ZZA ausgeführt? SCCM-Servername richtig?");
}
}
Instead of adding queryResult.ToString() like you do here:
PCDropDown.Items.Add(queryResult.ToString());
you need to add the correct field of queryResult, so in this case:
PCDropDown.Items.Add(queryResult["Name"].StringValue);
Also a quick note. I don't know for who you are writing this and what the next step would be but if this is a read only application that is only used by SCCM Admins I would consider ignoring WMI and going to the SCCM DB via SQL instead. It is a lot faster, SQL has far more powerful options for queries and it does not need the integration of those strange sccm console Dlls (although that is not 100% necessary for WMI either).
If you need write access to create devices or collections etc., or you need to work with the roles the sccm access rights systems implements however WMI is the better or only choice. (And in this case I'd rather really use those strange dlls because all of the MS examples rely on them and it can be hard to translate those tutorials to the vanilla WMI solution C# offers.
I have a directory that contains a large number of CSV files. I want to loop through the directory and insert each file into a specific table based on their file name.
I am able to successfully loop through the files but I am having some trouble inserting the data into the Access database. I believe I am very close though. Any feedback would be appreciated.
This is my code:
string folderPath = Path.Combine(Path.GetTempPath(), "Reports");
string folderPath2 = Path.GetTempPath() + #"\Reports\";
var dir = new DirectoryInfo(folderPath);
foreach (var fileInfo in dir.EnumerateFiles("crashes_*.csv"))
{
OleDbCommand cmdBulk = new OleDbCommand(#"INSERT INTO tbl_crashes " +
#"SELECT * FROM [Text;FMT=Delimited;HDR=Yes;ACCDB=Yes;Database=" + folderPath2 + "].[" + Directory.GetFiles(folderPath2, "crash*.csv") + "]", MyConn);
MyConn.Open();
cmdBulk.ExecuteNonQuery();
MyConn.Close();
}
This is the error that I am receiving:
Invalid bracketing of name 'System.String['.
Troubleshooting 101: When "something goes wrong", break the problem down to isolate the cause.
In this case, the error
Invalid bracketing of name 'System.String['.
is a pretty strong hint that there is something wrong with the CommandText string that we glued together, so let's look at the relevant bits of the string itself.
string folderpath2 = #"C:\__tmp\";
string str = "stuff [" + folderpath2 + "].[" + Directory.GetFiles(folderpath2, "zzzTest*.csv") + "] more_stuff";
Console.WriteLine(str);
produces
stuff [C:\__tmp\].[System.String[]] more_stuff
so we can see that the string concatenation does an implicit ToString on the result of the GetFiles method call, returning "System.String[]". That is, GetFiles returns an array of strings and the implicit ToString shows us what the result is, not what it contains.
So that explains the error: The extra square brackets got the OleDb provider upset.
The solution, however, is to just use the Name property of the FileInfo object that the foreach loop is providing for us
string folderpath2 = #"C:\__tmp\";
var di = new DirectoryInfo(folderpath2);
foreach (FileInfo fi in di.EnumerateFiles("zzzTest*.csv"))
{
string str = "stuff [" + folderpath2 + "].[" + fi.Name + "] more_stuff";
Console.WriteLine(str);
}
producing
stuff [C:\__tmp\].[zzzTest1.csv] more_stuff
stuff [C:\__tmp\].[zzzTest2.csv] more_stuff
which is more like what we want.
I tried to use the following code to create WMI query:
string query = "ASSOCIATORS OF {Win32_DiskDrive.PNPDeviceID='" + device["PNPDeviceID"].ToString().Replace(#"\", #"\\")
+ "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
But if the query variable looks like ASSOCIATORS OF {Win32_DiskDrive.PNPDeviceID='USBSTOR\\DISK&VEN_FLASH&PROD_DRIVE_SM_USB20&REV_1100\\7214030310011150&0'} WHERE AssocClass = Win32_DiskDriveToDiskPartition it gives me "Invalid object path" exception.
Why? What am I doing wrong? How can I fix it?
What is the appropriate way to escape all characters in the WMI query then?
Thanks in advance.
Wrong property chosen to query ASSOCIATORS OF. The Win32_DiskDriveToDiskPartition class represents an association between a disk drive and a partition existing on it with next relational properties:
==>wmic path Win32_DiskDriveToDiskPartition get /value
Antecedent="\\PC\root\cimv2:Win32_DiskDrive.DeviceID="\\.\PHYSICALDRIVE1""
Dependent="\\PC\root\cimv2:Win32_DiskPartition.DeviceID="Disk #1, Partition #0""
Antecedent="\\PC\root\cimv2:Win32_DiskDrive.DeviceID="\\.\PHYSICALDRIVE1""
Dependent="\\PC\root\cimv2:Win32_DiskPartition.DeviceID="Disk #1, Partition #1""
Antecedent="\\PC\root\cimv2:Win32_DiskDrive.DeviceID="\\.\PHYSICALDRIVE0""
Dependent="\\PC\root\cimv2:Win32_DiskPartition.DeviceID="Disk #0, Partition #0""
==>
Hence, using the "Where AssocClass = Win32_DiskDriveToDiskPartition" clause, you could query ASSOCIATORS OF either
string query = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + dd_ID
+ "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
or
string query = "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + pp_ID
+ "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
Here, with respect to the above scenario (see wmic output):
dd_ID could be either
"\\.\PHYSICALDRIVE0" or
"\\.\PHYSICALDRIVE1" (two-items collection returned, of course) and
pp_ID could be either
"Disk #0, Partition #0" or
"Disk #1, Partition #0" or
"Disk #1, Partition #1".
I have tested above queries in all data combinations in VBScript and hope it should work in c# as well (exclusively of there could come necessity of escaping backslashes in c#).