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

Skip to content

Commit 438a636

Browse files
committed
Fix for issue Issue #60
1 parent 76f7f90 commit 438a636

2 files changed

Lines changed: 95 additions & 77 deletions

File tree

lib/core/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3147,7 +3147,7 @@ def hashDBRetrieve(key, unserialize=False, checkConf=False):
31473147

31483148
_ = "%s%s%s" % (conf.url or "%s%s" % (conf.hostname, conf.port), key, HASHDB_MILESTONE_VALUE)
31493149
_ = conf.hashDB.retrieve(_, unserialize) if kb.resumeValues and not (checkConf and any([conf.flushSession, conf.freshQueries])) else None
3150-
if not kb.inferenceMode and _ and PARTIAL_VALUE_MARKER in _:
3150+
if not kb.inferenceMode and not kb.fileReadMode and _ and PARTIAL_VALUE_MARKER in _:
31513151
_ = None
31523152
return _
31533153

lib/techniques/error/use.py

Lines changed: 94 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from lib.core.settings import FROM_DUMMY_TABLE
3636
from lib.core.settings import MYSQL_ERROR_CHUNK_LENGTH
3737
from lib.core.settings import MSSQL_ERROR_CHUNK_LENGTH
38+
from lib.core.settings import PARTIAL_VALUE_MARKER
3839
from lib.core.settings import SLOW_ORDER_COUNT_THRESHOLD
3940
from lib.core.settings import SQL_SCALAR_REGEX
4041
from lib.core.settings import TURN_OFF_RESUME_INFO_LIMIT
@@ -44,90 +45,93 @@
4445
from lib.request.connect import Connect as Request
4546

4647
def __oneShotErrorUse(expression, field):
48+
offset = 1
49+
partialValue = None
50+
threadData = getCurrentThreadData()
4751
retVal = hashDBRetrieve(expression, checkConf=True)
4852

49-
threadData = getCurrentThreadData()
50-
threadData.resumed = retVal is not None
53+
if retVal and PARTIAL_VALUE_MARKER in retVal:
54+
partialValue = retVal = retVal.replace(PARTIAL_VALUE_MARKER, "")
55+
dataToStdout("[%s] [INFO] resuming partial value: '%s'\r\n" % (time.strftime("%X"), __formatPartialContent(partialValue)))
56+
offset += len(partialValue)
5157

52-
offset = 1
58+
threadData.resumed = retVal is not None and not partialValue
5359
chunk_length = None
5460

