I perceive the world: response.sendRedirect(String location)

Documentation:

So, documentation has a convention about first forward slash in URL, but real behaviour should depend on application server implementation, let’s check different application servers.

-bash-4.1$ cat r.jsp
<%
    response.sendRedirect(request.getParameter("r"));
%>

Weblogic:

-bash-4.1$ curl -I http://192.168.2.52:7002/da/r.jsp?r=http://moviepoopshoot.com
HTTP/1.1 302 Moved Temporarily
Connection: close
Date: Wed, 26 Nov 2014 03:26:36 GMT
Transfer-Encoding: chunked
Location: http://moviepoopshoot.com
Content-Type: text/html; charset=ISO-8859-1
X-Powered-By: Servlet/2.5 JSP/2.1

-bash-4.1$ curl -I http://192.168.2.52:7002/da/r.jsp?r=https://moviepoopshoot.com
HTTP/1.1 302 Moved Temporarily
Connection: close
Date: Wed, 26 Nov 2014 03:26:44 GMT
Transfer-Encoding: chunked
Location: https://moviepoopshoot.com
Content-Type: text/html; charset=ISO-8859-1
X-Powered-By: Servlet/2.5 JSP/2.1

-bash-4.1$ curl -I http://192.168.2.52:7002/da/r.jsp?r=ftp://moviepoopshoot.com
HTTP/1.1 302 Moved Temporarily
Connection: close
Date: Wed, 26 Nov 2014 03:27:14 GMT
Transfer-Encoding: chunked
Location: ftp://moviepoopshoot.com
Content-Type: text/html; charset=ISO-8859-1
X-Powered-By: Servlet/2.5 JSP/2.1

-bash-4.1$ curl -I http://192.168.2.52:7002/da/r.jsp?r=data://moviepoopshoot.com
HTTP/1.1 302 Moved Temporarily
Connection: close
Date: Wed, 26 Nov 2014 03:27:40 GMT
Transfer-Encoding: chunked
Location: data://moviepoopshoot.com
Content-Type: text/html; charset=ISO-8859-1
X-Powered-By: Servlet/2.5 JSP/2.1

-bash-4.1$ curl -I http://192.168.2.52:7002/da/r.jsp?r=data:moviepoopshoot.com
HTTP/1.1 302 Moved Temporarily
Date: Wed, 26 Nov 2014 03:29:25 GMT
Transfer-Encoding: chunked
Location: http://192.168.2.52:7002/da/data:moviepoopshoot.com
Content-Type: text/html; charset=ISO-8859-1
X-Powered-By: Servlet/2.5 JSP/2.1

-bash-4.1$ curl -I http://192.168.2.52:7002/da/r.jsp?r=//moviepoopshoot.com
HTTP/1.1 302 Moved Temporarily
Date: Wed, 26 Nov 2014 03:28:30 GMT
Transfer-Encoding: chunked
Location: http://192.168.2.52:7002//moviepoopshoot.com
Content-Type: text/html; charset=ISO-8859-1
X-Powered-By: Servlet/2.5 JSP/2.1

Tomcat:

-bash-4.1$ curl -I http://192.168.2.56:8280/webtop/r.jsp?r=http://moviepoopshoot.com
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: http://moviepoopshoot.com
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 0
Date: Wed, 26 Nov 2014 03:30:49 GMT

-bash-4.1$ curl -I http://192.168.2.56:8280/webtop/r.jsp?r=https://moviepoopshoot.com
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: https://moviepoopshoot.com
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 0
Date: Wed, 26 Nov 2014 03:31:04 GMT

-bash-4.1$ curl -I http://192.168.2.56:8280/webtop/r.jsp?r=ftp://moviepoopshoot.com
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: ftp://moviepoopshoot.com
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 0
Date: Wed, 26 Nov 2014 03:31:24 GMT

-bash-4.1$ curl -I http://192.168.2.56:8280/webtop/r.jsp?r=data://moviepoopshoot.com
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: data://moviepoopshoot.com
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 0
Date: Wed, 26 Nov 2014 03:31:31 GMT

-bash-4.1$ curl -I http://192.168.2.56:8280/webtop/r.jsp?r=data:moviepoopshoot.com
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: data:moviepoopshoot.com
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 0
Date: Wed, 26 Nov 2014 03:31:40 GMT

-bash-4.1$ curl -I http://192.168.2.56:8280/webtop/r.jsp?r=blablabla:moviepoopshoot.com
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: blablabla:moviepoopshoot.com
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 0
Date: Wed, 26 Nov 2014 03:32:00 GMT

-bash-4.1$ curl -I http://192.168.2.56:8280/webtop/r.jsp?r=//moviepoopshoot.com
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: http://moviepoopshoot.com
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 0
Date: Wed, 26 Nov 2014 03:32:20 GMT

So, different application servers treat url parameter differently: Tomcat has a special treatment for colon and tho leading slash characters, and I do believe that this behavior conforms to RFC 3986

How does this information relate to Documentum?

Eleven months ago I discovered XSRF a vulnerability in virtuallinkconnect component (now this component is trusted), the vulnerability was tracked as: WEBTOP-28391 (Security vulnerability – user credentials are submitted to the external website), and the remedy from EMC contains a check for “://” sequence in value of redirectUrl parameter 🙂

Maximum length of DQL statement

A week ago my colleague had got me stumped by asking following question: “What is the maximum length of DQL statement?”

Official documentation, as expected, does say nothing about such restrictions, so I had tried to make a small experiment:

from dctmpy.docbaseclient import DocbaseClient

def main():
    part1 = "SELECT r_object_id FROM dm_sysobject WHERE 1=1 "
    part2 = " AND 1=1" * 10000
    part3 = " ENABLE(RETURN_TOP 1)"
    session = DocbaseClient(host="192.168.2.52", port=10000,
                            username="test01", password="test01")
    for q in session.query("%s" % (part1 + part2 + part3)):
        print q['r_object_id']

if __name__ == '__main__':
    main()

but got a weird error:

error(10054, 'An existing connection was forcibly closed by the remote host')

Hmm, it seems that Content Server forcibly closes connection if receives too long query. Ok, let use a “binary” search algorithm to find the maximum allowed length of DQL statement:

from dctmpy.docbaseclient import DocbaseClient


def main():
    part1 = "SELECT r_object_id FROM dm_sysobject WHERE 1=1 "
    part2 = " AND 1=1"
    part3 = " ENABLE(RETURN_TOP 1)"
    i = 20
    s = 0
    args = ""
    while i >= 0:
        args = part1 + part2 * (s + 2 ** i) + part3
        print "Trying query with length=%d" % len(args)
        try:
            session = DocbaseClient(host="192.168.2.52", port=10000,
                                    username="test01", password="test01")
            for q in session.query("%s" % args):
                pass
        except Exception, e:
            i -= 1
            continue
        s += 2 ** i
        i -= 1
    print "Maximum query length: %d" % len(args)


if __name__ == '__main__':
    main()

Result:

Trying query with length=8388676
Trying query with length=4194372
Trying query with length=2097220
Trying query with length=1048644
Trying query with length=524356
Trying query with length=262212
Trying query with length=131140
Trying query with length=65604
Trying query with length=32836
Trying query with length=49220
Trying query with length=57412
Trying query with length=61508
Trying query with length=63556
Trying query with length=64580
Trying query with length=64068
Trying query with length=63812
Trying query with length=63940
Trying query with length=63876
Trying query with length=63844
Trying query with length=63828
Trying query with length=63820
Maximum query length: 63820

Now the resulting statement in DQL Editor:

Hmm, SET_PUSH_OBJECT_STATUS is a RPC-command, so, this error means that in case of long queries DFC sends extra RPCs and my home-grown implementation of Documentum protocol differs from EMC’s one. After some research I have found that DFC sends long RPCs using following way:

  • apply,c,,SET_PUSH_OBJECT_STATUS,_PUSHED_ID_,ID,objectId,_PUSH_STATUS_,B,T
  • apply,c,objectId,RPC_NAME,CHUNK1
  • apply,c,objectId,RPC_NAME,CHUNK2
  • apply,c,objectId,RPC_NAME,CHUNKN
  • apply,c,objectId,RPC_NAME,_USE_SESSION_CHUNKED_OBJ_STRING_
  • apply,c,,SET_PUSH_OBJECT_STATUS,_PUSHED_ID_,ID,objectId,_PUSH_STATUS_,B,F

and in case of EXEC RPC-command DFC sends 0000000000000000 as objectId, so, SET_PUSH_OBJECT_STATUS RPC-command raises a DM_SESSION_E_NON_EXIST_OBJ error. After some meditation over python code I decided that it would be a good idea to send a session identifier as objectId:

And now my implementation is able to execute much longer DQL statements:

from dctmpy.docbaseclient import DocbaseClient


def main():
    part1 = "SELECT r_object_id FROM dm_sysobject WHERE 1=1 "
    part2 = " AND 1=1" * 20000
    part3 = " ENABLE(RETURN_TOP 1)"
    print "Query length: %d" % (len(part1 + part2 + part3))
    session = DocbaseClient(host="192.168.2.52", port=10000,
                            username="test01", password="test01")
    for q in session.query("%s" % (part1 + part2 + part3)):
        print "r_object_id: %s" % q['r_object_id']


if __name__ == '__main__':
    main()

result:

Query length: 160068
r_object_id: 3a01fd0880000153

Now it’s obvious that DFC has a bug that does not allow to run DQL queries with length exceeding ~63000 bytes, unfortunately, Content Server has a so slow DQL parser, that it’s practically pointless to execute queries with length exceeding 128K:

from time import time

from dctmpy.docbaseclient import DocbaseClient


def main():
    part1 = "SELECT r_object_id FROM dm_sysobject WHERE 1=1 "
    part2 = " AND 1=1"
    part3 = " ENABLE(RETURN_TOP 1)"
    i = 0
    session = DocbaseClient(host="192.168.2.52", port=10000,
                            username="test01", password="test01")
    while True:
        i += 1
        args = part1 + part2 * (2 ** i) + part3
        t = time()
        for q in session.query("%s" % args):
            pass
        print "%d\t%d" % (len(args), time() - t)


if __name__ == '__main__':
    main()

Webtop 6.8 EAP available

ftp://ftp.documentum.com/Hotfixes/KnowledgeWorker/Webtop/6.8_RC/

Main changes:

  • EMC added a paragraph about dmc_wdk_presets_owner and dmc_wdk_preferences_owner accounts in WDK deployment guide (poor xcp and d2 customers – they will never know about these dangerous accounts)
  • Bundled JRE 1.7u51 – weird choice: 1.7u51 is not supported in previous webtop releases
  • EMC gave up an idea to audit webtop code for potential vulnerabilities: previous webtop fixes denied direct access (i.e. through specific url) to certain components:

    -bash-4.1$ grep -r 'url-addressable-disabled' *| \
    > grep '.xml' |sed -e 's/:.*//'|sort -u|xargs -n1 basename
    actiondispatcher_component.xml
    dm_query_actions.xml
    httpcancelcheckoutcontainer_component.xml
    savedocument_component.xml
    unassignqueuedtask_component.xml
    addvirtualdocumentnode_component.xml
    addvirtualdocumentnodefromclipboard_component.xml
    removevirtualdocumentnodecontainer_component.xml
    taskcomponentcontainer_component.xml
    search60_component.xml
    

    now only certain components are allowed for direct access:

    -bash-4.1$ grep -r 'url-addressable-enabled' *| \
    > grep '.xml' |sed -e 's/:.*//'|sort -u|xargs -n1 basename
    drl_component.xml
    errormessage_component.xml
    getcontent_component.xml
    httpmultifiledownload_component.xml
    residentucfinvoker_component.xml
    virtuallinkconnect_component.xml
    api_component.xml
    dql_component.xml
    viewcontainer_component.xml
    dqleditor_component.xml
    getmessagearchiveattachment_component.xml
    imagepicker_component.xml
    insertlink_component.xml
    spellchecker_component.xml
    searchmonitoring_component.xml
    vdmlist_component.xml
    about_component.xml
    loginex_component.xml
    logoff_component.xml
    mainex_component.xml
    messagebar_component.xml
    titlebar_component.xml
    

    Unfortunately, webtop 6.8 is still vulnerable to XSRF attacks.

