FasterXML / jackson-databind

General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s)
Apache License 2.0
3.52k stars 1.38k forks source link

Stack overflow error caused by serialization of `Map` with cyclic dependency -- NOT CVE #3972

Closed PoppingSnack closed 1 year ago

PoppingSnack commented 1 year ago

Stack overflow error caused by jackson serialization Map

Description

jackson before v2.15.2 was discovered to contain a stack overflow via the map parameter.

Error Log

Exception in thread "main" java.lang.StackOverflowError
    at java.base/java.lang.String.startsWith(String.java:1470)
    at com.fasterxml.jackson.databind.util.ClassUtil.isJDKClass(ClassUtil.java:1163)
    at com.fasterxml.jackson.databind.introspect.BasicClassIntrospector._findStdTypeDesc(BasicClassIntrospector.java:258)
    at com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forSerialization(BasicClassIntrospector.java:80)
    at com.fasterxml.jackson.databind.introspect.BasicClassIntrospector.forSerialization(BasicClassIntrospector.java:11)
    at com.fasterxml.jackson.databind.SerializationConfig.introspect(SerializationConfig.java:906)
    at com.fasterxml.jackson.databind.ser.BasicSerializerFactory.createKeySerializer(BasicSerializerFactory.java:210)
    at com.fasterxml.jackson.databind.SerializerProvider.findKeySerializer(SerializerProvider.java:915)
    at com.fasterxml.jackson.databind.SerializerProvider.findKeySerializer(SerializerProvider.java:926)
    at com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap.findAndAddKeySerializer(PropertySerializerMap.java:144)
    at com.fasterxml.jackson.databind.ser.std.StdKeySerializers$Dynamic._findAndAddDynamic(StdKeySerializers.java:284)
    at com.fasterxml.jackson.databind.ser.std.StdKeySerializers$Dynamic.serialize(StdKeySerializers.java:262)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:797)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:764)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:808)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:764)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:808)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:764)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:808)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:764)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:808)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:764)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:808)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:764)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:808)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:764)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:808)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:764)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:808)

PoC

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
        </dependency>
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;

public class PoC2 {

