Python SQL 주입 검사 플러그 인 인 인 스 턴 스 코드 구현

12521 단어 pythonsql 주입검출
스캐너 가 실현 해 야 할 기능 사고 지도

파충
먼저 파충 류 를 개발 하여 사이트 의 링크 를 수집 해 야 합 니 다.파충 류 는 이미 기어 오 른 링크 와 기어 오 르 려 는 링크 를 기록 하고 무 거 운 것 을 제거 해 야 합 니 다.Python 의 set()로 해결 할 수 있 습 니 다.대략적인 절 차 는:
URL 을 입력 하 십시오.
  • 다운로드 하여 URL 을 분석 합 니 다
  • URL 의 무 게 를 줄 이 고 본 사이트 의 여 부 를 판단 합 니 다
  • 4.567917.기어 오 르 기 목록 에 가입 합 니 다.4.567918.
    반복 순환
    SQL 판단 사고
  • URL 뒤에 AND%d=%d 또는 OR NOT(%d>%d)를 추가 합 니 다
  • 4
  • %d 뒤의 숫자 는 무 작위 로 가 변 적 인 것 이다
  • 4.567917.그리고 웹 페이지 의 특수 키 워드 를 검색 합 니 다.예 를 들 어 4.567918.
    MySQL 은 SQL syntax 입 니 다.*MySQL
    Microsoft SQL Server 는 Warning.*mssql
    Microsoft Access 는 Microsoft Access Driver 입 니 다.
    Oracle 은 Oracle error 입 니 다.
    IBM DB2 는 DB2 SQL error 입 니 다.
    SQLite 는 SQLite.Exception 입 니 다.
    ...
    이 키워드 들 을 통 해 사용 하 는 데이터 베 이 스 를 판단 할 수 있다.
    4.567917.waf 와 같은 것 도 판단 해 야 한다.이런 것 이 있 으 면 바로 멈춘다.간단 한 방법 은 특정 URL 로 접근 하 는 것 이다.IP banned,fierwall 같은 키워드 가 나 오 면 waf 로 판단 할 수 있다.구체 적 인 정규 표현 식 은(?i)(\A|\b)IP\b.*\b(banned|blocked|bl(a|o)ck\s?list|firewall) 개발 준비 전개 목록
    이 라 이브 러 리 를 설치 해 주세요.
    
    pip install requests
    pip install beautifulsoup4
    실험 환경 은 Linux 입 니 다.코드 디 렉 터 리 를 만 들 고 워 크 폴 더 를 만 들 고 작업 디 렉 터 리 로 사용 합 니 다.
    디 렉 터 리 구조
    /w8ay.py  // 프로젝트 시작 주 파일
    /lib/core/핵심 파일 저장 디 렉 터 리
    /lib/core/config.py/프로필
    /script   // 플러그 인 저장
    /exp      // exp 와 poc 저장
    순서
    SQL 검사 스 크 립 트 작성
    
    DBMS_ERRORS = {
      'MySQL': (r"SQL syntax.*MySQL", r"Warning.*mysql_.*", r"valid MySQL result", r"MySqlClient\."),
      "PostgreSQL": (r"PostgreSQL.*ERROR", r"Warning.*\Wpg_.*", r"valid PostgreSQL result", r"Npgsql\."),
      "Microsoft SQL Server": (r"Driver.* SQL[\-\_\ ]*Server", r"OLE DB.* SQL Server", r"(\W|\A)SQL Server.*Driver", r"Warning.*mssql_.*", r"(\W|\A)SQL Server.*[0-9a-fA-F]{8}", r"(?s)Exception.*\WSystem\.Data\.SqlClient\.", r"(?s)Exception.*\WRoadhouse\.Cms\."),
      "Microsoft Access": (r"Microsoft Access Driver", r"JET Database Engine", r"Access Database Engine"),
      "Oracle": (r"\bORA-[0-9][0-9][0-9][0-9]", r"Oracle error", r"Oracle.*Driver", r"Warning.*\Woci_.*", r"Warning.*\Wora_.*"),
      "IBM DB2": (r"CLI Driver.*DB2", r"DB2 SQL error", r"\bdb2_\w+\("),
      "SQLite": (r"SQLite/JDBCDriver", r"SQLite.Exception", r"System.Data.SQLite.SQLiteException", r"Warning.*sqlite_.*", r"Warning.*SQLite3::", r"\[SQLITE_ERROR\]"),
      "Sybase": (r"(?i)Warning.*sybase.*", r"Sybase message", r"Sybase.*Server message.*"),
    }
    정규 표현 식 을 통 해 어느 데이터베이스 인지 판단 할 수 있 습 니 다.
    
    for (dbms, regex) in ((dbms, regex) for dbms in DBMS_ERRORS for regex in DBMS_ERRORS[dbms]):
      if (re.search(regex,_content)):
        return True
    다음은 저희 테스트 문장의 payload 입 니 다.
    
    BOOLEAN_TESTS = (" AND %d=%d", " OR NOT (%d=%d)")
    틀린 문장 으로 정확 한 내용 과 잘못된 내용 을 되 돌려 대비 하 다
    
    for test_payload in BOOLEAN_TESTS:
      # Right Page
      RANDINT = random.randint(1, 255)
      _url = url + test_payload % (RANDINT, RANDINT)
      content["true"] = Downloader.get(_url)
      _url = url + test_payload % (RANDINT, RANDINT + 1)
      content["false"] = Downloader.get(_url)
      if content["origin"] == content["true"] != content["false"]:
        return "sql found: %" % url
    이 구절
    
    content["origin"] == content["true"] != content["false"]
    원본 웹 페이지 가 정확 한 웹 페이지 와 같 지 않 을 때 이 주소 에 구멍 이 있 음 을 판단 할 수 있다 는 뜻 이다.
    전체 코드:
    
    import re, random
    from lib.core import Download
    def sqlcheck(url):
      if (not url.find("?")): # Pseudo-static page
        return false;
      Downloader = Download.Downloader()
      BOOLEAN_TESTS = (" AND %d=%d", " OR NOT (%d=%d)")
      DBMS_ERRORS = {
        # regular expressions used for DBMS recognition based on error message response
        "MySQL": (r"SQL syntax.*MySQL", r"Warning.*mysql_.*", r"valid MySQL result", r"MySqlClient\."),
        "PostgreSQL": (r"PostgreSQL.*ERROR", r"Warning.*\Wpg_.*", r"valid PostgreSQL result", r"Npgsql\."),
        "Microsoft SQL Server": (r"Driver.* SQL[\-\_\ ]*Server", r"OLE DB.* SQL Server", r"(\W|\A)SQL Server.*Driver", r"Warning.*mssql_.*", r"(\W|\A)SQL Server.*[0-9a-fA-F]{8}", r"(?s)Exception.*\WSystem\.Data\.SqlClient\.", r"(?s)Exception.*\WRoadhouse\.Cms\."),
        "Microsoft Access": (r"Microsoft Access Driver", r"JET Database Engine", r"Access Database Engine"),
        "Oracle": (r"\bORA-[0-9][0-9][0-9][0-9]", r"Oracle error", r"Oracle.*Driver", r"Warning.*\Woci_.*", r"Warning.*\Wora_.*"),
        "IBM DB2": (r"CLI Driver.*DB2", r"DB2 SQL error", r"\bdb2_\w+\("),
        "SQLite": (r"SQLite/JDBCDriver", r"SQLite.Exception", r"System.Data.SQLite.SQLiteException", r"Warning.*sqlite_.*", r"Warning.*SQLite3::", r"\[SQLITE_ERROR\]"),
        "Sybase": (r"(?i)Warning.*sybase.*", r"Sybase message", r"Sybase.*Server message.*"),
      }
      _url = url + "%29%28%22%27"
      _content = Downloader.get(_url)
      for (dbms, regex) in ((dbms, regex) for dbms in DBMS_ERRORS for regex in DBMS_ERRORS[dbms]):
        if (re.search(regex,_content)):
          return True
      content = {}
      content['origin'] = Downloader.get(_url)
      for test_payload in BOOLEAN_TESTS:
        # Right Page
        RANDINT = random.randint(1, 255)
        _url = url + test_payload % (RANDINT, RANDINT)
        content["true"] = Downloader.get(_url)
        _url = url + test_payload % (RANDINT, RANDINT + 1)
        content["false"] = Downloader.get(_url)
        if content["origin"] == content["true"] != content["false"]:
          return "sql found: %" % url
    이 파일 을 sqlcheck.py 라 고 명명 하여/script 디 렉 터 리 에 놓 습 니 다.코드 의 네 번 째 줄 역할 은 URL 이 포함 되 어 있 는 지 찾 는 것 입 니 다.만약 포함 되 지 않 는 다 면,예 를 들 어 위 정적 페이지 는 주입 하기 가 쉽 지 않 을 수 있 으 므 로 여과 해 야 한다.
    파충류 의 집필
    파충류 의 사고방식 에서 말 했 듯 이 URL 관 리 를 먼저 완성 하고 우 리 는 단독으로 그것 을 하나의 종류 로 삼 아 파일 을/lib/core/UrlManager.py 에 저장한다.
    
    #-*- coding:utf-8 -*-
    
    class UrlManager(object):
      def __init__(self):
        self.new_urls = set()
        self.old_urls = set()
        
      def add_new_url(self, url):
        if url is None:
          return
        if url not in self.new_urls and url not in self.old_urls:
          self.new_urls.add(url)
       
      def add_new_urls(self, urls):
        if urls is None or len(urls) == 0:
          return
        for url in urls:
          self.add_new_url(url)
        
      def has_new_url(self):
        return len(self.new_urls) != 0
       
      def get_new_url(self):
        new_url = self.new_urls.pop()
        self.old_urls.add(new_url)
        return new_url
    편리 함 을 위해 서,우 리 는 다운로드 기능 을 단독으로 하나의 클래스 로 사용 할 것 이 며,파일 은 lib/core/Downloader.py 에 저 장 됩 니 다.
    
    #-*- coding:utf-8 -*-
    import requests
    
    class Downloader(object):
      def get(self, url):
        r = requests.get(url, timeout = 10)
        if r.status_code != 200:
          return None
        _str = r.text
        return _str
      
      def post(self, url, data):
        r = requests.post(url, data)
        _str = r.text
        return _str
      
      def download(self, url, htmls):
        if url is None:
          return None
        _str = {}
        _str["url"] = url
        try:
          r = requests.get(url, timeout = 10)
          if r.status_code != 200:
            return None
          _str["html"] = r.text
        except Exception as e:
          return None
        htmls.append(_str)
    특히,우리 가 쓰 려 는 파충 류 는 다 중 스 레 드 이기 때문에,클래스 중 하 나 는 다 중 스 레 드 다운로드 전용 다운로드 방법 입 니 다.
    lib/core/spider.py 에서 파충 류 를 작성 합 니 다.
    
    #-*- coding:utf-8 -*-
    
    from lib.core import Downloader, UrlManager
    import threading
    from urllib import parse
    from urllib.parse import urljoin
    from bs4 import BeautifulSoup
    
    class SpiderMain(object):
      def __init__(self, root, threadNum):
        self.urls = UrlManager.UrlManager()
        self.download = Downloader.Downloader()
        self.root = root
        self.threadNum = threadNum
      
      def _judge(self, domain, url):
        if (url.find(domain) != -1):
          return True
        return False
      
      def _parse(self, page_url, content):
        if content is None:
          return
        soup = BeautifulSoup(content, 'html.parser')
        _news = self._get_new_urls(page_url, soup)
        return _news
        
      def _get_new_urls(self, page_url, soup):
        new_urls = set()
        links = soup.find_all('a')
        for link in links:
          new_url = link.get('href')
          new_full_url = urljoin(page_url, new_url)
          if (self._judge(self.root, new_full_url)):
            new_urls.add(new_full_url)
        return new_urls
        
      def craw(self):
        self.urls.add_new_url(self.root)
        while self.urls.has_new_url():
          _content = []
          th = []
          for i in list(range(self.threadNum)):
            if self.urls.has_new_url() is False:
              break
            new_url = self.urls.get_new_url()
            
            ## sql check
            try:
              if (sqlcheck.sqlcheck(new_url)):
                print("url:%s sqlcheck is valueable" % new_url)
            except:
              pass
                
            print("craw:" + new_url)
            t = threading.Thread(target = self.download.download, args = (new_url, _content))
            t.start()
            th.append(t)
          for t in th:
            t.join()
          for _str in _content:
            if _str is None:
              continue
            new_urls = self._parse(new_url, _str["html"])
            self.urls.add_new_urls(new_urls)
    파충 류 는 craw()방법 으로 한 사이트 에 들 어가 기어 다 니 며 다 중 스 레 드 방법 으로 기어 다 니 는 사 이 트 를 다운로드 하고 다운로드 한 소스 코드 는parse 방법 은 BeautifulSoup 을 호출 하여 분석 한 다음 에 분 석 된 URL 목록 을 URL 관리자 에 버 립 니 다.이렇게 순환 하면 마지막 에 웹 페이지 를 오 르 기만 하면 파충류 가 멈 춥 니 다.
    threading 라 이브 러 리 는 열 어야 할 스 레 드 수 를 사용자 정의 할 수 있 습 니 다.스 레 드 가 열 리 면 모든 스 레 드 는 하나의 url 을 받 아 다운로드 한 다음 스 레 드 가 막 히 고 막 힌 후에 스 레 드 가 실 행 됩 니 다.
    파충류 와 SQL 검사 의 결합
    lib/core/spider.py 파일 에서 from script import sqlcheck 을 참조 하고 craw()방법 에서 새로운 URL 을 꺼 내 호출 합 니 다.
    
    ##sql check
    try:
      if(sqlcheck.sqlcheck(new_url)):
        print("url:%s sqlcheck is valueable"%new_url)
    except:
      pass
    try 로 발생 할 수 있 는 이상 을 감지 하고 돌아 서 파일 w8ay.py 에서 테스트 합 니 다.
    
    #-*- coding:utf-8 -*-
    '''
    Name: w8ayScan
    Author: mathor
    Copyright (c) 2019
    '''
    import sys
    from lib.core.Spider import SpiderMain
    def main():
      root = "https://wmathor.com"
      threadNum = 50
      w8 = SpiderMain(root, threadNum)
      w8.craw()
     
    if __name__ == "__main__":
      main()
    중요 한 점!lib 와 script 폴 더 의.py 파일 을 모듈 로 인식 할 수 있 도록 lib,lib/core,script 폴 더 에 을 만 드 십시오.init__.py 파일,파일 에 아무것도 쓸 필요 가 없습니다.
    총결산
    SQL 주입 검 측 은 일부 payload 를 통 해 페이지 에 오 류 를 일 으 키 고 원본 웹 페이지,정확 한 웹 페이지 를 판단 합 니 다.오류 웹 페이지 는 SQL 주입 구멍 이 있 는 지 확인 할 수 있 습 니 다.
    sql 에서 잘못된 정 보 를 일치 시 키 면 사용 하 는 데이터 베 이 스 를 정규 적 으로 판단 할 수 있 습 니 다.
    자,이상 이 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기