VYPR
High severity7.5NVD Advisory· Published Jun 14, 2023· Updated Jan 3, 2025

CVE-2023-34620

CVE-2023-34620

Description

An issue was discovered hjson thru 3.0.0 allows attackers to cause a denial of service or other unspecified impacts via crafted object that uses cyclic dependencies.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Crafted cyclic dependencies in hjson up to 3.0.0 cause stack overflow denial of service; fixed by adding nesting depth limits.

CVE-2023-34620 describes a denial-of-service vulnerability in the hjson library versions through 3.0.0. The issue stems from the parser's use of recursive function calls to handle nested objects. An attacker can craft an input string containing deeply nested or cyclic dependencies, causing the parser to exceed the call stack and result in a stack overflow (out-of-memory crash) [4].

The vulnerability can be triggered remotely by supplying a maliciously crafted JSON-like string to any application that parses untrusted hjson input. No authentication or special privileges are required; the attacker only needs to deliver the payload via an API endpoint, file upload, or other means. The exploit is straightforward: generating a document with thousands of levels of nesting or mutual references leads to a stack overflow, as demonstrated in the proof-of-concept for the Java implementation [4].

Successful exploitation causes the parser to crash, resulting in a denial of service. The official description also notes potential "other unspecified impacts," though the primary impact is a denial of service [4]. The CVSS score of 7.5 (high) reflects the low attack complexity and network-based attack vector.

Mitigations have been implemented in the form of pull requests across multiple language implementations: introducing a maximum nesting depth (default 10000) and replacing recursive calls with iterative loops [1][2][3]. Users are advised to update to the latest patched versions or apply the fixes manually. No workaround other than limiting input depth is available.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

3

Patches

2
2d1b8b4b158a

maxNestingDepth 10000 (#45)

https://github.com/hjson/hjson-phptrobroApr 20, 2025via ghsa
1 file changed · +20 2
  • src/HJSON/HJSONParser.php+20 2 modified
    @@ -11,6 +11,8 @@ class HJSONParser
         private $ch;   // The current character
         private $escapee = [];
         private $keepWsc; // keep whitespace
    +    private $maxNestingDepth = 10000;
    +    private $nestingDepth;
     
         public function __construct()
         {
    @@ -44,6 +46,7 @@ public function parse($source, $options = [])
     
         private function resetAt()
         {
    +        $this->nestingDepth = 0;
             $this->at = 0;
             $this->ch = ' ';
         }
    @@ -99,9 +102,15 @@ private function value()
             $this->white();
             switch ($this->ch) {
                 case '{':
    -                return $this->object();
    +                $this->nestingDepth++;
    +                $ret = $this->object();
    +                $this->nestingDepth--;
    +                return $ret;
                 case '[':
    -                return $this->_array();
    +                $this->nestingDepth++;
    +                $ret = $this->_array();
    +                $this->nestingDepth--;
    +                return $ret;
                 case '"':
                     return $this->string('"');
                 case '\'':
    @@ -157,6 +166,10 @@ private function _array()
             // Parse an array value.
             // assumeing ch === '['
     
    +        if ($this->nestingDepth > $this->maxNestingDepth) {
    +            $this->error("Exceeded max depth (".$this->maxNestingDepth.")");
    +        }
    +
             $array = [];
             $kw = null;
             $wat = null;
    @@ -210,6 +223,11 @@ private function _array()
         private function object($withoutBraces = false)
         {
             // Parse an object value.
    +
    +        if ($this->nestingDepth > $this->maxNestingDepth) {
    +            $this->error("Exceeded max depth (".$this->maxNestingDepth.")");
    +        }
    +
             $key = null;
             $object = new \stdClass();
             $kw = null;
    
326599cebc6e

maxNestingDepth 10000 (#67)

https://github.com/hjson/hjson-gotrobroApr 16, 2025via ghsa
2 files changed · +30 2
  • decode.go+20 0 modified
    @@ -12,6 +12,9 @@ import (
     
     const maxPointerDepth = 512
     
    +// This limits the max nesting depth to prevent stack overflow.
    +const maxNestingDepth = 10000
    +
     type commentInfo struct {
     	hasComment bool
     	cmStart    int
    @@ -73,6 +76,7 @@ type hjsonParser struct {
     	structTypeCache   map[reflect.Type]structFieldMap
     	willMarshalToJSON bool
     	nodeDestination   bool
    +	nestingDepth      int
     }
     
     var unmarshalerText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
    @@ -95,6 +99,7 @@ func (p *hjsonParser) setComment2(pCm *string, ciA, ciB commentInfo) {
     
     func (p *hjsonParser) resetAt() {
     	p.at = 0
    +	p.nestingDepth = 0
     	p.next()
     }
     
    @@ -549,6 +554,11 @@ func getElemTyperType(rv reflect.Value, t reflect.Type) reflect.Type {
     
     func (p *hjsonParser) readArray(dest reflect.Value, t reflect.Type) (value interface{}, err error) {
     	var node Node
    +
    +	if p.nestingDepth > maxNestingDepth {
    +		return nil, p.errAt(fmt.Sprintf("Exceeded max depth (%d)", maxNestingDepth))
    +	}
    +
     	array := make([]interface{}, 0, 1)
     
     	// Skip '['.
    @@ -624,6 +634,11 @@ func (p *hjsonParser) readObject(
     	// Parse an object value.
     	var node Node
     	var elemNode *Node
    +
    +	if p.nestingDepth > maxNestingDepth {
    +		return nil, p.errAt(fmt.Sprintf("Exceeded max depth (%d)", maxNestingDepth))
    +	}
    +
     	object := NewOrderedMap()
     
     	// If withoutBraces == true we use the input argument ciBefore as
    @@ -775,9 +790,13 @@ func (p *hjsonParser) readValue(dest reflect.Value, t reflect.Type) (ret interfa
     	// Parse an Hjson value. It could be an object, an array, a string, a number or a word.
     	switch p.ch {
     	case '{':
    +		p.nestingDepth++
     		ret, err = p.readObject(false, dest, t, ciBefore)
    +		p.nestingDepth--
     	case '[':
    +		p.nestingDepth++
     		ret, err = p.readArray(dest, t)
    +		p.nestingDepth--
     	case '"', '\'':
     		s, err := p.readString(true)
     		if err != nil {
    @@ -942,6 +961,7 @@ func orderedUnmarshal(
     		structTypeCache:   map[reflect.Type]structFieldMap{},
     		willMarshalToJSON: willMarshalToJSON,
     		nodeDestination:   nodeDestination,
    +		nestingDepth:      0,
     	}
     	parser.resetAt()
     	value, err := parser.rootValue(rv)
    
  • hjson_test.go+10 2 modified
    @@ -65,23 +65,25 @@ func run(t *testing.T, file string) {
     	if err := Unmarshal(testContent, &data); err != nil {
     		if !shouldFail {
     			t.Error(err)
    -		} else {
    -			return
     		}
    +		return
     	} else if shouldFail {
     		t.Error(errors.New(name + " should_fail!"))
    +		return
     	}
     
     	rjson, rhjson, cm2, cm3 := getResultContent(name)
     
     	actualHjson, err := Marshal(data)
     	if err != nil {
     		t.Error(err)
    +		return
     	}
     	actualHjson = append(actualHjson, '\n')
     	actualJSON, err := json.MarshalIndent(data, "", "  ")
     	if err != nil {
     		t.Error(err)
    +		return
     	}
     	actualJSON = append(actualJSON, '\n')
     	actualJSON = fixJSON(actualJSON)
    @@ -92,10 +94,12 @@ func run(t *testing.T, file string) {
     		decOpt.WhitespaceAsComments = false
     		if err := UnmarshalWithOptions(testContent, &node, decOpt); err != nil {
     			t.Error(err)
    +			return
     		}
     		actualCm2, err = Marshal(node)
     		if err != nil {
     			t.Error(err)
    +			return
     		}
     		if len(actualCm2) > 0 && actualCm2[len(actualCm2)-1] != '\n' {
     			actualCm2 = append(actualCm2, '\n')
    @@ -106,10 +110,12 @@ func run(t *testing.T, file string) {
     		var node Node
     		if err := Unmarshal(testContent, &node); err != nil {
     			t.Error(err)
    +			return
     		}
     		actualCm3, err = Marshal(node)
     		if err != nil {
     			t.Error(err)
    +			return
     		}
     		if len(actualCm3) > 0 && actualCm3[len(actualCm3)-1] != '\n' {
     			actualCm3 = append(actualCm3, '\n')
    @@ -146,6 +152,7 @@ func run(t *testing.T, file string) {
     		err = Unmarshal(actualCm2, &roundTrip)
     		if err != nil {
     			t.Error(err)
    +			return
     		}
     		if !reflect.DeepEqual(data, roundTrip) {
     			t.Errorf("cm2 roundtrip failed!")
    @@ -163,6 +170,7 @@ func run(t *testing.T, file string) {
     		err = Unmarshal(actualCm3, &roundTrip)
     		if err != nil {
     			t.Error(err)
    +			return
     		}
     		if !reflect.DeepEqual(data, roundTrip) {
     			t.Errorf("cm3 roundtrip failed!")
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

8

News mentions

0

No linked articles in our index yet.