Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <11dc9c61-7833-5503-75e6-f4ec78a60701@gmail.com>
Date: Thu, 8 Jun 2023 10:57:33 +0800
From: Hangyu Hua <hbh25y@...il.com>
To: oss-security@...ts.openwall.com
Subject: Re: Linux kernel: off-by-one in fl_set_geneve_opt

On 7/6/2023 18:41, Hangyu Hua wrote:
> On 7/6/2023 11:32, Hangyu Hua wrote:
>> Hi guys,
>>
>> I find a off-by-one bug in linux kernel's Flower
>> classifier(NET_CLS_FLOWER). It can cause denial-of-service and 
>> privilege escalation.
>>
>> # Details:
>>
>> static int fl_set_geneve_opt(const struct nlattr *nla, struct 
>> fl_flow_key *key,
>>       int depth, int option_len,
>>       struct netlink_ext_ack *extack)
>> {
>> struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1];
>> struct nlattr *class = NULL, *type = NULL, *data = NULL;
>> struct geneve_opt *opt;
>> int err, data_len = 0;
>>
>> if (option_len > sizeof(struct geneve_opt))
>> data_len = option_len - sizeof(struct geneve_opt);
>>
>> opt = (struct geneve_opt *)&key->enc_opts.data[key->enc_opts.len]; 
>> <--- [1]
>> memset(opt, 0xff, option_len);
>> opt->length = data_len / 4;
>> opt->r1 = 0;
>> opt->r2 = 0;
>> opt->r3 = 0;
>>
>> ...
>> if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]) {
>> int new_len = key->enc_opts.len;
>>
>> data = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA];
>> data_len = nla_len(data);
>> if (data_len < 4) {
>> NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4
>> bytes long");
>> return -ERANGE;
>> }
>> if (data_len % 4) {
>> NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a
>> multiple of 4 bytes long");
>> return -ERANGE;
>> }
>>
>> new_len += sizeof(struct geneve_opt) + data_len;
>> BUILD_BUG_ON(FLOW_DIS_TUN_OPTS_MAX != IP_TUNNEL_OPTS_MAX);
>> if (new_len > FLOW_DIS_TUN_OPTS_MAX) { <--- [2]
>> NL_SET_ERR_MSG(extack, "Tunnel options exceeds max size");
>> return -ERANGE;
>> }
>> opt->length = data_len / 4;
>> memcpy(opt->opt_data, nla_data(data), data_len); <--- [3]
>> }
>> ...
>> }
>>
>> We can see that opt use key->enc_opts.len to get its pointer from
>> key->enc_opts.data[] in [1]. Then length will be set to "data_len /
>> 4". The bug is that if we send two TCA_FLOWER_KEY_ENC_OPTS_GENEVE
>> packets and their total size is 252 bytes(key->enc_opts.len = 252)
>> then key->enc_opts.len = opt->length = data_len / 4 when the third
>> TCA_FLOWER_KEY_ENC_OPTS_GENEVE packet enters fl_set_geneve_opt. This
>> can bypass the check in [2] and cause out of bound write in
>> [3](opt->opt_data = key->enc_opts.data[257]).
>>
>> # Patch
>>
>> I already contacted the linux security team and made a patch:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/sched?id=4d56304e5827c8cc8cc18c75343d283af7c4825c
>>
>> # CVE
>>
>> Pending
>>
>> # EXP
>>
>> In order to avoid confusion i will publish it after I get CVE.
> 
> Hi guys,
> 
> I decide not to publish the exp for ethical reasons. Please email me if 
> any distribution's maintainers need the code.

Since some maintainers have requested code from me, I sent the code to
<linux-distros@...openwall.org>.

Thanks,
Hangyu

> 
> Thanks,
> Hangyu
> 
>>
>> Thanks,
>> Hangyu

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.