ref:https://gerbenjavado.com/manual-sql-injection-discovery-tips/

Manual SQL injection discovery tips

August 26, 2017

According to bugbountyforum.com's AMA format one of the most popular questions is How do you test for Server Side vulnerabilities such as SQLi?. Up until recently I was struggling with this question (especially towards SQLi) as well. The SQLi's I did find were often discovered and exploited by sqlmap after I found that the server responded weirdly to a single quote. However in the last 2 months I was forced to dive deeper into manual SQLi discovery since my target has a strong firewall making sqlmap useless (and yes, I looked into --tamper). In these two months I was able to find over 10 SQLi which I learned a ton from. This blog will focus on sharing my process and knowledge as well as showing real world examples. I will divide the blog in two parts: discovery and exploitation, since both are needed for a good report. Finally, this blog assumes MySQL is used since it holds over half of the market share.

Discovery

The first part of finding a valid SQLi is the discovery of the vulnerability. Here the most important thing is to know in which SQL context your input can end up. Here some basic examples:

  • SELECT user_input FROM tournament ORDER BY region;
  • INSERT INTO tbl_name (col1,col2) VALUES (15,user_input);
  • DELETE FROM somelog WHERE texts = 'user_input'

Here we can see our user_input ending up in range of different contexts: between (), between ' or without any delimiters. What these commands all have in common is that they will all turn invalid once a ' is injected as input. The first two don't even have delimiters and will also give an error if a non-existing system variable is used, like: @@versionz (instead of @@version).

Once you are able to make the server return an error (mostly HTTP 500 status) you have to confirm it is the SQL command that is causing the error and not something like a date parser. To do this you can use a range of tricks:

  • If a ' is causing the error try to see if \' will result in success message (since the backslash cancels out the single quote in MySQL).
  • You can also try if commenting out the ' results in a success message like: %23' or --'. This is because you tell MySQL to explicitly ignore everything after the comment include the extra '.
  • If a ' is not allowed you can use comparisons between valid and invalid system variables like @@versionz vs @@version or invalid vs valid functions SLEP(5) vs SLEEP(5).
  • Sometimes your input will end up between () make sure you test input)%23as well to see if you can break out of these and exploit Union SQLi for example (input) order by 5%23).
  • If the normal input is just an integer you can try to subtract some amount of it and see if that subtraction works (id=460-5).
  • Try to see if an even amount of quotes results in a success message (for example 460'' or 460-'') and an uneven amount results in an error (for example460' or 460-''').

Example

To make this more clear we have the following URL:
https://www.example.com/php/sales_dash_poc_handle.php?action=month-breakdown&type_of_report=billing&city=all&month=8&year=2017&poc=35141008

This basic URL returns a 200 status code. The input poc=35141008' returns a 500 error and poc=35141008'%23 does as well, but poc=35141008'' returns a 200 status. This hints that the parameter is likely not using any delimiters, thus I tried poc=35141008%23' which would return a 200 status code. Now we know that we can inject simply between 35141008 and %23. After this I tried a simple 35141008 OR 2 LIKE 2%23 which worked while 35141008 OR 2 LIKE 1%23 returned a 500 error, proving that boolean SQLi was possible here. It isn't however always this easy and this example doesn't show much impact, this is were the next section comes in, proving data retrieval.

Exploitation

After you have found a SQLi you always have to try to proof at least a difference in output (for boolean and sleep based) or sensitive data in the output (for error and union based). Unfortunately this is not always straightforward especially with firewalls and blacklisting in the way. This section is to help you get around those.

Firewall

