
-
메모리 무결성 강화로 앱 보호하기
하드웨어, 운영 체제, 컴파일러가 연동하여 유효하지 않은 메모리에 대한 액세스를 방지하고, 취약점이 악용되기 전에 프로그램 실행을 안전하게 중단하는 새로운 보안 기술인 메모리 무결성 강화(MIE)를 살펴보세요. MIE로 어떻게 공격자가 메모리 손상 버그를 악용하는 것이 훨씬 더 어려워지는지 확인해 보세요. 앱에 MIE를 도입하여 사용자와 데이터를 보호하는 동시에, 코드베이스에서 찾기 어려운 메모리 문제도 해결하는 방법을 알아보세요.
챕터
- 0:05 - Intro
- 1:29 - Memory overflow and use-after-free vulnerabilities
- 3:08 - Memory Integrity Enforcement
- 4:42 - Demo: Memory corruption use-after-free bug
- 5:52 - Enable Hardware Memory Tagging
- 6:20 - Demo: Hardware Memory Tagging interrupts exploit attempt
- 6:44 - Additional configuration options
- 7:25 - Demo: Fixing memory corruption bug
- 9:32 - Additional considerations
- 10:10 - Tag bits in pointers
- 10:30 - Hashing, comparison, and arithmetic of pointer values
- 11:02 - Soft Mode
- 11:31 - Next steps
리소스
-
비디오 검색…
Hello, my name is Julian. I'm an engineer on the developer security tools team. In this video, I will explain how you can secure your app with Memory Integrity Enforcement. Apps touch many parts of all of our lives. They are essential tools that everyone trusts with the private details of their life. Location and browsing history, photos, messages, contacts, finances, and so much more. At the same time, apps are connected to the internet, so security vulnerabilities in these apps can open users up to attack. The cost of attacks on individuals can range from fraud and identity theft to blackmail and even to threats on lives.
People expect their data to be kept private and secure. And it's a violation of trust if that promise is not upheld. Security is the technical foundation that enables privacy. Therefore, security is particularly important for messaging, social media, and browser apps. These apps handle large amounts of untrusted input and often have a broad reach that enables attackers to precisely target their victims. One of the most common security vulnerabilities is memory corruption. Attackers can take advantage of memory bugs to hijack control of an app and steal user-sensitive data. For example, buffer overflow corruptions ride past the bounds of a buffer and corrupt memory in another allocation. Then, when another pointer reads that memory, it can result in data corruption and hard-to-reproduce crashes. But even worse, by carefully crafting the write, an attacker may be able to trick your app to execute arbitrary code under their control.
Attackers can also exploit many use-after-free bugs. Suppose an app deallocates a chunk of memory but leaves a dangling pointer to it. Then, the app creates a new allocation, which happens to be placed at the same location in memory. If the app accidentally reads or writes through the dangling pointer, it will result in corrupted memory.
The best way to prevent memory corruption is to use memory-safe languages, such as Swift.
By managing memory for you, memory-safe languages ensure that programming mistakes cannot lead to memory corruption. But even if you're writing all of your new code in Swift, your app may still have some C and C++ code as part of an existing code base. Or it might rely on external libraries written in languages which do not provide memory safety. Memory Integrity Enforcement is a new technology that makes it very difficult for attackers to exploit memory corruption bugs. It is a major advancement in memory safety, where the hardware, the operating system, and the compiler all work together to prevent access to invalid memory by safely aborting program execution. The first supported devices are iPhone 17, iPhone Air, iPhone 17 Pro and Pro Max. Check out "Enabling enhanced security for your app" to learn which other Apple devices are supported. Here is how it works. Under Memory Integrity Enforcement, the system allocator assigns each heap allocation a tag, and also encodes that tag in the return pointer. On each load or store from memory, the hardware checks whether the tag in the pointer matches the tag of the allocation. For example, if a pointer with tag A is used to read or write memory that is also tagged A, the access is allowed to proceed.
But in a use-after-free scenario, the dangling A pointer will access a new allocation marked with a different tag. This is a tag mismatch, so the hardware aborts program execution, denying attackers the ability to corrupt memory. This approach protects against buffer overflow corruption as well, since the allocator assigns different tags to adjacent allocations. Here's my demo app that contains a memory corruption vulnerability. Note that the app itself is written in Swift, but also uses an external C library to parse images. I suspect that this library has a use-after-free bug, but I don't know where it is. I will run the app.
Looks like I've received a message. I want to see the photo, so I tap on it.
Oh no, what happened? It turns out that this wasn't just a photo of a cute dog, but rather a carefully crafted image sent by a malicious attacker. Behind the scenes, the image exploited a use-after-free bug to send my private messages to a server on the internet. This is very scary. Now, I've exaggerated the visual effect of this exploit. Attackers will usually try to keep the activity as hidden as possible.
In a more realistic attack, user data would be compromised without users even realizing it. To protect your app with Memory Integrity Enforcement in Xcode, go to the Signing and Capabilities Editor for your app target, click Add Capability, and select Enhanced Security. This will enable a number of powerful security protections, including Hardware Memory Tagging.
For local testing, check that Soft Mode for memory tagging is disabled. I will talk more about Soft Mode later. I've enabled hardware memory tagging and relaunched the app. I again tap on the malicious image.
Rather than allowing the attacker to steal my messages, the app now terminates. Clicking on the termination reason reveals that the app aborted because of a tag mismatch.
There are a few additional configuration options for hardware memory tagging. The "Memory Tag Pure Data" option extends protection to a wider set of allocations. If your app uses an interpreter or just-in-time compiler, then enable "Prevent Receiving Tagged Memory." Ensure that the Enhanced Security Type Allocator is enabled. It provides strong benefits against exploitation of use of the free bugs and can be combined with memory tagging for the best protection. Finally, there is the Soft Mode option that is used to validate your app's readiness for memory tagging. In my demo app, exploitation by attackers is now prevented. But the user experience isn't great. To prevent the app from crashing, I still need to fix the underlying memory corruption bug. Xcode helps you find and fix those bugs in your development environment before they affect your users. To do that, go to the Scheme Editor and enable Hardware Memory Tagging in the Diagnostics pane. I've enabled Hardware Memory Tagging Diagnostics in the Scheme Editor and ran the app again. I once again trigger the bug by tapping on the image. The app will again terminate, but now Xcode provides additional information to help me understand the problem. First, it notes that the tag mismatch is due to use of deallocated memory. A use-after-free. The debug navigator displays a helpful stack trace that shows me where the memory was deallocated. In this case, in the process_image_message function.
Going back to the point of the crash shows that it is in an asincurously dispatched block.
OK, I now understand what the problem is. The main thread deallocated the message before the background thread had a chance to process it. To fix the problem, I will move the deallocation of the message from the main thread to the end of the asynchronous block, so it is deallocated only after it has been processed.
I will launch the app again to make sure that my change fixed the underlying bug.
I will again tap on the image. Yep, that looks good. The app received the malicious image, but the attacker wasn't able to hijack the app with it.
Next, I will talk about a few more considerations on getting your app ready for Memory Integrity Enforcement. Now is the time to fix any remaining buffer overflow or use of the free bugs that occur during the app's normal usage. You can find and understand these bugs by enabling Hardware Memory Tagging diagnostics in your tests.
This will turn many hard-to-reproduce memory corruption bugs into actionable crashes.
If you don't have a device that supports Memory Integrity Enforcement, then use Address Sanitizer instead. Hardware memory tagging stores the tag in the upper bits of pointers to protect the allocations.
You need to ensure those bits are not used or modified by your app. If your app uses a custom-tagged pointer scheme, adapt it to store this information elsewhere.
It is also important to be careful when hashing, comparing, or doing arithmetic on pointers. With memory tagging enabled, pointers to different allocations will have distinct tags and therefore different high bits set.
Consider how this will affect hashing, comparison and arithmetic of pointer values.
Avoid comparison of pointers that originate from different allocations. And mask off tag bits when necessary.
Soft Mode helps you validate that you have found and fixed the memory corruption bugs in your app. It provides telemetry for tag mismatches in the form of simulated crash logs without terminating execution. Enable this in your test flight and customer populations to gain confidence that there are no remaining memory corruption bugs. Once your memory corruption bugs are fixed, disable Soft Mode to protect your users. Here are your next steps. If your app handles unvalidated inputs, adopt Memory Integrity Enforcement to protect your users. It is especially important for messaging, social media, and browser apps. Get your app ready by fixing known memory corruption bugs. And ensure your app isn't using pointed tag bits for other purposes.
Test your app with Hardware Memory Tagging diagnostics enabled. And validate your fixes using Soft Mode. Then disable soft mode to protect your users. Thanks for watching and for adopting Memory Integrity Enforcement.
-