There’s a fun logic flaw in how multiple online services handle OAuth logins, that abuses Microsoft’s Azure Active Directory service to allow account takeovers. The problem is how a site handles the “Sign In With Microsoft” option, when there’s an existing account under the same email address. This is an irritating problem for an end-user, when a site offers multiple sign-in options. Trying to remember which option was used to set up an account is a struggle, so many services automatically merge accounts.
The problem is that the Microsoft Azure authentication information includes an email address, but Microsoft hasn’t done any verification that the account in question actually controls that address. And in fact, it’s trivial for the Azure admin to change that address at whim. So if the service accepts that email address as authoritative, and auto-merges the accounts, it’s a trivial account takeover. And it’s more than just a theoretical problem, as researchers at descope were able to demonstrate the attack, and have found multiple medium and large services that were vulnerable, as well as at least two authentication providers that themselves were vulnerable to this attack.
Microsoft has pushed updates to the Azure AD service to make the issue easier to avoid, though it seems that the unverified “email” field is still being sent on authentication transactions. There is a new flag, “RemoveUnverifiedEmailClaim” that eliminates the issue, and is enabled by default for new applications. Unfortunately this means that existing vulnerable applications will continue to be vulnerable until fixed on the application side.
MiniDLNA
[Hyprdude] was looking for bugs on his Netgear RAX45, so he pulled the GPL sources and started doing some fuzzing. The application that caught his attention was MiniDLNA, a lightweight media hosting application. It has an HTTP stack, and that’s where a handful of bugs were found. The juicy one, CVE-2023-33476, is an out-of-bounds (OOB) read and write vulnerability in the HTTP chunked encoding handling. The HTTP one-liner that crashes a vulnerable instance is a simple GET /status HTTP/1.0\r\nTransport-Encoding:chunked\r\n\r\nffffff\r\n0\r\n\r\n
.
Now there’s a weird wrinkle here. The RAX45 doesn’t crash when sent this code. Netgear seems to be maintaining a private branch of MiniDLNA that has fixes, and isn’t included in their GPL bundles. That’s almost certainly a GPL violation, unless it’s based solely on the MiniDLNA code developed by Netgear employees. With that discovery made, [hyprdude] turned to the main MiniDLNA code base, and found the vulnerability there. It’s an order of operations error, hidden by a programmer trying to be too smart.
while( (line req_buf + h->req_buflen)) &&
(h->req_chunklen = strtol(line, &endptr, 16) > 0) &&
(endptr != line) )
That while loop is looking for three conditions, that the line
pointer hasn’t overflowed its output, that there is a value left in the input buffer, and that the line
pointer hasn’t reached the end of the input. The middle condition is where some programmer was trying too hard, to use the results of the strtol()
call for the comparison, and also to assign it to the req_chunklen
variable. But instead, that variable is set to the result of the comparison — either 0 or 1. And since that value is used to iterate through the input, the sanity checking intended to happen here is quite handily sidestepped.
The logic that actually copies the data gets this right:
while ((h->req_chunklen = strtol(chunk, &endptr, 16)) > 0 && (endptr != chunk) )
The extra pair of parenthesis make the value assignment work as intended. But we’ve already sneaked bad data through the filter, meaning that packets are accepted with chunk sizes bigger than the packet itself.
And then in part two, we get a fantastic walkthrough on how to turn this bug into a working exploit. The primitive ends up being the ability to slide data from beyond the end of the packet buffer. It requires a bit of creativity, in the form of including whitespace in the HTTP chunks, and then using other HTTP packets to set up a neighboring memory location. Finally, the flaw is used to slide one of those pre-sent packets over a recently free’d memory chunk, corrupting the memory metadata about where to find free chunks. And then the next HTTP packet can be steered to allocated memory, namely the Global Offset Table. Overwrite the location of the free()
function, pointing it at previous HTTP data, and you’ve got code execution. It’s great work, and the fix has already landed upstream.
Ticket to Ride
France and Germany launched a publicity campaign recently, where they gave away 30,000 free tickets for travel between the two countries. And somewhat predictably, the demand for free tickets DDoS’d the servers. But it turns out, there were far worse problems. Like the password reset function, pointing at a non-existent Vercel application. Oops. A kind soul registered that application, and made it a simple test page rather than something malicious.
But it gets better. The campaign intended to give out 30,000 tickets. But what about people still filling out the form when the 30,000th ticket was given out? Yes, they still get tickets, too. That was handled with an emailed link to those few stuck in limbo, allowing them to finish registration. The problem was that the code to initiate that process was left in place — accessible to anyone with a browser’s debug console.
After spending way too much effort trying to find a security contact, our intrepid researchers apparently got word of problems to the right ears, as this secret page was closed. But the ride doesn’t end there, as the API itself would still happily hand out free tickets. After another wild goose chase trying to do security disclosure, that problem *finally* got fixed. No more free tickets.
So armed with this experience, the team took a look at a previous campaign, DiscoverEU. Same concept, different government, but the same agencies doing the implementation. Not vulnerable in the same way, but there was a dashboard discovered through Certificate Transparency. Since they were already familiar with the API, why not try using it to create an account? Not only did it work, that account could access the name, email, country, and state of almost 250,000 users. At least they finally knew who to contact for security reports.
Bits and Bytes
There are a trio of speculative executions attacks against Ethereum. Unlike speculative execution on a CPU, these issues just manage a Denial of Service attack on the network. Ethereum tries to prevent DOS attacks by charging a small fee for each transaction, but these attacks trade on the insight that invalid transactions don’t actually cost those fees. The first example is a technique to run a chain of 140 minor transactions, that ends in a transaction to a sanctioned entity. A node that honors those sanctions will run through the previous transactions, only to discard the work once the final interaction is discovered. A second technique creates valid-looking transactions, that use up a great deal of memory to process, leading to eviction of other, valid transactions from the client’s memory pool. The last technique manipulates nodes into including transactions with censored entities, which is only caught by other nodes, and can rapidly harm a specific node’s reputation rating.
Researchers have published a paper on arxiv detailing a Global SMS Positioning system, which uses the timing of SMS delivery reports to track down the location of receivers. The heavy lifting was done by a machine learning model, and the caveat is that it only really works when there’s a known list of locations where the target is likely to be. Still impressive.
And Fortigate RCEs are still a thing, with two more vulnerabilities found recently. At least one has been fixed independently in the latest firmware update. It’s another example of one vulnerability finding leading to several others, as attention is drawn to some new or unexplored possible vector. It’s not entirely clear what the services are intended to do, listening on ports 1050 and 5555. Sadly, it’s probably not related to Daft Punk’s Interstella 5555.
Important parenthesis?
> while( (line req_buf + h->req_buflen)) &&
> (h->req_chunklen = strtol(line, &endptr, 16) > 0) &&
> (endptr != line) )
Isn’t there on too many in the first line?
It closes the “while” before the first “&&”
Good catch. This may well be some html auto formatting thing for the forum, but it also further highlights the underlying issue.
For over 30 years it’s known that programmers trying to be smart are not smart at all.
It’s quite easy to write spaghetti code in C (C++ or any other language), but it’s far more difficult to write comprehensible and easy to read, debug and maintain code.
Quite a long time ago I read a statement that the main task of program sourcecode is to be easily understood by humans reading it, and compared to that, compiled output is just a secondary and much less important goal. This took a while to sink in, but now I firmly believe in this statement. Programmers spend many (expensive) hours writing and reading source code. From the first concept and design through debugging, rewrites during refactoring and maintenance and then all the way to formal code reviews and verification. And each time a human reads the code it costs human effort and money in the form of salary.
And one of the very simple rules to make better maintainable code is to never put more then one statement on a line. Keeping functions small (to about 1 or sometimes maybe 2 pages) is also such a simple and very valuable guideline.
It’s quite easy to write code that has no obvious errors, but much harder to write code that obviously has no errors.
trying to be to clever is often the problem, and in the many decades I’ve be doing c/c++ I’ve learnt to never do statements like this one (even if it had of been correct).
I think programmers sometimes do this as they think it is helping the compiler optimize – but in fact there is no real difference – through to it runs slower as you can’t guarantee the order of the sub expressions evaluating (which the programmer can probably optimize..)
That sort of rule “never put more than XYZ in one place” is a bit dependent on context. It’s very valid when someone tries to make some tangled mess with regex golf, ternary operators, assigning things in conditionals, and abuse of quirks. But sometimes I really don’t see a benefit in ballooning something out into a lot of fluff. If you can write a pithy comment that fully describes the logic, then even if the code is a bit long it might be fine. I wish people didn’t avoid such comments when they have short bits of unintuitive code just because they aren’t very long.
Of course, if you want to separate subconditions per line based on their meaning, that’s very often helpful. If you then write a comment on the annoying parts, you should read the code, start to write what it actually does (set something to 0 or 1 that shouldn’t be) and make it do what you wanted instead.