Binex Exploitation

Ahmet Göker
15 min readSep 24, 2023

--

Hello everyone, welcome back to my technical blog post on exploitation. Today, we have three challenges that delve into SUID (Set User ID), buffer overflow, and path manipulation. So, I hope you will find this information both informative and engaging. If you are ready to embark on this guided journey, let’s get started.

SUID exploit

First of all, let me explain about SUID exploitation before starting it.

SUID exploitation is a type of privilege escalation attack that involves exploiting a binary with the SUID bit set. The SUID bit is a special permission that allows a binary to be executed with the privileges of its owner, regardless of the privileges of the user who is executing it.

SUID binaries are often used to provide system functionality to users who do not have root privileges. For example, the sudo binary is a SUID binary that allows users to execute commands with root privileges.

However, SUID binaries can also be exploited by attackers to gain root privileges. There are a number of ways to exploit SUID binaries, but the most common method is to craft a malicious input that causes the binary to execute arbitrary code.

Once an attacker has exploited a SUID binary, they can gain root privileges and execute any command on the system. This can allow them to install malware, steal data, or even take complete control of the system.

It is important to understand about SUID exploitation because the first level will be about this part.

Now I will walk and through this level together.

We got an IP address, and with this IP we will enumerate to see if the IP is reachable, if so, now the pentest should be started. In order to enumerate this IP we are able to use NMAP. There are numerous ways to scan this IP.

Enumeration

As you can see, I used to use only TCP scan. We can see that 3 ports appeared to us. I will start to enumerate port 139 and 445. You might be asking to yourself; what are these ports to be used for?

port 139

NetBIOS (Network Basic Input Output System) is a software protocol that allows applications, PCs, and Desktops on a local area network (LAN) to communicate with network hardware and to transmit data across the network.

port 445

Port 139 is technically referred to as ‘NBT over IP,’ while Port 445 is labeled as ‘SMB over IP.’ SMB, or Server Message Block, is alternatively known as the Common Internet File System, operating as an application-layer network protocol primarily facilitating shared access to files, printers, serial ports, and various forms of communication between network nodes.

There are tools to enumerate these ports such as: username, password and more…

In this case, I utilized “smbclient” to gather details regarding this IP address. The fundamental question we must address is whether we can retrieve both the password and the username, or if this is merely a diversion. This question can be elucidated as we delve further into the case.

To further investigate this situation, I will employ “enum4linux” to extract additional information about the IP.

There are 2 domains:

BUILTIN and THM_EXPLOIT

Awesome! We can observe that there are four users available. It’s now feasible to utilize the “Hydra” tool to attempt password retrieval for these users.
I’ve consolidated all of these users into a single file. The objective now is to employ this file as an input in the Hydra command. Please be aware that the password retrieval process might take a few minutes.

It might take few minutes to get the password or not, who knows :)

Awesome! I got the password and the username. we are able to login into ssh server.

Awesome! We are logged on.

SUID manipulation

Awesome! We’ve successfully gained access, but our objective is to conduct a deeper analysis of this machine, including the search for potential exploits. The ultimate goal is to achieve root-level access and gain full control of the machine. Allow me to elucidate what SUID (Set User ID) is and why it is pivotal in our quest to uncover and exploit vulnerabilities.

SUID exploitation is a type of privilege escalation attack that involves exploiting a binary with the SUID bit set. The SUID bit is a special permission that allows a binary to be executed with the privileges of its owner, regardless of the privileges of the user who is executing it.

SUID binaries are often used to provide system functionality to users who do not have root privileges. For example, the sudo binary is a SUID binary that allows users to execute commands with root privileges.

However, SUID binaries can also be exploited by attackers to gain root privileges. There are a number of ways to exploit SUID binaries, but the most common method is to craft a malicious input that causes the binary to execute arbitrary code.

Once an attacker has exploited a SUID binary, they can gain root privileges and execute any command on the system. This can allow them to install malware, steal data, or even take complete control of the system.

If you understood, lets dive into it.

I used to use this command:

find / -type f -perm -4001 -exec ls -h {} \; 2> /dev/null

The above command will find the files (-type f) in the root directory (/) with having suid bit set and world executable permission set (-perm -4001) and execute ls -l (-exec ls -l {} \;) on each match. It will then redirect stderr to /dev/null (2> /dev/null)

I always recommend to use Linux enumeration scripts to get information. It has some possibilities to find an exploit in a target machine. You have to have a roadmap after obtaining the credential, because the goal is to be a root user.

As I mentioned, it is useful to use Linux enumeration tools. You can find how to exploit SUIDs.

Link: https://gtfobins.github.io/gtfobins/find/

It was really easy to manipulate the system. This was called SUID manipulation/ exploitation.

Buffer overflow-1

Awesome! As you can observe, we have commenced our exploration by employing enumeration techniques to gather information about the target IP. Subsequently, our attention shifted toward scrutinizing users and exploring potential SUID (Set User ID) exploits, among other avenues.
Following a structured roadmap and maintaining comprehensive notes is paramount in our pursuit of exploiting a machine. It’s worth noting that while our efforts may lead to successful exploitation, there’s also the possibility that the target may not be vulnerable or may turn out to be a mere distraction.

In order to use this technique it will be great to explain what buffer overflow is.

A buffer overflow is a type of software vulnerability that occurs when data overflows from one buffer (a temporary data storage area in computer memory) into another adjacent memory location. It is a common security flaw that can lead to serious security breaches and system crashes. Here’s a more detailed explanation of buffer overflows:

  1. Buffers: Buffers are used in computer programs to store data temporarily. They have a fixed size and are typically used for tasks like reading data from files, receiving input from users, or processing network data.
  2. Vulnerability: The vulnerability arises when a program writes more data into a buffer than it can hold. This can happen due to programming errors, lack of proper input validation, or malicious intent.
  3. Overflow: When more data is written into a buffer than its capacity allows, the excess data spills over into adjacent memory locations. These memory locations may contain critical data, control structures, or even code.
  4. Exploitation: In a security context, attackers can take advantage of buffer overflows by carefully crafting input data to overwrite critical data or manipulate the program’s execution flow. This can lead to arbitrary code execution, denial of service, or unauthorized access to a system.

Buffer overflows can have severe security implications, allowing attackers to gain unauthorized access, execute arbitrary code, or crash systems. It’s crucial for software developers to follow best practices in coding and input validation and for system administrators to keep systems updated with security patches to mitigate these vulnerabilities.

In order to exploit this vulnerability, it is useful to check the file:

Let me use “python print()” oneliner:

As you can see we can use such technique to calculate the offset, but suppose that the written code was not given to us. We could use “gdb” debugger.

Now, you can switch to intel, this makes it easier to understand the code.

let me step by step explain the code.

  • 0x00000000000007fa <+0>: push rbp: This instruction pushes the value of the base pointer register (rbp) onto the stack. It's commonly used to save the previous value of rbp before setting it to a new value for a function's local stack frame.
  • 0x00000000000007fb <+1>: mov rbp, rsp: This instruction copies the value of the stack pointer (rsp) into the base pointer register (rbp). This typically establishes a new stack frame for a function.
  • 0x00000000000007fe <+4>: sub rsp, 0x260: This instruction subtracts 0x260 (or 608 in decimal) bytes from the stack pointer, allocating space on the stack for local variables or buffers.
  • 0x0000000000000805 <+11>: lea rdi, [rip+0x128]: This instruction loads the effective address (lea) of the string located at rip + 0x128 into the destination register rdi. This is often used to set up the first argument for a function call, typically used for printing a string.

Oke, I am now going to run the file while using GDB. Do you remember? That the application could not be pushed 1000 characters because of crashing the application.

The goal is just fuzzing the application. I stored the “A” in a file called “crash.in” Now, I am going to use GDB to run the file.

X → integer, print as hexadecimal

RBP -> rbp is the base pointer, which points to the base of the current stack frame

Printing 100 bytes from the top of the stack.

In order to calculate the offset, i will use “pattern_create”

  1. Saved Frame/Base Pointer Overwrite: On a 64-bit machine, the base pointer (rbp register) typically plays a crucial role in maintaining the structure of the stack frame. It's used to locate local variables and navigate the function's call stack. In your analysis, it's noted that the value stored in the rbp register will be overwritten if we write past 608 bytes into the stack.
  2. Addresses are 8 Bytes: In a 64-bit machine, memory addresses are 8 bytes long. This is because 64-bit architectures can handle larger memory spaces compared to 32-bit architectures.
  3. Overwriting Saved Frame/Base Pointer: To overwrite the “saved frame/base pointer,” we need to write 608 bytes because the rbp is at an offset of 608 bytes from the beginning of the stack frame.
  4. Saved Return Address Overwrite: Just above the “saved frame/base pointer” lies the “saved return address.” In stack-based buffer overflows, overwriting this address is often a crucial step in hijacking program control flow. To overwrite the “saved return address,” we need to write an additional 8 bytes beyond the 608 bytes required to overwrite the rbp.
  5. Endianess: The machine is described as “little endian.” This means that in memory, the least significant byte of a multi-byte value is stored at the lowest memory address. This affects how data is stored and retrieved from memory.

So, in summary, to exploit a buffer overflow vulnerability on this 64-bit little-endian machine:

  • Write 608 bytes to overwrite the “saved frame/base pointer” (rbp).
  • Write an additional 8 bytes to overwrite the “saved return address.”
  • By overwriting the return address, an attacker can potentially control the program’s execution flow and execute arbitrary code.
  1. Register RBP and Memory Structure: The value stored in the rbp register is crucial for maintaining the stack frame's integrity. During analysis using Metasploit's "pattern_offset.rb" script, it was determined that overwriting the "saved frame/base pointer" requires writing beyond 608 bytes into the stack.
  2. Address Size in a 64-Bit Machine: In a 64-bit architecture, memory addresses are 8 bytes in length. This larger address size allows the system to access a significantly larger memory space than in 32-bit architectures.
  3. Bytes Required for Overwrite: To successfully overwrite the “saved frame/base pointer” in the stack, an attacker needs to write 616 bytes. This accounts for the 608 bytes to reach the rbp and an additional 8 bytes to reach the "saved return address," which is located just above the rbp.
  4. Canonical Addresses: It’s important to note that only canonical addresses can be used to overwrite the “saved return address.” Canonical addresses adhere to certain bit patterns to ensure proper memory access and security.

(python -c “print(‘A’*608 + ‘B’*8 + ‘C’*5 + ‘\x00’*2)”)

  1. We already know that the offset is: 608
  2. I added 8 times of B that will be our RBP
  3. when you store 5 times C that will be the RSP thus stack pointer

Our goal is to reach RIP pointer. We need to overwrite the RIP in order to execute and control the RIP.

Awesome! As you can see when we use this oneliner:

(python -c “print(‘A’*608 + ‘B’*8 + ‘C’*6 + ‘\x00’*2)”) > rip.txt

  • I added the word “occurrences” to clarify that you are referring to the number of times “A,” “B,” and “C” were stored.
  • I placed single quotation marks around the letters “A,” “B,” and “C” to indicate that they are characters or placeholders.
  • I rephrased the sentence slightly for clarity.

Let me demonstrate the code:

This Python code appears to be creating a payload for a buffer overflow or shellcode injection exploit. Let’s break down the code and understand what it does:


from struct import pack

This line imports the `pack` function from the `struct` module. This function is used for packing data into bytes, which is commonly used in exploiting vulnerabilities like buffer overflows.


buf=”\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05"

The `buf` variable contains a sequence of hexadecimal values that represent machine code instructions. This sequence is often referred to as “shellcode.” Shellcode is typically used to spawn a shell or execute specific actions when injected into a vulnerable program.

Here’s a rough breakdown of what this shellcode does:
- It prepares registers and memory with specific values to invoke the `execve` system call to execute a shell.
- It sets up the registers with the necessary arguments (e.g., the path to “/bin/sh”) for the `execve` call.
- It makes a syscall (system call) to execute the shell.


payload=”\x90"*400

The `payload` variable contains 400 bytes of the hexadecimal value `\x90`, which corresponds to the NOP (No-Operation) instruction. NOPs are often used as padding to create space in the exploit payload.


payload += buf

This line appends the `buf` (shellcode) to the `payload`.


payload += “A” * (208 -len(buf))

This part adds “A” characters to the `payload` to reach a total length of 208 bytes. The length of the `buf` is subtracted from 208 to determine how many “A” characters are needed for padding.


payload += “B” * 8

This adds 8 “B” characters to the `payload`.


payload += pack(“<Q”, 0x7fffffffe300)

This line uses the `pack` function to convert the hexadecimal value `0x7fffffffe300` into little-endian format (the `<` specifies little-endian) and appends it to the `payload`. This value is often used to overwrite a return address on the stack, potentially redirecting program execution.


print payload

Finally, the code prints the resulting payload, which is a combination of NOPs, shellcode, padding, and a value that may be used for overwriting a return address. This payload could be injected into a vulnerable program to exploit a buffer overflow vulnerability and execute arbitrary code.

Awesome!

Path Manipulation

The next level will be about path manipulation, but let me explain a bit about this technique:

Privilege escalation can occur through the manipulation and exploitation of the PATH variable in Linux. To grasp this concept, it’s essential to understand the purpose of the PATH variable itself.

Typically, the PATH variable resembles something like this: `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin`. You can view it by running the command `echo $PATH` in the terminal.

Running a binary, such as `ps`, is straightforward. You can simply enter `ping` into the terminal, regardless of whether the binary is in the current working directory (cwd) or not. This ease of execution stems from the fact that the `ps` binary is located in `/usr/bin`, a directory included in the PATH variable. The Linux operating system systematically scans all directories listed in the PATH variable, searching for the binary (and executing it if found).

The search process follows a left-to-right sequence. In other words, the OS first checks in `/usr/local/sbin`, then in `/usr/local/bin`, followed by `/usr/sbin`, and so on.

However, if the binary is not discovered in any of the specified directories, the system will issue an error message (e.g., `xyz: command not found`).

  1. setuid(0) and setgid(0):
  • These functions are used to set the effective user ID (UID) and effective group ID (GID) of the current process to 0.
  • A UID of 0 typically represents the “root” user, which has superuser privileges and can perform actions with elevated permissions.
  • Similarly, setting the GID to 0 means that the process belongs to the root group.
  1. system("ps"):
  • The system function in C is used to execute a shell command as if it were typed in the terminal.
  • In this case, it executes the command “ps,” which is a common Unix/Linux command used to list running processes.
  • Since the program has been granted superuser privileges via setuid(0) and setgid(0), it can execute the "ps" command with root privileges.

In summary, this C program first elevates its permissions by setting its UID and GID to 0 (root). Then, it uses these elevated privileges to execute the “ps” command, which lists the running processes on the system. This code demonstrates how an application can escalate its privileges to perform actions that would typically require superuser access. However, it’s important to note that using setuid(0) and setgid(0) should be done with great caution, as it can introduce security vulnerabilities if not properly controlled and validated.

A use-case for that privilege escalation (PE) method is a binary that has the SUID bit set and executes another program (eg: ps).

An attacker logged in on a server as a non-root user could now manipulate his PATH variable by adding the home directory of the user to the front of it.

This can be done with export PATH=/home/kel:$PATH (where /home/kel is my home directory).

The binary executing ps would now search for that binary in /home/kel first and if it finds it execute it.

If (like above) the PATH variable got modified the attacker can execute his code by creating another file called “ps” in /home/kel. Since Linux will first look for the ps binary in /home/kel and then move on this file would get executed instead of the original file.

For example in /home/kel the attacker creates a file called “ps” containing the following code:

Awesome!

Thank you for taking the time to explore this technical blog! I hope you found it both informative and engaging. I aimed to provide practical explanations in an accessible manner. Should you have any questions or require further clarification on any topic discussed, please don’t hesitate to reach out to me via social media.

Social Media

Twitter: https://twitter.com/lockpin010_

LinkedIn: https://www.linkedin.com/in/ahmetgoker/

Ahmet | Security Researcher | Sociologist

--

--

Ahmet Göker
Ahmet Göker

Written by Ahmet Göker

Full stack Reverser | C-ASM | Security

No responses yet