Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dependency isolation and/or conflicting versions #10

Open
frohoff opened this issue Feb 24, 2016 · 9 comments
Open

Support dependency isolation and/or conflicting versions #10

frohoff opened this issue Feb 24, 2016 · 9 comments

Comments

@frohoff
Copy link
Owner

frohoff commented Feb 24, 2016

Project should be refactored to allow gadgets/chains to be generated (and unit tested) with only exactly the exact required dependencies and versions, even in cases where two different gadgets/chains require a different version of the same library (See #16). This should also reduce the likelihood of unintended classes or dependencies accidentally leaking into the payloads.

The already included jboss shrinkwrap should suffice for runtime dependency management. Make sure dependencies for gadget chains can still be bundled in the jar somehow.

It is also a goal to keep the project build and code as simple as possible for people to contribute gadgets/chains.

Option 1: Reflection

Write or use a reflection DSL that can be used by payload generation code that can use gadget classes dynamically instead of using statically linked code.

Something like jOOR might be useful for reflection.

Pros: Simple build process
Cons: Convoluted reflection-based payload generation code

Option 2: Maven/Build Voodoo

Split up project into multi-module with aggregator project to generate all-in-one jar. Gadgets/chains can go into an arbitrary number of separate sub-projects according to any dependency version conflicts.

Pros: Simple, statically linked payload generation code
Cons: Convoluted, splintered build process

@frohoff frohoff changed the title generate payloads object graphs with reflection in an isolated classloader Support dependency isolation and/or conflicting versions Mar 4, 2016
@h3xstream
Copy link
Contributor

@frohoff Are you actively working on the option 2?
I would try using classloader isolation to support conflicting librairies.
Also, I would suggest doing a multi-module project with Gradle to keep the needed complexity to the minimum.

@frohoff
Copy link
Owner Author

frohoff commented Mar 8, 2016

I haven't been actively working on either but have been weighing the options. @mbechler was giving his thoughts on this over on #20 but perhaps we can move the discussion here.

There's already some limited classloader isolation implemented as part of the unit test harness here that uses JBoss Shrinkwrap to resolve dependencies at runtime:
https://github.com/frohoff/ysoserial/blob/master/src/test/java/ysoserial/payloads/PayloadsTest.java#L91

I don't have much experience with Gradle. Presuming we went with Option 2, would Gradle provide advantages over Maven's multi-module and aggregator support?

In addition to the already discussed concerns/trade-offs, conflicting dependency versions also present a packaging issue since two versions of the same jars/classes can't be simply shaded into the same "all" jar. It seems like this can be addressed by either:

  • packaging each dependency/version inside the "all" jar as an isolated nested jar and/or directory and implementing a custom ClassLoader a la One-Jar
  • don't package dependencies with the application at all and resolve (and download) them all at runtime

@h3xstream
Copy link
Contributor

@frohoff Quick response to the Gradle interrogation. The use of Gradle of would be analog to a Maven project for submodule.

I would push Gradle because it would make build configuration smaller and simpler to understand. The "scripting" capabilities will help greatly handling the custom packaging (lib isolation, etc).

@pwntester
Copy link
Contributor

Not sure, how the maven/gradle option would work, but ideally users should not specify special goals to generate the payloads. The reflection way seems better to me if we can define which jars should be loaded in runtime for a particular gadget, although that may involve heavy use of Reflection to operate the loaded classes

@mbechler
Copy link
Contributor

It looks to me that we are facing three kinds of incompatibilities:

  1. pure serialization - i.e. across different library versions (many of the gadgets don't have a serialization api/version) or even different standardized implementations (like my EL stuff):
    For that case runtime isolation is enough, a nice way to specify and resolve these things at runtime would be great. Bundling is of limited help.
  2. partial binary/source - some class in the gadget chain was slightly modified (maybe renamed) across versions (my hibernate stuff would be an example)
    Starting from here we cannot statically compile against the library using a single compile classpath. But compiling using multiple modules will also result in code duplication (potentially multiple times if there would be more of these changes). Here, I think, using reflection definitely scales better. Bundling could still be useful, although potentially could blow the JAR size out of proportion.
  3. full - i.e. a gadget chain is present in one version, another has another one (looks like the spring aop PR is one of those - but maybe also 2.)
    In that case it could be nice (if you don't have to use reflection anyways) to not have to use reflection (for at least one of them), that could be achieved by a multi module build. But how common will these really be - I wouldn't know. Bundling may be useful (so that each payload has one working bundled variant).

So in conclusion,

  • I don't think that generally splitting up all the payloads into separate modules is worth the effort (and the introduced complexity). There may be cases where this is helpful - so maybe it could be done optionally (like having a base module and packaging others as embedded jars). Using reflection, even without any additonal support, isn't really that hurtful.
  • I think it would be a good idea to remove the embedded gadget dependencies and resolve them at runtime. The resulting jar is already getting quite big now (without multiple versions of the same library) and there are cases where one has to match a specific version anyways.
  • Having that runtime classpath isolation is a must in any case, so that would be a good way to start.

@frohoff
Copy link
Owner Author

frohoff commented Apr 11, 2016

Another option to consider might be implementing gadget chains in Groovy, potentially using Grape for dependency management.

@Marcono1234
Copy link

Marcono1234 commented Jan 12, 2022

Would another alternative be to create the serialization data manually instead of using ObjectOutputStream? This could have the following advantages:

  • It does not require the dependencies on the class path.
  • It does not require reflection to access internals of classes.
    This will probably become even more important over time, especially for JDK classes, since it will be necessary to provide command line flags to allow illegal reflective access, see for example JEP 403 which prevents illegal reflective access on JDK classes by default for Java 17 and newer (Ysoserial doesn't work with Java version 17  #203).
  • It would allow creating serialization data for JDK classes without requiring a specific Java version at runtime.
  • In some corner cases it might allow creating serialization data which cannot be achieved by only using reflection (e.g. because a writeObject implementation validates or normalizes field data)

The main disadvantage is that manually creating the serialization data could be more error-prone and verbose. One approach could be to obtain the current serialization data produced by ysoserial, pass it to a tool such as SerializationDumper and then based on its output manually use a library for creating the serialization data1. It looks also like ObjectInputStream still works when 'redundant' data (at least from the standpoint of ysoserial), such as superclass data or unused fields, are omitted. This would allow creating smaller payloads and would make the manual creation of serialization data not as verbose.


1 A bit of self advertisement, but I recently created a library called 'serial-builder' for creating serialization data. Though it is not unlikely that there are better or less verbose libraries out there.

Edit: I added code generation support to my 'serial-builder' library so it should hopefully be a lot easier to use and to get started with, in case there is interest in using it here. Any feedback is appreciated!

@mbechler
Copy link
Contributor

@Marcono1234 I also implemented a custom serializer a while ago https://github.com/mb-syss/ruby-serialize, unfortunately in ruby (as the original intention was to contribute this to metasploit, which did not work out) that also already contains property based defintions for most of the ysoserial payloads. Maybe that is of some help in case you want to go further.

@Marcono1234
Copy link

Marcono1234 commented Dec 23, 2023

For testing Gradle supports Test Suites which can have different dependencies. So this would allow to have multiple test suites with different versions of the same dependency.

But I am not sure if that would provide a big advantage compared to the Maven multi-module approach described in the comments above, since the test classes might also differ between the modules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants