nft_parse_register
함수에서 arg가 default로 넘어갈 때에 대한 검증을 하지 않아 nft_do_chain
함수의 stack에서 oob가 터짐.
이때 payload expression을 이용하면 read, write를 둘 다 할 수 있다.
payload는 아래와 같은 동작을 하게 된다.
즉, 인덱스를 적절히 조절하면 stack에서 oob read, write를 할 수 있음.
하지만, regs는 validation을 하는 함수가 존재한다.
default:
if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE)
return -EINVAL;
if (len == 0)
return -EINVAL;
if (reg * NFT_REG32_SIZE + len >
sizeof_field(struct nft_regs, data))
return -ERANGE;
if (data != NULL && type != NFT_DATA_VALUE)
return -EINVAL;
return 0;
위의 코드를 보면, reg * NFT_REG32_SIZE + len > sizeof_field(struct nft_regs, data) 를 만족해야한다.
상수 넣어주면 reg * 4 + len > 80 이거 만족하면 된다.
즉, reg에 맞게 적당히 len를 조절해줘야한다.
-
nft_regs
struct nft_regs { union { u32 data[20]; struct nft_verdict verdict; }; };
-
nft_do_chain
unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv) { const struct nft_chain *chain = priv, *basechain = chain; const struct net *net = nft_net(pkt); struct nft_rule *const *rules; const struct nft_rule *rule; const struct nft_expr *expr, *last; struct nft_regs regs; ... }
-
nft_parse_register
static unsigned int nft_parse_register(const struct nlattr *attr) { unsigned int reg; reg = ntohl(nla_get_be32(attr)); switch (reg) { case NFT_REG_VERDICT...NFT_REG_4: return reg * NFT_REG_SIZE / NFT_REG32_SIZE; default: return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00; } }