CompTIA Pentest+ PT0-002 – Section 11: Application Vulnerabilities Part 1

  • By
  • January 24, 2023
0 Comment

99. Application Vulnerabilities (OBJ 3.3)

In this section of the course, we’re going to discuss the different vulnerabilities that can affect web applications and how these are exploited by attackers. Now, as we move into this section we’re going to be continuing to look at attacks and exploits that we can use during the third stage of our engagement. As we move throughout this section, we’re going to be focused on all the different types of vulnerabilities that can exist inside of a web application that’s hosted by a given company, or software as a service product. Now, this section of the course is going to be our first half of our coverage for Objective 3.3. Now, this objective states, given a scenario you must research attack vectors and perform application-based attacks.

Application vulnerabilities are really important to understand as a penetration tester, for a couple of different reasons. The first reason is that you need to be able to identify these vulnerabilities so that you can create exploits to take advantage of them, and gain access to a given enterprise network. The second reason, is that when you do find a method to break into a given enterprise network by exploiting one of these vulnerabilities, it’s important that you understand how you can provide mitigation recommendations in your final report to your client at the end of the engagement, based on the different attacks you conducted.

Now, when it comes to web application vulnerabilities, one of the definitive lists of vulnerabilities is created by the Open Web Application Security Project, known as OWASP. Now every three to four years OWASP releases their top 10 list at owasp.org/top10. This top 10 is a standard awareness document for developers and web application security engineers. It represents a broad consensus about the most critical security risk to web applications, and provides information on how to prevent those risks. The top 10 for 2021 has broken access control, cryptographic failures, injections, insecure design, security misconfigurations, vulnerable and outdated components, identification and authentication failures, software and data integrity failures, and server-side request forgery as the top 10 categories of vulnerabilities. With each release of the top 10, a few categories are added and a few may be removed. But around 80% of these categories have remained consistent over the past 20 years.

Now, one of the new categories we find in the top 10 for 2021, is known as server-side request forgery, which has entered the list at number 10. So, what exactly is a server-side request forgery, and how can it be prevented? Well, a server-side request forgery is a type of attack that takes advantage of the trust relationship between the server and the other resources that it can access. One of the reasons there’s been a rise in server-side request forgeries is that more and more of our web applications are built using containerization, microservices, or serverless architectures, that are relying heavily on server to server interconnections to access those resources. This is especially true when you work in a cloud-based architecture.

Now, a server-side request forgery vulnerability occurs whenever a web application is fetching a remote resource, without first validating the user-supplied URL. This allows an attacker to coerce the application to send a crafted request to an unexpected destination, even if it’s protected by a firewall, VPN or other type of network access control list. To prevent a server-side request forgery attack from being successful, you should segment your remote resource access functionality into separate networks, and enforce a deny by default firewall, or ACL policy. You should also ensure that all web applications are sanitizing and validating any client-supplied input data prior to using it in the logic of your programs.

I recommend that you spend a little bit of time reading through the OWASP Top 10 at owasp.org/top10. Here, you’re going to be able to read a description of the vulnerability, how to prevent it, example of tax scenarios complete with code, links to relevant, common weakness enumeration, or CWE numbers, and a list of references for how to test for them during an engagement. For the exam, you do not need to memorize the OWASP Top 10 Vulnerabilities, but instead you should know that the OWASP Top 10 is a great resource for you to learn more about vulnerabilities that are commonly seen in the field as a penetration tester.

As well as how they’re exploited, and how you can prevent them from being exploited. Now, as we move through this section of the course we’re going to discuss many of the different vulnerabilities listed by the OWASP Top 10 and other common web application vulnerabilities. As I discuss each of these vulnerabilities, I’m going to discuss them from the network owner and developer’s perspective, because most of the time we are focused on vulnerabilities and not the attacks themself for this portion of the objective. Now, first we’re going to be discussing race conditions and buffer overflows. And then I’m going to demonstrate how a buffer overflow attack is conducted by an attacker. Next, we’re going to dive into authentication and reference vulnerabilities, as well as some vulnerabilities associated with insecure software development, such as improper error handling, and the use of improper headers. After that, we’ll discuss code signing and how the lack of code signing can be used by attackers to get users to run malicious code or applications. We’re also going to cover vulnerable components in software development, and some software composition vulnerabilities you may come across. Finally, we’re going to discuss privilege escalation and how it works. And then I’m going to show you how an attacker can conduct privilege escalation using the Medisploit framework with the Meterpreter payload. Remember, application vulnerabilities are everywhere, because web applications are constantly being updated with new features and functions, and all of this new code can insert vulnerabilities for an attacker, or a penetration tester to exploit. For this reason, it is important that you understand what vulnerabilities may exist, how to identify these vulnerabilities, and how you might be able to exploit them using your penetration testing frameworks. All right, let’s continue our coverage of Domain three, Attacks and Exploits, with application vulnerabilities in this section of the course.

100. Race Conditions (OBJ 3.3)

In this lesson, we’re going to talk about race conditions and the vulnerabilities associated with them. Now, what exactly is a race condition? Well, a race condition is a software vulnerability that occurs when the resulting outcome from executing processes is directly dependent on the order and timing of certain events and those events fail to execute in the order and timing that was intended by the developer. Essentially, this is a complicated way of saying that the computer is unexpectedly trying to race itself in the processing of certain types of data and you get an unexpected result. For example, if you’re trying to do something legitimate and the attacker’s trying to do something malicious at the same time, they might be able to get their request in before yours so that they can actually take advantage of a race condition vulnerability in the program’s code to run their processes before you can run yours. Race condition vulnerabilities are found when there are multiple threads attempting to write to a particular variable or object at the same memory location at the same time.

One of the ways this can happen is by a dereference or a no pointer dereference exploit that’s being exploited to trigger that race condition. Dereferencing is a software vulnerability that occurs when the code attempts to remove the relationship between a pointer and the thing that pointer is pointing to inside of the memory. Dereferencing is going to break the reference pairing between the pointer and that thing that’s being pointed to in memory and this allows changes to that location in memory or the variable that contain that memory. Race conditions are really difficult to detect and they’re really difficult to mitigate inside of modern networks. The Dirty COW exploit, for example, was an extremely popular exploit in 2016 that’s an excellent example of a race condition that’s being exploited in the real world. COW is actually an acronym and it stands for copy-on-write. In the Dirty COW exploit, Linux operating systems and some Android devices were being exploited to allow a local privilege escalation to occur by exploiting a race condition vulnerability based on the implementation of the programming for the copy-on-write function that was used inside of the kernel’s memory management system. Because of this race condition that existed in the code, if an attacker used the right timing, they could actually exploit the copy-on-write mechanism to turn the read-only mapping of a file into a write mapping. This would then allow the attacker to have escalated privileges because they could write to a file even though they only were supposed to have read-only access.

Now, what made this exploit even more dangerous is that it was difficult to detect because it didn’t even leave anything inside the system logs to let you know that it was happening. This is why race conditions become really dangerous because they often happen outside of our normally logged processes inside of our systems, and therefore, we can’t see that they’ve happened. While the Dirty COW vulnerability allowed a race condition against memory management systems to occur, race conditions can also be used against databases and your file system. When race conditions are used to exploit a database or file system, they’re generally going to take the form of a time-to-check to time-to-use vulnerability known as a TOCTOU vulnerability. Now, a time-to-check, time-to-use vulnerability occurs when there’s a change between when the application checks a resource and when the application actually uses that resource. Essentially, when a vulnerability is going to be exploited here, it’s going to allow us to change or invalidate the check that was previously made. Think about it this way. If the attacker can identify the time-to-check happened and then they do something before it was actually used, that becomes a race condition that they can exploit and then, they can manipulate the data after it’s already been checked but before the application uses it. To protect against a race condition like this, you can use locks and Mutexes to lock resources while the process is being run. Now, a Mutex is a Mutually Exclusive Flag and this acts as a gatekeeper to a section of code so that only one thread can be processed at a time and this will help prevent a race condition that would otherwise be caused by concurrent execution of that code or function.

Now, most databases have a method that can be used to lock the resource while it’s being processed. If you’ve ever used SharePoint at work to access a particular file for editing, for example, it actually puts a lock on that file so others can read it while you’re editing it but nobody else can check out that file for editing while you have it in use. This prevents two users from changing the file at the same time and it helps to maintain version control. This same concept is going to be used to lock out a record in the database or by using a Mutex to lock out the execution of a function or part of the code until the other thread is completed. Overall, the uses of locks and Mutexes work really well to prevent race conditions but sometimes they can cause a deadlock to occur. Now, a deadlock occurs when a lock takes effect but the process that set that lock is waiting for it to be terminated, crashes or simply doesn’t finish properly. And that causes the lock to not be removed from that resource, even though the processing was already completed. When this occurs, the system recode can lose access to that resource until that lock is overridden or manually removed. To prevent deadlocking, it’s important that you properly design and test any locks or mutexes that you’re going to be using to prevent a race condition in your code.

101. Buffer Overflows (OBJ 3.3)

In this lesson, we’re going to discuss Buffer Overflows. A Buffer Overflow occurs when a process in a program stores data outside the memory range that’s allocated by the developer. This memory range is known as a Buffer and a Buffer is simply a temporary storage area that a program uses to store its data. To better help understand this concept, let’s use an illustration. Let’s pretend that you have a glass of water sitting on a table that can hold a certain amount of water. For example, let’s say we have a cup that’s designed to hold 16 ounces of liquid but you try to put in 20 ounces, what’s going to happen? Well, our cup is going to overflow with water and our table is going to get soaking wet. Now in this illustration, the glass is our buffer or our all allocated range of memory that we can use to hold data. And if we overflow it with data, in this illustration our water, that means that extra data is going to spill out onto the table and make a huge mess. Similarly in the world of cybersecurity, Buffer overflows can also create big messes for us. In fact, 85% of the data breaches have been tracked back to a known buffer overflow attack being used as the initial attack vector. So let’s take a closer look at how a buffer overflow attack really works. Let’s pretend you wanted to store my phone number into your contacts list. Here in the United States our phone numbers consist of 10 digits. The first three digits are area codes and this represents the city we live in. The last seven digits represents the person’s unique phone number.

Now, before we had cell phones, you’d actually just pick up the phone and dial the seven digits of the phone number because the telephone company assumed that you wanted to make a local call. And so they would add your area code by default. Now let’s pretend the person who designed the contact list application on your phone decide they wanted to save some memory space. And so they decide to use the smallest buffer possible. So they decide to use an eight digit buffer because we’re going to assume that they don’t need to store the area code because you’re always going to be making local calls. So you go ahead and try to store a phone number like 555-1234 into the eight digit buffer. Now, when you do this, you’re going to see it takes up the first seven boxes labeled zero through six because computers always start counting with zero. Now because we could store eight digits here. And the number was only seven digits, everything is going to work just fine, but what would happen if we try to enter a number that’s too long? Well, buffer A isn’t the only memory buffer sitting there in your contact list application. Right after A comes buffer B and then buffer C, and buffer D and so on. Now, if you go off on vacation and you meet a new friend, you may want to save their phone number into your contact application. So you try to enter their phone number like (787) 555-1234. Now this includes the area code for San Juan Puerto Rico.

And now we have 10 digits we need a store, but each buffer was only set eight digits long because our programmer didn’t quite think through all the different types of phone numbers that a person might want to store in their contact list. Now, our contact list application is going to try to store this 10 digit number into the eight digit buffer. But those last two digits are going to overflow from buffer A and go into buffer B. This is exactly what happens when a buffer overflow condition occurs. Now, why is this a bad thing? Well, to explain that we’re going to have to get a little-bit technical. Each program reserves a chunk of memory when it first starts up. This allows it to have a place to store data that it’s going to have to use during processing. This area of memory is known as the Stack. A Stack is any reserved area of memory where the program saves the return address when a function call instruction is going to be received. Now, for example, here’s a stack that we’re going to look at. This is a first in last out or FILO type of stack. Basically, this means that the first thing that’s placed in the stack is going to be the last thing to be removed. And data is filled from the bottom of the memory which is shown at the top of our screen to the top of the memory, which is shown at the bottom of our screen. Now I know this looks a little upside down but this is actually how we use it in a standard convention inside the computer programming world. So I’m going to stick with it here. Now, if an attacker places too much information into the stack, or they change the value of that return pointer, they can then carry out an attack. This is exactly what a threat actor is trying to do when they perform a buffer overflow. They’re attempting to overwrite the return address of that pointer so it’ll point to a different place in the stack where they may have already placed some kind of malicious code. That way when the non-malicious code is run like our contact list application, it may hit the return pointer by act accident and then return the attacker’s malicious code like the slash bin slash shell code that you see here.

