Linux kernel Tree Connect CIFS Remote Buffer Overflow
This is a pretty awesome bug found by fefe and affects almost every 2.6 kernel. This massive destruction bug can be found under fs/cifs/connect.c. Here is the code from 2.6.28.9 release of the Linux kernel:
3441 int
3442 CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
3443 const char *tree, struct cifsTconInfo *tcon,
3444 const struct nls_table *nls_codepage)
3445 {
3446 struct smb_hdr *smb_buffer;
3447 struct smb_hdr *smb_buffer_response;
3448 TCONX_REQ *pSMB;
3449 TCONX_RSP *pSMBr;
3450 unsigned char *bcc_ptr;
3451 int rc = 0;
3452 int length;
3453 __u16 count;
...
3458 smb_buffer = cifs_buf_get();
...
3561 if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
3562 length = UniStrnlen((wchar_t *) bcc_ptr, 512);
3563 if ((bcc_ptr + (2 * length)) -
3564 pByteArea(smb_buffer_response) <=
3565 BCC(smb_buffer_response)) {
3566 kfree(tcon->nativeFileSystem);
3567 tcon->nativeFileSystem =
3568 kzalloc(length + 2, GFP_KERNEL);
...
3579 /* else do not bother copying these information fields*/
...
3606 cifs_buf_release(smb_buffer);
3607 return rc;
3608 }
As you can see, if the user controlled buffer smb_buffer (line 3458) is Unicode (line 3561), it calculates its length (again, this is a signed integer for some reason but that’s irrelevant here) using UniStrnlen() and if this is less than, or equal to smb_buffer_response‘s bytes using BCC() macro which is defined as fs/cifs/cifspdu.h simply like this:
416 /* given a pointer to an smb_hdr retrieve the value of byte count */
417 #define BCC(smb_var) (*(__u16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount)))
418 #define BCC_LE(smb_var) (*(__le16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount)))
Then, free the allocated native filesystem memory (line 3566), and re-allocate it using kzalloc() at line 3568. This should have enough size to store the unicode string but instead of doing:
(length + 1) * 2
Since unicode needs at least 2 bytes for each character and another one for NULL termination, it does this:
length + 2
Clearly, this insufficient allocation leads to a nice remotely exploitable buffer overflow. The patch was simple as that:
tcon->nativeFileSystem =
- kzalloc(length + 2, GFP_KERNEL);
+ kzalloc(2*(length + 1), GFP_KERNEL);
if (tcon->nativeFileSystem)
This definitely worth more research since it might still be exploitable. Did you read Suresh Jayaraman’s of SUSE email? Have fun! :-)
UniStrnLen returns a character count, not byte count. Illegal UTF-8 encodings can have up to six octets, legal ones can have up to four. There’s a convientient constant defined exactly for this purpose: NLS_MAX_CHARSET_SIZE, defined as 6.
You do know we’re talking about string conversion using codepages, right? Right, that’s why you keep refering to Unicode as if it’s the only possible encoding we might be dealing with (I know there’s an if testing for Uni, I’m tlking about what you do after that…)
Strat
April 13, 2009 at 14:51
thanks this post. I made some adjustments
Степан
June 11, 2009 at 11:08
I’m surely missing something, but I think that this is not remotely exploitable unless you can remotely trigger a mount. Seems more like a client-side bug to me.
Alfred
December 3, 2009 at 00:09