Linux-CTFHub

动态加载器

学习 Linux ELF Dynaamic Loader 技术。在 ELF 无 x 权限时运行 ELF 文件。

image-20250619171826208

这里对题目进行解释。尤其是755、644以及-rw-r--r--

什么是ELF文件?

格式层面:

  • ELF(Executable and Linkable Format)是 Linux/Unix 世界的二进制文件格式标准。

  • PE(Portable Executable)是 Windows 世界的二进制文件格式标准。

    这两个是“底层格式”,决定了二进制文件的结构、加载方式等。 在这两种格式之下,才有“具体用途”的文件类型:

  • Linux 下:

    • ELF 可执行文件(比如 /bin/ls),就是用 ELF 格式存储的主程序(一般没有扩展名,有时叫 a.out)
    • ELF 动态库文件(.so,Shared Object),也是用 ELF 格式存储,只是类型是“共享库”
    • ELF 不只是可执行文件,.so 动态库、目标文件(.o)等也都用 ELF 格式。
  • Windows 下:

    • .exe 文件:采用 PE 格式的“可执行文件”,用于直接运行(主程序)
    • .dll 文件:采用 PE 格式的“动态链接库”,不能直接运行,只能被其他程序调用
    • PE 也不仅仅是 .exe 和 .dll,有时候驱动(.sys)等也用 PE 结构。

对应关系总结为

  • Linux 的 ELF 可执行文件 ≈ Windows 的 .exe 文件(PE格式,可执行文件类型)
  • Linux 的 .so(ELF动态库) ≈ Windows 的 .dll(PE格式,动态链接库类型)
  • ELF 和 PE 是不同操作系统下的二进制文件“容器”标准

所以,你可以这样理解:

Linux/Unix 世界 Windows 世界
ELF 可执行文件 .exe (PE格式)
ELF 动态库(.so文件) .dll (PE格式)

典型表示法

Linux/Unix 文件权限的典型表示法,每一位都有固定的含义。详细解释如下:

各位含义:

位置 字符 含义
1 - 文件类型(或特殊标志)
2-4 rwx 所有者(User)权限
5-7 r-x 所属组(Group)权限
8-10 r-x 其他用户(Other)权限

下面讲一下这10位中每一位可能的选项:

  • 第1位(类型位):只可有以下几种常见选项
    • - 普通文件
    • d 目录
    • l 符号链接
    • c 字符设备
    • b 块设备
    • s 套接字
    • p 管道
  • 第2-10位(权限位):每三位为一组(rwx),分别对应User、Group、Other
    • r(read) :有读权限,没有则为 -
    • w(write) :有写权限,没有则为 -
    • x(execute):有执行权限,没有则为 -
    • 还可能被特殊权限(setuid、setgid、sticky bit)覆盖为 s、S、t、T(一般在x位置)

总结

  • 第1位(类型标志):7种常见选项(-、d、l、c、b、s、p)
  • 每组三位(rwx):每位2个选项(有该权限或无该权限),即 r/-、w/-、x/-
  • 总共10位

上面我们讲到每一组的第三位

可能被特殊权限(setuid、setgid、sticky bit)覆盖为 s、S、t、T(一般在x位置)

下面我们详细讲一下:

setuid(Set User ID)

  • 作用:可执行文件被运行时,进程的有效用户ID临时变为文件所有者的ID(通常用于 root 权限提升)。
  • 位置:文件所有者的“x”位(即第4位)。
  • 显示:
    • x 和 setuid 都有时,显示为 s(如:rwsr-xr-x)
    • 没有 x 权限但设置了 setuid,显示为 S(如:rwSr-xr-x)

setgid(Set Group ID)

  • 作用1(文件):可执行文件运行时,进程的有效组ID临时变为文件所属组ID。
  • 作用2(目录):新创建的文件/目录会继承父目录的组。
  • 位置:所属组的“x”位(第7位)。
  • 显示:
    • x 和 setgid 都有时,显示为 s(如:rwxr-sr-x)
    • 没有 x 权限但设置了 setgid,显示为 S(如:rwxr-Sr-x)

sticky bit

  • 作用(一般用于目录):只有文件所有者、目录所有者或 root 才能删除该目录下的文件(如 /tmp)。
  • 位置:其他用户的“x”位(第10位)。
  • 显示:
    • x 和 sticky bit 都有时,显示为 t(如:rwxrwxrwt)
    • 没有 x 权限但设置了 sticky bit,显示为 T(如:rwxrwxrwT)

总结表

权限位 含义 有 x 权限 显示 无 x 权限 显示
setuid 文件所有者的 x 位 s rws S rwS
setgid 所属组的 x 位 s r-s S r-S
sticky 其他用户的 x 位 t rwt T rwT

这里我们再解释上面提到的:-rw-r--r--

位置 字符 含义
第1位 - 文件类型(- 代表普通文件)
第2-4位 rw- 所有者(User)的权限:读(r)、写(w)、无执行(-)
第5-7位 r– 所属组(Group)的权限:读(r)、无写(-)、无执行(-)
第8-10位 r– 其他用户(Other)的权限:读(r)、无写(-)、无执行(-)

所以这里我们是没有执行权限的。

数字表示法

在 Linux 或类 Unix 系统中,用来设置文件或目录的权限的数字表示法。下面详细解释:

一、数字权限的含义

Linux 文件权限分为三组:所有者(User)、所属组(Group)、其他人(Other)。 每组权限用一个数字表示,范围 0-7,共三位数。

每一位数字的含义如下:

  • 4:r(read,读权限)
  • 2:w(write,写权限)
  • 1:x(execute,执行权限)

三者可以相加,组合成不同的权限:

  • 7 = 4 + 2 + 1 = rwx(读、写、执行)
  • 6 = 4 + 2 = rw-(读、写)
  • 5 = 4 + 1 = r-x(读、执行)
  • 4 = 4 = r–(只读)
  • 0 = —(无权限)

二、三位数字代表顺序

  • 第一位:所有者权限(User)
  • 第二位:所属组权限(Group)
  • 第三位:其他人权限(Other)

我们再解释一下上面提到的755和644

  1. chmod 755 /readflag
  • 7(所有者):rwx(读、写、执行)
  • 5(组):r-x(读、执行)
  • 5(其他):r-x(读、执行)

用 ls -l 显示出来就是:-rwxr-xr-x

  1. chmod 644 /readflag
  • 6(所有者):rw-(读、写)
  • 4(组):r–(只读)
  • 4(其他):r–(只读)

用 ls -l 显示出来就是:-rw-r--r--

上面的命令大概就是先开始给elf文件755权限可读可写可运行,后面再给644权限就不行了

解题步骤

上面我们知道,这里已经将/readflag权限修改了,我们无法执行。

但是我们可以用动态加载器去执行。

这里我们提一下/lib/ld-linux.so.2以及它的64位版本/lib64/ld-linux-x86-64.so.2:

/lib/ld-linux.so.2以及它的64位版本/lib64/ld-linux-x86-64.so.2虽然看起来是共享库文件,但实际上他们可以独立运行。他们的功能是负责动态加载。它们通过读取可执行文件的头部信息来确定哪些库文件是必须的,以及哪些需要加载。加载完成后,它会通过修正执行文件里的相关的地址指针来和加载的库文件完成动态链接,此时程序就可以运行了。

实际上,这两个文件的路径和名字,是 Linux 约定俗成的:

  • /lib/ld-linux.so.2 —— 32位 x86 程序使用
  • /lib64/ld-linux-x86-64.so.2 —— 64位 x86_64 程序使用

这两个文件都是动态链接器(Dynamic Linker/Loader) ,是一个特殊的 ELF 可执行文件,负责在程序启动时,加载并链接所有依赖的动态库(.so 文件),然后把控制权交给你的主程序。

我们已经学过静态链接与动态链接:

静态链接是在链接的时候,就把所依赖的第三方库函数都打包到了一起,导致最终的可执行文件非常大。而动态链接在链接的时候并不将那些库文件直接拿过来,而是在运行时,发现用到某些库中的某些函数时,再从这些第三方库中读取自己所需的方法。

而上面两个文件,就是用在动态链接上:

  • 当你运行一个动态链接的 ELF 程序(比如 C 编译出来的可执行文件),操作系统不会直接加载 main 函数,而是先加载这个动态链接器。
  • 链接器根据 ELF 文件头部的“interpreter”字段,找到自己(ld-linux…so),加载所有依赖的 .so 动态库(如 libc.so.6),解决符号(函数、变量)引用。
  • 一切准备好后,才真正跳转到你的程序入口(如 main)。

而我们这里,就可以用动态加载去运行/readflag

原理如下:

  • 动态链接器本身就是一个可执行程序,它能读取 ELF 文件的内容,解析并加载所需的段,然后在自身进程空间“执行”它。
  • 只要 ELF 文件有 r(读)权限,动态链接器就可以读取它的内容,不再检查 x(执行)权限。
  • 这本质上是“用别的程序来读和跑你的程序”,而不是直接“让系统执行你的程序”。

这里我们先确定用的是哪个动态加载器,

如何确定用哪个 ld-linux(动态加载器)?

方法一:readelf 工具

最常用、标准的方法是用 readelf -l <可执行文件>,比如:

1
2
3
readelf -l /readflag | grep 'interpreter'
输出类似:
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

方法二:file 命令

file /readflag 也会显示 interpreter。例如:

1
2
file /readflag
/readflag: ELF 64-bit LSB ... interpreter /lib64/ld-linux-x86-64.so.2 ...

为什么不同的 ELF 文件可能用不同的 ld-linux?

  • 32位程序用 /lib/ld-linux.so.2
  • 64位程序用 /lib64/ld-linux-x86-64.so.2
  • 某些架构(arm/aarch64)会用 /lib/ld-linux-armhf.so.3
  • interpreter 路径是编译阶段由编译器/链接器决定的

image-20250619180603440

然后拿到flag

注意,不是所有没有 x 权限的文件都能“动态加载”执行。

必须满足以下条件:

  1. 文件是合法的 ELF 格式(即 Linux 可执行文件格式),不能是随便的文本或其他二进制文件。
  2. 文件至少有 r(读)权限,否则动态加载器无法读取内容。
  3. 你有权限运行动态链接器(/lib/ld-linux.so.*),并且该链接器支持你要加载的 ELF 类型(比如架构一致)。

如果是普通文本文件、图片、脚本(没有 shebang 或 x 权限),就不能这样“动态加载”执行。

/proc/cmdline

未更新

/proc/environ

未更新

/proc/fd

未更新


Linux-CTFHub
http://example.com/2025/07/13/27Linux-CTFHub/
作者
sangnigege
发布于
2025年7月13日
许可协议