mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2024-11-25 11:03:47 +03:00
Adding initial support for Cubrid
This commit is contained in:
parent
4278bbce11
commit
264a270985
|
@ -1381,4 +1381,71 @@
|
||||||
<blind query="SELECT DISTINCT(nspname) FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND %s" query2="SELECT DISTINCT(relname) FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND nspname='%s'" count="SELECT COUNT(DISTINCT(nspname)) FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND %s" count2="SELECT COUNT(DISTINCT(relname)) FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND nspname='%s'" condition="attname" condition2="nspname" condition3="relname"/>
|
<blind query="SELECT DISTINCT(nspname) FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND %s" query2="SELECT DISTINCT(relname) FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND nspname='%s'" count="SELECT COUNT(DISTINCT(nspname)) FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND %s" count2="SELECT COUNT(DISTINCT(relname)) FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND nspname='%s'" condition="attname" condition2="nspname" condition3="relname"/>
|
||||||
</search_column>
|
</search_column>
|
||||||
</dbms>
|
</dbms>
|
||||||
|
|
||||||
|
<dbms value="Cubrid">
|
||||||
|
<cast query="CAST(%s AS VARCHAR(4000))"/>
|
||||||
|
<length query="CHAR_LENGTH(%s)"/>
|
||||||
|
<isnull query="IFNULL(%s,' ')"/>
|
||||||
|
<delimiter query="||"/>
|
||||||
|
<limit query="LIMIT %d,%d"/>
|
||||||
|
<limitregexp query="\s+LIMIT\s+([\d]+)\s*\,\s*([\d]+)" query2="\s+LIMIT\s+([\d]+)"/>
|
||||||
|
<limitgroupstart query="1"/>
|
||||||
|
<limitgroupstop query="2"/>
|
||||||
|
<limitstring query=" LIMIT "/>
|
||||||
|
<order query="ORDER BY %s ASC"/>
|
||||||
|
<count query="COUNT(%s)"/>
|
||||||
|
<comment query="--" query2="/*" query3="//"/>
|
||||||
|
<substring query="MID((%s),%d,%d)"/>
|
||||||
|
<concatenate query="%s||%s"/>
|
||||||
|
<case query="SELECT (CASE WHEN (%s) THEN 1 ELSE 0 END)"/>
|
||||||
|
<hex query="HEX(%s)"/>
|
||||||
|
<inference query="ASCII(MID((%s),%d,1))>%d"/>
|
||||||
|
<banner query="VERSION()"/>
|
||||||
|
<current_user query="CURRENT_USER"/>
|
||||||
|
<current_db query="CURRENT_USER"/>
|
||||||
|
<hostname/>
|
||||||
|
<table_comment query="SELECT comment FROM db_class WHERE owner_name='%s' AND class_name='%s'"/>
|
||||||
|
<column_comment query="SELECT db_attribute.comment FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE owner_name='%s' AND db_class.class_name='%s' AND attr_name='%s'"/>
|
||||||
|
<is_dba query="CURRENT_USER='DBA'"/>
|
||||||
|
<check_udf query="(SELECT meth_name FROM db_method WHERE meth_name='%s' LIMIT 0,1)='%s'"/>
|
||||||
|
<users>
|
||||||
|
<inband query="SELECT name FROM db_user"/>
|
||||||
|
<blind query="SELECT name FROM db_user LIMIT %d,1" count="SELECT COUNT(name) FROM db_user"/>
|
||||||
|
</users>
|
||||||
|
<passwords/>
|
||||||
|
<privileges>
|
||||||
|
<inband query="SELECT grantee,auth_type FROM DB_AUTH" condition="grantee"/>
|
||||||
|
<blind query="SELECT DISTINCT(auth_type) FROM DB_AUTH WHERE grantee %s '%s' LIMIT %d,1" count="SELECT COUNT(DISTINCT(auth_type)) FROM DB_AUTH WHERE grantee %s '%s'"/>
|
||||||
|
</privileges>
|
||||||
|
<roles/>
|
||||||
|
<statements/>
|
||||||
|
<dbs>
|
||||||
|
<inband query="SELECT owner_name FROM db_class"/>
|
||||||
|
<blind query="SELECT DISTINCT(owner_name) FROM db_class LIMIT %d,1" count="SELECT COUNT(DISTINCT(owner_name)) FROM db_class"/>
|
||||||
|
</dbs>
|
||||||
|
<tables>
|
||||||
|
<inband query="SELECT owner_name,class_name FROM db_class" condition="owner_name"/>
|
||||||
|
<blind query="SELECT class_name FROM db_class WHERE owner_name='%s' LIMIT %d,1" count="SELECT COUNT(class_name) FROM db_class WHERE owner_name='%s'"/>
|
||||||
|
</tables>
|
||||||
|
<columns>
|
||||||
|
<inband query="SELECT attr_name,data_type FROM db_attribute WHERE class_name='%s'" condition="attr_name"/>
|
||||||
|
<blind query="SELECT attr_name FROM db_attribute WHERE class_name='%s'" query2="SELECT data_type FROM db_attribute WHERE class_name='%s' AND attr_name='%s'" count="SELECT COUNT(attr_name) FROM db_attribute WHERE class_name='%s'" condition="attr_name"/>
|
||||||
|
</columns>
|
||||||
|
<dump_table>
|
||||||
|
<inband query="SELECT %s FROM %s.%s"/>
|
||||||
|
<blind query="SELECT %s FROM %s.%s LIMIT %d,1" count="SELECT COUNT(*) FROM %s.%s"/>
|
||||||
|
</dump_table>
|
||||||
|
<search_db>
|
||||||
|
<inband query="SELECT name FROM db_user WHERE %s" condition="name"/>
|
||||||
|
<blind query="SELECT name FROM db_user WHERE %s" count="SELECT COUNT(name) FROM db_user WHERE %s" condition="name"/>
|
||||||
|
</search_db>
|
||||||
|
<search_table>
|
||||||
|
<inband query="SELECT owner_name,class_name FROM db_class WHERE %s" condition="class_name" condition2="owner_name"/>
|
||||||
|
<blind query="SELECT DISTINCT(owner_name) FROM db_class WHERE %s" query2="SELECT DISTINCT(class_name) FROM db_class WHERE owner_name='%s'" count="SELECT COUNT(DISTINCT(owner_name)) FROM db_class WHERE %s" count2="SELECT COUNT(DISTINCT(class_name)) FROM db_class WHERE owner_name='%s'" condition="class_name" condition2="owner_name"/>
|
||||||
|
</search_table>
|
||||||
|
<search_column>
|
||||||
|
<inband query="SELECT owner_name,db_class.class_name FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE %s" condition="attr_name" condition2="owner_name" condition3="db_class.class_name"/>
|
||||||
|
<blind query="SELECT DISTINCT(owner_name) FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE %s" query2="SELECT DISTINCT(db_class.class_name) FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE owner_name='%s'" count="SELECT COUNT(DISTINCT(owner_name)) FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE %s" count2="SELECT COUNT(DISTINCT(db_class.class_name)) FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE owner_name='%s'" condition="attr_name" condition2="owner_name" condition3="db_class.class_name"/>
|
||||||
|
</search_column>
|
||||||
|
</dbms>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -14,6 +14,7 @@ from lib.core.exception import SqlmapConnectionException
|
||||||
from lib.core.settings import ACCESS_ALIASES
|
from lib.core.settings import ACCESS_ALIASES
|
||||||
from lib.core.settings import ALTIBASE_ALIASES
|
from lib.core.settings import ALTIBASE_ALIASES
|
||||||
from lib.core.settings import CRATEDB_ALIASES
|
from lib.core.settings import CRATEDB_ALIASES
|
||||||
|
from lib.core.settings import CUBRID_ALIASES
|
||||||
from lib.core.settings import DB2_ALIASES
|
from lib.core.settings import DB2_ALIASES
|
||||||
from lib.core.settings import DERBY_ALIASES
|
from lib.core.settings import DERBY_ALIASES
|
||||||
from lib.core.settings import FIREBIRD_ALIASES
|
from lib.core.settings import FIREBIRD_ALIASES
|
||||||
|
@ -40,6 +41,8 @@ from plugins.dbms.altibase.connector import Connector as AltibaseConn
|
||||||
from plugins.dbms.altibase import AltibaseMap
|
from plugins.dbms.altibase import AltibaseMap
|
||||||
from plugins.dbms.cratedb.connector import Connector as CrateDBConn
|
from plugins.dbms.cratedb.connector import Connector as CrateDBConn
|
||||||
from plugins.dbms.cratedb import CrateDBMap
|
from plugins.dbms.cratedb import CrateDBMap
|
||||||
|
from plugins.dbms.cubrid.connector import Connector as CubridConn
|
||||||
|
from plugins.dbms.cubrid import CubridMap
|
||||||
from plugins.dbms.db2.connector import Connector as DB2Conn
|
from plugins.dbms.db2.connector import Connector as DB2Conn
|
||||||
from plugins.dbms.db2 import DB2Map
|
from plugins.dbms.db2 import DB2Map
|
||||||
from plugins.dbms.derby.connector import Connector as DerbyConn
|
from plugins.dbms.derby.connector import Connector as DerbyConn
|
||||||
|
@ -105,6 +108,7 @@ def setHandler():
|
||||||
(DBMS.ALTIBASE, ALTIBASE_ALIASES, AltibaseMap, AltibaseConn),
|
(DBMS.ALTIBASE, ALTIBASE_ALIASES, AltibaseMap, AltibaseConn),
|
||||||
(DBMS.MIMERSQL, MIMERSQL_ALIASES, MimerSQLMap, MimerSQLConn),
|
(DBMS.MIMERSQL, MIMERSQL_ALIASES, MimerSQLMap, MimerSQLConn),
|
||||||
(DBMS.CRATEDB, CRATEDB_ALIASES, CrateDBMap, CrateDBConn),
|
(DBMS.CRATEDB, CRATEDB_ALIASES, CrateDBMap, CrateDBConn),
|
||||||
|
(DBMS.CUBRID, CUBRID_ALIASES, CubridMap, CubridConn),
|
||||||
]
|
]
|
||||||
|
|
||||||
_ = max(_ if (conf.get("dbms") or Backend.getIdentifiedDbms() or kb.heuristicExtendedDbms or "").lower() in _[1] else () for _ in items)
|
_ = max(_ if (conf.get("dbms") or Backend.getIdentifiedDbms() or kb.heuristicExtendedDbms or "").lower() in _[1] else () for _ in items)
|
||||||
|
|
|
@ -301,7 +301,7 @@ class Agent(object):
|
||||||
comment = getTechniqueData().comment if comment is None else comment
|
comment = getTechniqueData().comment if comment is None else comment
|
||||||
|
|
||||||
if any((comment or "").startswith(_) for _ in ("--", GENERIC_SQL_COMMENT_MARKER)):
|
if any((comment or "").startswith(_) for _ in ("--", GENERIC_SQL_COMMENT_MARKER)):
|
||||||
if not GENERIC_SQL_COMMENT.startswith(queries[Backend.getIdentifiedDbms()].comment.query):
|
if Backend.getIdentifiedDbms() and not GENERIC_SQL_COMMENT.startswith(queries[Backend.getIdentifiedDbms()].comment.query):
|
||||||
comment = queries[Backend.getIdentifiedDbms()].comment.query
|
comment = queries[Backend.getIdentifiedDbms()].comment.query
|
||||||
|
|
||||||
if comment is not None:
|
if comment is not None:
|
||||||
|
@ -660,7 +660,7 @@ class Agent(object):
|
||||||
elif fieldsNoSelect:
|
elif fieldsNoSelect:
|
||||||
concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.chars.start, concatenatedQuery, kb.chars.stop)
|
concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.chars.start, concatenatedQuery, kb.chars.stop)
|
||||||
|
|
||||||
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.CRATEDB):
|
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2, DBMS.MONETDB, DBMS.DERBY, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.CRATEDB, DBMS.CUBRID):
|
||||||
if fieldsExists:
|
if fieldsExists:
|
||||||
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
|
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
|
||||||
concatenatedQuery += "||'%s'" % kb.chars.stop
|
concatenatedQuery += "||'%s'" % kb.chars.stop
|
||||||
|
@ -949,7 +949,7 @@ class Agent(object):
|
||||||
fromFrom = limitedQuery[fromIndex + 1:]
|
fromFrom = limitedQuery[fromIndex + 1:]
|
||||||
orderBy = None
|
orderBy = None
|
||||||
|
|
||||||
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL):
|
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.MIMERSQL, DBMS.CUBRID):
|
||||||
limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num, 1)
|
limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num, 1)
|
||||||
limitedQuery += " %s" % limitStr
|
limitedQuery += " %s" % limitStr
|
||||||
|
|
||||||
|
|
|
@ -4076,7 +4076,7 @@ def safeSQLIdentificatorNaming(name, isTable=False):
|
||||||
if retVal.upper() in kb.keywords or (retVal or " ")[0].isdigit() or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ('.' if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal)
|
if retVal.upper() in kb.keywords or (retVal or " ")[0].isdigit() or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ('.' if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal)
|
||||||
retVal = unsafeSQLIdentificatorNaming(retVal)
|
retVal = unsafeSQLIdentificatorNaming(retVal)
|
||||||
|
|
||||||
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.SQLITE): # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users)
|
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE): # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users)
|
||||||
retVal = "`%s`" % retVal
|
retVal = "`%s`" % retVal
|
||||||
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB):
|
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB):
|
||||||
retVal = "\"%s\"" % retVal
|
retVal = "\"%s\"" % retVal
|
||||||
|
@ -4114,7 +4114,7 @@ def unsafeSQLIdentificatorNaming(name):
|
||||||
retVal = name
|
retVal = name
|
||||||
|
|
||||||
if isinstance(name, six.string_types):
|
if isinstance(name, six.string_types):
|
||||||
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.SQLITE):
|
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE):
|
||||||
retVal = name.replace("`", "")
|
retVal = name.replace("`", "")
|
||||||
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB):
|
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB):
|
||||||
retVal = name.replace("\"", "")
|
retVal = name.replace("\"", "")
|
||||||
|
|
|
@ -13,6 +13,7 @@ from lib.core.settings import ACCESS_ALIASES
|
||||||
from lib.core.settings import ALTIBASE_ALIASES
|
from lib.core.settings import ALTIBASE_ALIASES
|
||||||
from lib.core.settings import BLANK
|
from lib.core.settings import BLANK
|
||||||
from lib.core.settings import CRATEDB_ALIASES
|
from lib.core.settings import CRATEDB_ALIASES
|
||||||
|
from lib.core.settings import CUBRID_ALIASES
|
||||||
from lib.core.settings import DB2_ALIASES
|
from lib.core.settings import DB2_ALIASES
|
||||||
from lib.core.settings import DERBY_ALIASES
|
from lib.core.settings import DERBY_ALIASES
|
||||||
from lib.core.settings import FIREBIRD_ALIASES
|
from lib.core.settings import FIREBIRD_ALIASES
|
||||||
|
@ -214,6 +215,7 @@ DBMS_DICT = {
|
||||||
DBMS.ALTIBASE: (ALTIBASE_ALIASES, None, None, None),
|
DBMS.ALTIBASE: (ALTIBASE_ALIASES, None, None, None),
|
||||||
DBMS.MIMERSQL: (MIMERSQL_ALIASES, "mimerpy", "https://github.com/mimersql/MimerPy", None),
|
DBMS.MIMERSQL: (MIMERSQL_ALIASES, "mimerpy", "https://github.com/mimersql/MimerPy", None),
|
||||||
DBMS.CRATEDB: (CRATEDB_ALIASES, "python-psycopg2", "http://initd.org/psycopg/", "postgresql"),
|
DBMS.CRATEDB: (CRATEDB_ALIASES, "python-psycopg2", "http://initd.org/psycopg/", "postgresql"),
|
||||||
|
DBMS.CUBRID: (CUBRID_ALIASES, "CUBRID-Python", "https://github.com/CUBRID/cubrid-python", None),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reference: https://blog.jooq.org/tag/sysibm-sysdummy1/
|
# Reference: https://blog.jooq.org/tag/sysibm-sysdummy1/
|
||||||
|
@ -245,6 +247,7 @@ HEURISTIC_NULL_EVAL = {
|
||||||
DBMS.ALTIBASE: "TDESENCRYPT(NULL,NULL)",
|
DBMS.ALTIBASE: "TDESENCRYPT(NULL,NULL)",
|
||||||
DBMS.MIMERSQL: "ASCII_CHAR(256)",
|
DBMS.MIMERSQL: "ASCII_CHAR(256)",
|
||||||
DBMS.CRATEDB: "(NULL~NULL)",
|
DBMS.CRATEDB: "(NULL~NULL)",
|
||||||
|
DBMS.CUBRID: "(NULL SETEQ NULL)",
|
||||||
}
|
}
|
||||||
|
|
||||||
SQL_STATEMENTS = {
|
SQL_STATEMENTS = {
|
||||||
|
|
|
@ -53,6 +53,7 @@ class DBMS(object):
|
||||||
ALTIBASE = "Altibase"
|
ALTIBASE = "Altibase"
|
||||||
MIMERSQL = "MimerSQL"
|
MIMERSQL = "MimerSQL"
|
||||||
CRATEDB = "CrateDB"
|
CRATEDB = "CrateDB"
|
||||||
|
CUBRID = "Cubrid"
|
||||||
|
|
||||||
class DBMS_DIRECTORY_NAME(object):
|
class DBMS_DIRECTORY_NAME(object):
|
||||||
ACCESS = "access"
|
ACCESS = "access"
|
||||||
|
@ -76,6 +77,7 @@ class DBMS_DIRECTORY_NAME(object):
|
||||||
ALTIBASE = "altibase"
|
ALTIBASE = "altibase"
|
||||||
MIMERSQL = "mimersql"
|
MIMERSQL = "mimersql"
|
||||||
CRATEDB = "cratedb"
|
CRATEDB = "cratedb"
|
||||||
|
CUBRID = "cubrid"
|
||||||
|
|
||||||
class FORK(object):
|
class FORK(object):
|
||||||
MARIADB = "MariaDB"
|
MARIADB = "MariaDB"
|
||||||
|
|
|
@ -18,7 +18,7 @@ from lib.core.enums import OS
|
||||||
from thirdparty.six import unichr as _unichr
|
from thirdparty.six import unichr as _unichr
|
||||||
|
|
||||||
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
||||||
VERSION = "1.4.2.6"
|
VERSION = "1.4.2.7"
|
||||||
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
|
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
|
||||||
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
|
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)
|
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
|
||||||
|
@ -267,6 +267,7 @@ PRESTO_SYSTEM_DBS = ("information_schema",)
|
||||||
ALTIBASE_SYSTEM_DBS = ("SYSTEM_",)
|
ALTIBASE_SYSTEM_DBS = ("SYSTEM_",)
|
||||||
MIMERSQL_SYSTEM_DBS = ("information_schema", "SYSTEM",)
|
MIMERSQL_SYSTEM_DBS = ("information_schema", "SYSTEM",)
|
||||||
CRATEDB_SYSTEM_DBS = ("information_schema", "pg_catalog", "sys")
|
CRATEDB_SYSTEM_DBS = ("information_schema", "pg_catalog", "sys")
|
||||||
|
CUBRID_SYSTEM_DBS = ("",)
|
||||||
|
|
||||||
# Note: (<regular>) + (<forks>)
|
# Note: (<regular>) + (<forks>)
|
||||||
MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms")
|
MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms")
|
||||||
|
@ -290,13 +291,14 @@ PRESTO_ALIASES = ("presto",)
|
||||||
ALTIBASE_ALIASES = ("altibase",)
|
ALTIBASE_ALIASES = ("altibase",)
|
||||||
MIMERSQL_ALIASES = ("mimersql", "mimer")
|
MIMERSQL_ALIASES = ("mimersql", "mimer")
|
||||||
CRATEDB_ALIASES = ("cratedb", "crate")
|
CRATEDB_ALIASES = ("cratedb", "crate")
|
||||||
|
CUBRID_ALIASES = ("cubrid",)
|
||||||
|
|
||||||
DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_"))
|
DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_"))
|
||||||
|
|
||||||
SUPPORTED_DBMS = 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
|
SUPPORTED_DBMS = 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
|
||||||
SUPPORTED_OS = ("linux", "windows")
|
SUPPORTED_OS = ("linux", "windows")
|
||||||
|
|
||||||
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_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))
|
||||||
|
|
||||||
USER_AGENT_ALIASES = ("ua", "useragent", "user-agent")
|
USER_AGENT_ALIASES = ("ua", "useragent", "user-agent")
|
||||||
REFERER_ALIASES = ("ref", "referer", "referrer")
|
REFERER_ALIASES = ("ref", "referer", "referrer")
|
||||||
|
|
|
@ -56,6 +56,8 @@ def checkDependencies():
|
||||||
__import__("prestodb")
|
__import__("prestodb")
|
||||||
elif dbmsName == DBMS.MIMERSQL:
|
elif dbmsName == DBMS.MIMERSQL:
|
||||||
__import__("mimerpy")
|
__import__("mimerpy")
|
||||||
|
elif dbmsName == DBMS.CUBRID:
|
||||||
|
__import__("CUBRIDdb")
|
||||||
except:
|
except:
|
||||||
warnMsg = "sqlmap requires '%s' third-party library " % data[1]
|
warnMsg = "sqlmap requires '%s' third-party library " % data[1]
|
||||||
warnMsg += "in order to directly connect to the DBMS "
|
warnMsg += "in order to directly connect to the DBMS "
|
||||||
|
|
30
plugins/dbms/cubrid/__init__.py
Normal file
30
plugins/dbms/cubrid/__init__.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||||
|
See the file 'LICENSE' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.enums import DBMS
|
||||||
|
from lib.core.settings import CUBRID_SYSTEM_DBS
|
||||||
|
from lib.core.unescaper import unescaper
|
||||||
|
|
||||||
|
from plugins.dbms.cubrid.enumeration import Enumeration
|
||||||
|
from plugins.dbms.cubrid.filesystem import Filesystem
|
||||||
|
from plugins.dbms.cubrid.fingerprint import Fingerprint
|
||||||
|
from plugins.dbms.cubrid.syntax import Syntax
|
||||||
|
from plugins.dbms.cubrid.takeover import Takeover
|
||||||
|
from plugins.generic.misc import Miscellaneous
|
||||||
|
|
||||||
|
class CubridMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
||||||
|
"""
|
||||||
|
This class defines Cubrid methods
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.excludeDbsList = CUBRID_SYSTEM_DBS
|
||||||
|
|
||||||
|
for cls in self.__class__.__bases__:
|
||||||
|
cls.__init__(self)
|
||||||
|
|
||||||
|
unescaper[DBMS.CUBRID] = Syntax.escape
|
59
plugins/dbms/cubrid/connector.py
Normal file
59
plugins/dbms/cubrid/connector.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||||
|
See the file 'LICENSE' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
import CUBRIDdb
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from lib.core.common import getSafeExString
|
||||||
|
from lib.core.data import conf
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.exception import SqlmapConnectionException
|
||||||
|
from plugins.generic.connector import Connector as GenericConnector
|
||||||
|
|
||||||
|
class Connector(GenericConnector):
|
||||||
|
"""
|
||||||
|
Homepage: https://github.com/CUBRID/cubrid-python
|
||||||
|
User guide: https://github.com/CUBRID/cubrid-python/blob/develop/README.md
|
||||||
|
API: https://www.python.org/dev/peps/pep-0249/
|
||||||
|
License: BSD License
|
||||||
|
"""
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
self.initConnection()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.connector = CUBRIDdb.connect(hostname=self.hostname, username=self.user, password=self.password, database=self.db, port=self.port, connect_timeout=conf.timeout)
|
||||||
|
except CUBRIDdb.DatabaseError as ex:
|
||||||
|
raise SqlmapConnectionException(getSafeExString(ex))
|
||||||
|
|
||||||
|
self.initCursor()
|
||||||
|
self.printConnected()
|
||||||
|
|
||||||
|
def fetchall(self):
|
||||||
|
try:
|
||||||
|
return self.cursor.fetchall()
|
||||||
|
except CUBRIDdb.DatabaseError as ex:
|
||||||
|
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex))
|
||||||
|
return None
|
||||||
|
|
||||||
|
def execute(self, query):
|
||||||
|
try:
|
||||||
|
self.cursor.execute(query)
|
||||||
|
except CUBRIDdb.DatabaseError as ex:
|
||||||
|
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex))
|
||||||
|
except CUBRIDdb.Error as ex:
|
||||||
|
raise SqlmapConnectionException(getSafeExString(ex))
|
||||||
|
|
||||||
|
self.connector.commit()
|
||||||
|
|
||||||
|
def select(self, query):
|
||||||
|
self.execute(query)
|
||||||
|
return self.fetchall()
|
32
plugins/dbms/cubrid/enumeration.py
Normal file
32
plugins/dbms/cubrid/enumeration.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||||
|
See the file 'LICENSE' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.data import logger
|
||||||
|
from plugins.generic.enumeration import Enumeration as GenericEnumeration
|
||||||
|
|
||||||
|
class Enumeration(GenericEnumeration):
|
||||||
|
def getPasswordHashes(self):
|
||||||
|
warnMsg = "on Cubrid it is not possible to enumerate password hashes"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def getStatements(self):
|
||||||
|
warnMsg = "on Cubrid it is not possible to enumerate the SQL statements"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
|
def getRoles(self, *args, **kwargs):
|
||||||
|
warnMsg = "on Cubrid it is not possible to enumerate the user roles"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def getHostname(self):
|
||||||
|
warnMsg = "on Cubrid it is not possible to enumerate the hostname"
|
||||||
|
logger.warn(warnMsg)
|
11
plugins/dbms/cubrid/filesystem.py
Normal file
11
plugins/dbms/cubrid/filesystem.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||||
|
See the file 'LICENSE' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
from plugins.generic.filesystem import Filesystem as GenericFilesystem
|
||||||
|
|
||||||
|
class Filesystem(GenericFilesystem):
|
||||||
|
pass
|
94
plugins/dbms/cubrid/fingerprint.py
Normal file
94
plugins/dbms/cubrid/fingerprint.py
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||||
|
See the file 'LICENSE' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.common import Backend
|
||||||
|
from lib.core.common import Format
|
||||||
|
from lib.core.data import conf
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.enums import DBMS
|
||||||
|
from lib.core.session import setDbms
|
||||||
|
from lib.core.settings import CUBRID_ALIASES
|
||||||
|
from lib.request import inject
|
||||||
|
from plugins.generic.fingerprint import Fingerprint as GenericFingerprint
|
||||||
|
|
||||||
|
class Fingerprint(GenericFingerprint):
|
||||||
|
def __init__(self):
|
||||||
|
GenericFingerprint.__init__(self, DBMS.CUBRID)
|
||||||
|
|
||||||
|
def getFingerprint(self):
|
||||||
|
value = ""
|
||||||
|
wsOsFp = Format.getOs("web server", kb.headersFp)
|
||||||
|
|
||||||
|
if wsOsFp:
|
||||||
|
value += "%s\n" % wsOsFp
|
||||||
|
|
||||||
|
if kb.data.banner:
|
||||||
|
dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp)
|
||||||
|
|
||||||
|
if dbmsOsFp:
|
||||||
|
value += "%s\n" % dbmsOsFp
|
||||||
|
|
||||||
|
value += "back-end DBMS: "
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
value += DBMS.CUBRID
|
||||||
|
return value
|
||||||
|
|
||||||
|
actVer = Format.getDbms()
|
||||||
|
blank = " " * 15
|
||||||
|
value += "active fingerprint: %s" % actVer
|
||||||
|
|
||||||
|
if kb.bannerFp:
|
||||||
|
banVer = kb.bannerFp.get("dbmsVersion")
|
||||||
|
|
||||||
|
if banVer:
|
||||||
|
banVer = Format.getDbms([banVer])
|
||||||
|
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
|
||||||
|
|
||||||
|
htmlErrorFp = Format.getErrorParsedDBMSes()
|
||||||
|
|
||||||
|
if htmlErrorFp:
|
||||||
|
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def checkDbms(self):
|
||||||
|
if not conf.extensiveFp and Backend.isDbmsWithin(CUBRID_ALIASES):
|
||||||
|
setDbms(DBMS.CUBRID)
|
||||||
|
|
||||||
|
self.getBanner()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
infoMsg = "testing %s" % DBMS.CUBRID
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
result = inject.checkBooleanExpression("{} SUBSETEQ (CAST ({} AS SET))")
|
||||||
|
|
||||||
|
if result:
|
||||||
|
infoMsg = "confirming %s" % DBMS.CUBRID
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
result = inject.checkBooleanExpression("DRAND()<2")
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
warnMsg = "the back-end DBMS is not %s" % DBMS.CUBRID
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
setDbms(DBMS.CUBRID)
|
||||||
|
|
||||||
|
self.getBanner()
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
warnMsg = "the back-end DBMS is not %s" % DBMS.CUBRID
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return False
|
23
plugins/dbms/cubrid/syntax.py
Normal file
23
plugins/dbms/cubrid/syntax.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||||
|
See the file 'LICENSE' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.convert import getOrds
|
||||||
|
from plugins.generic.syntax import Syntax as GenericSyntax
|
||||||
|
|
||||||
|
class Syntax(GenericSyntax):
|
||||||
|
@staticmethod
|
||||||
|
def escape(expression, quote=True):
|
||||||
|
"""
|
||||||
|
>>> from lib.core.common import Backend
|
||||||
|
>>> Syntax.escape("SELECT 'abcdefgh' FROM foobar") == "SELECT CHR(97)||CHR(98)||CHR(99)||CHR(100)||CHR(101)||CHR(102)||CHR(103)||CHR(104) FROM foobar"
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
|
||||||
|
def escaper(value):
|
||||||
|
return "||".join("CHR(%d)" % _ for _ in getOrds(value))
|
||||||
|
|
||||||
|
return Syntax._escape(expression, quote, escaper)
|
28
plugins/dbms/cubrid/takeover.py
Normal file
28
plugins/dbms/cubrid/takeover.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||||
|
See the file 'LICENSE' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.exception import SqlmapUnsupportedFeatureException
|
||||||
|
from plugins.generic.takeover import Takeover as GenericTakeover
|
||||||
|
|
||||||
|
class Takeover(GenericTakeover):
|
||||||
|
def osCmd(self):
|
||||||
|
errMsg = "on Cubrid it is not possible to execute commands"
|
||||||
|
raise SqlmapUnsupportedFeatureException(errMsg)
|
||||||
|
|
||||||
|
def osShell(self):
|
||||||
|
errMsg = "on Cubrid it is not possible to execute commands"
|
||||||
|
raise SqlmapUnsupportedFeatureException(errMsg)
|
||||||
|
|
||||||
|
def osPwn(self):
|
||||||
|
errMsg = "on Cubrid it is not possible to establish an "
|
||||||
|
errMsg += "out-of-band connection"
|
||||||
|
raise SqlmapUnsupportedFeatureException(errMsg)
|
||||||
|
|
||||||
|
def osSmb(self):
|
||||||
|
errMsg = "on Cubrid it is not possible to establish an "
|
||||||
|
errMsg += "out-of-band connection"
|
||||||
|
raise SqlmapUnsupportedFeatureException(errMsg)
|
|
@ -87,7 +87,7 @@ class Databases(object):
|
||||||
warnMsg += "schema names for enumeration as the counterpart to database "
|
warnMsg += "schema names for enumeration as the counterpart to database "
|
||||||
warnMsg += "names on other DBMSes"
|
warnMsg += "names on other DBMSes"
|
||||||
singleTimeWarnMessage(warnMsg)
|
singleTimeWarnMessage(warnMsg)
|
||||||
elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE,):
|
elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.CUBRID):
|
||||||
warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms()
|
warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms()
|
||||||
warnMsg += "user names for enumeration as the counterpart to database "
|
warnMsg += "user names for enumeration as the counterpart to database "
|
||||||
warnMsg += "names on other DBMSes"
|
warnMsg += "names on other DBMSes"
|
||||||
|
@ -115,7 +115,7 @@ class Databases(object):
|
||||||
|
|
||||||
infoMsg = "fetching database (schema) names"
|
infoMsg = "fetching database (schema) names"
|
||||||
|
|
||||||
elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE,):
|
elif Backend.getIdentifiedDbms() in (DBMS.ALTIBASE, DBMS.CUBRID):
|
||||||
warnMsg = "user names are going to be used on %s " % Backend.getIdentifiedDbms()
|
warnMsg = "user names are going to be used on %s " % Backend.getIdentifiedDbms()
|
||||||
warnMsg += "for enumeration as the counterpart to database "
|
warnMsg += "for enumeration as the counterpart to database "
|
||||||
warnMsg += "names on other DBMSes"
|
warnMsg += "names on other DBMSes"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user