ARM64 Reversing And Exploitation – Part 10 – Intro To Arm Memory Tagging Extension (MTE)

IoT 5个月前 admin
26 0 0

Hey all! In this blog, we will give a brief introduction to a relatively new security feature called MTE (Memory Tagging Extension). Even though it was announced years ago, there was no implementation of this. But recently, the Google Pixel 8 devices have implemented support for these.
大家好!在这篇博客中,我们将简要介绍一种相对较新的安全功能,称为 MTE(内存标记扩展)。尽管它是几年前宣布的,但没有实施。但最近,谷歌 Pixel 8 设备已经实现了对这些的支持。

Memory Tagging Extension
内存标记扩展

So what is MTE ?
那么什么是MTE?

Memory Tagging Extension (MTE) is a feature introduced in ARMv8.5-A architecture, and it helps in detecting and preventing certain types of memory safety issues, such as out-of-bounds memory accesses, buffer overflows, use after free, double free etc. MTE is also known as “ASAN on steroids.” We are aware that ASAN can be used to detect memory corruptions but cannot be deployed in production devices due to performance issues. However, in the case of MTE, it’s a hardware feature, so it won’t affect performance. Even though there are other protection mechanisms like ASLR, Stack canary, PIE etc, the ability of MTE to detect memory corruption exploitation takes it into the next level. Having MTE on a device is a big improvement, and it can help make it a lot tougher to exploit 0-days. As the name suggests, MTE uses tags that marks each memory allocation/deallocation with additional metadata. It assigns a tag to a memory location, which then can be associated with pointers that reference that memory location.
内存标记扩展 (MTE) 是 ARMv8.5-A 架构中引入的一项功能,它有助于检测和防止某些类型的内存安全问题,例如越界内存访问、缓冲区溢出、释放后使用、双重释放等。 MTE 也被称为“类固醇上的 ASAN”。我们知道,ASAN 可用于检测内存损坏,但由于性能问题,无法部署在生产设备中。但是,就 MTE 而言,它是一种硬件功能,因此不会影响性能。尽管还有其他保护机制,如 ASLR、Stack Canary、PIE 等,但 MTE 检测内存损坏利用的能力将其提升到了一个新的水平。在设备上安装 MTE 是一项重大改进,它可以帮助使利用 0 天变得更加困难。顾名思义,MTE 使用标记,用额外的元数据标记每个内存分配/释放。它将标记分配给内存位置,然后可以将其与引用该内存位置的指针相关联。

There are two ways to implement the tags.
有两种方法可以实现标记。

  • A 4-bit tag is stored in the top byte of a pointer.
    4 位标记存储在指针的顶部字节中。

    • In ARM64 architecture, it uses 64-bit pointers to access memory. However, only 48 to 52 of these bits are used. In userspace, 48 bits are in use, while the remaining 16 bits doesn’t serve any purpose. The MTE make use of this bits to implement the tags. MTE stores a four-bit “key” in the lower nibble in the top byte of an address. This key is considered as the tag.
      在 ARM64 体系结构中,它使用 64 位指针来访问内存。但是,仅使用了其中的 48 到 52 个位。在用户空间中,有 48 位正在使用中,而其余 16 位没有任何用途。MTE 利用此位来实现标记。MTE 将一个四位“密钥”存储在地址顶部字节的下半字节中。此键被视为标记。

  • A 4-bit tag is created and separately for every 16 byte aligned memory.
    创建一个 4 位标签,并为每个 16 字节对齐的内存单独创建。

    • In this implementation, MTE generates a unique 4-bit tag for each 16-byte aligned memory block. MTE assigns a special 4-bit tag to each block of 16 bytes in memory. This way, MTE can keep track of different memory areas in small chunks. It’s like putting a label on every 16-byte piece of memory.
      在此实现中,MTE 为每个 16 字节对齐的内存块生成一个唯一的 4 位标记。MTE 为内存中每个 16 字节的块分配一个特殊的 4 位标记。这样,MTE 就可以以小块的形式跟踪不同的内存区域。这就像在每个 16 字节的内存上贴上一个标签。

There is a set of instruction associated with this.
有一组与此相关的指令。

Instruction 指令 Name 名字 Format 格式
ADDG Add with Tag 添加标签 ADDG <Xd/SP>, <Xn/SP>, #<uimm6>, #<uimm4>
CMPP Compare with Tag 与 Tag 比较 CMPP <Xn/SP>, <Xm/SP>
GMI Tag Mask Insert 标签掩码插入 GMI <Xd>, <Xn/SP>, <Xm>
IRG Insert Random Tag 插入随机标签 IRG <Xd/SP>, <Xn/SP>{, <Xm>}
LDG Load Allocation Tag 负载分配标签 LDG <Xt>, [<Xn/SP>{, #<simm>}]
LDGV Load Tag Vector 加载标签向量 LDGV <Xt>, [<Xn/SP>]!

If you’re interested in other MTE instructions, check out wikichip. The IRG and STG instructions are the most important ones here. IRG will generate a random key and store it into an address. STG accepts a pointer value and assigns the key from that pointer to the 16-byte memory it points to.
如果您对其他 MTE 说明感兴趣,请查看 wikichip。 IRG 和 STG 说明是这里最重要的说明。 IRG 将生成一个随机密钥并将其存储到一个地址中。 STG 接受指针值,并将该指针中的键分配给它所指向的 16 字节内存。

Now let’s see an example for an heap overflow and how MTE prevents it.
现在,让我们看一个堆溢出的示例,以及 MTE 如何防止它。

(Image is taken from https://hackmd.io/@mhyang/rJ5JOnWvv)
(图片取自 https://hackmd.io/@mhyang/rJ5JOnWvv)

When dealing with heap allocations, memory is aligned by 16 bytes, and a 4-bit tag is chosen. Above, we can see that both the pointer and the memory associated with it are tagged, represented by the color green. The pointer p attempts to allocate 20 bytes of memory, resulting in 32 bytes being returned due to the 16-byte alignment. If you examine the pointer, you’ll notice the tag at the top byte, indicated by the green color. When the pointer attempts to access memory outside of the tagged green region, the program traps because the tags don’t match.
在处理堆分配时,内存按 16 个字节对齐,并选择一个 4 位标记。在上面,我们可以看到指针和与之关联的内存都被标记了,用绿色表示。指针 p 尝试分配 20 个字节的内存,由于 16 字节的对齐方式,导致返回 32 个字节。如果检查指针,您会注意到顶部字节处的标记,由绿色表示。当指针尝试访问标记的绿色区域之外的内存时,程序会捕获,因为标记不匹配。

Let’s also see an example for use-after-free.
我们再看一个释放后使用的示例。

(Image is taken from https://hackmd.io/@mhyang/rJ5JOnWvv)
(图片取自 https://hackmd.io/@mhyang/rJ5JOnWvv)

Similar to the above example, p is allocating 20 bytes of memory, resulting in 32 bytes due to 16-byte alignment. Both the pointer and the memory are tagged, represented using the color green. Now, when this memory is freed using the delete() operator, the memory is retagged with a different tag. Note that p still is represented by green. However, the memory is indicated by purple. If the pointer tries to access the memory again, the program will trap as the tags are different.
与上面的示例类似,分配了 20 个字节的内存, p 由于 16 字节的对齐,导致 32 个字节。指针和内存都进行了标记,用绿色表示。现在,当使用 delete() 运算符释放此内存时,内存将使用不同的标记重新标记。请注意, p 仍然用绿色表示。但是,内存用紫色表示。如果指针再次尝试访问内存,程序将因标记不同而陷入困境。

Modes In MTE MTE 中的模式

MTE has three operating modes.
MTE 有三种操作模式。

  1. Synchronous Mode (SYNC): 同步模式 (SYNC):

    • It focuses on catching bugs accurately, even if it means sacrificing a bit of speed.
      它专注于准确地捕捉错误,即使这意味着牺牲一点速度。

    • Acts like a bug detection tool and stops the program immediately if there’s a tag mismatch.
      就像一个错误检测工具,如果出现标签不匹配,会立即停止程序。

    • Good for testing and in real use, especially for places where attacks might happen.
      适合测试和实际使用,尤其是可能发生攻击的地方。

    • Gives you detailed reports to help find and fix bugs easily.
      为您提供详细的报告,帮助您轻松查找和修复错误。

  2. Asynchronous Mode (ASYNC):
    异步模式 (ASYNC):

    • Cares more about keeping things fast than catching every bug.
      比起抓住每一个错误,更关心的是保持速度。

    • If there’s a tag mismatch, it keeps going until it hits a certain point, then stops with minimal info.
      如果标签不匹配,它会一直持续到达到某个点,然后以最少的信息停止。

    • Best for well-tested systems where there aren’t many memory bugs expected.
      最适合经过良好测试的系统,预计不会出现很多内存错误。

  3. Asymmetric Mode (ASYMM): 非对称模式 (ASYMM):

    • A newer feature in Arm v8.7-A, it checks memory reads closely and memory writes not so much.
      Arm v8.7-A 中的一项新功能是,它密切检查内存读取,而内存写入则不那么多。

    • Works fast like ASYNC but has more coverage.
      像 ASYNC 一样工作速度快,但覆盖范围更大。

    • Can be set up in the operating system to work smoothly.
      可以在操作系统中设置以顺利工作。

Example 

Let’s see a small example program to understand MTE better.
让我们看一个小示例程序来更好地理解 MTE。

Consider the following C program, taken from here. We have made slight modifications to the source code.
考虑以下 C 程序,取自此处。我们对源代码进行了轻微的修改。

/*
 * Memory Tagging Extension (MTE) example for Linux
 *
 * Compile with gcc and use -march=armv8.5-a+memtag
 *    gcc mte-example.c -o mte-example -march=armv8.5-a+memtag
 *
 * Compilation should be done on a recent Arm Linux machine for the .h files to include MTE support.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/prctl.h>

/*
 * Insert a random logical tag into the given pointer.
 * IRG instruction.
 */
#define insert_random_tag(ptr) ({                       \
        uint64_t __val;                                 \
        asm("irg %0, %1" : "=r" (__val) : "r" (ptr));   \
        __val;                                          \
})

/*
 * Set the allocation tag on the destination address.
 * STG instruction.
 */
#define set_tag(tagged_addr) do {                                      \
        asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \
} while (0)

int main(void)
{
    unsigned char *ptr;   // pointer to memory for MTE demonstration
    int index;
    int data;
    /*
     * Use the architecture dependent information about the processor
     * from getauxval() to check if MTE is available.
     */
    if (!((getauxval(AT_HWCAP2)) & HWCAP2_MTE))
    {
        printf("MTE is not supported\n");
        return EXIT_FAILURE;
    }
    else
    {
        printf("MTE is supported\n");
    }

    /*
     * Enable MTE with synchronous checking
     */
    if (prctl(PR_SET_TAGGED_ADDR_CTRL,
              PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | (0xfffe << PR_MTE_TAG_SHIFT),
              0, 0, 0))
    {
            perror("prctl() failed");
            return EXIT_FAILURE;
    }

    /*
     * Allocate 1 page of memory with MTE protection
     */
    ptr = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ | PROT_WRITE | PROT_MTE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (ptr == MAP_FAILED)
    {
        perror("mmap() failed");
        return EXIT_FAILURE;
    }

    /*
     * Print the pointer value with the default tag (expecting 0)
     */
    printf("pointer is %p\n", ptr);


    ptr = (unsigned char *) insert_random_tag(ptr);

    /*
     * Set the key on the pointer to match the lock on the memory  (STG instruction)
     */
    set_tag(ptr);

    /*
     * Print the pointer value with the new tag
     */
    printf("pointer is now %p\n", ptr);

    /*
     * /*
     * Write to memory beyond the 16 byte granule (offsest 0x10)
     * MTE should generate an exception
     * If the offset is less than 0x10 no SIGSEGV will occur.
     */


    printf("Enter the index to insert data : ");
    scanf("%d",&index);
    printf("Enter the data to insert : ");
    scanf("%d",&data);
    ptr[index] = data;

    /*
     * Program only reaches this if no SIGSEGV occurs
     */
    printf("...no SIGSEGV was received\n");

    return EXIT_FAILURE;
}

The code demonstrates the use of the Memory Tagging extension in ARM64. Let’s examine the code.
该代码演示了如何在 ARM64 中使用内存标记扩展。让我们检查一下代码。

#define insert_random_tag(ptr) ({                       \
        uint64_t __val;                                 \
        asm("irg %0, %1" : "=r" (__val) : "r" (ptr));   \
        __val;                                          \
})

This micro inserts a random tag to a pointer using the irg instruction we saw above.
这个 micro 使用我们上面看到的 irg 指令将一个随机标签插入到指针。

#define set_tag(tagged_addr) do {                                      \
        asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \
} while (0)

