Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2022-4722: Make username case-insensitive · ikus060/rdiffweb@d1aaa96

Authentication Bypass by Primary Weakness in GitHub repository ikus060/rdiffweb prior to 2.5.5.

CVE
#sql#web#git#auth#ssh

@@ -15,74 +15,91 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/.
import logging import sys
import cherrypy from sqlalchemy import event from sqlalchemy.exc import IntegrityError
from ._repo import RepoObject # noqa from ._session import DbSession, SessionObject # noqa from ._sshkey import SshKey # noqa from ._token import Token # noqa from ._user import DuplicateSSHKeyError, UserObject # noqa from ._user import DuplicateSSHKeyError, UserObject, user_username_index # noqa
Base = cherrypy.tools.db.get_base()
logger = logging.getLogger(__name__)

def _column_add(connection, column): if _column_exists(connection, column): return table_name = column.table.fullname column_name = column.name column_type = column.type.compile(connection.engine.dialect) connection.engine.execute(‘ALTER TABLE %s ADD COLUMN %s %s’ % (table_name, column_name, column_type))

def _column_exists(connection, column): table_name = column.table.fullname column_name = column.name if ‘SQLite’ in connection.engine.dialect.__class__.__name__: sql = "SELECT COUNT(*) FROM pragma_table_info(‘%s’) WHERE LOWER(name)=LOWER(‘%s’)" % ( table_name, column_name, ) else: sql = “SELECT COUNT(*) FROM information_schema.columns WHERE table_name=’%s’ and column_name=’%s’” % ( table_name, column_name, ) data = connection.engine.execute(sql).first() return data[0] >= 1

def _index_exists(connection, index_name): if ‘SQLite’ in connection.engine.dialect.__class__.__name__: sql = “SELECT name FROM sqlite_master WHERE type = ‘index’ AND name = '%s’;” % (index_name) else: sql = “SELECT * FROM pg_indexes WHERE indexname = '%s’” % (index_name) return connection.engine.execute(sql).first() is not None

@event.listens_for(Base.metadata, ‘after_create’) def db_after_create(target, connection, **kw): “"” Called on database creation to update database schema. “"”
def exists(column): table_name = column.table.fullname column_name = column.name if ‘SQLite’ in connection.engine.dialect.__class__.__name__: sql = "SELECT COUNT(*) FROM pragma_table_info(‘%s’) WHERE LOWER(name)=LOWER(‘%s’)" % ( table_name, column_name, ) else: sql = “SELECT COUNT(*) FROM information_schema.columns WHERE table_name=’%s’ and column_name=’%s’” % ( table_name, column_name, ) data = connection.engine.execute(sql).first() return data[0] >= 1
def add_column(column): if exists(column): return table_name = column.table.fullname column_name = column.name column_type = column.type.compile(connection.engine.dialect) connection.engine.execute(‘ALTER TABLE %s ADD COLUMN %s %s’ % (table_name, column_name, column_type))
if getattr(connection, '_transaction’, None): connection._transaction.commit()
# Add repo’s Encoding add_column(RepoObject.__table__.c.Encoding) add_column(RepoObject.__table__.c.keepdays) _column_add(connection, RepoObject.__table__.c.Encoding) _column_add(connection, RepoObject.__table__.c.keepdays)
# Create column for roles using “isadmin” column. Keep the # original column in case we need to revert to previous version. if not exists(UserObject.__table__.c.role): add_column(UserObject.__table__.c.role) if not _column_exists(connection, UserObject.__table__.c.role): _column_add(connection, UserObject.__table__.c.role) UserObject.query.filter(UserObject._is_admin == 1).update({UserObject.role: UserObject.ADMIN_ROLE})
# Add user’s fullname column add_column(UserObject.__table__.c.fullname) _column_add(connection, UserObject.__table__.c.fullname)
# Add user’s mfa column add_column(UserObject.__table__.c.mfa) _column_add(connection, UserObject.__table__.c.mfa)
# Re-create session table if Number column is missing if not exists(SessionObject.__table__.c.Number): if not _column_exists(connection, SessionObject.__table__.c.Number): SessionObject.__table__.drop() SessionObject.__table__.create()
if getattr(connection, '_transaction’, None): connection._transaction.commit()
# Remove preceding and leading slash (/) generated by previous # versions. Also rename ‘.’ to ‘’ result = RepoObject.query.all() @@ -101,3 +118,22 @@ def add_column(column): row.delete() else: prev_repo = (row.userid, row.repopath)
# Fix username case insensitive unique if not _index_exists(connection, ‘user_username_index’): duplicate_users = ( UserObject.query.with_entities(func.lower(UserObject.username)) .group_by(func.lower(UserObject.username)) .having(func.count(UserObject.username) > 1) ).all() try: user_username_index.create() except IntegrityError: msg = ( 'Failure to upgrade your database to make Username case insensitive. ' 'You must downgrade and deleted duplicate Username. ' ‘%s’ % '\n’.join([str(k) for k in duplicate_users]), ) logger.error(msg) print(msg, file=sys.stderr) raise SystemExit(12)

Related news

GHSA-wf33-6x33-wcf9: rdiffweb vulnerable to Authentication Bypass by Primary Weakness

In rdiffweb prior to 2.5.5, the username field is not unique to users. This allows exploitation of primary key logic by creating the same name with different combinations & may allow unauthorized access.

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda