dl题内存布局初探

2024-10-08

前情提要

   众所周知,在选择dl攻击时,往往没有回显,也就是无法得到attachment、libc、ld的在内存中的加载基址,
   一般来说,这三者的加载地址应该是互不相关的,但根据个人经验来看,libc和ld一般是连在一起的(中间可能有别的内存页),也就是知道其中一个的基址以及内存的布局,就可以知道另一个的基址
   那么__dl_runtime_resolve时如何找到,这三者的关系,依靠的是在ld中的linkmap表,这个表记录三个文件(或许更多)的linkmap地址,而linkmap中就含有加载基址的信息。
   以下的所有调试和maps的查看均以之前的boss题为例,但实际上重点在于该程序mmap()一个0x2000大小可读写的内存段,本文的一个重心将会是这个mmap()得到的内存的相对位置。
pwndbg> linkmap
Node           Objfile                                                            Load Bias      Dynamic Segment 
0x7ffff7ffe2e0 <Unknown, likely /home/pwn/worktable/cnss2024/boss/src/attachment> 0x555555554000 0x555555557df8  
0x7ffff7ffe890 linux-vdso.so.1                                                    0x7ffff7fc1000 0x7ffff7fc13a0  
0x7ffff7fbb160 /lib/x86_64-linux-gnu/libc.so.6                                    0x7ffff7d83000 0x7ffff7f9cbc0  
0x7ffff7ffdaf0 /lib64/ld-linux-x86-64.so.2                                        0x7ffff7fc3000 0x7ffff7ffce80  
   值得注意的是,linkmap的位置在ld.so中的一段可读写的位置,也就是说算好偏移就可以篡改linkmap。

调试方法不同时内存布局的不同

   首先探索的是调试方法不同时,内存布局的不同
   一般使用gdb有两种方法,gdb attachment以及gdb --pid=xxxx,也就是gdb直接调试文件或者链接进程进行调试,实际上这两者就算仅仅是从效果上看就有很大不同。
   首先看gdb --pid=xxx的vmmap,这个结果更加接近一个进程的真实vmmap,也就是和cat /proc/xxx/maps的结果相近
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x5631723b9000     0x5631723ba000 r--p     1000      0 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x5631723ba000     0x5631723bb000 r-xp     1000   1000 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x5631723bb000     0x5631723bc000 r--p     1000   2000 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x5631723bc000     0x5631723bd000 r--p     1000   2000 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x5631723bd000     0x5631723be000 rw-p     1000   3000 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x7f99a605f000     0x7f99a6062000 rw-p     3000      0 [anon_7f99a605f]
    0x7f99a6062000     0x7f99a608a000 r--p    28000      0 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7f99a608a000     0x7f99a621f000 r-xp   195000  28000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7f99a621f000     0x7f99a6277000 r--p    58000 1bd000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7f99a6277000     0x7f99a6278000 ---p     1000 215000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7f99a6278000     0x7f99a627c000 r--p     4000 215000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7f99a627c000     0x7f99a627e000 rw-p     2000 219000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7f99a627e000     0x7f99a628b000 rw-p     d000      0 [anon_7f99a627e]
    0x7f99a6298000     0x7f99a629c000 rw-p     4000      0 [anon_7f99a6298] # <---mmap得到的空间
    0x7f99a629c000     0x7f99a629e000 r--p     2000      0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7f99a629e000     0x7f99a62c8000 r-xp    2a000   2000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7f99a62c8000     0x7f99a62d3000 r--p     b000  2c000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7f99a62d4000     0x7f99a62d6000 r--p     2000  37000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7f99a62d6000     0x7f99a62d8000 rw-p     2000  39000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffd84bf2000     0x7ffd84c14000 rw-p    22000      0 [stack]
    0x7ffd84c46000     0x7ffd84c4a000 r--p     4000      0 [vvar]
    0x7ffd84c4a000     0x7ffd84c4c000 r-xp     2000      0 [vdso]
   然后是使用gdb attachment的效果,可以看到的是mmap得到的空间和ld.so之间多了0x6000的[vvar]和[vsdo],这两个本来是在栈段下方的。
   其次,如果多次调试发现,attachment、libc、ld的加载地址实际上是固定的,也就是0x555555554000、0x7ffff7d83000、0x7ffff7fc3000实际上没变,应该是出于方便调试所以固定了加载地址。
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x555555554000     0x555555555000 r--p     1000      0 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x555555555000     0x555555556000 r-xp     1000   1000 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x555555556000     0x555555557000 r--p     1000   2000 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x555555557000     0x555555558000 r--p     1000   2000 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x555555558000     0x555555559000 rw-p     1000   3000 /home/pwn/worktable/cnss2024/boss/src/attachment
    0x7ffff7d80000     0x7ffff7d83000 rw-p     3000      0 [anon_7ffff7d80]
    0x7ffff7d83000     0x7ffff7dab000 r--p    28000      0 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7dab000     0x7ffff7f40000 r-xp   195000  28000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7f40000     0x7ffff7f98000 r--p    58000 1bd000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7f98000     0x7ffff7f99000 ---p     1000 215000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7f99000     0x7ffff7f9d000 r--p     4000 215000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7f9d000     0x7ffff7f9f000 rw-p     2000 219000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7f9f000     0x7ffff7fac000 rw-p     d000      0 [anon_7ffff7f9f]
    0x7ffff7fb9000     0x7ffff7fbd000 rw-p     4000      0 [anon_7ffff7fb9] # <-- mmap得到的地方
    0x7ffff7fbd000     0x7ffff7fc1000 r--p     4000      0 [vvar]
    0x7ffff7fc1000     0x7ffff7fc3000 r-xp     2000      0 [vdso]
    0x7ffff7fc3000     0x7ffff7fc5000 r--p     2000      0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7fc5000     0x7ffff7fef000 r-xp    2a000   2000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7fef000     0x7ffff7ffa000 r--p     b000  2c000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffb000     0x7ffff7ffd000 r--p     2000  37000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffd000     0x7ffff7fff000 rw-p     2000  39000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffffffdd000     0x7ffffffff000 rw-p    22000      0 [stack]

使用patchelf后的内存布局

   一般pwn题时,尤其C的pwn题时,会选择使用patchelf,更改libc和ld为指定glibc版本的来获得和远程相近的本地环境,但是patchelf也会使进程的内存布局发生变化。
   首先获得一个patchelf后的文件
$ ldd attachment # patchelf之前
        linux-vdso.so.1 (0x00007ffd469a6000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8a8e7db000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8a8ea1a000)
$ patchelf --replace-needed libc.so.6 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6 attachment
$ patchelf --set-interpreter /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/ld-linux-x86-64.so.2 attachment
$ ldd attachment
        linux-vdso.so.1 (0x00007ffd73beb000)
        /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6 (0x00007f9a1054c000)
        /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f9a1077d000)
   然后gdb --pi=xxx尝试调试。
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x56272a397000     0x56272a398000 r--p     1000      0 /home/pwn/testtable/attachment
    0x56272a398000     0x56272a399000 r-xp     1000   1000 /home/pwn/testtable/attachment
    0x56272a399000     0x56272a39a000 r--p     1000   2000 /home/pwn/testtable/attachment
    0x56272a39a000     0x56272a39b000 r--p     1000   2000 /home/pwn/testtable/attachment
    0x56272a39b000     0x56272a39e000 rw-p     3000   3000 /home/pwn/testtable/attachment
    0x7fc2b8155000     0x7fc2b815a000 rw-p     5000      0 [anon_7fc2b8155] # <-----mmap的位置
    0x7fc2b815a000     0x7fc2b8182000 r--p    28000      0 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6
    0x7fc2b8182000     0x7fc2b8317000 r-xp   195000  28000 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6
    0x7fc2b8317000     0x7fc2b836f000 r--p    58000 1bd000 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6
    0x7fc2b836f000     0x7fc2b8373000 r--p     4000 214000 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6
    0x7fc2b8373000     0x7fc2b8375000 rw-p     2000 218000 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6
    0x7fc2b8375000     0x7fc2b8384000 rw-p     f000      0 [anon_7fc2b8375]
    0x7fc2b8384000     0x7fc2b8386000 r--p     2000      0 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/ld-linux-x86-64.so.2
    0x7fc2b8386000     0x7fc2b83b0000 r-xp    2a000   2000 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/ld-linux-x86-64.so.2
    0x7fc2b83b0000     0x7fc2b83bb000 r--p     b000  2c000 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/ld-linux-x86-64.so.2
    0x7fc2b83bc000     0x7fc2b83be000 r--p     2000  37000 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/ld-linux-x86-64.so.2
    0x7fc2b83be000     0x7fc2b83c0000 rw-p     2000  39000 /home/pwn/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/ld-linux-x86-64.so.2
    0x7fff90916000     0x7fff90938000 rw-p    22000      0 [stack]
    0x7fff90968000     0x7fff9096c000 r--p     4000      0 [vvar]
    0x7fff9096c000     0x7fff9096e000 r-xp     2000      0 [vdso]
   可以看到,mmap获得的空间现在变成了在libc上方,原因暂时未知。

xinted转发会话时启动的进程

   之前docker内可行的exp,在访问端口(本地docker或者服务器远程)上的题目时,发现无法打通,于是猜测是xinted转发的进程内存布局不同
   在正式查看之前,先做准备工作,第一步先修改以下xinted的设置
service ctf
{
    disable = no
    socket_type = stream
    protocol    = tcp
    wait        = no
    user        = root
    type        = UNLISTED
    port        = 9999
    bind        = 0.0.0.0
    # 设置xinetd连接启动后的服务程序
    server      = /usr/sbin/chroot
    # 设置chroot的相关参数
    server_args = --userspec=0000:0000 /home/ctf ./attachment # <---- 这里改为以root用户执行文件,否则之后看maps没权限
    banner_fail = /etc/banner_fail
    # safety options
    per_source	= 10 # the maximum instances of this service per source IP address
    rlimit_cpu	= 20 # the maximum number of CPU seconds that the service may use
    #rlimit_as  = 1024M # the Address Space resource limit for the service
    #access_times = 2:00-9:00 12:00-24:00
}

   然后构建镜像
$ docker build -t boss .
$ docker run -p 8000:9999 boss
# 切换一个shell
$ docker exec -it <容器名> /bin/bash
<容器名>/home/ctf$ apt-get install gdb
   再打开一个shell,nc localhost 8000,现在docker容器中就存在一个attachment的进程,由xindted转发
   此时先找到pid
<容器名>/home/ctf$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4364  3164 ?        Ss   00:16   0:00 /bin/bash /docker-entrypoint.sh
root        19  0.0  0.0   2792  1036 ?        S    00:16   0:00 sleep infinity
root        20  0.0  0.0  13784  2392 ?        Ss   00:16   0:00 /usr/sbin/xinetd -pidfile /run/xinetd.pid -stayalive -inetd_c
root        21  0.0  0.0   4628  3720 pts/0    Ss   00:16   0:00 /bin/bash
root        29  0.0  0.0   2652   264 ?        Ss   00:17   0:00 ./attachment # <--- 在这里
root        30  0.0  0.0   7064  1552 pts/0    R+   00:17   0:00 ps aux
   现在调试这个pid
<容器名>/home/ctf$ gdb --pi=29
(gdb) x/10gx 0x56341ccb5000 + 0x40a0
0x56341ccb90a0: 0x00007ff9322bc000      0x0000000000000000
0x56341ccb90b0: 0x0000000000000000      0x0000000000000000
0x56341ccb90c0: 0x0000000000000000      0x0000000000000000
0x56341ccb90d0: 0x0000000000000000      0x0000000000000000
0x56341ccb90e0: 0x0000000000000000      0x0000000000000000
<容器名>/home/ctf$ cat /proc/29/maps
56341ccb5000-56341ccb6000 r--p 00000000 08:30 20412                      /home/ctf/attachment
56341ccb6000-56341ccb7000 r-xp 00001000 08:30 20412                      /home/ctf/attachment
56341ccb7000-56341ccb8000 r--p 00002000 08:30 20412                      /home/ctf/attachment
56341ccb8000-56341ccb9000 r--p 00002000 08:30 20412                      /home/ctf/attachment
56341ccb9000-56341ccba000 rw-p 00003000 08:30 20412                      /home/ctf/attachment
7ff9322bc000-7ff9322c1000 rw-p 00000000 00:00 0 # <------- 这里
7ff9322c1000-7ff9322e9000 r--p 00000000 08:30 19693                      /home/ctf/lib/x86_64-linux-gnu/libc.so.6
7ff9322e9000-7ff93247e000 r-xp 00028000 08:30 19693                      /home/ctf/lib/x86_64-linux-gnu/libc.so.6
7ff93247e000-7ff9324d6000 r--p 001bd000 08:30 19693                      /home/ctf/lib/x86_64-linux-gnu/libc.so.6
7ff9324d6000-7ff9324d7000 ---p 00215000 08:30 19693                      /home/ctf/lib/x86_64-linux-gnu/libc.so.6
7ff9324d7000-7ff9324db000 r--p 00215000 08:30 19693                      /home/ctf/lib/x86_64-linux-gnu/libc.so.6
7ff9324db000-7ff9324dd000 rw-p 00219000 08:30 19693                      /home/ctf/lib/x86_64-linux-gnu/libc.so.6
7ff9324dd000-7ff9324ec000 rw-p 00000000 00:00 0 
7ff9324ec000-7ff9324ee000 r--p 00000000 08:30 19672                      /home/ctf/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ff9324ee000-7ff932518000 r-xp 00002000 08:30 19672                      /home/ctf/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ff932518000-7ff932523000 r--p 0002c000 08:30 19672                      /home/ctf/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ff932524000-7ff932526000 r--p 00037000 08:30 19672                      /home/ctf/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ff932526000-7ff932528000 rw-p 00039000 08:30 19672                      /home/ctf/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ffdbc5a8000-7ffdbc5c9000 rw-p 00000000 00:00 0                          [stack]
7ffdbc5ec000-7ffdbc5f0000 r--p 00000000 00:00 0                          [vvar]
7ffdbc5f0000-7ffdbc5f2000 r-xp 00000000 00:00 0                          [vdso]
   结果发现这个xinted转发的进程的内存布局和patchelf的似乎一致,mmap的位置都在libc上面,其他长度也是对应的.
   这个结果就有点子幽默了,本来就是patchelf无法得到完全一致的内存布局所以给了Dockerfile,结果现在xinted转发的进程内存布局不一样,反倒和patchelf的结果正好对上了😅😅😅

END