This micro sets the tag to the memory address using the stg store with tag instruction.
此 micro 使用 stg 带有标签的存储指令将标签设置为内存地址。

In the main function, the first it checks is if MTE is supported or not.
在 main 函数中,它首先检查的是是否支持 MTE。

if (!((getauxval(AT_HWCAP2)) & HWCAP2_MTE))
{
    printf("MTE is not supported\n");
    return EXIT_FAILURE;
}
else
{
    printf("MTE is supported\n");
}

The next two blocks will configure MTE and allocate a page.
接下来的两个块将配置 MTE 并分配一个页面。

if (prctl(PR_SET_TAGGED_ADDR_CTRL,
          PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | (0xfffe << PR_MTE_TAG_SHIFT),
          0, 0, 0))
{
        perror("prctl() failed");
        return EXIT_FAILURE;
}

/*
 * Allocate 1 page of memory with MTE protection
 */
ptr = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ | PROT_WRITE | PROT_MTE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED)
{
    perror("mmap() failed");
    return EXIT_FAILURE;
}

After this, the pointer will be given a random tag using the insert_random_tag macro, and the tag is also set on the memory using the set_tag macro. Next, the program asks us to enter an index and data.
在此之后,将使用宏为指针分配一个随机标签,并且该标签也使用 insert_random_tag set_tag 宏在内存上设置。接下来,程序要求我们输入索引和数据。

printf("...no SIGSEGV was received\n");

This line won’t get executed if the program has a segmentation fault.
如果程序存在分段错误,则不会执行此行。

We can cross compile and try this out. For this, we can install the aarch64 cross compiler using the below command.
我们可以交叉编译并尝试一下。为此,我们可以使用以下命令安装 aarch64 交叉编译器。

sudo apt install gcc-aarch64-linux-gnu

You should also need qemu to emulate this program on aarch64.
您还需要 qemu 在 aarch64 上模拟此程序。

sudo apt install qemu-user-static

Now let’s run this cross compile this.
现在让我们运行这个交叉编译这个。

arch64-linux-gnu-gcc mte.c -o mte -march=armv8.5-a+memtag -static

Let’s run this using qemu.
让我们使用 qemu 运行它。

8ksec@pop-os:~/Desktop/mte$ qemu-aarch64-static ./mte
MTE is supported
pointer is 0x5500802000
pointer is now 0x600005500802000
Enter the index to insert data :

We can see that before inserting the tag, the pointer was 0x5500802000. After inserting the tag, the pointer is 0x600005500802000. The tag is inserted at the top byte. Now the program asks for an index to input our data. Also, the tag is random. If we run again we will get a different tag.
我们可以看到,在插入标签之前,指针是 0x5500802000 .插入标签后,指针为 0x600005500802000 .标记插入到顶部字节。现在程序要求一个索引来输入我们的数据。此外,标签是随机的。如果我们再次运行,我们将得到一个不同的标签。

8ksec@pop-os:~/Desktop/mte$ qemu-aarch64-static ./mte
MTE is supported
pointer is 0x5500802000
pointer is now 0xa00005500802000
Enter the index to insert data :

We are aware that MTE is enabled, right ? Therefore, the memory will be 16-byte aligned. So if we try to write outside the 16 byte (Index : 0 to 15 ) boundary, the program will trap. Let’s try this.
我们知道 MTE 已启用,对吧?因此,内存将是 16 字节对齐的。因此,如果我们尝试在 16 字节(索引:0 到 15 )边界之外写入,程序将捕获。让我们试试这个。

8ksec@pop-os:~/Desktop/mte$ qemu-aarch64-static ./mte
MTE is supported
pointer is 0x5500802000
pointer is now 0xa00005500802000
Enter the index to insert data : 16
Enter the data to insert : 2
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault (core dumped)

As we can see, a segmentation fault has occurred.
正如我们所看到的,发生了分段错误。

Let’s run the program again and enter an index below 16.
让我们再次运行程序并输入低于 16 的索引。

ad2001@pop-os:~/Desktop/mte$ qemu-aarch64-static ./mte
MTE is supported
pointer is 0x5500802000
pointer is now 0x300005500802000
Enter the index to insert data : 15
Enter the data to insert : 3
...no SIGSEGV was received

There’s no segfault this time.
这次没有段错误。

Setting Up MTE In Android
在 Android 中设置 MTE

Currently, only the Pixel 8 devices in the market have support for MTE, but we will see many more devices with MTE in the future. Let’s see how this is setup in a pixel device.
目前,市场上只有 Pixel 8 设备支持 MTE,但未来我们将看到更多配备 MTE 的设备。让我们看看如何在像素设备中设置它。

First turn your USB debugging and connect the device to your pc. Get a shell using adb.
首先打开USB调试并将设备连接到PC。使用 adb 获取 shell。

arm64:/$ setprop arm64.memtag.bootctl memtag

arm64:/$ setprop persist.arm64.memtag.default sync

arm64:/$ setprop persist.arm64.memtag.app_default sync

arm64:/$ reboot
  • First, we configure the bootloader to turn on MTE during boot.
    首先,我们将引导加载程序配置为在引导期间打开 MTE。

  • The second command sets the default MTE mode for regular programs on the device.
    第二个命令为设备上的常规程序设置默认 MTE 模式。

  • The third command sets the default MTE mode for apps.
    第三个命令为应用程序设置默认 MTE 模式。

  • App developers can individually enable MTE in their app settings, but the system property makes it the default for apps, meaning it’s active unless developers choose otherwise.
    应用开发者可以在其应用设置中单独启用 MTE,但系统属性使其成为应用的默认属性,这意味着除非开发者另有选择,否则它处于活动状态。

After reboot you can check if MTE is enabled.
重新启动后,您可以检查是否启用了 MTE。

arm64$ getprop | grep memtag

[arm64.memtag.bootctl]: [memtag]

[persist.arm64.memtag.app.com.android.nfc]: [off]

[persist.arm64.memtag.app.com.android.se]: [off]

[persist.arm64.memtag.app.com.google.android.bluetooth]: [off]

[persist.arm64.memtag.app_default]: [sync]

[persist.arm64.memtag.default]: [sync]

[persist.arm64.memtag.system_server]: [off]

[ro.arm64.memtag.bootctl_supported]: [1]

We can some of them are still disabled. Let’s check if it’s working for native executables.
我们可以其中一些仍然被禁用。让我们检查一下它是否适用于本机可执行文件。

arm64:/ $ cat /proc/self/smaps | grep mt

VmFlags: rd wr mr mw me ac mt

VmFlags: rd wr mr mw me ac mt

VmFlags: rd wr mr mw me ac mt

VmFlags: rd wr mr mw me ac mt

VmFlags: rd wr mr mw me ac mt

VmFlags: rd wr mr mw me ac mt

VmFlags: rd wr mr mw me ac mt

We can see that the cat process has mappings with the mt bit set, so MTE has been enabled.
我们可以看到 cat 进程具有与 mt 位集的映射,因此已启用 MTE。

You can also test this with the below application.
您也可以使用以下应用程序进行测试。

https://play.google.com/store/apps/details?id=com.sanitizers.app.production

ARM64 Reversing And Exploitation – Part 10 – Intro To Arm Memory Tagging Extension (MTE)

Conclusion 结论

Currently, MTE has support only for Pixel devices, but we can expect that various devices will adopt it in the future. This could bring about a significant change in the 0-day space, making exploitation more challenging for hackers. For further information, please refer to the provided source below.
目前,MTE 仅支持 Pixel 设备,但我们可以预期未来各种设备将采用它。这可能会给 0 天空间带来重大变化,使黑客的漏洞利用更具挑战性。有关更多信息,请参阅下面提供的来源。

原文始发于8ksecresearch :ARM64 Reversing And Exploitation – Part 10 – Intro To Arm Memory Tagging Extension (MTE)

版权声明:admin 发表于 2023年12月12日 下午2:11。
转载请注明:ARM64 Reversing And Exploitation – Part 10 – Intro To Arm Memory Tagging Extension (MTE) | CTF导航

相关文章

暂无评论

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