Здравствуй, зимнее время! Часть II

Оказывается, у прошлой истории есть продолжение.

Со слов заказчика проблема проявляется следующим образом:

Выявилась проблема со стандартным элементом управления задания даты.
Например, такой контрол используется в DA для задания параметров Job.

Описание проблемы:

Если открыть контрол на дате позднее февраля 2014 (25.02.2014) и попробовать стрелками перейти на более раннюю дату, то январь 2014 отображаться не будет.
Если открыть контрол на дате раньше декабря 2013 (12.11.2013) и попробовать стрелками перейти на более позднюю дату, то декабрь 2013 года отобразиться дважды, и дальше продвинутся уже будет нельзя.

Гугл, конечно, великий помощник, главное знать, что искать, и я знал. Наиболее адекватное обсуждение по теме: Хром, укравший рождество

Патч для WDK:

--- wdk/include/popupCalendar.js.orig	Thu Jan 31 01:54:20 2013
+++ wdk/include/popupCalendar.js	Sat Nov  8 00:17:33 2014
@@ -48,11 +48,11 @@
 pcPopupDIV.m_inputDate = getInputDate();
 if (pcPopupDIV.m_inputDate == null)
 {
-pcPopupDIV.m_currentMonth = new Date(getYear(pcPopupDIV.m_todaysDate), pcPopupDIV.m_todaysDate.getMonth(), 1);
+pcPopupDIV.m_currentMonth = new Date(getYear(pcPopupDIV.m_todaysDate), pcPopupDIV.m_todaysDate.getMonth(), 1, 12);
 }
 else
 {
-pcPopupDIV.m_currentMonth = new Date(getYear(pcPopupDIV.m_inputDate), pcPopupDIV.m_inputDate.getMonth(), 1);
+pcPopupDIV.m_currentMonth = new Date(getYear(pcPopupDIV.m_inputDate), pcPopupDIV.m_inputDate.getMonth(), 1, 12);
 }
 pcPopupDIV.className = CLASS_CAL;
 pcPopupDIV.onmouseover = onMouseOver;
@@ -134,7 +134,7 @@
 month = 11;
 year -= 1;
 }
-pcPopupDIV.m_currentMonth = new Date(year, month, 1);
+pcPopupDIV.m_currentMonth = new Date(year, month, 1, 12);
 renderCalendar();
 if(g_clientInfo.isPlatform(ClientInfo.MACOS) && g_clientInfo.isBrowser(ClientInfo.MSIE))
 return false;
@@ -149,7 +149,7 @@
 month = 0;
 year += 1;
 }
-pcPopupDIV.m_currentMonth = new Date(year, month, 1);
+pcPopupDIV.m_currentMonth = new Date(year, month, 1, 12);
 renderCalendar();
 if(g_clientInfo.isPlatform(ClientInfo.MACOS) && g_clientInfo.isBrowser(ClientInfo.MSIE))
 return false;
@@ -498,7 +498,7 @@
 {
 year += 1900;
 }
-return new Date(year, month - 1, day);
+return new Date(year, month - 1, day, 12);
 }
 return null;
 }
@@ -621,7 +621,7 @@
 function getTodaysDate()
 {
 var now = new Date();
-var today = new Date(getYear(now), now.getMonth(), now.getDate());
+var today = new Date(getYear(now), now.getMonth(), now.getDate(), 12);
 return today;
 }
 function indexOfDigit(strString, indexFrom)

Q&A. I

Q:

Dear Andrey,

Your blog is awesome and interesting things in Documentum. Even though working in documentum for several years, we did not come across these things. Would it be possible to open up the password protected posts? Or at least share the posts with few?

I don’t see any meaning in password protecting the posts which are made public.

Thank you.

A:

Raj,

that information would be useless for you unless you are a security researcher.