jsonparser.gs expects "val" to be never used (leads into numbers being replaced by an unrelated String)
I'm tired : I just passed SIX hours trying to understand a bug with one of my several-includes-long script, before understanding it was from my json parser all along, based on a miniscript parser found on google.
... It seems that we're based on the same one because it shares 99% of the source, including the same bug, so I'll document it here just in case you get it one day :)
I can't really provide a minimal example for this one, but basically, the json parser can provide a glitched result based on some (unknown) use of a variable named "val" in the calling function
Result of the bug :
If the parser is glitched, all numbers in the JSON will be parsed as the same (seemingly unrelated) String
As a secondary result, the same input can result into two different objects at two different point in a script's lifetime (really sneaky, right?)
In other words : 'toJSON(parse(input)) != input) because parse doesn't provide the expected JSON anymore
Reason of the bug :
The 'problem' lies in _parseNumber, line 254 : 'result = val(self.source[startPos : self._p])', note that in this case val is not a method of String, like String.to_int
My calling script somehow redirected the 'val' standard call to a local variable named 'val'. A search-and-replace of all uses of "val" into "value" in my scripts fixed the JSON loading problem
How to avoid the bug :
- Make sure to never use a variable named 'val' anywhere just to be safe
- If a script is uber-critical, don't use numerical values in an object meant to be converted into JSON
How to correct the bug :
... Honestly, I don't know at this time.
A) I finally understood this bug when I replaced this unknown val call with a (str).to_int, but it breaks the compatibility for floating numbers...
Other UNTESTED ideas :
B) Maybe there's a way to avoid triggering it from function variables by using "globals.val(" ?
C) SAVING @val in the Parser object should avoid redirecting the variable during the lifespan of the script, but even that wouldn't save against a script highjacking the global variable before the json parser got included
In my case, it broke all my vulnerabilities saving system because
{"complete":1,"libName":"libssh.so","libVersion":"1.0.0","exploits":{"0x4696F4D9":[{"value":"minusb","result":"","conditions":[]},{"value":"trangeundostartion","result":"","conditions":[{"type":5}]}]}}'
became
{"complete":"minusb","libName":"libssh.so","libVersion":"1.0.0","exploits":{"0x4696F4D9":[{"value":"minusb","result":"","conditions":[]},{"value":"trangeundostartion","result":"","conditions":[{"type":"minusb"}]}]}}
because the memory VALue of my first exploit was stored in "val" before calling the toJSON method
[EDIT] Minimal example ( complete src file ):
- Copy jsonparser.gs (we'll name it "wtfjson.src")
- Add a bit of code at the end to allow to run the test
JSONbreaker = function()
json = "{""numeric"":5}"
print(toJSON(parse(json),true))
val = "wtf"
print(toJSON(parse(json),true))
end function
JSONbreaker()
- Run "build wtfjson.src /bin/" then "wtfjson"
Console output :
Unit testing : json
All tests passed. Huzzah!
{"numeric":5}
{"numeric":"wtf"}
Note that this test did NOT use the preprocessor