Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 340e250

Browse files
boy-hackstamparm
authored andcommitted
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`
1 parent 3b3774a commit 340e250

6 files changed

Lines changed: 124 additions & 4 deletions

File tree

lib/core/common.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
from lib.core.exception import SqlmapValueException
9999
from lib.core.log import LOGGER_HANDLER
100100
from lib.core.optiondict import optDict
101-
from lib.core.settings import BANNER
101+
from lib.core.settings import BANNER, CHUNKED_KEYWORDS
102102
from lib.core.settings import BOLD_PATTERNS
103103
from lib.core.settings import BOUNDED_INJECTION_MARKER
104104
from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES
@@ -4895,3 +4895,50 @@ def firstNotNone(*args):
48954895
break
48964896

48974897
return retVal
4898+
4899+
def generateChunkDdata(data):
4900+
"""
4901+
Convert post data to chunked format data. If the keyword is in a block, the keyword will be cut.
4902+
4903+
>>> generateChunkDdata('select 1,2,3,4 from admin')
4904+
4;AZdYz
4905+
sele
4906+
2;fJS4D
4907+
ct
4908+
5;qbCOT
4909+
1,2,
4910+
7;KItpi
4911+
3,4 fro
4912+
2;pFu1R
4913+
m
4914+
5;uRoYZ
4915+
admin
4916+
0
4917+
4918+
4919+
"""
4920+
dl = len(data)
4921+
ret = ""
4922+
keywords = CHUNKED_KEYWORDS
4923+
index = 0
4924+
while index < dl:
4925+
chunk_size = random.randint(1, 9)
4926+
if index + chunk_size >= dl:
4927+
chunk_size = dl - index
4928+
salt = ''.join(random.sample(string.ascii_letters + string.digits, 5))
4929+
while 1:
4930+
tmp_chunk = data[index:index + chunk_size]
4931+
tmp_bool = True
4932+
for k in keywords:
4933+
if k in tmp_chunk:
4934+
chunk_size -= 1
4935+
tmp_bool = False
4936+
break
4937+
if tmp_bool:
4938+
break
4939+
index += chunk_size
4940+
ret += "%s;%s\r\n" % (hex(chunk_size)[2:], salt)
4941+
ret += "%s\r\n" % tmp_chunk
4942+
4943+
ret += "0\r\n\r\n"
4944+
return ret

lib/core/option.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import cookielib
99
import glob
10+
import httplib
1011
import inspect
1112
import logging
1213
import os
@@ -139,6 +140,7 @@
139140
from lib.request.connect import Connect as Request
140141
from lib.request.dns import DNSServer
141142
from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler
143+
from lib.request.httphandler import HTTPHandler
142144
from lib.request.httpshandler import HTTPSHandler
143145
from lib.request.pkihandler import HTTPSPKIAuthHandler
144146
from lib.request.rangehandler import HTTPRangeHandler
@@ -156,6 +158,7 @@
156158
from xml.etree.ElementTree import ElementTree
157159

158160
authHandler = urllib2.BaseHandler()
161+
httpHandler = HTTPHandler()
159162
httpsHandler = HTTPSHandler()
160163
keepAliveHandler = keepalive.HTTPHandler()
161164
proxyHandler = urllib2.ProxyHandler()
@@ -1106,7 +1109,7 @@ def _setHTTPHandlers():
11061109
debugMsg = "creating HTTP requests opener object"
11071110
logger.debug(debugMsg)
11081111

1109-
handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, httpsHandler])
1112+
handlers = filter(None, [multipartPostHandler, proxyHandler if proxyHandler.proxies else None, authHandler, redirectHandler, rangeHandler, httpHandler, httpsHandler])
11101113

11111114
if not conf.dropSetCookie:
11121115
if not conf.loadCookies:
@@ -2602,6 +2605,15 @@ def initOptions(inputOptions=AttribDict(), overrideOptions=False):
26022605
_setKnowledgeBaseAttributes()
26032606
_mergeOptions(inputOptions, overrideOptions)
26042607

2608+
def _setHttpChunked():
2609+
conf.chunk = conf.chunk and conf.data
2610+
if conf.chunk:
2611+
def hook(self, a, b):
2612+
pass
2613+
2614+
httplib.HTTPConnection._set_content_length = hook
2615+
2616+
26052617
def init():
26062618
"""
26072619
Set attributes into both configuration and knowledge base singletons
@@ -2627,6 +2639,7 @@ def init():
26272639
_listTamperingFunctions()
26282640
_setTamperingFunctions()
26292641
_setPreprocessFunctions()
2642+
_setHttpChunked()
26302643
_setWafFunctions()
26312644
_setTrafficOutputFP()
26322645
_setupHTTPCollector()

lib/core/settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,9 @@
794794
# Letters of lower frequency used in kb.chars
795795
KB_CHARS_LOW_FREQUENCY_ALPHABET = "zqxjkvbp"
796796

797+
# Keywords that need to be cut in the chunked
798+
CHUNKED_KEYWORDS = ['select', 'update', 'insert', 'from', 'load_file', 'sysdatabases', 'msysaccessobjects', 'msysqueries', 'sysmodules', 'information_schema', 'union']
799+
797800
# CSS style used in HTML dump format
798801
HTML_DUMP_CSS_STYLE = """<style>
799802
table{

lib/parse/cmdline.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ def cmdLineParser(argv=None):
220220

221221
request.add_option("--eval", dest="evalCode",
222222
help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")")
223+
224+
request.add_option("--chunk", dest="chunk", action="store_true", help="all requests will be added headers with 'Transfer-Encoding: Chunked' and sent by transcoding")
223225

224226
# Optimization options
225227
optimization = OptionGroup(parser, "Optimization", "These options can be used to optimize the performance of sqlmap")

lib/request/connect.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class WebSocketException(Exception):
6161
from lib.core.common import unsafeVariableNaming
6262
from lib.core.common import urldecode
6363
from lib.core.common import urlencode
64+
from lib.core.common import generateChunkDdata
6465
from lib.core.data import conf
6566
from lib.core.data import kb
6667
from lib.core.data import logger
@@ -271,9 +272,13 @@ def getPage(**kwargs):
271272
checking = kwargs.get("checking", False)
272273
skipRead = kwargs.get("skipRead", False)
273274
finalCode = kwargs.get("finalCode", False)
275+
chunked = conf.chunk
274276

275277
if multipart:
276278
post = multipart
279+
if chunked:
280+
post = urllib.unquote(post)
281+
post = generateChunkDdata(post)
277282

278283
websocket_ = url.lower().startswith("ws")
279284

@@ -396,6 +401,9 @@ def getPage(**kwargs):
396401

397402
if conf.keepAlive:
398403
headers[HTTP_HEADER.CONNECTION] = "keep-alive"
404+
405+
if chunked:
406+
headers[HTTP_HEADER.TRANSFER_ENCODING] = "Chunked"
399407

400408
if auxHeaders:
401409
headers = forgeHeaders(auxHeaders, headers)
@@ -455,7 +463,7 @@ class _(dict):
455463
requestHeaders += "\r\n%s" % ("Cookie: %s" % ";".join("%s=%s" % (getUnicode(cookie.name), getUnicode(cookie.value)) for cookie in cookies))
456464

457465
if post is not None:
458-
if not getRequestHeader(req, HTTP_HEADER.CONTENT_LENGTH):
466+
if not getRequestHeader(req, HTTP_HEADER.CONTENT_LENGTH) and not chunked:
459467
requestHeaders += "\r\n%s: %d" % (string.capwords(HTTP_HEADER.CONTENT_LENGTH), len(post))
460468

461469
if not getRequestHeader(req, HTTP_HEADER.CONNECTION):
@@ -466,7 +474,8 @@ class _(dict):
466474
if post is not None:
467475
requestMsg += "\r\n\r\n%s" % getUnicode(post)
468476

469-
requestMsg += "\r\n"
477+
if not chunked:
478+
requestMsg += "\r\n"
470479

471480
if not multipart:
472481
threadData.lastRequestMsg = requestMsg

lib/request/httphandler.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
5+
See the file 'LICENSE' for copying permission
6+
"""
7+
8+
import urllib2
9+
import httplib
10+
from lib.core.data import conf
11+
12+
13+
class HTTPHandler(urllib2.HTTPHandler):
14+
"""
15+
The hook http_requests function ensures that the chunk function is working properly.
16+
"""
17+
18+
def _hook(self, request):
19+
host = request.get_host()
20+
if not host:
21+
raise urllib2.URLError('no host given')
22+
23+
if request.has_data(): # POST
24+
data = request.get_data()
25+
if not request.has_header('Content-type'):
26+
request.add_unredirected_header(
27+
'Content-type',
28+
'application/x-www-form-urlencoded')
29+
if not request.has_header('Content-length') and not conf.chunk:
30+
request.add_unredirected_header(
31+
'Content-length', '%d' % len(data))
32+
33+
sel_host = host
34+
if request.has_proxy():
35+
scheme, sel = urllib2.splittype(request.get_selector())
36+
sel_host, sel_path = urllib2.splithost(sel)
37+
38+
if not request.has_header('Host'):
39+
request.add_unredirected_header('Host', sel_host)
40+
for name, value in self.parent.addheaders:
41+
name = name.capitalize()
42+
if not request.has_header(name):
43+
request.add_unredirected_header(name, value)
44+
return request
45+
46+
http_request = _hook

0 commit comments

Comments
 (0)