oss-sec mailing list archives

Re: Linux kernel: off-by-one in fl_set_geneve_opt


From: Hangyu Hua <hbh25y () gmail com>
Date: Wed, 7 Jun 2023 18:41:34 +0800

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.

Thanks,
Hangyu


Thanks,
Hangyu


Current thread: