mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-08-04 14:55:40 +00:00
Adding CUBRID to dbwire
This commit is contained in:
parent
dad2f02f28
commit
59933891e0
6 changed files with 451 additions and 5 deletions
|
|
@ -1509,7 +1509,7 @@
|
|||
<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'"/>
|
||||
<column_comment query="SELECT db_attribute.comment FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE db_class.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>
|
||||
|
|
@ -1532,8 +1532,8 @@
|
|||
<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 JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE db_class.class_name='%s' AND owner_name='%s'" condition="attr_name"/>
|
||||
<blind query="SELECT attr_name FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE db_class.class_name='%s' AND owner_name='%s'" query2="SELECT data_type FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE db_class.class_name='%s' AND owner_name='%s'" count="SELECT COUNT(attr_name) FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE db_class.class_name='%s' AND owner_name='%s'" condition="attr_name"/>
|
||||
<inband query="SELECT attr_name,data_type FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE db_class.class_name='%s' AND db_class.owner_name='%s'" condition="attr_name"/>
|
||||
<blind query="SELECT attr_name FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE db_class.class_name='%s' AND db_class.owner_name='%s'" query2="SELECT data_type FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE db_class.class_name='%s' AND db_class.owner_name='%s'" count="SELECT COUNT(attr_name) FROM db_attribute JOIN db_class ON db_attribute.class_name=db_class.class_name WHERE db_class.class_name='%s' AND db_class.owner_name='%s'" condition="attr_name"/>
|
||||
</columns>
|
||||
<dump_table>
|
||||
<inband query="SELECT %s FROM %s.%s"/>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ A wire protocol is shared across a whole family of products, so one client serve
|
|||
| `mysql.py` | MySQL client/server | MySQL, MariaDB, TiDB, Aurora (MySQL), Percona |
|
||||
| `tds.py` | TDS | Microsoft SQL Server, Sybase |
|
||||
| `firebird.py` | Firebird wire | Firebird 3 / 4 / 5 |
|
||||
| `cubrid.py` | CUBRID CAS | CUBRID |
|
||||
| `clickhouse.py` | HTTP (TabSeparated) | ClickHouse and HTTP-compatible forks |
|
||||
| `monetdb.py` | MAPI | MonetDB |
|
||||
| `presto.py` | HTTP/REST (JSON) | Presto, Trino |
|
||||
|
|
@ -59,6 +60,8 @@ parameter binding, prepared statements, bulk load/`COPY`, or TLS. Notable per-pr
|
|||
require TLS and are not supported here; the native driver or SQLAlchemy tier covers those.
|
||||
- **Firebird** - SRP-256 (and SRP) authentication with ChaCha20 or RC4 wire encryption, as required by
|
||||
default on Firebird 3 and later. Legacy (pre-SRP) authentication is not implemented.
|
||||
- **CUBRID** - cleartext login over the CAS broker protocol. Large objects (BLOB/CLOB) are returned as
|
||||
their raw locator handles rather than fetched inline.
|
||||
|
||||
When a case is not supported, the client raises a clear `NotSupportedError` rather than returning
|
||||
wrong data, so `-d` cleanly falls through to another connector tier where possible.
|
||||
|
|
|
|||
442
extra/dbwire/cubrid.py
Normal file
442
extra/dbwire/cubrid.py
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
|
||||
See the file 'LICENSE' for copying permission
|
||||
"""
|
||||
|
||||
"""
|
||||
Minimal pure-python CUBRID client speaking the CAS (Common Application Server) broker protocol (stdlib
|
||||
only, no CUBRID-Python/CCI). Does the 10-byte broker handshake (+ optional CAS-worker redirect), a
|
||||
cleartext OPEN_DATABASE login, then prepare / execute / fetch with column-metadata decoding. Read-oriented
|
||||
for sqlmap: execute() takes a fully-formed query string, binary (BIT/VARBIT/BLOB) values come back as bytes
|
||||
(sqlmap hex-encodes them). Auto-commit is enabled so each statement is independent.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import socket
|
||||
import struct
|
||||
|
||||
from extra.dbwire import DatabaseError
|
||||
from extra.dbwire import DataError
|
||||
from extra.dbwire import InterfaceError
|
||||
from extra.dbwire import IntegrityError
|
||||
from extra.dbwire import NotSupportedError
|
||||
from extra.dbwire import OperationalError
|
||||
from extra.dbwire import ProgrammingError
|
||||
|
||||
_MAGIC = b"CUBRK"
|
||||
_CLIENT_JDBC = 3
|
||||
_CAS_VERSION = 0x48 # PROTO_INDICATOR(0x40) | VERSION(8)
|
||||
|
||||
# function codes (first raw byte of each request)
|
||||
_FC_END_TRAN = 1
|
||||
_FC_PREPARE = 2
|
||||
_FC_EXECUTE = 3
|
||||
_FC_SET_DB_PARAMETER = 5
|
||||
_FC_CLOSE_REQ_HANDLE = 6
|
||||
_FC_FETCH = 8
|
||||
_FC_CON_CLOSE = 31
|
||||
|
||||
_TRAN_COMMIT = 1
|
||||
_TRAN_ROLLBACK = 2
|
||||
_PARAM_AUTO_COMMIT = 4
|
||||
|
||||
_OID_SIZE = 8
|
||||
_STMT_SELECT = 21
|
||||
|
||||
# CUBRID CCI_U_TYPE column type codes
|
||||
_T_CHAR = 1
|
||||
_T_STRING = 2
|
||||
_T_NCHAR = 3
|
||||
_T_VARNCHAR = 4
|
||||
_T_BIT = 5
|
||||
_T_VARBIT = 6
|
||||
_T_NUMERIC = 7
|
||||
_T_INT = 8
|
||||
_T_SHORT = 9
|
||||
_T_MONETARY = 10
|
||||
_T_FLOAT = 11
|
||||
_T_DOUBLE = 12
|
||||
_T_DATE = 13
|
||||
_T_TIME = 14
|
||||
_T_TIMESTAMP = 15
|
||||
_T_OBJECT = 19
|
||||
_T_BIGINT = 21
|
||||
_T_DATETIME = 22
|
||||
_T_BLOB = 23
|
||||
_T_CLOB = 24
|
||||
_T_ENUM = 25
|
||||
_T_JSON = 34
|
||||
_STRING_TYPES = frozenset((_T_CHAR, _T_STRING, _T_NCHAR, _T_VARNCHAR, _T_ENUM, _T_JSON))
|
||||
_BINARY_TYPES = frozenset((_T_BIT, _T_VARBIT, _T_BLOB, _T_CLOB))
|
||||
|
||||
_MAX_MESSAGE_LENGTH = 0x40000000 # guard against a hostile/corrupt length
|
||||
|
||||
class _Writer(object):
|
||||
# builds a request payload (after the 8-byte header): raw function code + length-prefixed args
|
||||
def __init__(self, fc):
|
||||
self._buf = bytearray(struct.pack(">B", fc))
|
||||
|
||||
def raw_int(self, v):
|
||||
self._buf += struct.pack(">i", v); return self
|
||||
|
||||
def raw_byte(self, v):
|
||||
self._buf += struct.pack(">B", v); return self
|
||||
|
||||
def arg_int(self, v):
|
||||
self._buf += struct.pack(">ii", 4, v); return self
|
||||
|
||||
def arg_byte(self, v):
|
||||
self._buf += struct.pack(">iB", 1, v); return self
|
||||
|
||||
def arg_null(self):
|
||||
self._buf += struct.pack(">i", 0); return self
|
||||
|
||||
def arg_cache_time(self):
|
||||
self._buf += struct.pack(">iii", 8, 0, 0); return self
|
||||
|
||||
def arg_nts(self, s): # null-terminated string arg: [len(utf8)+1][utf8][00]
|
||||
b = s.encode("utf-8")
|
||||
self._buf += struct.pack(">i", len(b) + 1) + b + b"\x00"; return self
|
||||
|
||||
def payload(self):
|
||||
return bytes(self._buf)
|
||||
|
||||
class _Reader(object):
|
||||
def __init__(self, buf):
|
||||
self._buf = buf
|
||||
self._off = 0
|
||||
|
||||
def remaining(self):
|
||||
return len(self._buf) - self._off
|
||||
|
||||
def byte(self):
|
||||
v = struct.unpack_from(">B", self._buf, self._off)[0]; self._off += 1; return v
|
||||
|
||||
def short(self):
|
||||
v = struct.unpack_from(">h", self._buf, self._off)[0]; self._off += 2; return v
|
||||
|
||||
def int(self):
|
||||
v = struct.unpack_from(">i", self._buf, self._off)[0]; self._off += 4; return v
|
||||
|
||||
def long(self):
|
||||
v = struct.unpack_from(">q", self._buf, self._off)[0]; self._off += 8; return v
|
||||
|
||||
def float(self):
|
||||
v = struct.unpack_from(">f", self._buf, self._off)[0]; self._off += 4; return v
|
||||
|
||||
def double(self):
|
||||
v = struct.unpack_from(">d", self._buf, self._off)[0]; self._off += 8; return v
|
||||
|
||||
def raw(self, n):
|
||||
v = self._buf[self._off:self._off + n]; self._off += n; return bytes(v)
|
||||
|
||||
def skip(self, n):
|
||||
self._off += n
|
||||
|
||||
def nts(self, n): # n bytes, drop one trailing NUL if present
|
||||
b = self.raw(n)
|
||||
if b and bytearray(b)[-1:] == bytearray(b"\x00"):
|
||||
b = b[:-1]
|
||||
return b
|
||||
|
||||
def _decode_text(raw):
|
||||
try:
|
||||
return raw.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
return raw # non-UTF-8 -> keep bytes (sqlmap hex-encodes), not lossy
|
||||
|
||||
def _decode_value(reader, col_type, size):
|
||||
if col_type in _STRING_TYPES:
|
||||
return _decode_text(reader.nts(size))
|
||||
if col_type in (_T_BIT, _T_VARBIT):
|
||||
return reader.raw(size)
|
||||
if col_type == _T_NUMERIC:
|
||||
return _decode_text(reader.nts(size)) # DECIMAL arrives as ASCII text
|
||||
if col_type == _T_INT:
|
||||
return str(reader.int())
|
||||
if col_type == _T_SHORT:
|
||||
return str(reader.short())
|
||||
if col_type == _T_BIGINT:
|
||||
return str(reader.long())
|
||||
if col_type == _T_FLOAT:
|
||||
return repr(reader.float())
|
||||
if col_type in (_T_DOUBLE, _T_MONETARY):
|
||||
return repr(reader.double())
|
||||
if col_type == _T_DATE:
|
||||
y, mo, d = reader.short(), reader.short(), reader.short()
|
||||
return "%s" % datetime.date(y, mo, d)
|
||||
if col_type == _T_TIME:
|
||||
h, mi, s = reader.short(), reader.short(), reader.short()
|
||||
return "%s" % datetime.time(h, mi, s)
|
||||
if col_type == _T_TIMESTAMP:
|
||||
vals = [reader.short() for _ in range(6)]
|
||||
return "%s" % datetime.datetime(*vals)
|
||||
if col_type == _T_DATETIME:
|
||||
y, mo, d, h, mi, s, ms = (reader.short() for _ in range(7))
|
||||
return "%s" % datetime.datetime(y, mo, d, h, mi, s, ms * 1000)
|
||||
if col_type == _T_OBJECT:
|
||||
page, slot, vol = reader.int(), reader.short(), reader.short()
|
||||
return "OID:@%d|%d|%d" % (page, slot, vol)
|
||||
return reader.raw(size) # BLOB/CLOB locator or unknown type -> raw bytes
|
||||
|
||||
class _Column(object):
|
||||
__slots__ = ("name", "type", "scale", "precision")
|
||||
|
||||
class Cursor(object):
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
self.description = None
|
||||
self.rowcount = -1
|
||||
self._rows = []
|
||||
self._pos = 0
|
||||
|
||||
def execute(self, query, params=None):
|
||||
if params is not None:
|
||||
raise NotSupportedError("parameter binding is not supported; pass a fully-formed query string")
|
||||
self.description, self.rowcount, self._rows, self._pos = None, -1, [], 0
|
||||
self.description, self._rows, self.rowcount = self.connection._query(query)
|
||||
return self
|
||||
|
||||
def fetchall(self):
|
||||
retVal = self._rows[self._pos:]
|
||||
self._pos = len(self._rows)
|
||||
return retVal
|
||||
|
||||
def fetchone(self):
|
||||
if self._pos >= len(self._rows):
|
||||
return None
|
||||
retVal = self._rows[self._pos]
|
||||
self._pos += 1
|
||||
return retVal
|
||||
|
||||
def close(self):
|
||||
self._rows = []
|
||||
|
||||
class Connection(object):
|
||||
def __init__(self, host, port, user, password, database, timeout):
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._user = user
|
||||
self._password = password
|
||||
self._database = database
|
||||
self._timeout = timeout
|
||||
self._sock = None
|
||||
self._cas_info = b"\x00\x00\x00\x00"
|
||||
self._protocol_version = 8
|
||||
self._open()
|
||||
|
||||
def cursor(self):
|
||||
return Cursor(self)
|
||||
|
||||
def commit(self):
|
||||
self._call(_Writer(_FC_END_TRAN).arg_byte(_TRAN_COMMIT))
|
||||
|
||||
def rollback(self):
|
||||
self._call(_Writer(_FC_END_TRAN).arg_byte(_TRAN_ROLLBACK))
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
if self._sock is not None:
|
||||
self._send(_Writer(_FC_CON_CLOSE).payload())
|
||||
except Exception:
|
||||
pass
|
||||
self._safe_close()
|
||||
|
||||
# ---- connection / framing ----
|
||||
|
||||
def _safe_close(self):
|
||||
try:
|
||||
if self._sock is not None:
|
||||
self._sock.close()
|
||||
except Exception:
|
||||
pass
|
||||
self._sock = None
|
||||
|
||||
def _recvn(self, n):
|
||||
buf = b""
|
||||
while len(buf) < n:
|
||||
chunk = self._sock.recv(n - len(buf))
|
||||
if not chunk:
|
||||
raise InterfaceError("connection closed by server")
|
||||
buf += chunk
|
||||
return buf
|
||||
|
||||
def _open(self):
|
||||
# broker handshake (may redirect to a dedicated CAS worker port), then cleartext OPEN_DATABASE login
|
||||
try:
|
||||
sock = socket.create_connection((self._host, self._port), timeout=self._timeout)
|
||||
sock.settimeout(None)
|
||||
sock.sendall(_MAGIC + struct.pack(">BB", _CLIENT_JDBC, _CAS_VERSION) + b"\x00\x00\x00")
|
||||
self._sock = sock
|
||||
(port,) = struct.unpack(">i", self._recvn(4))
|
||||
if port < 0:
|
||||
raise OperationalError("CUBRID broker rejected the connection (status %d)" % port)
|
||||
if port > 0: # redirected to a CAS worker: reconnect there, no second handshake
|
||||
self._safe_close()
|
||||
sock = socket.create_connection((self._host, port), timeout=self._timeout)
|
||||
sock.settimeout(None)
|
||||
self._sock = sock
|
||||
except (socket.error, socket.timeout) as ex:
|
||||
self._safe_close()
|
||||
raise OperationalError("could not connect to '%s:%s' (%s)" % (self._host, self._port, ex))
|
||||
|
||||
login = self._fixed(self._database, 32) + self._fixed(self._user, 32) + self._fixed(self._password, 32)
|
||||
login += b"\x00" * 532 # 512 extended-info + 20 reserved
|
||||
self._sock.sendall(login)
|
||||
reader = self._read_response()
|
||||
reader.int() # response_code (>=0; errors already raised in _read_response)
|
||||
broker = reader.raw(8)
|
||||
self._protocol_version = bytearray(broker)[4] & 0x3f
|
||||
# enable auto-commit so each statement is independent (avoids the CAS keep-connection handshake dance)
|
||||
self._call(_Writer(_FC_SET_DB_PARAMETER).arg_int(_PARAM_AUTO_COMMIT).arg_int(1))
|
||||
|
||||
@staticmethod
|
||||
def _fixed(value, length):
|
||||
b = (value or "").encode("utf-8")[:length]
|
||||
return b + b"\x00" * (length - len(b))
|
||||
|
||||
def _send(self, payload):
|
||||
# frame: [payload_len(4)][cas_info(4)][payload]
|
||||
self._sock.sendall(struct.pack(">i", len(payload)) + self._cas_info + payload)
|
||||
|
||||
def _read_response(self):
|
||||
(data_length,) = struct.unpack(">i", self._recvn(4))
|
||||
if data_length < 0 or data_length > _MAX_MESSAGE_LENGTH:
|
||||
raise InterfaceError("invalid CAS response length (%d)" % data_length)
|
||||
body = self._recvn(data_length + 4) # cas_info(4) + payload(data_length)
|
||||
self._cas_info = body[:4]
|
||||
reader = _Reader(body[4:])
|
||||
peek = struct.unpack_from(">i", body, 4)[0]
|
||||
if peek < 0: # error response: response_code(<0), errno, message
|
||||
reader.int()
|
||||
errno = reader.int()
|
||||
message = _decode_text(reader.nts(reader.remaining()))
|
||||
if not isinstance(message, str):
|
||||
message = "errno %d" % errno
|
||||
self._raise(errno, "(remote) %s" % message.strip())
|
||||
return reader
|
||||
|
||||
def _call(self, writer):
|
||||
# reconnect transparently if the CAS worker was released after a previous auto-committed statement
|
||||
if self._sock is None or bytearray(self._cas_info)[0] == 0:
|
||||
self._open()
|
||||
try:
|
||||
self._send(writer.payload() if isinstance(writer, _Writer) else writer)
|
||||
return self._read_response()
|
||||
except (struct.error, IndexError, ValueError) as ex:
|
||||
raise InterfaceError("malformed server response: %s" % ex)
|
||||
|
||||
@staticmethod
|
||||
def _raise(errno, message):
|
||||
text = message.lower()
|
||||
if any(k in text for k in ("unique", "duplicate", "foreign key", "constraint violat")):
|
||||
raise IntegrityError(message)
|
||||
if any(k in text for k in ("syntax", "unknown class", "does not exist", "not found", "before ' '")):
|
||||
raise ProgrammingError(message)
|
||||
if any(k in text for k in ("cast", "conversion", "overflow", "truncat")):
|
||||
raise DataError(message)
|
||||
raise ProgrammingError(message)
|
||||
|
||||
# ---- query ----
|
||||
|
||||
def _query(self, query):
|
||||
reader = self._call(_Writer(_FC_PREPARE).arg_nts(query).arg_byte(0).arg_byte(0))
|
||||
handle = reader.int()
|
||||
reader.int() # result cache lifetime
|
||||
stmt_type = reader.byte()
|
||||
reader.int() # bind count
|
||||
reader.byte() # is_updatable
|
||||
columns = self._parse_columns(reader, reader.int())
|
||||
|
||||
exec_writer = (_Writer(_FC_EXECUTE).arg_int(handle).arg_byte(0).arg_int(0).arg_int(0)
|
||||
.arg_null().arg_byte(1 if stmt_type == _STMT_SELECT else 0)
|
||||
.arg_byte(0).arg_byte(1).arg_cache_time().arg_int(0))
|
||||
reader = self._call(exec_writer)
|
||||
|
||||
total = reader.int()
|
||||
reader.byte() # cache reusable
|
||||
result_count = reader.int()
|
||||
result_infos = [self._parse_result_info(reader) for _ in range(result_count)]
|
||||
if self._protocol_version > 1:
|
||||
reader.byte() # includes_column_info
|
||||
if self._protocol_version > 4:
|
||||
reader.int() # shard_id
|
||||
|
||||
description, rows, rowcount = None, [], -1
|
||||
if stmt_type == _STMT_SELECT and columns:
|
||||
description = [(c.name, c.type, None, None, c.precision, c.scale, None) for c in columns]
|
||||
if reader.remaining() >= 8:
|
||||
reader.int() # fetch code
|
||||
tuple_count = reader.int()
|
||||
rows = self._parse_rows(reader, tuple_count, columns)
|
||||
rows += self._fetch_remaining(handle, columns, len(rows), total)
|
||||
elif result_infos:
|
||||
rowcount = result_infos[0]
|
||||
self._call(_Writer(_FC_CLOSE_REQ_HANDLE).arg_int(handle))
|
||||
return description, rows, rowcount
|
||||
|
||||
def _fetch_remaining(self, handle, columns, fetched, total):
|
||||
rows = []
|
||||
while fetched + len(rows) < total:
|
||||
reader = self._call(_Writer(_FC_FETCH).arg_int(handle)
|
||||
.arg_int(fetched + len(rows) + 1).arg_int(100).arg_byte(0).arg_int(0))
|
||||
reader.int() # response code (>=0)
|
||||
tuple_count = reader.int()
|
||||
if tuple_count <= 0:
|
||||
break
|
||||
rows += self._parse_rows(reader, tuple_count, columns)
|
||||
return rows
|
||||
|
||||
def _parse_result_info(self, reader):
|
||||
reader.byte() # stmt type
|
||||
count = reader.int() # affected rows
|
||||
reader.raw(_OID_SIZE)
|
||||
reader.int(); reader.int() # cache time sec/usec
|
||||
return count
|
||||
|
||||
def _parse_columns(self, reader, count):
|
||||
columns = []
|
||||
for _ in range(count):
|
||||
col = _Column()
|
||||
legacy = reader.byte()
|
||||
col.type = reader.byte() if legacy & 0x80 else legacy
|
||||
col.scale = reader.short()
|
||||
col.precision = reader.int()
|
||||
col.name = _to_str(reader.nts(reader.int()))
|
||||
reader.nts(reader.int()) # real name
|
||||
reader.nts(reader.int()) # table name
|
||||
reader.byte() # is_nullable
|
||||
reader.nts(reader.int()) # default value
|
||||
reader.skip(7) # auto_inc/unique/primary/rev_index/rev_unique/foreign/shared
|
||||
columns.append(col)
|
||||
return columns
|
||||
|
||||
def _parse_rows(self, reader, tuple_count, columns):
|
||||
rows = []
|
||||
for _ in range(tuple_count):
|
||||
reader.int() # row index
|
||||
reader.skip(_OID_SIZE)
|
||||
row = []
|
||||
for col in columns:
|
||||
size = reader.int()
|
||||
if size <= 0:
|
||||
row.append(None)
|
||||
else:
|
||||
row.append(_decode_value(reader, col.type, size))
|
||||
rows.append(tuple(row))
|
||||
return rows
|
||||
|
||||
def _to_str(b):
|
||||
v = _decode_text(b)
|
||||
return v if isinstance(v, str) else v.decode("latin-1")
|
||||
|
||||
def connect(host=None, port=33000, user=None, password=None, database=None, connect_timeout=None, **kwargs):
|
||||
try:
|
||||
return Connection(host or "localhost", int(port or 33000), user or "public",
|
||||
password or "", database or "", connect_timeout)
|
||||
except (DatabaseError, InterfaceError):
|
||||
raise
|
||||
except Exception as ex:
|
||||
raise OperationalError("CUBRID connection failed (%s)" % ex)
|
||||
|
|
@ -272,6 +272,7 @@ DBWIRE_MODULES = {
|
|||
DBMS.PRESTO: "presto",
|
||||
DBMS.FIREBIRD: "firebird",
|
||||
DBMS.VERTICA: "postgres", # Vertica speaks a PostgreSQL-v3-derived wire protocol (trust/cleartext/md5 auth)
|
||||
DBMS.CUBRID: "cubrid",
|
||||
}
|
||||
|
||||
# Reference: https://blog.jooq.org/tag/sysibm-sysdummy1/
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ from lib.core.enums import OS
|
|||
from thirdparty import six
|
||||
|
||||
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
||||
VERSION = "1.10.7.90"
|
||||
VERSION = "1.10.7.91"
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class Connector(GenericConnector):
|
|||
# CUBRIDdb.connect takes a positional URL 'CUBRID:host:port:db:::' then user/password positionally
|
||||
# (it does not accept hostname/username/database keyword args, which raised a TypeError before)
|
||||
self.connector = CUBRIDdb.connect("CUBRID:%s:%s:%s:::" % (self.hostname, self.port, self.db), str(self.user), str(self.password))
|
||||
except Exception as ex:
|
||||
except CUBRIDdb.Error as ex:
|
||||
raise SqlmapConnectionException(getSafeExString(ex))
|
||||
|
||||
self.initCursor()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue