Skip to content
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

Add kernelCTF CVE-2023-52620_lts_cos_mitigation #117

Merged
merged 6 commits into from
Dec 10, 2024

Conversation

mingi
Copy link
Contributor

@mingi mingi commented Jul 22, 2024

No description provided.

Copy link
Collaborator

@koczkatamas koczkatamas left a comment

Choose a reason for hiding this comment

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

Hey!

Thank you for your submission!

I've reviewed the submission, both the writeup and the code was very clear and readable, I consider this a high quality submission, congratulations!

I still requested few changes for extra details and some explanations - please see them below.

There should be 36 comments (don't worry, some of them are same just repeated for the 3 exploits), please make sure you open all comments as the Github UI only shows 10 comments by default.

};
```

When the vulnerability is triggered, the reference counter of `nft_chain` is decremented twice. Therefore, we used `immediate expr` to create a dangling pointer referencing this victim `nft_chain`. After binding `immediate expr` to the victim `nft_chain`, the dangling pointer is created by triggering the vulnerability to free the victim chain. The name of the freed chain can then be read through `immediate expr`. We spray `nft_expr` (`kmalloc-cg-16`) and `nft_rule` (`kmalloc-cg-96`) to the freed `chain->name` to read `nft_expr->ops` and `nft_rule->list` to get the kernel text address and heap address of `kmalloc-cg-96`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please mention that immediate expr is internally stored as a struct nft_expr with the data field containing a nft_immediate_expr and the mentioned nft_chain is reachable via nft_immediate_expr.data.verdict.chain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the description as you mentioned.


read(sock, buffer, 4);

sleep(3);
Copy link
Collaborator

@koczkatamas koczkatamas Sep 25, 2024

Choose a reason for hiding this comment

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

Please comment here what are you waiting for. Mention the name of the kernel function(s) you are waiting for to be ran and/or which objects to be freed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed it because it is unnecessary sleep.

err(1, "mnl_socket_send");
}

usleep(10*1000);
Copy link
Collaborator

@koczkatamas koczkatamas Sep 25, 2024

Choose a reason for hiding this comment

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

Please comment here what are you waiting for. Mention the name of the kernel function(s) you are waiting for to be ran and/or which objects to be freed.


del_chain(trig_chain_name);

usleep(300*1000);
Copy link
Collaborator

@koczkatamas koczkatamas Sep 25, 2024

Choose a reason for hiding this comment

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

Please comment here what are you waiting for. Mention the name of the kernel function(s) you are waiting for to be ran and/or which objects to be freed.

err(1, "mnl_socket_send");
}

usleep(10*1000);
Copy link
Collaborator

@koczkatamas koczkatamas Sep 25, 2024

Choose a reason for hiding this comment

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

Please comment here what are you waiting for. Mention the name of the kernel function(s) you are waiting for to be ran and/or which objects to be freed.


# Post-RIP

The ROP payload is stored in `chain->blob_gen_0` allocated in `kmalloc-cg-192` and the leaked heap address allocated in `kmalloc-cg-96`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Mention here that on COS kmalloc-cg-192 contains the fake chain->blob_gen_0 aka struct nft_rule_blob including the fake struct nft_expr with the ops pointer pointing to the leaked kmalloc-cg-96 address and then the first part of the ROP chain. Also detail that kmalloc-cg-96 contains the fake struct nft_expr_ops stored at the beginning and then the second part of the ROP chain.

Mention that where RBX points to when eval() is called. I think on LTS it points to the fake struct nft_expr (so to kmalloc-cg-192 + 0x08), while on COS it seems to be pointing to kmalloc-cg-192 + 0x00.

Also detail the differences between the ROP chains on LTS and COS (as they are different).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the description as you mentioned.

int i = 0;

// expr->ops->eval()
data[i++] = kbase + PUSH_RBX_POP_RSP;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Rename PUSH_RBX_POP_RSP to PUSH_RBX_POP_RBP_RSP as it is important that we are jumping to rbx+0x08 (== nft_expr+0x08 == rop_chain_192+0x18) here, not to rbx+0x00.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I renamed PUSH_RBX_POP_RSP to PUSH_RBX_POP_RSP_RBP because RSP is popped first.

};
```

When the vulnerability is triggered, the freed `chain->blob_gen_0` can be accessed via `immediate expr`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

If I understand correctly you are keeping the freed nft_chain in the freelist and its content is not overwritten, so the nft_chain.blob_gen_0 pointer still points to the freed nft_rule_blob structure which can be reallocated by spraying structures into the right cache. Is this assumption correct? First it was not clear if you spray nft_rule_blob or nft_chain.

If it is, then please explain these details here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, your understanding is right. I added an explanation as follows.

When the vulnerability is triggered, the freed chain->blob_gen_0 can be accessed via immediate expr. We leave the chain freed and spray an object to create a fake blob in blob_gen_0.

data[0] = kmalloc_192;
data[1] = 0;

spray_table(data, KMALLOC_32_SIZE);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Comment here what you are overwriting here in kmalloc-32-cg and which step created that structure (add a comment to the step where you are creating that structure that this structure will get into kmalloc-32-cg).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a comment that it creates a fake nft_chain.rules_gen_0.

}
```

`chain->blob_gen_0` is used in `nft_do_chain`, and `expr->ops->eval` is called to evaluate the expression in `expr_call_ops_eval`. We set the ops of the fake expr to the leaked heap address (LTS, COS) or CPU entry area (mitigation) to control the RIP. For LTS and COS kernel, the fake blob object with the fake expr is allocated in `kmalloc-cg-192`. For mitigation kernel, we allocate the fake blob object larger than 0x2000 to use page allocator.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Make a comment somewhere why you chose kmalloc-cg-192, if I understand correctly, you could've chosen different cache too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added the details why I selected that slab cache.

Copy link
Collaborator

@koczkatamas koczkatamas left a comment

Choose a reason for hiding this comment

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

Sorry, one additional question about triggering the vuln.

}
```

Then, when the gc is executed in `nft_set_commit_update`, `nft_trans_gc_catchall_sync` calls `nft_setelem_data_deactivate` to deactivate the objects mapped to the set element. As a result, the `nft_chain` or `nft_object` mapped to the set element can be deactivated twice.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you comment here more about how the reference counter changes of the nft_chain object?

Let's consider the chain_leak: you use for the immediate expr, so the ref. counter becomes 1, then you set up the anonymous set and the lookup expression for the chain (ref count becomes 2), and then you wait for the timeout (ref count becomes 1) and then how the lookup expression is destroyed? Is it during the del_chain call?

If yes, then the ref count becomes 0 at this point but the immediate expr is still referencing the nft_chain object?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The set element in the victim set that the lookup expr is binding to, not the lookup expr, is referencing the chain. If the gc is executed while deleting the lookup expr (deleting the lookup expr deactivates the bound set if it is an anonymous set), the set element will be deactivated twice, decreasing the reference count of the victim chain to twice.

I added a description about this to the exploit.md.

@koczkatamas koczkatamas force-pushed the master branch 2 times, most recently from b2e2b9f to 43a5a81 Compare September 27, 2024 21:30
@mingi
Copy link
Contributor Author

mingi commented Nov 20, 2024

Hello,

Thank you for reviewing my report and considering it as a high quality report.

I've updated the write-up base on your comments.

Please check it.

Thanks

@koczkatamas koczkatamas merged commit 9f97ffa into google:master Dec 10, 2024
9 checks passed
@mingi mingi deleted the CVE-2023-52620 branch December 11, 2024 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants