ClickCease The Bugs Behind the Vulnerabilities Part 2

Join Our Popular Newsletter

Join 4,500+ Linux & Open Source Professionals!

2x a month. No spam.

The Bugs Behind the Vulnerabilities Part 2

by Joao Correia

November 14, 2022 - Technical Evangelist

We continue to look at the code issues that cause the vulnerabilities impacting the IT world. In this installment of our five-part blog series exploring these bugs, we go through bugs #20 to #16 in the Mitre CWE Top 25 list for 2022 – providing context and additional information on the actual code problems that lead to vulnerabilities in applications and systems at large.

You can find the previous post in this series here.

20 – Incorrect Default Permissions

This class of bugs comes from the way software packages are prepared – either at the operating system level or the application level – by including permissions for a subset of items that is too broad in scope. Usually, this refers to executable files that have permissions that enable unprivileged users to execute said files when they shouldn’t be able to or having those files run with such privileges that they can manipulate other components of the system in unintended ways.

It can happen the other way around as well: having a permission on an executable file that is too restrictive, and then having the operating system automatically find an alternative located in an attacker-controlled location (rather than the component included in the original package).

The main risk is that these are the default permissions, so as soon as the package is deployed it will immediately be at risk without requiring any other action. This shifts the risk vector from usage to deployment.

19 – Improper Restriction of Operations within the Bounds of a Memory Buffer

Usually referred to as “buffer overflow” or “buffer overrun”, this anomaly attempts to read a position in memory outside of the pre-arranged space for a given variable, or write to such a location. 

This can happen either by intentionally passing a memory location that falls outside of the buffer or by using the result of an operation as the position to read/write without validating if it is acceptable or not in the given context.

Take the following example:


int main (int argc, char **argv) {

char *items[] = {"A", "B", "C", "D"};

int index = AskUserForIndex();

printf("You selected %s\n", items[index-1]);

}

(Code sample adapted from the one present at https://cwe.mitre.org/data/definitions/119.html)

In this example, if the user enters “5” as the index, the program will happily comply and return the content of a memory location outside the array. Depending on the operating system and memory architecture, several things can happen: memory values outside the program space can be returned, the program can crash, another program can crash (if a write happens), the system state can be compromised, or nothing crashes and the attack can be repeated. It is possible to read large amounts of memory by repeating the process with different index values.

This class of bugs can create problems when reading, but also when writing values to a buffer – by writing more data than the buffer was created to accommodate. If the memory region right after the buffer happens to hold a flag that indicates if the user has privileges or not, a simple byte change could turn a regular user into an administrator.

While this type of code bug is usually taught in programming classes, as well as how to avoid it by placing checks before any memory access, it sometimes isn’t obvious where the problem happens in the first place. Static code analysis tools, peer review, and pair programming are all ways to try and catch these before they make it into production code.

Another related concept in programming is the “canary” (analogous to a canary in a coal mine), where a value is written right after the buffer and then checked for its presence after a value is written to the buffer. If the original value is no longer there, then the programmer can assume a buffer overflow happened and intentionally crash the application to prevent further damage or deal with it by alerting the user or some other mechanism. While useful, clever attackers can work around these by including the canary value as part of the new value in order to not trigger it.

18 – Missing Authentication for a Critical Function

Any functionality-changing feature should validate if the actor performing the action is entitled to do so or not, and prevent the action when the actor is not allowed to do so. When an application fails to validate this, then it falls into this category.

Applications will often perform tasks like adding, removing, modifying, or deleting data, users or connections. All it takes is one of those actions to be missing, such an authentication check for the whole application, for it to be compromised or unreliable. And there are several steps involved in each of those operations, so the validation should happen ideally at each step or, barring that, at the entry points for each process.

Another typical example is missing authentication checks in cloud-based storage (ie, “leaky S3 buckets”), where confidential data is accidentally stored in a publicly accessible way.

17 – Improper Neutralization of Special Elements used in a Command

Whenever any input is accepted by an application and then executed, if it is not properly checked, it could contain additional commands, parameters, or flags that modify its intended purpose. 

If a system application is supposed to execute a command with a user-supplied parameter, a crafty user might include a command termination character followed by a different command that would then run with the same privilege level as the original program. These types of issues are closely related to “SQL injection” and “improper control of generated code” bugs, as they all come from implicitly trusting inputs. 

The source of the problem usually lies at the architectural level, where security concerns are not considered adequately, thus introducing insecure behaviors in applications. As a rule of thumb, no input should ever be trusted as good – ideally even at the individual function level, but at the very least at the application level.

16 – Missing Authorization

In #18 we saw how parts of an application could miss an authentication check. In this one, we look at how missing authorization can also lead to problems.

It is common for websites to protect sensitive sections behind authentication mechanisms, but each individual page has to enforce this check at the risk of direct access to a URL divulging information otherwise unreachable. This extends to downloads/uploads/queries and not just page accesses.

At the traditional application level, missing authorization happens when implicit trust is given to a user or external-calling application. 

Like #17, the flaw usually stems from the architectural phase of development – not considering adequate security tactics to enforce proper access control mechanisms at all levels. Identifying and then correcting the code for a given application after this type of bug is identified usually carries a hefty workload price, as the code needs extensive refactoring to be properly secured after the fact.

At the operating system level, this type of issue occurs when a tool has no access control list associated with it, thus allowing any user to execute it.

 

Summary
The Bugs Behind the Vulnerabilities  Part 2
Article Name
The Bugs Behind the Vulnerabilities Part 2
Description
We continue to look at bugs that cause the vulnerabilities impacting the IT world. Keep reading our five-part blog series
Author
Publisher Name
TuxCare
Publisher Logo

Looking to automate vulnerability patching without kernel reboots, system downtime, or scheduled maintenance windows?

Become a TuxCare Guest Writer

Mail

Help Us Understand
the Linux Landscape!

Complete our survey on the state of Open Source and you could win one of several prizes, with the top prize valued at $500!

Your expertise is needed to shape the future of Enterprise Linux!