{"id":289,"date":"2018-06-06T08:00:37","date_gmt":"2018-06-06T06:00:37","guid":{"rendered":"http:\/\/cwiok.pl\/?p=289"},"modified":"2018-06-06T14:14:27","modified_gmt":"2018-06-06T12:14:27","slug":"power-bi-sql-injection-attack-f","status":"publish","type":"post","link":"https:\/\/cwiok.pl\/index.php\/en\/2018\/06\/06\/power-bi-sql-injection-attack-f\/","title":{"rendered":"SQL Injection in Power BI Service"},"content":{"rendered":"<p>While this case might be obvious for the most of the experienced data engineers and other specialists, it really surprised me. Our Principal Architect in Clouds on Mars &#8211; Pawe\u0142 Potasi\u0144ski pointed me this possibility. Basically, in a controlled environment, it will be possible to inject SQL code through Power BI parameters and do (almost) whatever one pleases. You can read more about this type of attack <a href=\"https:\/\/en.wikipedia.org\/wiki\/SQL_injection\">here<\/a> or watch this <a href=\"https:\/\/www.youtube.com\/watch?v=ciNHn38EyRc\">video<\/a>. If you want to read more about some M tricks first, try <a href=\"http:\/\/cwiok.pl\/index.php\/en\/2018\/05\/23\/looping-through-a-table-to-get-data-from-web-in-powerbi\/\">here<\/a> or <a href=\"http:\/\/cwiok.pl\/index.php\/en\/2018\/05\/30\/authenticating-to-a-datasource-with-multiple-credentials\/\">here<\/a>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-307\" src=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/sql_injection_photo.png\" alt=\"\" width=\"1200\" height=\"628\" \/><\/p>\n<p>The task was to enable the change of how many rows from the table are used as a data source. With parameters manipulation being available in Power BI, this was rather an easy task. To set up the parameter I did:<\/p>\n<pre class=\"toolbar:1 toolbar-delay:false lang:default decode:true\">\"1\" meta [IsParameterQuery=true, Type=\"Number\", IsParameterQueryRequired=true]<\/pre>\n<p>&nbsp;<\/p>\n<p>Set this as a decimal number. You must give your parameter a type &#8211; it cannot be &#8220;Any&#8221;. This is crucial, if you want to change the parameter in Power BI Service.<\/p>\n<p>Later in M I have included my SQL code:<\/p>\n<pre class=\"toolbar:1 toolbar-delay:false lang:default decode:true\">let\r\n\r\nSource = Sql.Database(\"\", \"\", [Query=\"SELECT TOP \" &amp; Text.From(Number_of_rows)&amp;\" * FROM [dbo].[tblAuthors]\"])\r\nin\r\nSource<\/pre>\n<p>&nbsp;<\/p>\n<p>It all worked well. Then together with Pawe\u0142 we changed the type to text and tried to input the parameter value as<\/p>\n<pre class=\"toolbar:1 toolbar-delay:false lang:default decode:true\">100 * FROM Sys.Tables-- \r\n<\/pre>\n<p>Because the credentials in that environment were mine it worked without an issue:<\/p>\n<p><a href=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-304 size-full aligncenter\" src=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection1.png\" alt=\"\" width=\"847\" height=\"114\" srcset=\"https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection1.png 847w, https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection1-300x40.png 300w, https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection1-768x103.png 768w\" sizes=\"auto, (max-width: 847px) 100vw, 847px\" \/><\/a><\/p>\n<p>I decided to experiment more with it, just to see what I get. After I had published the report to Power BI Service, I changed the parameter in a same fashion as in desktop:<\/p>\n<p><a href=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-303 size-full aligncenter\" src=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection2.png\" alt=\"\" width=\"467\" height=\"164\" srcset=\"https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection2.png 467w, https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection2-300x105.png 300w\" sizes=\"auto, (max-width: 467px) 100vw, 467px\" \/><\/a><\/p>\n<p>And got an error:<\/p>\n<p><a href=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-302 size-full aligncenter\" src=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection3.png\" alt=\"\" width=\"546\" height=\"160\" srcset=\"https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection3.png 546w, https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection3-300x88.png 300w\" sizes=\"auto, (max-width: 546px) 100vw, 546px\" \/><\/a><\/p>\n<p>It seems, that Power BI Service expects certain columns when refreshing the data. I decided to try something like this, to cheat it. However, a simple UNION would be also enough.<\/p>\n<pre class=\"toolbar:1 toolbar-delay:false lang:default decode:true\">100 name as Author_name, object_id as id, schema_id as country FROM Sys.tables--\"\r\n<\/pre>\n<p>And it worked perfectly:<br \/>\n<a href=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection4.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-301 size-full aligncenter\" src=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection4.png\" alt=\"\" width=\"522\" height=\"472\" srcset=\"https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection4.png 522w, https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection4-300x271.png 300w\" sizes=\"auto, (max-width: 522px) 100vw, 522px\" \/><\/a><\/p>\n<p>However,\u00a0 the parameter will not always be set before FROM statement. Sometimes it will be in a stored procedure or in WHERE statement. What happens if I try to just input a second SQL statement after a semicolon? Two SELECT statements in a query will get you only the first as an output. If you put anything else, it is going to execute. I decided to try the worst first:<\/p>\n<p><a href=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection5.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-305 size-full aligncenter\" src=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection5.png\" alt=\"\" width=\"490\" height=\"146\" srcset=\"https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection5.png 490w, https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/SQL_Injection5-300x89.png 300w\" sizes=\"auto, (max-width: 490px) 100vw, 490px\" \/><\/a><\/p>\n<p>And it worked:<br \/>\n<a href=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/drop_database.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-329 size-full\" src=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/drop_database.png\" alt=\"\" width=\"501\" height=\"78\" srcset=\"https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/drop_database.png 501w, https:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/drop_database-300x47.png 300w\" sizes=\"auto, (max-width: 501px) 100vw, 501px\" \/><\/a><br \/>\nIt executes without any errors, which is worrying. According to the<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/t-sql\/statements\/drop-database-transact-sql?view=sql-server-2017\"> documentation<\/a> &#8220;You must be connected to the master database to drop a database&#8221; and &#8220;The DROP DATABASE statement must be the only statement in a SQL batch and you can drop only one database at a time&#8221;, which is not the case. I am still to analyze logs of how Power BI batches the queries sent. Nonetheless, we can treat it as a curiosity, because you cannot change the parameters unless you have access to the database anyway. I will be playing with it more in spare time, but if you manage to cheat Power BI in a similar way, let me know!<\/p>\n<p>Thanks<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I know that this case might be obvious for the most of the experienced data engineers and other specialists, but it really surprised me. Our Principal Architect in Clouds on Mars &#8211; Pawe\u0142 Potasi\u0144ski pointed me this possibility.<br \/>\n&nbsp;<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-307\" src=\"http:\/\/cwiok.pl\/wp-content\/uploads\/2018\/06\/sql_injection_photo.jpg\" alt=\"\" width=\"1200\" height=\"628\" \/><\/p>\n<div class=\"tech_read_more\"><a href=\"https:\/\/cwiok.pl\/index.php\/en\/2018\/06\/06\/power-bi-sql-injection-attack-f\/\">Read More<\/a><\/div>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-289","post","type-post","status-publish","format-standard","hentry","category-powerbi"],"_links":{"self":[{"href":"https:\/\/cwiok.pl\/index.php\/wp-json\/wp\/v2\/posts\/289","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cwiok.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cwiok.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cwiok.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cwiok.pl\/index.php\/wp-json\/wp\/v2\/comments?post=289"}],"version-history":[{"count":0,"href":"https:\/\/cwiok.pl\/index.php\/wp-json\/wp\/v2\/posts\/289\/revisions"}],"wp:attachment":[{"href":"https:\/\/cwiok.pl\/index.php\/wp-json\/wp\/v2\/media?parent=289"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cwiok.pl\/index.php\/wp-json\/wp\/v2\/categories?post=289"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cwiok.pl\/index.php\/wp-json\/wp\/v2\/tags?post=289"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}