245 - A PHP and Joomla Bug and some DOM Clobbering
Just another caching issue, this time we’ve got a GraphQL API that is being used to serve some static files/content. Those requests that should be cached include a reqIdentifier
parameter in the URL that acts as the cache key. So an attacker can craft a GraphQL query that will return user information from an authenticated user include the reqIdentifier
parameter and then when the victim visits the page it will be cached so the attacker can see the response.
Three deserialization related issues, two stemming from core of Lucee, and one in Mura a CMS built on Lucee. Lucee is a ColdFusion Markup Language based scripting language the runs on the JVM (Java Virtual Machine) and is intended for the development of web applications. As such it has some niceties around building web-apps like support for routing and processing of incoming requests.
Which is where the first issue is, you can setup REST mappings to certain functions, if the mapping includes taking in some arguments then Lucee will attempt to parse the request body taking the user-provided Content-Type
header into account. If Content-Type
is application/java
it will attempt to deserialize the user-provided content which naturally leads to a deserialize attack. While the Apple service being targeted was not vulnerable to this, the Lucee update server was.
The second issue is in support CFML expressions when loading data from cookies. As CFML can contain <cfscript>
tags, this can be used for code execution. From within Lucee core there were three functions that would lead to this sessionInvalidate()
, sessionRotate()
and PageContext.scope()
While these functions could be vulnerable sinks, the Lucee codebase doesn’t actually call them. However the Mura/Masa CMS which are built on Lucee and used by Apple does use them. The are behind configuration options not being used by Apple though.
The third issue, which finally was also something that the Apple instance was vulnerable to was found with Mura CMS code, they had discovered the VariableInterpreter.parse
would ultimately fall into the same CFMLExpressionInterpreter
seen in the prior issue, so an insecure call to that could lead to an insecure CFML interpreetation. Insecure call in this case means sending the limited
argument as false
. The challenge was that many of the user’s of this like isDefined("...")
or Empty("...")
would be called with static strings that a user couldn’t control. It turned out though that within the FEED API in Mura it would call isDefined
with a variable #param.method#
to be expanded before being passed into the CFMLExpressionInterpreter
allowing attacker content to get passed through to the interpreter and code execution to be achieved.
The vulnerabilities here are ultimately just user input ending up in dangerous functions, but do give the original a read for some insight into how they did the code review to find the issues.
This was a really cool XSS filter bypass due to a parsing differential between PHP’s multibyte string functions: mb_strpos
and mb_substr
when dealing with invalid UTF-8 sequences.
At a really high-level in UTF-8 encoding characters may take up one or more bytes, so the first byte of any character will indicate how many bytes that follow are part of this one character and then all of the following bytes (if any) will start with the bits 10
indicating it is a continuation. When mb_strpos
would parse process the string byte-by-byte. In that it would read hte first byte, it would indicate how many bytes are to follow, then it would read the next bytes one-by-one ensuring they were continuation bytes until it got to the end of the character. If one of those continuation bytes were to be invalid, it would consider that the end of the previous character and attempt to interpret this new byte as the start of a new character. This means that when parsing the characters to find the some position it could have a UTF-8 leading byte indicating that this is a 4 byte character, but then if the second byte were invalid that 4-byte character would end after just one byte.
On the other hand mb_substr
would read that leading character and then skip over the next however many bytes were indicating and read the next leading-byte. This parsing difference means that characters could be smuggled in when the XSS filter would run because it could improperly calculate how many characters into the string it is. Really cool bug, and the fix wasn’t backported into older PHP versions so there is a chance this will stick around for a little while.
A rather simple Chrome permission bypass. Basically chrome.pageCapture.saveAsMHTML()
shouldn’t be able to save pages it doesn’t have the permissions to read like pages belonging to another extension or chrome pages. This check was vulnerable to a race condition and by repeatedly navigating from an allowed page to a disallowed page and back eventually it would end up capturing the disallowed page.