From 340e250fb1c517ebb401234320d7b6aa1a2190e6 Mon Sep 17 00:00:00 2001 From: boyhack <34109680@qq.com> Date: Tue, 19 Mar 2019 20:26:29 +0800 Subject: [PATCH] Support for chunked requests (#3536) * Add the `--chunk` option to send requests in chunks * solve the httplib&urllib2 content-legnth * remove info * Solve the error caused by the mix of get mode and chunk * add CHUNKED_KEYWORDS `union` --- lib/core/common.py | 49 +++++++++++++++++++++++++++++++++++++- lib/core/option.py | 15 +++++++++++- lib/core/settings.py | 3 +++ lib/parse/cmdline.py | 2 ++ lib/request/connect.py | 13 ++++++++-- lib/request/httphandler.py | 46 +++++++++++++++++++++++++++++++++++ 6 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 lib/request/httphandler.py diff --git a/lib/core/common.py b/lib/core/common.py index 6a8afd535..863d297e1 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -98,7 +98,7 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapValueException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict -from lib.core.settings import BANNER +from lib.core.settings import BANNER, CHUNKED_KEYWORDS from lib.core.settings import BOLD_PATTERNS from lib.core.settings import BOUNDED_INJECTION_MARKER from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES @@ -4895,3 +4895,50 @@ def firstNotNone(*args): break return retVal + +def generateChunkDdata(data): + """ + Convert post data to chunked format data. If the keyword is in a block, the keyword will be cut. + + >>> generateChunkDdata('select 1,2,3,4 from admin') + 4;AZdYz + sele + 2;fJS4D + ct + 5;qbCOT + 1,2, + 7;KItpi + 3,4 fro + 2;pFu1R + m + 5;uRoYZ + admin + 0 + + + """ + dl = len(data) + ret = "" + keywords = CHUNKED_KEYWORDS + index = 0 + while index < dl: + chunk_size = random.randint(1, 9) + if index + chunk_size >= dl: + chunk_size = dl - index + salt = ''.join(random.sample(string.ascii_letters + string.digits, 5)) + while 1: + tmp_chunk = data[index:index + chunk_size] + tmp_bool = True + for k in keywords: + if k in tmp_chunk: + chunk_size -= 1 + tmp_bool = False + break + if tmp_bool: + break + index += chunk_size + ret += "%s;%s\r\n" % (hex(chunk_size)[2:], salt) + ret += "%s\r\n" % tmp_chunk + + ret += "0\r\n\r\n" + return ret diff --git a/lib/core/option.py b/lib/core/option.py index 4204de6db..2b241a966 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -7,6 +7,7 @@ See the file 'LICENSE' for copying permission import cookielib import glob +import httplib import inspect import logging import os @@ -139,6 +140,7 @@ from lib.request.basic import checkCharEncoding from lib.request.connect import Connect as Request from lib.request.dns import DNSServer from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler +from lib.request.httphandler import HTTPHandler from lib.request.httpshandler import HTTPSHandler from lib.request.pkihandler import HTTPSPKIAuthHandler from lib.request.rangehandler import HTTPRangeHandler @@ -156,6 +158,7 @@ from thirdparty.socks import socks from xml.etree.ElementTree import ElementTree authHandler = urllib2.BaseHandler() +httpHandler = HTTPHandler() httpsHandler = HTTPSHandler() keepAliveHandler = keepalive.HTTPHandler() proxyHandler = urllib2.ProxyHandler() @@ -1106,7 +1109,7 @@ def _setHTTPHandlers(): debugMsg = "creating HTTP requests opener object" logger.debug(debugMsg) - handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, httpsHandler]) + handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, httpHandler, httpsHandler]) if not conf.dropSetCookie: if not conf.loadCookies: @@ -2602,6 +2605,15 @@ def initOptions(inputOptions=AttribDict(), overrideOptions=False): _setKnowledgeBaseAttributes() _mergeOptions(inputOptions, overrideOptions) +def _setHttpChunked(): + conf.chunk = conf.chunk and conf.data + if conf.chunk: + def hook(self, a, b): + pass + + httplib.HTTPConnection._set_content_length = hook + + def init(): """ Set attributes into both configuration and knowledge base singletons @@ -2627,6 +2639,7 @@ def init(): _listTamperingFunctions() _setTamperingFunctions() _setPreprocessFunctions() + _setHttpChunked() _setWafFunctions() _setTrafficOutputFP() _setupHTTPCollector() diff --git a/lib/core/settings.py b/lib/core/settings.py index 9584dbd6c..192bf3f59 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -794,6 +794,9 @@ KB_CHARS_BOUNDARY_CHAR = 'q' # Letters of lower frequency used in kb.chars KB_CHARS_LOW_FREQUENCY_ALPHABET = "zqxjkvbp" +# Keywords that need to be cut in the chunked +CHUNKED_KEYWORDS = ['select', 'update', 'insert', 'from', 'load_file', 'sysdatabases', 'msysaccessobjects', 'msysqueries', 'sysmodules', 'information_schema', 'union'] + # CSS style used in HTML dump format HTML_DUMP_CSS_STYLE = """