The first thing you should try when dealing with a firewall is see if you can find a misconfiguration in the setup. For most of these firewalls and CDN's you can access the unprotected website by visiting the original IP (which the firewall is standing in front of) and then using the original domain name as host value. The first step here is to find the original IP of the website, this is often not too hard using services that keep track of the IP's a website has used (http://viewdns.info/iphistory/). Often the one used before the firewall IP comes in is the one they still use (basically the one after it says cloudflare or akamai). Shodan can also be really usefull when it comes to finding an original IP.

After you have found the original IP address try to access the website with the original Host header. In cURL this works like this (adding a header also works in sqlmap):

$ curl --header 'Host: www.example.com' 'https://54.165.170.2/' -v -k

On my target this was unfortunately forbidden, thus I had to be creative. Here I found that by adding a dot to the host (www.example.com.) wouldn't be restricted and still allowed me to browse the direct IP. Adding:

54.165.170.2	www.example.com

in your /etc/hosts file will allow you to browse www.example.com in the browser without any firewall in between. From here any restrictions should be gone and SQLi exploitation should be fairly easy.

Bypassing the blacklist

Sometimes you can't bypass the firewall which will require you to bypass the (likely) blacklist that is in place. Here my biggest tip is that Google is your friend. Find niche functions with powerful capabilities that require you to somehow extract data. However here are some of the common tips:

  • Using comments in your query as spaces to break a firewall regex or word combination. For example: 2/*dhab bc*/OR/*dahdshka*/2/*sd*/LIKE/*da*/"2"/**/%23 translates to: 2 OR 2=2%23.
  • Use LIKE instead of =, 2 instead of 1 and " instead of ' as the example shows above. Many people are lazy and so are firewalls, walking the path less traveled will catch many firewalls off guard.
  • Like I said above use Google to find niche functions. I found for example that MID() works the same as SUBSTRING(), however the latter was banned the first one wasn't. Same goes for variables CURRENT_USER() is banned and CURRENT_USER wasn't.

Putting it all together

Out of the SQL injections found this was probably my favorite since the exploitation was fairly hard. So I'll describe it in detail and hope that you guys can learn something from it.

I came across this URL:
https://www.example.com/php/analyticsExcel.php?action=res_unique_analytics&resid=2100935&start_date=2016-07-11 00:00:00&end_date=2017-08-11 23:59:59&action=res_unique_analytics&entity_type=restaurant

Here I have legitimate access to 2100935 and requesting 2100934 would result in an unauthenticated error. The weird thing was that adding a single quote after 2100935 would result in a 500 status error and adding two would make the URL work again. Here to make exploitation easier I used my www.example.com. bypass (described in the Firewall section to get rid of the Akamai WAF). From there I tried to find a place to inject my own SQL commands in the input, however I didn't manage to inject a comment in the string, thus I concluded the query was quite complex. Seeing this I decided to focus on a simpler OR statement which can be injected almost anywhere. Here I noticed that ' OR 1='1 would return a 200 status, ' OR 1='2would be exactly the same, ' AND 1='1 the same, ' AND 1='2 the same and finally none of the sleep() commands seem to have any effect.

Super weird I can basically confirm the syntax I injected is correct but none of the techniques allowed to extract data. Here I tried ' OR @@version=5 which resulted in a 200 status and ' OR @@versionz=5 resulted in a 500 error. This at least confirmed I was working with MySQL and showed that the injection was really there. This pointed me towards the MySQL IF function. My thought was that if I was able to return an invalid function based on whether the if statement was true or false I could proof data extraction. This didn't go quite as planned since MySQL seems to validate command before executing the IF statement, but the IF statement ended up being part of the solution. Feeding a valid SLEEP command in the IF statement with a positive integer would result in the server timing out, while return a simple integer from the IF statement would return in a fast 200 status code. From here I developed the following POC:

TRUE: if @@version starts with a 5:
2100935' OR IF(MID(@@version,1,1)='5',sleep(1),1)='2
Response:
HTTP/1.1 500 Internal Server Error

False: if @@version starts with a 4:
2100935' OR IF(MID(@@version,1,1)='4',sleep(1),1)='2
Response:
HTTP/1.1 200 OK

Summary

If injecting a single quote leads to different output in the response try the different techniques outlined in this blog to see if you are dealing with a SQLi. After you have determined in which SQL context you are working develop a POC that either shows sensitive data (error and union based) or shows a difference in output depending on whether the question asked is true or false (boolean and time based).

ref:Manual SQL injection discovery tips的更多相关文章

  1. mybatis 一次执行多条SQL MySql+Mybatis+Druid之SqlException:sql injection violation, multi-statement not allow

    如果用JDBC jdbc.jdbcUrl=jdbc:mysql://127.0.0.1:3306/database?useUnicode=true&characterEncoding=utf8 ...

  2. How to prevent SQL injection attacks?

    In our earlier tutorial on SQL Injection, one way to have prevented the SQL injection attack was by ...

  3. SQL injection

    SQL injection is a code injection technique, used to attack data-driven applications, in which malic ...

  4. 防sql注入之参数绑定 SQL Injection Attacks and Defense

    http://php.net/manual/zh/pdo.prepared-statements.php 预处理语句与存储过程 很多更成熟的数据库都支持预处理语句的概念.什么是预处理语句?可以把它看作 ...

  5. 防sql注入之参数绑定 SQL Injection Attacks and Defense 预处理语句与存储过程

    http://php.net/manual/zh/pdo.prepared-statements.php 预处理语句与存储过程 很多更成熟的数据库都支持预处理语句的概念.什么是预处理语句?可以把它看作 ...

  6. druid sql黑名单 报异常 sql injection violation, part alway true condition not allow

    最近使用druid,发现阿里这个连接池 真的很好用,可以监控到连接池活跃连接数 开辟到多少个连接数 关闭了多少个,对于我在项目中查看错误 问题,很有帮助, 但是最近发现里面 有条sql语句 被拦截了, ...

  7. PHP+MYSQL网站SQL Injection攻防

    程序员们写代码的时候讲究TDD(测试驱动开发):在实现一个功能前,会先写一个测试用例,然后再编写代码使之运行通过.其实当黑客SQL Injection时,同样是一个TDD的过程:他们会先尝试着让程序报 ...

  8. SQL Injection(SQL注入漏洞)

    审计前准备: 1.安�php程序(推荐phpStudy) 2.高亮编辑器(推荐 Sublimetext Notepad++) 3.新建一个文本,复制以下变量,这些变量是审计中需要在源码中寻找的 ### ...

  9. HP+MYSQL网站SQL Injection攻防

    WebjxCom提示:程序员们写代码的时候讲究TDD(测试驱动开发):在实现一个功能前,会先写一个测试用例,然后再编写代码使之运行通过.其实当黑客SQL Injection时,同样是一个TDD的过程: ...

随机推荐

  1. Jenkins使用教程之创建job

    第一部分:常规设置(general) 1.点击新建,进入新建项目页面 2.输入项目名称,选择构建一个自由风格的软件项目(里面可以自己自由进行配置,使用更加灵活),点击ok 3.项目名称与描述 4.gi ...

  2. BZOJ2588:LCA+主席树来实现树上两点之间第K大点权查询

    对于每个节点维护这个节点到根的权值线段树 对于每个询问(x,y),这条路径上的线段树 tree[x]+tree[y]-tree[lca(x,y)]-tree[fa[lca(x,y)]] #includ ...

  3. 【洛谷 P1502】 窗口的星星(扫描线)

    题目链接 把每个星星作为左下角,做出长为\(w-0.5\),宽为\(h-0.5\)的矩形. \(-0.5\)是因为边框上的不算. 离散化\(y\)坐标. 记录\(2n\)个\(4\)元组\((x,y1 ...

  4. 天梯赛 L1-009 N个数求和 (模拟)

    本题的要求很简单,就是求N个数字的和.麻烦的是,这些数字是以有理数"分子/分母"的形式给出的,你输出的和也必须是有理数的形式. 输入格式: 输入第一行给出一个正整数N(<=1 ...

  5. bzoj 3083 树链剖分

    首先我们先将树提出一个根变成有根树,那么我们可以通过树链剖分来实现对于子树的最小值求解,那么按照当前的根和询问的点的相对位置关系我们可以将询问变成某个子树和或者除去某颗子树之后其余的和,前者直接询问区 ...

  6. Mysql储存过程2:变量定义与参数传递

    #储存过程 中的变量定义 declare 变量名 类型 可选类型 -- 跟建表差不多 create procedure p() begin ); ; select age+number; end$ / ...

  7. WPF之换肤

    WPF之换肤 设计原理 WPF换肤的设计原理,利用资源字典为每种皮肤资源添加不同的样式,在后台切换皮肤资源文件. 截图 上图中,第一张图采用规则样式,第二张图采用不规则样式,截图的时候略有瑕疵. 资源 ...

  8. ActiveMQ-Prefetch机制和constantPendingMessageLimitStrategy

    首先简要介绍一下prefetch机制.ActiveMQ通过prefetch机制来提高性能,这意味这 客户端的内存里可能会缓存一定数量的消息.缓存消息的数量由prefetch limit来控 制.当某个 ...

  9. Hibernate根据配置文件,生成建表语句

    import org.hibernate.cfg.Configuration; import org.hibernate.tool.hbm2ddl.SchemaExport; public class ...

  10. 防范XSS跨站

    所有jsp页面输出全部使用<c:out value="{}"/> 默认就是escapeXml="true" java中间件,<c:out /& ...