This gives them a shell or a command prompt on the victim’s system where they can then perform a remote code execution using that newly spawned shell. If the attacker’s able to do this it’s known as smashing the stack. Now smashing the stack frequently occurs when an attacker fills up the buffer with a bunch of NOP instructions or NOPs. Now an NOP is a non-operation instruction. And so that return address, they may find any of these NOPs and then continue on until they find the attacker’s code and then run it. When this series of NOPs is hit by a non-malicious program because the attackers filled it up with this buffer of NOPs and a return address at the end of it, this is known as a NOPs slide. The NOP instruction essentially is an instruction that tells the system to do nothing and simply skip ahead to the next instruction. So when the code finds a NOP, it’s simply going to move to the next instruction. And if that’s a NOP as well it’s going to continue to move forward until it finds something that is not a NOP. This way it slides down all the way until it hits that final return pointer which causes the program to branch over to that memory address, which contains the attacker’s code. There are four main ways to protect against the buffer overflow and you have to understand these. First you can patch your applications.

Buffer overflow vulnerabilities exist because of poorly written application code. When these vulnerabilities are discovered and exploited by attackers, the vendors of the application will create a patch and release it to be able to patch up those security vulnerabilities and prevent buffer overflows from occurring. This is one of the reasons that maintaining a good patch management program in your organization is so critical because it can help identify applications that need to be patched to eliminate these buffer overflow conditions. Second, if your organization is the developer of the program you should always use secure coding practices. By using secure coding practices, this means you’re going to use software development practices that include carefully checking your code for vulnerabilities prior to releasing it. One of the best secure coding practices that can help prevent a buffer overflow is to conduct boundary checking and input validation on any data being entered into your program by your end user. For example, if I was making that contact list application, I would make sure I’m conducting input validation and rejecting any phone number that’s larger than eight digits, because this would prevent a buffer overflow condition from occurring. Third, we can use address space layout randomization to prevent buffer overflow attacks. Now address space layout, randomization or ASLR is a programming technique that helps prevent an attacker’s ability to guess where the return pointer for a non-malicious program has been set to call back to by randomizing the memory addresses being used by well known programs, such as parts of the operating system. Now ASLR was first introduced all the way back with Windows Vista as a way to prevent buffer overflow attacks. So it’s commonly used in all modern operating systems.

Fourth, we can use data execution protection to prevent buffer overflow attacks as well. Now data execution protection, or DEP, is a form of mal are protection that’s designed to block applications that attempt to run from protected memory locations. DEP is used by the operating system to identify areas of memory that can contain executable code as well as other areas that are only allowed to store user data in non-executable code. If executable code like malware is being stored in the user data locations, it’s going to be marked as non-executable and unable to be run or executed. In addition to a standard buffer overflow though, there’s also other specialized types like an buffer overflow. Now an Integer Overflow is an attack in which the computed result from a mathematical operation is simply too large to fit into its assigned variable type for storage. When an integer overflow occurs, it can lead to crashing or data corruption or it can be used to trigger a buffer overflow. buffer overflows, and buffer overflows can also lead to arbitrary code execution, and in turn privilege escalations. An integer is essentially any whole number like one, two, three or four.

And if I counted negatively, I could then use negative numbers like negative one, negative two, negative three, or negative four. And those are all integers too. But if I use a number like 1.5 or one and a half or three quarters, those are fractional and those are decimals. So they’re called floating points, not integers. Integers are a very special data type in computer programming, and they only accept whole numbers either positive or negative, and integers are limited to a certain size. This certain size is known as the boundary and we have upper and lower bounds for each variable space that’s going to contain an integer value. Let’s pretend for a moment that I have a two digit storage space for my integers in a program and it’s called Score. Now I can store numbers either positive or negative but I can only store up to two digits in this variable known as Score. So how big of numbers can I use and store inside of the Score variable? Well, the numbers can be anything from zero to 99 because I only have two digits available in the program’s bounds. So let’s say I was writing a program and I’m going to add up two numbers, and then I’m going to store the result inside the variable Score. If I get two numbers like 90 and 17 and I try to add them together, could I do that?