55-
if retVal is None:
56-
while True:
57-
check = "%s(?P<result>.*?)%s" % (kb.chars.start, kb.chars.stop)
58-
trimcheck = "%s(?P<result>.*?)</" % (kb.chars.start)
59-
60-
nulledCastedField = agent.nullAndCastField(field)
61-
62-
if Backend.isDbms(DBMS.MYSQL):
63-
chunk_length = MYSQL_ERROR_CHUNK_LENGTH
64-
nulledCastedField = queries[DBMS.MYSQL].substring.query % (nulledCastedField, offset, chunk_length)
65-
elif Backend.isDbms(DBMS.MSSQL):
66-
chunk_length = MSSQL_ERROR_CHUNK_LENGTH
67-
nulledCastedField = queries[DBMS.MSSQL].substring.query % (nulledCastedField, offset, chunk_length)
68-
69-
# Forge the error-based SQL injection request
70-
vector = kb.injection.data[PAYLOAD.TECHNIQUE.ERROR].vector
71-
query = agent.prefixQuery(vector)
72-
query = agent.suffixQuery(query)
73-
injExpression = expression.replace(field, nulledCastedField, 1)
74-
injExpression = unescaper.unescape(injExpression)
75-
injExpression = query.replace("[QUERY]", injExpression)
76-
payload = agent.payload(newValue=injExpression)
77-
78-
# Perform the request
79-
page, headers = Request.queryPage(payload, content=True)
80-
81-
incrementCounter(PAYLOAD.TECHNIQUE.ERROR)
82-
83-
# Parse the returned page to get the exact error-based
84-
# SQL injection output
85-
output = reduce(lambda x, y: x if x is not None else y, [ \
86-
extractRegexResult(check, page, re.DOTALL | re.IGNORECASE), \
87-
extractRegexResult(check, listToStrValue(headers.headers \
88-
if headers else None), re.DOTALL | re.IGNORECASE), \
89-
extractRegexResult(check, threadData.lastRedirectMsg[1] \
90-
if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \
91-
threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)], \
92-
None)
93-
94-
if output is not None:
95-
output = getUnicode(output, kb.pageEncoding)
96-
else:
97-
trimmed = extractRegexResult(trimcheck, page, re.DOTALL | re.IGNORECASE) \
98-
or extractRegexResult(trimcheck, listToStrValue(headers.headers \
99-
if headers else None), re.DOTALL | re.IGNORECASE) \
100-
or extractRegexResult(trimcheck, threadData.lastRedirectMsg[1] \
101-
if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \
102-
threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)
103-
104-
if trimmed:
105-
warnMsg = "possible server trimmed output detected (due to its length): "
106-
warnMsg += trimmed
107-
logger.warn(warnMsg)
108-
109-
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)):
110-
if offset == 1:
111-
retVal = output
61+
if retVal is None or partialValue:
62+
try:
63+
while True:
64+
check = "%s(?P<result>.*?)%s" % (kb.chars.start, kb.chars.stop)
65+
trimcheck = "%s(?P<result>.*?)</" % (kb.chars.start)
66+
67+
nulledCastedField = agent.nullAndCastField(field)
68+
69+
if Backend.isDbms(DBMS.MYSQL):
70+
chunk_length = MYSQL_ERROR_CHUNK_LENGTH
71+
nulledCastedField = queries[DBMS.MYSQL].substring.query % (nulledCastedField, offset, chunk_length)
72+
elif Backend.isDbms(DBMS.MSSQL):
73+
chunk_length = MSSQL_ERROR_CHUNK_LENGTH
74+
nulledCastedField = queries[DBMS.MSSQL].substring.query % (nulledCastedField, offset, chunk_length)
75+
76+
# Forge the error-based SQL injection request
77+
vector = kb.injection.data[PAYLOAD.TECHNIQUE.ERROR].vector
78+
query = agent.prefixQuery(vector)
79+
query = agent.suffixQuery(query)
80+
injExpression = expression.replace(field, nulledCastedField, 1)
81+
injExpression = unescaper.unescape(injExpression)
82+
injExpression = query.replace("[QUERY]", injExpression)
83+
payload = agent.payload(newValue=injExpression)
84+
85+
# Perform the request
86+
page, headers = Request.queryPage(payload, content=True)
87+
88+
incrementCounter(PAYLOAD.TECHNIQUE.ERROR)
89+
90+
# Parse the returned page to get the exact error-based
91+
# SQL injection output
92+
output = reduce(lambda x, y: x if x is not None else y, [ \
93+
extractRegexResult(check, page, re.DOTALL | re.IGNORECASE), \
94+
extractRegexResult(check, listToStrValue(headers.headers \
95+
if headers else None), re.DOTALL | re.IGNORECASE), \
96+
extractRegexResult(check, threadData.lastRedirectMsg[1] \
97+
if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \
98+
threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)], \
99+
None)
100+
101+
if output is not None:
102+
output = getUnicode(output, kb.pageEncoding)
112103
else:
113-
retVal += output if output else ''
114-
115-
if output and len(output) >= chunk_length:
116-
offset += chunk_length
104+
trimmed = extractRegexResult(trimcheck, page, re.DOTALL | re.IGNORECASE) \
105+
or extractRegexResult(trimcheck, listToStrValue(headers.headers \
106+
if headers else None), re.DOTALL | re.IGNORECASE) \
107+
or extractRegexResult(trimcheck, threadData.lastRedirectMsg[1] \
108+
if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \
109+
threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)
110+
111+
if trimmed:
112+
warnMsg = "possible server trimmed output detected (due to its length): "
113+
warnMsg += trimmed
114+
logger.warn(warnMsg)
115+
116+
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)):
117+
if offset == 1:
118+
retVal = output
119+
else:
120+
retVal += output if output else ''
121+
122+
if output and len(output) >= chunk_length:
123+
offset += chunk_length
124+
else:
125+
break
126+
127+
if kb.fileReadMode and output:
128+
dataToStdout(__formatPartialContent(output).replace(r"\n", "\n"))
117129
else:
130+
retVal = output
118131
break
119-
120-
if kb.fileReadMode and output:
121-
_ = output
122-
try:
123-
_ = safecharencode(output.decode("hex")).replace(r"\n", "\n")
124-
except:
125-
pass
126-
finally:
127-
dataToStdout(_)
128-
else:
129-
retVal = output
130-
break
132+
except:
133+
hashDBWrite(expression, "%s%s" % (retVal, PARTIAL_VALUE_MARKER))
134+
raise
131135

132136
retVal = decodeHexValue(retVal) if conf.hexConvert else retVal
133137

@@ -194,6 +198,20 @@ def __errorReplaceChars(value):
194198

195199
return retVal
196200

201+
def __formatPartialContent(value):
202+
"""
203+
Prepares (possibly hex) partial content for safe console output
204+
"""
205+
206+
if value and isinstance(value, basestring):
207+
try:
208+
value = value.decode("hex")
209+
except:
210+
pass
211+
finally:
212+
value = safecharencode(value)
213+
return value
214+
197215
def errorUse(expression, expected=None, dump=False):
198216
"""
199217
Retrieve the output of a SQL query taking advantage of the error-based

0 commit comments

Comments
 (0)