Skip to content

Add two checks for zend_mm_heap's integrity #13943

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 23, 2024

Conversation

jvoisin
Copy link
Contributor

@jvoisin jvoisin commented Apr 11, 2024

No description provided.

@dstogov
Copy link
Member

dstogov commented Apr 12, 2024

Have you seen the related heap corruptions?

@jvoisin
Copy link
Contributor Author

jvoisin commented Apr 12, 2024

Not in production no, but given how easy it is to control php's heap, even in remote exploits, adding those checks to guard against unlink is a low-hanging fruit.

The performance impact should be in the noise level, since all the data used in the checks is accessed afterwards anyway.

Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see big problems accepting this. This shouldn't affect performance.

On the other hand, I'm not sure if this significantly increases the heap protection.
Adaptation of exploit to bypassing this protection would take 5 additional minutes.

@iluuu1994 @nielsdos @arnaud-lb what do you think?

@jvoisin
Copy link
Contributor Author

jvoisin commented Apr 17, 2024

The idea here is to defend against linear heap overflows providing a partial write, I don't see how this safe-unlink could be trivially bypassed :/

@dstogov
Copy link
Member

dstogov commented Apr 17, 2024

The idea here is to defend against linear heap overflows providing a partial write, I don't see how this safe-unlink could be trivially bypassed :/

Just write two addresses into the "poisoned chunk".

chunk->next = chunk;
chunk->prev = chunk;

@arnaud-lb
Copy link
Member

In addition to checking prev->next, PartitionAlloc stores a shadow pointer in the free block to detect corruptions. The original pointer is stored in big endian so that a partial corruption (like it is the case in the video) has more chance of resulting to an invalid address instead of an adjacent block. The XNU allocator used similar technics, and XORs the shadow with a secret.

This would make it considerably more difficult to successfully override a pointer in the freelist, but not if the attacker can override the entire block and was able to disclose the secret.

Ideally we should store the freelist externally in a bitmap so it can not be overriden with a linear overflow, but this is less ideal for performance.

We could also isolate types in different heaps to make type confusion/use after free more difficult, as in kalloc_type. An additional benefit of type isolation + freelist bitmap is that we woundn't need the object_store anymore to enumerate objects.

We could also allocate strings and array buckets in GigaCages so that a corrupt length doesn't allow to access anything else than other strings/array buckets.

I think the proposed change is already useful and could be merged as-is. It would be interesting to measure the overhead of the endianness trick, shadow, and shadow XOR technics.

@arnaud-lb arnaud-lb merged commit 07337df into php:master Apr 23, 2024
10 checks passed
@arnaud-lb
Copy link
Member

Thank you @jvoisin!

@jvoisin jvoisin deleted the heap_integrity branch April 26, 2024 13:51
@jvoisin jvoisin mentioned this pull request Apr 30, 2024
6 tasks
jvoisin added a commit to jvoisin/php-src that referenced this pull request May 27, 2024
This is in the same spirit as php#13943: low-hanging,
not in a hot-path, trivial, removing a limited-linear-write → arbitrary-write
primitive, … moreover, given how many filters are available, having some
low-hanging hardening there shouldn't hurt.

cc @arnaud-lb
jvoisin added a commit to jvoisin/php-src that referenced this pull request Jun 6, 2024
This is in the same spirit as php#13943: low-hanging,
not in a hot-path, trivial, removing a limited-linear-write → arbitrary-write
primitive, … moreover, given how many filters are available, having some
low-hanging hardening there shouldn't hurt.

cc @arnaud-lb
jvoisin added a commit to jvoisin/php-src that referenced this pull request Jun 6, 2024
This is in the same spirit as php#13943: low-hanging,
not in a hot-path, trivial, removing a limited-linear-write → arbitrary-write
primitive, … moreover, given how many filters are available, having some
low-hanging hardening there shouldn't hurt.

cc @arnaud-lb
jvoisin added a commit to jvoisin/php-src that referenced this pull request Nov 4, 2024
This is in the same spirit as php#13943: low-hanging,
not in a hot-path, trivial, removing a limited-linear-write → arbitrary-write
primitive, … moreover, given how many filters are available, having some
low-hanging hardening there shouldn't hurt.

cc @arnaud-lb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants