追è¨:2020-06-05
ãã®ã¨ã³ããªã®èæ¯ãéã ã£ãã®ã§ä»¥ä¸ã«è£è¶³è¨äºãæ¸ãã¾ãããå
ã«ãã¡ãã«ç®ãéãã¦ããã ããæ¹ãè¯ãããããã¾ããã
https://hb.matsumoto-r.jp/entry/2020/06/05/110709
speakerdeck.com
ä»tcprivã¨ããã½ããã¦ã§ã¢ãéçºãã¦ããã®ã ããç´°ããå
容ã«ã¤ãã¦ã¯ä¸è¨ã®ã¹ã©ã¤ããè¦ã¦ãããã¨ãã¦ããããããã¨ã¯ãTCPã»ãã·ã§ã³ã確ç«ããããã»ã¹ã®ãªã¼ãæ
å ±ãæ¥ç¶å
ã®ããã»ã¹ã§ééçã«æ¤è¨¼ããã¨ããå¦çã§ããã
github.com
以ä¸ã§ã¯ãã®å®è£
ã®æ¦è¦ãç´¹ä»ãã¤ã¤ã仿¤è¨ãã¦ãããã¨ã«ã¤ãã¦ã話ãããã
æ¥ç¶å
ããã»ã¹ã¯ä¸è¬ã¦ã¼ã¶ãæ³å®ãã¦ãããèå¼±æ§ãªã©ã«ãã£ã¦æªæã®ããã¦ã¼ã¶ã«ã®ã£ã¨ããããã¨ãããããããã¨ããããã»ã¹ãå©ç¨ããèªè¨¼æ
å ±ãæ¼ãããã¨ããããã¨ãæ³å®ãã¦ããã
ããããæ
å ±ãæ¼ããã¨ãã¦ããé©åãªãªã¼ãããã¨ãã®ãªã¼ãã«ç´ä»ããèªè¨¼æ
å ±ãå©ç¨ããªãã¨ããªã¢ã¼ãå
ã®ããã»ã¹ã§èªè¨¼ãæå¦åºæ¥ããããªããã¿ãèãã¦ããã
ããããå¤è¦ç´ èªè¨¼ã®ãµã¼ãééä¿¡çã¨èãã¦ãããã®ãããããªãã
ä¾ãã°ãã¨ãããã«ãããã³ãã·ã¹ãã é飿ºã«ããã¦ãã¨ããããã»ã¹ãå©ç¨ãã¦ããDBã®ID/PASSããããã¯ããã¼ã¯ã³ãæ¼ããæã«ããããã®èªè¨¼æ
å ±ã使ã£ã¦ãåã·ã¹ãã å
ã®å¥ã®ãªã¼ãã®ããã»ã¹ãDBã«æ¥ç¶ã試ã¿ãå ´åããªã¼ããéãã®ã§ID/PASSããã¼ã¯ã³ãä¸è´ãã¦ãã¦ãèªè¨¼ãéããªãã
ãã«ãããã³ãåã®ããã¼ã¸ãã·ã¹ãã å
ã§ãããã»ã¹ã¬ãã«ã§éé¢ã¯ããã¦ãããOSä¸ã«å
±åãã¦ããããã»ã¹ããåãIPã¢ãã¬ã¹ãå²ãå½ã¦ããã¦ããã³ã³ãããªã©ãæ¥ç¶å
ã¨ãã¦æ³åããã¨è¯ããããããªãã
ãã¨ã¯ã¬ã³ã¿ã«ãµã¼ãã®ä¾ãã°WordPressçãªWebã¢ããªã±ã¼ã·ã§ã³ã¨ãã
ãªã¼ããæ¨©éã§åé¢ããã¦ããæ²¢å±±ã®WordPressã«ããã¦ããã¨ãã¯ã¨ããWordPressã®DBæ¥ç¶ã®ããã®ID/PASSãæ¼ããã¨ãã¦ãããã®ID/PASSã使ãã¹ãããã»ã¹ãªã¼ãããã®ã¿ãããã®ID/PASSã®èªè¨¼ãæ¥ç¶å
ã®DBã§èªè¨¼ããªããã¨ãããããªã±ã¼ã¹ã§ããã
ã¾ããæ¥ç¶æã«ãªã¼ãã®æ
å ±ãè©ç§°ã§ããªãããã«ããã®ä¸é£ã®ãªã¼ãã«é¢ããæ
å ±ãã«ã¼ãã«å´ã§ééçã«å®ç¾ãã¦ããã
ããªãã¡ãèªè¨¼æ
å ±ããããå½è©²ããã»ã¹ããã®ã¿ã®ã¢ã¯ã»ã¹ãèå¼±æ§ã§ä¹ã£ããããããã»ã¹ããã®ã¿ãããèªè¨¼æ
å ±ãå©ç¨ãã¦æ¥ç¶ã§ããªããªãã®ã§ã被害ç¯å²ã屿åã§ããã
ãªã¼ãæ
å ±ã®ä»ä¸ãééçã«ã«ã¼ãã«ã§å®ç¾ããããã«ãã«ã¼ãã«å
ã®Netfilterã®ããã¯ãã¤ã³ããå©ç¨ãã¦ãã«ã¼ãã«ã¢ã¸ã¥ã¼ã«ã«ããã«ã¼ãã«ã®TCPã¹ã¿ãã¯åå¾ã§TCPãªãã·ã§ã³ãããã«ãªã¼ãæ
å ±ãæ¸ãè¾¼ãé åãå®ç¾©ããæ¸ãè¾¼ãã§ããã
static int __init tcpriv_init(void)
{
printk(KERN_INFO TCPRIV_INFO "open\n");
printk(KERN_INFO TCPRIV_INFO "An Access Control Architecture Separating Privilege Transparently via TCP Connection "
"Based on Process Information\n");
nfho_in.hook = hook_local_in_func;
nfho_in.hooknum = NF_INET_LOCAL_IN;
nfho_in.pf = PF_INET;
nfho_in.priority = NF_IP_PRI_FIRST;
nf_register_net_hook(&init_net, &nfho_in);
nfho_out.hook = hook_local_out_func;
nfho_out.hooknum = NF_INET_LOCAL_OUT;
nfho_out.pf = PF_INET;
nfho_out.priority = NF_IP_PRI_FIRST;
nf_register_net_hook(&init_net, &nfho_out);
return 0;
}
ãããªæãã§ããã¼ã«ã«ã«å
¥ã£ã¦ãããã±ããã¨åºã¦ãããã±ãããããã¯ãã¦ããã
åºã¦ãããã§ã¼ãºãããªãã¡ããªã¢ã¼ããµã¼ãã«TCPã§æ¥ç¶ãã«ãããããªç¶æ³ã§ã¯ãsynãã±ããéåºæã«ãªãã·ã§ã³ã®ãã§ãã¯ã¨tcprivãªãã·ã§ã³ã®ã»ãã(tcpriv_tcp_syn_options)ãããã«ãå®ç¾©ããå®é¨çTCPãªãã·ã§ã³ãã£ã¼ã«ãã«ãªã¼ãæ
å ±ãæ¸ãè¾¼ãã§ãã(tcpriv_tcp_options_write)ã
static unsigned int hook_local_out_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *iphdr = ip_hdr(skb);
struct tcphdr *tcphdr = tcp_hdr(skb);
if (iphdr->version == 4) {
if (iphdr->protocol == IPPROTO_TCP && tcphdr->syn) {
struct tcp_out_options opts;
struct sock *sk;
struct tcp_md5sig_key *md5;
printk(KERN_INFO TCPRIV_INFO "found local out TCP syn packet from %pI4.\n", &iphdr->saddr);
sk = state->sk;
memset(&opts, 0, sizeof(opts));
tcpriv_tcp_syn_options(sk, skb, &opts, &md5);
tcpriv_tcp_options_write((__be32 *)(tcphdr + 1), NULL, &opts);
}
}
return NF_ACCEPT;
}
ç¾å¨ãIANAãè¦å®ãã¦ããTCPãªãã·ã§ã³ãããã¯è¤æ°åå¨ãï¼åæã«ï¼HOST_IDãLinuxã«ã¼ãã«ãã¼ã¸ã§ã³5ç³»ã§å®è£
ããã¦ããShared Memory communications over RMDA protocol(以éSMC-R)*1ã¨ãã£ãå®é¨çãªTCPãªãã·ã§ã³ãåå¨ããã
ä¾ãã°TCP Fast Open Coolieã¯2014å¹´ã«æ¨æºåãããIANAããæ£å¼ãªTCPãªãã·ã§ã³ã¨ãã¦kindãã³ãã¼ãä»ä¸ããã¦ããã
䏿¹ã§ãæ¯è¼çæ°ããTCPãªãã·ã§ã³ã§ãããããä¾ç¶ã¨ãã¦Linuxã«ã¼ãã«ã®ãã¼ã¸ã§ã³ã«ãã£ã¦ã¯ãå®é¨çãªkindãã³ãã¼ãå
±æãã¦ä»ã®å®é¨çãªTCPãªãã·ã§ã³ã¨å©ç¨ããå®è£
ã«ãªã£ã¦ãã*2ã
ãã®ãããå®è£
ã«ããã¦ã¯ãTCP Fast Open Cookieãåºæã®kindãã³ãã¼ãä¿ã¤å ´åã¨å
±æã®å®é¨çãªkindãã³ãã¼ãæã¤å ´åãæ³å®ãã¦å®è£
ãè¡ã£ã¦ããã
ã¾ããLinuxã«ã¼ãã«ãã¼ã¸ã§ã³5ç³»ã«ããã¦ã¯ãå
±æã®å®é¨çãªkindãã³ãã¼ã«SMC-Rãå©ç¨ãã¦ãããããIANAããSMC-Rã«å²ãå½ã¦ããã¦ããTCP Experimental Option Experiment Identifiers(以éTCP ExIDs)*3ã¨ã¯å¥ã®TCP ExIDsãæ«å®ã§å²ãæ¯ã£ã¦åºå¥ããã
ç¹ã«ãã±ããã®TCPããããªãã·ã§ã³ãã£ã¼ã«ãã®è§£æãè¡ãéã«ãæ¢åã®TCPãªãã·ã§ã³ã¯ãã®ã¾ã¾ã«ãææ¡ææ³ç¨ã®å®é¨çãªãã·ã§ã³ã®å
±ækindãã³ãã¼ããã¼ã¿ã¬ã³ã°ã¹ãTCP ExIDsããã¼æ
å ±ãæ¸ãè¾¼ãã
å®é¨çãªãã·ã§ã³ã®ä»æ§ãå¿ãã¦ããã¨ãExIDsã®å®ç¾©ãå¿ãã¡ããã®ã§æ³¨æã
æ¸ãè¾¼ã¿å¦çã¯ä»¥ä¸ã®ãããªæãã
static void tcpriv_options_write(__be32 *ptr, u16 *options)
{
if (unlikely(OPTION_TCPRIV & *options)) {
kuid_t uid = current_uid();
kgid_t gid = current_gid();
*ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_EXP << 8) | (TCPOLEN_EXP_TCPRIV_BASE));
*ptr++ = htonl(TCPOPT_TCPRIV_MAGIC);
TODO
*ptr++ = htonl(uid.val);
*ptr++ = htonl(gid.val);
}
}
static void tcpriv_tcp_options_write(__be32 *ptr, struct tcp_sock *tp, struct tcp_out_options *opts)
{
u16 options = opts->options;
if (unlikely(OPTION_MD5 & options)) {
*ptr++;
ptr += 4;
}
if (unlikely(opts->mss)) {
*ptr++;
}
if (likely(OPTION_TS & options)) {
if (unlikely(OPTION_SACK_ADVERTISE & options)) {
*ptr++;
options &= ~OPTION_SACK_ADVERTISE;
} else {
*ptr++;
}
*ptr++;
*ptr++;
}
if (unlikely(OPTION_SACK_ADVERTISE & options)) {
*ptr++;
}
if (unlikely(OPTION_WSCALE & options)) {
*ptr++;
}
if (unlikely(opts->num_sack_blocks)) {
int this_sack;
*ptr++;
for (this_sack = 0; this_sack < opts->num_sack_blocks; ++this_sack) {
*ptr++;
*ptr++;
}
}
if (unlikely(OPTION_FAST_OPEN_COOKIE & options)) {
struct tcp_fastopen_cookie *foc = opts->fastopen_cookie;
u8 *p = (u8 *)ptr;
u32 len;
if (foc->exp) {
len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
p += TCPOLEN_EXP_FASTOPEN_BASE;
} else {
len = TCPOLEN_FASTOPEN_BASE + foc->len;
*p++;
*p++ = len;
}
ptr += (len + 3) >> 2;
}
tcpriv_options_write(ptr, &options);
}
ç¶ãã¦ãå
¥ã£ã¦ãããã§ã¼ãºã§ã¯ãsyncãã±ããåä¿¡æã«TCPãªãã·ã§ã³ããããã£ã¼ã«ããparseãããªãã·ã§ã³ãå®ç¾©ããã¦ããã°å½è©²ãã£ã¼ã«ããããªã¼ãæ
å ±ï¼uid32bit+gid32bitï¼ãåå¾ããããã«ãã¦ãã(tcpriv_tcp_parse_options)ã
static unsigned int hook_local_in_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *iphdr = ip_hdr(skb);
struct tcphdr *tcphdr = tcp_hdr(skb);
struct tcp_options_received tmp_opt;
if (iphdr->version == 4) {
if (iphdr->protocol == IPPROTO_TCP && tcphdr->syn) {
printk(KERN_INFO TCPRIV_INFO "found local in TCP syn packet from %pI4.\n", &iphdr->saddr);
memset(&tmp_opt, 0, sizeof(tmp_opt));
tcpriv_tcp_clear_options(&tmp_opt);
tcpriv_tcp_parse_options(&init_net, skb, &tmp_opt, 0, NULL);
}
}
return NF_ACCEPT;
}
parseã«ã¤ãã¦ã¯ãã«ã¼ãã«ã®ã³ã¼ããååã«åèã«ããªãããã²ã¨ã¤ãã¤ãªãã·ã§ã³ãã£ã¼ã«ãã®ãã©ã°ã¨ã¬ã³ã°ã¹ã®ãã§ãã¯ãè¡ã£ã¦ãtcprivãªãã·ã§ã³ããããã°ãã®æ
å ±ãåå¾ããããã«ãã¦ããã
static void tcpriv_parse_options(const struct tcphdr *th, struct tcp_options_received *opt_rx, const unsigned char *ptr,
int opsize)
{
if (th->syn && !(opsize & 1) && opsize >= TCPOLEN_EXP_TCPRIV_BASE && get_unaligned_be32(ptr) == TCPOPT_TCPRIV_MAGIC) {
TODO
u32 uid, gid;
uid = get_unaligned_be32(ptr + 4);
gid = get_unaligned_be32(ptr + 8);
printk(KERN_INFO TCPRIV_INFO "found client process info: uid=%u gid=%u\n", uid, gid);
}
}
void tcpriv_tcp_parse_options(const struct net *net, const struct sk_buff *skb, struct tcp_options_received *opt_rx,
int estab, struct tcp_fastopen_cookie *foc)
{
const unsigned char *ptr;
const struct tcphdr *th = tcp_hdr(skb);
int length = (th->doff * 4) - sizeof(struct tcphdr);
ptr = (const unsigned char *)(th + 1);
opt_rx->saw_tstamp = 0;
while (length > 0) {
int opcode = *ptr++;
int opsize;
switch (opcode) {
case TCPOPT_EOL:
return;
case TCPOPT_NOP:
length--;
continue;
default:
if (length < 2)
return;
opsize = *ptr++;
if (opsize < 2)
return;
if (opsize > length)
return;
switch (opcode) {
case TCPOPT_EXP:
if (opsize >= TCPOLEN_EXP_FASTOPEN_BASE && get_unaligned_be16(ptr) == TCPOPT_FASTOPEN_MAGIC) {
} else if (th->syn && !(opsize & 1) && opsize >= TCPOLEN_EXP_SMC_BASE &&
get_unaligned_be16(ptr) == TCPOPT_SMC_MAGIC) {
} else {
tcpriv_parse_options(th, opt_rx, ptr, opsize);
}
break;
}
ptr += opsize - 2;
length -= opsize;
}
}
}
ããã§ãã§ãããã§ããããªã¢ã¼ãå
ã®ããã»ã¹å´ã§æ¥ç¶å
ããã»ã¹ã®ãªã¼ãæ
å ±ãåå¾ã§ããããã§ãããããã¦ããããã¦ã¼ã¶ã©ã³ãã§åä½ããããã«ã¦ã§ã¢çã§ãã®ãã¼ã¿ãã©ã®ããã«åå¾ããããèããå¿
è¦ãããã
ç¾ç¶ã§ã¯ã以ä¸ã®å³ã«ããããã«ãsocket APIã使ããããã¯åæ§ã®APIãå®è£
ãããã¨ã«ãã£ã¦ãtcprivãªãã·ã§ã³ãæå¹åã©ããããã§ãã¯ãã颿°ãç¨æããã

ãããç¨ãã¦ãããã«ã¦ã§ã¢ããããã¯ããã®å端ã«ãããããã«ã¦ã§ã¢å¯¾å¿ã®ãããã·ã§ãã»ãã·ã§ã³ã確ç«ããéã«ãã®é¢æ°ã§tcprivãªãã·ã§ã³ããã§ãã¯ããã
ãã®ä¸ã§ãtcprivãªãã·ã§ã³ãenabledã§ããã°/procãã¡ã¤ã«ã·ã¹ãã ã®æå®ã®å ´æã«ãtcpriv/ipaddress+src-portã¿ãããªãã¡ã¤ã«ãè¦ãã¨ãæ¥ç¶å
ã®ãªã¼ãæ
å ±ãåå¾ã§ããããã«ãã¦ããããããè¡ãã颿°ãç¨æããã
ãã®ããã«ããtcprivãå®è£
ãã¦ããã«ã¼ãã«ã¢ã¸ã¥ã¼ã«ã®ä¸ã§ããªã¼ãæ
å ±ãparseããå¾ã«ããã®æ
å ±ã/proc以ä¸ã«æ¸ãè¾¼ãããã«ãã¦ããã
ããã¨ãTCPã»ãã·ã§ã³ç¢ºç«æã«æ¥ç¶å
ããã»ã¹ã®ãªã¼ãæ
å ±ãåå¾ã§ããã®ã§ããã®æ®µéããããã¯ããã«ã¦ã§ã¢ã¨ãã¦ã®èªè¨¼ãè¡ã段éã§ããã®ãªã¼ãæ
å ±ã徿¥ã®ID/PASSããã¼ã¯ã³ãçªãåããããã¨ã§èªè¨¼è¡ãã
ã¨ã¾ããããã¾ã§ãé ã®ä¸ã§å¤§ä½ã§ãããã ã¨èãã¦ããè¨è¨ãªã®ã ãããã£ã¨ã·ã³ãã«ã«ãªã¼ãæ
å ±ãã¦ã¼ã¶ã©ã³ãã§ã»ãã·ã§ã³ç¢ºç«æã«åå¾ã§ããæ¹æ³ã¯ãªããã¨æè¿èãã¦ããã
scoket APIãä½ã£ããæ¡å¼µããããããã¨ã¯ã§ããããããå°ãããã¾ã/procã«tcprivã«é¢ããæ
å ±ãé
ç½®ããã ãã§ãã»ãã·ã§ã³ç¢ºç«æã«ããããã°é¢æ°ã使ããªãã¦ãreadã¨ãã ãã§æ±ãããããªè¨è¨ã¯ãªããæ¤è¨ãã¦ããã
ããã¢ã¤ãã¢ãããã¾ãããããæç¤ºããã ããã¨å¹¸ãã§ãããã¾ãã