|
62 | 62 | from lib.core.settings import GET_VALUE_UPPERCASE_KEYWORDS |
63 | 63 | from lib.core.settings import IS_TTY |
64 | 64 | from lib.core.settings import INFERENCE_MARKER |
| 65 | +from lib.core.settings import INVALID_UNICODE_PRIVATE_AREA |
65 | 66 | from lib.core.settings import MAX_TECHNIQUES_PER_VALUE |
66 | 67 | from lib.core.settings import SQL_SCALAR_REGEX |
67 | 68 | from lib.core.settings import UNICODE_ENCODING |
@@ -575,6 +576,24 @@ def _etaTicker(): |
575 | 576 |
|
576 | 577 | return results |
577 | 578 |
|
| 579 | +def _pageCharsetCorrupted(value): |
| 580 | + """ |
| 581 | + True if a retrieved value carries reversibly-decoded (undecodable) bytes - a sign that the |
| 582 | + web page charset could not represent the DBMS data (cf. the 'reversible' codec). Such a |
| 583 | + UNION/error value is silently corrupt and should be re-fetched via DBMS-side hexadecimal. |
| 584 | + """ |
| 585 | + |
| 586 | + retVal = [False] |
| 587 | + |
| 588 | + def _(item): |
| 589 | + if not retVal[0] and isinstance(item, six.string_types): |
| 590 | + if re.search(r"\\x[89a-f][0-9a-f]", item) or (INVALID_UNICODE_PRIVATE_AREA and any(0xF0000 <= ord(_) <= 0xF00FF for _ in item)): |
| 591 | + retVal[0] = True |
| 592 | + return item |
| 593 | + |
| 594 | + applyFunctionRecursively(value, _) |
| 595 | + return retVal[0] |
| 596 | + |
578 | 597 | @lockedmethod |
579 | 598 | @stackedmethod |
580 | 599 | def getValue(expression, blind=True, union=True, error=True, time=True, fromUser=False, expected=None, batch=False, unpack=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False, suppressOutput=None, expectingNone=False, safeCharEncode=True): |
@@ -672,6 +691,28 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser |
672 | 691 | count += 1 |
673 | 692 | found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE |
674 | 693 |
|
| 694 | + # Auto-recover from a page/DBMS charset mismatch: a UNION/error value carrying |
| 695 | + # undecodable bytes (the page charset couldn't represent the DBMS data) is silently |
| 696 | + # corrupt. Re-fetch it via DBMS-side hex, which travels as ASCII regardless of the |
| 697 | + # page charset - no user '--hex'/'--encoding' knowledge required. Gated, so clean |
| 698 | + # or ASCII data pays nothing. |
| 699 | + if (found and not conf.hexConvert and not conf.binaryFields and expected not in (EXPECTED.BOOL, EXPECTED.INT) |
| 700 | + and getTechnique() in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY) |
| 701 | + and Backend.getIdentifiedDbms() and hasattr(queries[Backend.getIdentifiedDbms()], "hex") |
| 702 | + and _pageCharsetCorrupted(value)): |
| 703 | + warnMsg = "retrieved data appears corrupted because of a charset mismatch between the " |
| 704 | + warnMsg += "DBMS and the web page. Re-fetching using hexadecimal encoding" |
| 705 | + singleTimeWarnMessage(warnMsg) |
| 706 | + |
| 707 | + conf.hexConvert = True |
| 708 | + try: |
| 709 | + _value = _goUnion(query, unpack, dump) if getTechnique() == PAYLOAD.TECHNIQUE.UNION else errorUse(query, dump) |
| 710 | + finally: |
| 711 | + conf.hexConvert = False |
| 712 | + |
| 713 | + if _value is not None: |
| 714 | + value = _value |
| 715 | + |
675 | 716 | if found and conf.dnsDomain: |
676 | 717 | _ = "".join(filterNone(key if isTechniqueAvailable(value) else None for key, value in {'E': PAYLOAD.TECHNIQUE.ERROR, 'Q': PAYLOAD.TECHNIQUE.QUERY, 'U': PAYLOAD.TECHNIQUE.UNION}.items())) |
677 | 718 | warnMsg = "option '--dns-domain' will be ignored " |
|
0 commit comments