    public static void main(String[] args) {
        HashMap<String,Object> map=new HashMap<>();
        map.put("t",map);
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String jsonString = objectMapper.writeValueAsString(map);
            System.out.println(jsonString);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

Rectification Solution

  1. Refer to the solution of jackson-databind: Add the depth variable to record the current parsing depth. If the parsing depth exceeds a certain threshold, an exception is thrown. (https://github.com/FasterXML/jackson-databind/commit/fcfc4998ec23f0b1f7f8a9521c2b317b6c25892b)

  2. Refer to the GSON solution: Change the recursive processing on deeply nested arrays or JSON objects to stack+iteration processing.((https://github.com/google/gson/commit/2d01d6a20f39881c692977564c1ea591d9f39027))

References

  1. https://github.com/jettison-json/jettison/issues/52
  2. https://github.com/jettison-json/jettison/pull/53/files
yawkat commented 1 year ago

This happens with any recursive data structure. Jackson will not detect loops in data structures. For example:

    public static void main(String[] args) throws JsonProcessingException {
        A a = new A();
        A b = new A();
        a.next = b;
        b.next = a;
        new ObjectMapper().writeValueAsString(a); // will fail
    }

    static class A {
        A next;

        public A getNext() {
            return next;
        }
    }

imo this is not really an issue. For example, calling hashCode on the same map would also cause a stack overflow, but this is not considered a security issue. You can't construct such a data structure using jackson deserialization. Others may disagree on this though.

BeanSerializer has some error handling to make the error a bit nicer, but it's still a stack overflow with all the issues associated with that.

This vulnerability allows attackers to cause a Denial of Service (DoS) via a crafted string.

Your test case does not demonstrate this, can you please show how this can be exploited via a crafted string?

cowtowncoder commented 1 year ago

Also quick note on Rectification Solutions: Jackson 2.15 does both -- default maximum nesting depth on reader side is set at 1000 levels, and List / JsonNode deserializers both have non-JDK-stack based handling.

pjfanning commented 1 year ago

@cowtowncoder I guess the pressure to make a change is going to come on - even if we do successfully engage with whoever assigned CVE-2023-35116 to get it revoked.

There are lots of ways to create data structures where you can get infinite recursion.

The serialize APIs tend to take thse params - (T value, JsonGenerator gen, SerializerProvider provider).

So as not to have to change the APIs to sneak in another value in the param list, could we consider tracking this in the JsonGenerator? JsonWriteContext looks like a good place to track the depth. protected int _nestingDepth is already inherited from JsonStreamContext.

Specifically, we need to track the depth - how many levels down have we gone since the we started serializing the original instance?

I have https://github.com/FasterXML/jackson-core/pull/1055 open for discussion - a partial implementation.

This could also be tracked via the SerializerProvider. Again, we could track the depth. Alternatively, we could keep track of the values, so that if we are asked to serialize a child value, we can check if we have already serialized that value. I think depth is a safer approach - value equality is something that is hard to rely - some classes will have equals() implementations that will not suit our needs.

cowtowncoder commented 1 year ago

Whoever filed CVE https://www.mend.io/vulnerability-database/CVE-2023-35116 should go fuck themselves.

@PoppingSnack I assume you did not file this -- given that as a project we do not agree in assessment. But there are so many bottom-feeding security "researchers" that it is more likely someone simply saw this issue report (which is not an invalid thing to report at all!) and decided to score points for filing an CVE. Which is why this whole security issue theater is so very annoying; lots of incompetent and/or malicious actors not following the process and trying to short-circuit modest checks there are.

ajakk commented 1 year ago

@PoppingSnack I assume you did not file this

I wouldn't be so sure...

20230618_10h49m26s_grim

pjfanning commented 1 year ago

@cowtowncoder @ajakk it most likely is that @PoppingSnack user - see https://github.com/janino-compiler/janino/issues/201 - another CVE raised despite the Janino team explaining that that tool is not designed for untrusted input - there has to be a point at which lib maintainers can say that users have to understand that they have to police against malicious input themselves

yawkat commented 1 year ago

This is laughable. Who's gonna file a CVE against JDK Map.hashCode which has the same "issue"?

cowtowncoder commented 1 year ago

Ok I gave too much benefit of doubt I guess.

But fundamentally beyond blatant disregard to project accepting a report as vulnerability, reproduction here is not anything an attacker could do as shown. It is a standalone Foot Gun example in which developer of a service can DoS their own service. Ridiculous. And description still makes baseless claim of "crafted String" -- while there is nothing of the sort in alleged reproduction.

So this is not in any way shape or from legit basis for a CVE: there is no attack vector for malicious clients.

cowtowncoder commented 1 year ago

@pjfanning Thank you for tackling that jackson-core issue that I filed earlier: I think that is the best way to go, based on similar thing on reader-side included in 2.15. There may still be reasons to tackle actual cyclic part (there have been PRs in the past proposing ways to keep track of parent object stack), but while related I think this one would tackle SO aspect reliably and efficiently.

(cycle tracking could actually possible be done at streaming level since there is hierarchic tracking of "current object" already... but that is still more complicated and adds significant overhead for deeper object hierarchies (so probably opt-in feature); whereas simple counter approach can be enabled for all users by default)

AB-xdev commented 1 year ago

For those coming from https://github.com/jeremylong/DependencyCheck:

So I just came in this morning and was a bit surprised to see that all our projects with the dependency - even with up to date versions - are marked as vulnerable.

The CVE is still tracked inside Sonatype's OSS INDEX (which is used by DependencyCheck) - I wrote them a mail to get this fixed.

See also: https://github.com/jeremylong/DependencyCheck/issues/5779

millems commented 1 year ago

Joining here from https://github.com/aws/aws-sdk-java-v2 and https://github.com/aws/aws-sdk-java. I understand this is not exploitable, but our customers demand that we upgrade to a "fixed" version. We're "stuck" on 2.12. @cowtowncoder would you accept a pull request from us backporting https://github.com/FasterXML/jackson-databind/commit/fcfc4998ec23f0b1f7f8a9521c2b317b6c25892b, and also do us the favor of releasing a 2.12.7.2 with the "fix"?

pjfanning commented 1 year ago

@millems I think you are confusing CVEs. This issue has nothing to do with CVE-2020-36518 mentioned in https://github.com/FasterXML/jackson-databind/commit/fcfc4998ec23f0b1f7f8a9521c2b317b6c25892b

yawkat commented 1 year ago

@millems fcfc4998ec23f0b1f7f8a9521c2b317b6c25892b does not relate to this issue. fcfc4998ec23f0b1f7f8a9521c2b317b6c25892b is a deserializer issue where deeply nested input could cause a stack overflow.

This issue relates to the serializer. In my opinion you should push back against treating this as a security issue, because Map.hashCode has literally the same issue on the same input, and if we start treating these as security critical we'll never hear the end of it.

pjfanning commented 1 year ago

@millems -- @cowtowncoder will be on vacation for a few weeks. My understanding is that Jackson 2.12 is no longer supported. Why not upgrade to a version that has the https://github.com/FasterXML/jackson-databind/commit/fcfc4998ec23f0b1f7f8a9521c2b317b6c25892b fix?

millems commented 1 year ago

Thanks for straightening me out. I've just been woken up by a page on CVE-2023-35116, so still a bit foggy. Our security policy requires us to treat literally every CVE as a problem. I can't push back on treating this as a security issue. I understand this issue isn't at all exploitable.

@pjfanning We've had a few conversations about this in the past with @cowtowncoder. It's a surprisingly large lift for us to upgrade to a new Jackson version if there have been any backwards-incompatible changes. We did it back in 2021: https://aws.amazon.com/blogs/developer/aws-sdk-for-java-version-1-12/. It sounds like we should do it again.

I have 2 short-term options:

  1. Get CVE-2023-35116 revoked.
  2. Appeal to the generosity of the Jackson developers to allow a backport of whatever "fix" was made for CVE-2023-35116 to 2.12.

Either way, I'll see about starting the process of getting Jackson upgraded to HEAD for all of our tools like we did back in 2021. I wasn't aware that 2.12 had officially moved to "not supported", although I am in no way surprised.

Which one of these options do you think is more likely to succeed?

pjfanning commented 1 year ago

@millems CVE-2023-35116 is not an issue. We reject that it is a security issue. There is nothing committed to jackson yet but we do intend to put some hardening in place in Jackson 2.16.0 - a release that is many months away. This fix will not be backported to any previous Jackson versions. https://github.com/FasterXML/jackson-core/pull/1055 is the proposed PR. To reiterate, this includes new APIs and will not be backported.

millems commented 1 year ago

@pjfanning Thanks! Is there anything we can do to help get CVE-2023-35116 revoked?

yawkat commented 1 year ago

@millems fyi the supported versions are listed here: https://github.com/FasterXML/jackson/wiki/Jackson-Releases – sounds like you should indeed upgrade.

(it incorrectly says 2.12 was closed in April 2022 when it was closed in April 2023, @pjfanning can you fix this? I don't have access.)

pjfanning commented 1 year ago

@pjfanning Thanks! Is there anything we can do to help get CVE-2023-35116 revoked?

I sent a request to Mitre using https://cveform.mitre.org/

I don't know if it is worth having multiple people chime in. I'll update this issue thread with any feedback from Mitre. There is no guarantee that they'll listen to me. A very high fraction of the CVEs are very low quality in terms of what they are reporting and often when they document real issues, the CVE description itself is still vague or inaccurate.

millems commented 1 year ago

Thanks! Sorry you have to deal with this. I'll tell our customers that you all are working to have the CVE revoked.

cowtowncoder commented 1 year ago

Joining here from https://github.com/aws/aws-sdk-java-v2 and https://github.com/aws/aws-sdk-java. I understand this is not exploitable, but our customers demand that we upgrade to a "fixed" version. We're "stuck" on 2.12. @cowtowncoder would you accept a pull request from us backporting fcfc499, and also do us the favor of releasing a 2.12.7.2 with the "fix"?

Sorry, would not accept a PR. Like @pjfanning said above, change that will catch too deep nesting will go in 2.16 and cannot be backported due to requiring API additions. Not even to 2.15, much less earlier.

And 2.16 itself is months out. The only short term way to resolve this problem is to get rid of CVE.

DarekDan commented 1 year ago

@PoppingSnack here is a cubic foot of stainless steel. Why don't you check if anything is vulnerable to dropping it from one yard of height in standard atmospheric conditions. Report back when ready with data to support your findings.

yawkat commented 1 year ago

@pjfanning have you heard back from MITRE? Otherwise i can try asking corp if they have anyone to contact there.

pjfanning commented 1 year ago

@yawkat I haven't heard back from Mitre

pjfanning commented 1 year ago

the CVE has been updated to say 'DISPUTED' but they made the text worse by adding 'NOTE: the vendor's perspective is that the product is not intended for use with untrusted input.'

This is absolutely not the case, this issue is about output not input. We have enhancements to police for malicious input.

https://github.com/CVEProject/cvelist/commit/e7481e1d5efe3ddea0cdb01706056b94f4bfb11e#diff-1d23b5f10a1be30eac5d043258c91f067a6f7e0cc86e7c6fc3e5c7462d6323f9

cowtowncoder commented 1 year ago

Ugh. That is quite unfortunate. I would not agree that Jackson should not be used for untrusted input as a general rule: if you (f.ex) use mapper.readTree() or even readValue() without default typing, there are no current exploits to be concerned about.

Going forward I guess we should refrain from mentioning "do not use for untrusted input" in general: it is good advice for specific cases but I guess can be misread as general statement, instead of specific case suggestion.

jumarko commented 1 year ago

Our dependency check jobs started failing again. Is there any reasonable action other than to exclude this in the dep check configuration?

AB-xdev commented 1 year ago

Our dependency check jobs started failing again.

https://nvd.nist.gov/vuln/detail/CVE-2023-35116#VulnChangeHistorySection

Initial Analysis by NIST 6/26/2023 12:52:40 PM
Action
Type Old Value New Value
Added CPE Configuration   OR *cpe:2.3:a:fasterxml:jackson-databind:*:*:*:*:*:*:*:* versions up to (including) 2.15.2
Added CVSS V3.1   NIST AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Added CWE   NIST CWE-502
Changed Reference Type https://github.com/FasterXML/jackson-databind/issues/3972 No Types Assigned https://github.com/FasterXML/jackson-databind/issues/3972 Exploit, Issue Tracking

Congratulations to NIST looks like they are unable to read this issue properly and are also a week late. Maybe they should consider partnering with Internet Explorer. Now they also classified it as affecting all Jackson Databind versions.... and according to them this issue contains an exploit... Yeah sure I also always copy java code into the system I want to exploit.

Also the CVSS score makes zero sense: https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=CVE-2023-35116&vector=AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H&version=3.1&source=NIST

If someone has the motivation they may contact NIST and try to get it revoked. I'm not motivated anymore and going to file an issue at my company that we disable CVE scanning and just keep our dependencies up-to-date because this is a clown show.

apetrelli commented 1 year ago

Excuse me, the original reporter closed this issues on 19th June. What gives?

OrangeDog commented 1 year ago

An inaccurate CVE in the database is OK, at least people can investigate and ignore it. But now it's lying about what "the vendor" said, which is much worse. How did that even happen? Just massive incompetence?

cowtowncoder commented 1 year ago

@OrangeDog I suspect that orgs maintaining reports are bit like patent office; overworked and with too little resources to investigate in detail, mostly relying on reporters' competency and good will. And where last 2 are lacking results are like here. :-(

Bhashkarjya commented 1 year ago

I'm a bit late to the party. So, I recently faced a security vulnerability for this issue in one of my Docker images. When can we expect for CVE-2023-35116 to get revoked?

pjfanning commented 1 year ago

I'm a bit late to the party. So, I recently faced a security vulnerability for this issue in one of my Docker images. When can we expect for CVE-2023-35116 to get revoked?

We have no control over the CVEs. Talk to whoever provides your security analysis tool.

cowtowncoder commented 1 year ago

Alas, project owners are at mercy of CVE maintainers, who can update state and descriptions. It puzzles me, for example, how "project has the view that the library is not to be used for untrusted content" came about -- this is not the gist of what is being said here at all.

Rather, the project team considers this an invalid report: there is no valid reproduction of a security vulnerability.

hdeweirdt commented 1 year ago

I sent the NVD a message through their contact form, 🤞

JooHyukKim commented 1 year ago

Is it false positive?

Yes, please refer to discussions above 👍🏼

derkork commented 1 year ago

The person who filed this CVE should really think long and hard about whether their personal pride was worth all the needless work people around the world now have to spend on suppressing this CVE in countless projects. Well done.

OrangeDog commented 1 year ago

The person who filed this CVE should really think

Closing the stable door after the horse has bolted.

cowtowncoder commented 1 year ago

The person who filed this CVE should really think

Closing the stable door after the horse has bolted.

"Why start now?" :)

ajakk commented 1 year ago

I sent the NVD a message through their contact form, crossed_fingers

MITRE maintains the CVE data, not NVD. NVD is really just a frontend for that data. You need to talk to MITRE to get a CVE revoked or changed or anything:

https://cveform.mitre.org

levinebw commented 1 year ago

@cowtowncoder are you folks registered as CNAs for this project with MITRE? Not sure, but that might help get the issue removed or corrected.

attritionorg commented 1 year ago

It would. No other CNA could assign for your software in that case.

cowtowncoder commented 1 year ago

I don't think we are: I don't know what all that would entail. I did file CVEs via mitre UI when polymorphic deserialization issues were a thing (pre-2.10) and that was quite a bit of work, even without being an official contact point.

pjfanning commented 1 year ago

I'm not an expert but my understanding is that registering as a CNA for all com.fasterxml projects is pretty straightforward.

As a CNA, we could still register CVEs - probably via the cvemitre site like has already been done in the past. It would mean that Mitre should contact us if someone tries to register a CVE bypassing us. Reporters can still appeal to Mitre if we do not deal with the security report. I have seen some issues raised for ASF projects and that were not accepted as CVEs by the ASF being effectively appealed to Mitre. Mitre have then gotten back to the ASF to query the reasoning.

I would support going down the CNA route.

cowtowncoder commented 1 year ago

Ok. My only concern is with extra work for filing CVEs, but then again number is fairly low (after polymorphic deser flood), and at least we'd have more say in actually blocking invalid reports (and not just get optional FYI from submitter). So I am not against application.

hdeweirdt commented 1 year ago

I sent the NVD a message through their contact form, crossed_fingers

MITRE maintains the CVE data, not NVD. NVD is really just a frontend for that data. You need to talk to MITRE to get a CVE revoked or changed or anything:

https://cveform.mitre.org

I sent a new report there.

hdeweirdt commented 1 year ago

I sent the NVD a message through their contact form, crossed_fingers

MITRE maintains the CVE data, not NVD. NVD is really just a frontend for that data. You need to talk to MITRE to get a CVE revoked or changed or anything: https://cveform.mitre.org

I sent a new report there.

Response:

Hello,

Thank you for your request. After review we have decided to keep the record but flag it as disputed. We have also noted a reason for the dispute which will allow the community to observe the different perspective. This request will now be closed. Respectfully, CVE Content Team

OrangeDog commented 1 year ago

We have also noted a reason for the dispute

Did you not point out that they reason they have noted is a lie?

cowtowncoder commented 1 year ago

I filed another request to at least try to get description changed to indicate that we actually disagree this is a valid vulnerability (reject).

Rohit23110 commented 1 year ago

What is the latest update on this issue? Has MITRE caved in and indicated that this CVE would be removed?