撸撸看sqlmap 盲注返回判断

  • A+
所属分类:WooYun-Zone

撸撸看sqlmap 盲注返回判断

1 背景

研究这个主要碰到了两个疑问:

1):自己的插件在安全测试某些盲注点的时候,经常出现误报

2):如果在返回的页面里面出现了url请求中提交的内容,由于每次提交的内容不一样,这样会导致返回的内容也不一样,这种情况下是如何判断是否存在注入的呢?

2 场景

搭建了个场景,大概情况如下,我每次都会输出执行的sql语句,aid参数存在sql注入漏洞,这样当我输入 and 123=123 和and 321=123的时候,除了a b显示情况的差异,输出的sql语句是否也会影响我们的注入结果呢.

撸撸看sqlmap 盲注返回判断

3 分析

好久没有看相关代码了,流程有点不熟悉了,于是重调试了一下代码。

大致逻辑:

1):sqlmap.py  main()函数下调用了start()

2):controller.py  start()函数调用了checkSqlInjection()

3):checks.py   checkSqlInjection()函数里发生了盲注相关的逻辑判断。

关键部分代码在checkSqlInjection函数中如下:

撸撸看sqlmap 盲注返回判断

大致意思就是首先去获得了一个trueResult 和 一个 falseResult。trueResult的请求代表的就是 and 1=1,而falseResult的请求代表的就是 and 1=2。接着我们就详细的看一下这个trueResult和falseResult的逻辑与处理。

我们下断点跟踪到trueResult处:

撸撸看sqlmap 盲注返回判断

此时打印出来的reqPayload为and 1798=1798 和我们预期的一样,我们跟进下,进入到Request.queryPage函数,位于connect.py文件下。

撸撸看sqlmap 盲注返回判断

利用Connect.getPage将内容发送出去了。返回的结果存储到了page,headers,code中

接着下面的逻辑就会处理page页面了,我们通过调试可以看到第一个关键函数,removeReflectiveValues,

撸撸看sqlmap 盲注返回判断

感觉将page和payload联系到了一起,它俩应该有关系。通过该函数的处理后我们去输出结果:

撸撸看sqlmap 盲注返回判断

画横线部分本来应该是and 1=1类似的内容,但是sqlmap认为该内容属于reflect内容,在处理page之前,想将该部分内容做了替换。

接着跟进代码,进入到最后的comparsion,应该是对于该内容进行相关比较,与谁比较呢?

撸撸看sqlmap 盲注返回判断

比较部分的代码位于comparison.py下,函数为_comparsion。

将template的值赋给了seq1,猜测的话应该是和原始的请求进行比较。将and 1=1 和原始的页面比较。

撸撸看sqlmap 盲注返回判断

在该函数的处理的时候,又碰到一个关键处理函数removeDynamicContent,通过查看上下文,大致意思去除动态的内容,动态的内容是根据页面生成的。

撸撸看sqlmap 盲注返回判断

处理动态内容之后,就是比较seq1和seq2了,核心部分如下,比较seq1和seq2的时候,将字符串REFLECTED_VALUE_MARKER替换为空,这个时候我们的内容如下:

撸撸看sqlmap 盲注返回判断

撸撸看sqlmap 盲注返回判断

其实在这里就已经知道了我们之前的两个疑问的答案了,我们接着分析完整个流程,这里返回结果为True,接着就会进入到:

撸撸看sqlmap 盲注返回判断

如果falseResult为False,流程和之前的差不多,则存在注入。

4 补充

看到上面之后,可能会有个疑问,就是有了这样的page的值之后道,sqlmap到底是如何进行比较的。我们接着看下sqlmap到底是如何处理的。

撸撸看sqlmap 盲注返回判断

上面得到了seq1和seq2后,存入到了seqMatcher。然后需要计算一个ratio的值:

ratio = round(seqMatcher.quick_ratio(), 3),一个比例值。

我们跟踪quick_ratio(),得到如下的代码,位于difflab.py文件下:

撸撸看sqlmap 盲注返回判断

这里计算的思路是统计了seq1和seq2中各个字符出现的次数,然后将seq1和seq2进行了match记录了匹配的次数,然后结果进入到了calculate_ratio中进行计算:

撸撸看sqlmap 盲注返回判断

可以看到计算的值就是2倍匹配的次数除以二者长度之和。

撸撸看sqlmap 盲注返回判断

我们的逻辑进入到了最后的一个else的判断中,true的时候返回了true,false的时候返回了false,于是证明存在注入。

5 结果

先看我这种场景下的注入显示的结果。

撸撸看sqlmap 盲注返回判断

以上说明sqlmap是能够自动识别我的这种情况的,并且添加了处理。这种场景经常出现在例如一些报错注入的情况下,还有一些服务器打印调试信息的情况下。

  1. 1#

    king7 | 2016-04-11 17:03

    大叼~~

  2. 2#

    RedFree (‮11:11 11-11-1112 |※(器杀制自) | 2016-04-11 18:02

    占座鼓掌,干的漂亮!

  3. 3#

    苦咖啡 | 2016-04-11 18:21

    叼 叼  期待你的神器

  4. 4#

    _Thorns (舍就是得。) | 2016-04-11 18:23

    前排,鼓掌!!!

  5. 5#

    隐形人真忙 (0 . 0) | 2016-04-11 18:57

    sqlmap里的页面动态内容的去除   这个我也看过  
    附上抽取的demo:
    # coding=utf-8
    import re
    import difflib

    def trimAlphaNum(value):
      """
      工具方法,去掉字符串开头和结尾的字母数字
      """
      while value and value[-1].isalnum():
        value = value[:-1]
      while value and value[0].isalnum():
        value = value[1:]

      return value

    def findDynamicContent(firstPage, secondPage):
      """
      寻找页面中的动态内容
      """
      DYNAMICITY_MARK_LENGTH = 32
      if not firstPage or not secondPage:
        return
      blocks = difflib.SequenceMatcher(None, firstPage, secondPage).get_matching_blocks()
      dynamicMarkings = []

        # Removing too small matching blocks
      for block in blocks[:]:
        (_, _, length) = block

        if length <= DYNAMICITY_MARK_LENGTH:
          blocks.remove(block)

        # Making of dynamic markings based on prefix/suffix principle
      if len(blocks) > 0:
        blocks.insert(0, None)
        blocks.append(None)

        for i in xrange(len(blocks) - 1):
          prefix = firstPage[blocks[i][0]:blocks[i][0] + blocks[i][2]] if blocks[i] else None
          suffix = firstPage[blocks[i + 1][0]:blocks[i + 1][0] + blocks[i + 1][2]] if blocks[i + 1] else None

          if prefix is None and blocks[i + 1][0] == 0:
            continue

          if suffix is None and (blocks[i][0] + blocks[i][2] >= len(firstPage)):
            continue

          prefix = trimAlphaNum(prefix)
          suffix = trimAlphaNum(suffix)

          dynamicMarkings.append((prefix[-DYNAMICITY_MARK_LENGTH / 2:] if prefix else None, suffix[:DYNAMICITY_MARK_LENGTH / 2] if suffix else None))

      if len(dynamicMarkings) > 0:
        return dynamicMarkings

    def removeDynamicContent(page, dynamicMarkings):
      """
      去掉页面中的动态内容
      """
      if page:
        for item in dynamicMarkings:
          prefix, suffix = item

          if prefix is None and suffix is None:
            continue
          elif prefix is None:
            page = re.sub(r'(?s)^.+%s' % re.escape(suffix), suffix.replace('\\', r'\\'), page)
          elif suffix is None:
            page = re.sub(r'(?s)%s.+$' % re.escape(prefix), prefix.replace('\\', r'\\'), page)
          else:
            page = re.sub(r'(?s)%s.+%s' % (re.escape(prefix), re.escape(suffix)), '%s%s' % (prefix.replace('\\', r'\\'), suffix.replace('\\', r'\\')), page)
      return page

    def demo():
      # page1
      a = """56f231816b288<br/>1||admin||admin<br/>"""

      # page2
      b = """56f2318735582<br/>1||admin||admin<br/>"""
      # 对动态内容进行标记
      dynamicMarkings = findDynamicContent(a, b)
      # 获取去掉动态内容之后的页面
      print removeDynamicContent(a, dynamicMarkings)

    if __name__ == '__main__':
      demo()

  6. 6#

    隐形人真忙 (0 . 0) | 2016-04-11 19:06

    发下我的理解
    调用SequenceMatcher的quick_ratio()方法来获取相似度。
    其中有个get_matching_blocks方法,这个方法返回一个列表,列表中的元素是三元组,(i,j,n),i,j表示索引,n表示子串长度,有a[i:i+n] == a[j:j+n]。
    撸撸看sqlmap 盲注返回判断
    removeDynamicContent函数在sqlmap\lib\core\common.py中。

  7. 7#

    MayIKissYou (“><svg>) | 2016-04-11 19:21

    @RedFree Mr Red 你好

  8. 8#

    MayIKissYou (“><svg>) | 2016-04-11 19:23

    @隐形人真忙 666666666 我之前的环境这部分的dynamicMarkings 列表获取的是空的 上面文档没有分析到  主要还是返回内容的reflect 以及 如何做内容返回判断的研究。 待我找个环境调试下 再交流哈 感谢补充

  9. 9#

    隐形人真忙 (0 . 0) | 2016-04-11 19:24

    @MayIKissYou 2333333  研究啥   还不赶紧备战西恩dota

  10. 10#

    纷纭 (:-)) | 2016-04-11 19:39

    mark

  11. 11#

    LoveSnow (我要努力,争取开发自己的神器) | 2016-04-11 22:30

    @MayIKissYou May I Kiss You?么么哒。哈哈

  12. 12#

    Mark0smith (多看猪猪侠的帖子) | 2016-04-26 18:28

    mark