Well, no, because 90 plus 17 is 107, which is three digits. And those three digits cannot be stored inside my variable score because it only holds two digits. If my program tries to store 107 in the score variable it’s either going to give me 10 or zero seven depending on if our system enters data from left to right or right to left when it fills up the memory buffers for a variable. Either way, though the system is going to drop one of those digits either the front digit of one or the back digit of seven because of the bounding of this variable. This is a problem for us and if we had another variable in a memory location to the right of our Score variable that extra digit could overflow into that space too. Now, this is a really silly example because we’re never going to use a system with only two digits but computers do limit the size of integers that they can utilize. And this can cause buffer overflows to occur. To prevent an integer overflow condition, you need to perform boundary checks and input validation in your programming, using your secure coding techniques. When coding an integer, you’re going to use an integer of 8-bits, 16-bits, 32-bits or 64-bits. And those variables can either be signed or unsigned. For example, if you’re using an integer that is a signed 8-bit value, it can hold any number between negative 128 and positive 127. This gives us a total boundary size of 256. Now, if you used an unsigned 8-bit integer though you now can have a number from zero to 255 instead because again, you still have 256 places to store.

Now, if you have a 16-bit integer you get a balance of 65,535. If you use a 32-bit integer, you can get up to 4.2 million. And if you use a 64-bit integer you can have a value of 18 quadrillion. So you may be wondering why don’t programmers just always use the largest value by using 64-bits for each integer that’s going to be stored? Well, this comes down to resource management. As a programmer, you need to ensure you have the appropriately sized variable balance for your different variables to ensure you’re not wasting resources. When you’re operating in the cloud environment for example, you’re paying for every megabyte of RAM on your virtual servers. So programming your code to be efficient in terms of memory and storage can save you a lot of money in the long term.

If I was using a variable to store the year that somebody bought one of my courses, for example, I don’t need to use a 64-bit integer to do that because a 16-bit unsigned integer is much more appropriate to store a four digit value representing your year of purchase. So remember when it comes to buffer overflow conditions and buffer overflow conditions, the best things you can do to mitigate them is to first ensure your systems are fully patched and up to date. Second, you should always use secure coding practices. And remember secure coding helps prevent both buffer overflows and buffer overflows. Third, you want to utilize ASLR or address based layout randomization, and fourth, you want to utilize DEP or Data Execution Protection.

Comments
* The most recent comment are at the top

Interesting posts

The Growing Demand for IT Certifications in the Fintech Industry

The fintech industry is experiencing an unprecedented boom, driven by the relentless pace of technological innovation and the increasing integration of financial services with digital platforms. As the lines between finance and technology blur, the need for highly skilled professionals who can navigate both worlds is greater than ever. One of the most effective ways… Read More »

CompTIA Security+ vs. CEH: Entry-Level Cybersecurity Certifications Compared

In today’s digital world, cybersecurity is no longer just a technical concern; it’s a critical business priority. With cyber threats evolving rapidly, organizations of all sizes are seeking skilled professionals to protect their digital assets. For those looking to break into the cybersecurity field, earning a certification is a great way to validate your skills… Read More »

The Evolving Role of ITIL: What’s New in ITIL 4 Managing Professional Transition Exam?

If you’ve been in the IT service management (ITSM) world for a while, you’ve probably heard of ITIL – the framework that’s been guiding IT professionals in delivering high-quality services for decades. The Information Technology Infrastructure Library (ITIL) has evolved significantly over the years, and its latest iteration, ITIL 4, marks a substantial shift in… Read More »

SASE and Zero Trust: How New Security Architectures are Shaping Cisco’s CyberOps Certification

As cybersecurity threats become increasingly sophisticated and pervasive, traditional security models are proving inadequate for today’s complex digital environments. To address these challenges, modern security frameworks such as SASE (Secure Access Service Edge) and Zero Trust are revolutionizing how organizations protect their networks and data. Recognizing the shift towards these advanced security architectures, Cisco has… Read More »

CompTIA’s CASP+ (CAS-004) Gets Tougher: What’s New in Advanced Security Practitioner Certification?

The cybersecurity landscape is constantly evolving, and with it, the certifications that validate the expertise of security professionals must adapt to address new challenges and technologies. CompTIA’s CASP+ (CompTIA Advanced Security Practitioner) certification has long been a hallmark of advanced knowledge in cybersecurity, distinguishing those who are capable of designing, implementing, and managing enterprise-level security… Read More »

Azure DevOps Engineer Expert Certification: What’s Changed in the New AZ-400 Exam Blueprint?

The cloud landscape is evolving at a breakneck pace, and with it, the certifications that validate an IT professional’s skills. One such certification is the Microsoft Certified: DevOps Engineer Expert, which is validated through the AZ-400 exam. This exam has undergone significant changes to reflect the latest trends, tools, and methodologies in the DevOps world.… Read More »

img