WWW.YOUINFO.SITE
标签聚合 glibc

/tag/glibc

LinuxDo 最新话题 · 2026-06-09 09:33:03+08:00 · tech

背景:需要在完全离线的国产 Linux(老版本 glibc)服务器上完成 llama.cpp CUDA 版本编译。 限制条件:1. 无 root 权限 2. 无 yum/apt 3. 无在线下载。 坑1:系统 GCC 版本过低 解决方案:使用 conda-forge 构建 GCC11 环境,通过 conda-pack 打包后在离线服务器使用 conda create -n gcc11 gcc=11 gxx=11 conda-pack 打包后解压使用 注意点:需要显式指定编译器,否则 CMake 可能仍使用系统 gcc export CC=$CONDA_PREFIX/bin/x86_64-conda-linux-gnu-gcc export CXX=$CONDA_PREFIX/bin/x86_64-conda-linux-gnu-g++ 坑2:libcuda.so.1 找不到 本质:不是驱动损坏或 CUDA 未安装,而是 CUDA 链接阶段缺少 -lcuda 对应的开发库 解决方案:使用 CUDA Toolkit 中的 stub 库进行链接阶段补全 /usr/local/cuda/lib64/stubs/libcuda.so 配置方式: export LIBRARY_PATH=/usr/local/cuda/lib64/stubs:$LIBRARY_PATH 说明:该方式仅用于编译链接阶段,不参与运行时加载,运行时仍依赖系统 NVIDIA driver 坑3:GLIBC 版本不满足 错误处理建议:不要尝试升级系统 glibc、不要替换 /lib64/libc.so,这类操作风险极高,可能导致系统直接不可用 解决方案:使用 conda 提供的用户态动态链接器绕过系统 glibc $CONDA_ENV/lib/ld-linux-x86-64.so.2 --library-path $CONDA_ENV/lib ./llama-server 原理说明:不是替换系统 glibc,而是直接使用 conda 的 ELF interpreter 启动程序,使程序运行在 conda 用户态运行时环境中 整体方案如下: GCC11(Conda toolchain) CUDA stubs(解决 libcuda 链接问题) Conda glibc runtime(解决运行时兼容问题) 最终在完全离线国产服务器环境中成功编译并运行 llama.cpp CUDA 版本。 1 个帖子 - 1 位参与者 阅读完整话题

LinuxDo 最新话题 · 2026-05-22 20:35:28+08:00 · tech

前言 如果你的服务器是 Ubuntu 18.04(或更老)但想用 COMSOL 6.3 / 6.4 这种新版本,安装时大概率会被这个错误劝退: glibc 2.28 not found This installation of COMSOL requires glibc 2.28 or later Ubuntu 18.04 自带 glibc 2.27,COMSOL 6.3+ 要求 2.28+, 版本差一点点装不上 。 不要手动升级 glibc。glibc 是所有二进制程序的基础,强行替换会让系统启动不了、SSH 也登不进去,云服务器直接报废。 正确的解决方案是 Docker 容器化 :在容器里跑新版 Ubuntu,里面装 COMSOL;宿主机系统完全不动。本文以 Ubuntu 18.04 + COMSOL 6.4 为例,记录完整流程和踩过的坑。 一、整体思路 文件永远在宿主机上 ,容器只是临时借用 CPU/内存来运行 COMSOL 装在宿主机的 /opt/comsol64/ 里,容器删了文件还在 容器和宿主机之间通过 挂载(bind mount) 共享目录 GUI 通过宿主机的 X11 socket 转给已有的 VNC 桌面 二、安装 Docker(Ubuntu 18.04专属) 2.1 Docker 官方源不再支持 Ubuntu 18.04 Ubuntu 18.04 在 2023 年 EOL, Docker 官方仓库已经不再为 bionic 发包 。如果你用网上常见的 docker-ce 教程,会报: E: 仓库 "https://download.docker.com/linux/ubuntu bionic Release" 没有 Release 文件 直接用 Ubuntu 自带源的 docker.io 就行,版本 20.10,跑容器完全够用。 2.2 安装 sudo apt update sudo apt install -y docker.io # 启动并开机自启 sudo systemctl enable --now docker # 加当前用户到 docker 组(免 sudo) sudo usermod -aG docker $USER # 必须重新登录让组生效 exit # 重新 SSH 进来 2.3 配置镜像加速(国内必做) 直连 Docker Hub 经常超时,配国内镜像加速: sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json > /dev/null << 'EOF' { "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.nju.edu.cn", "https://hub-mirror.c.163.com" ] } EOF sudo systemctl daemon-reload sudo systemctl restart docker 2.4 验证 docker --version docker run --rm hello-world # 拉一下我们要用的 Ubuntu 22.04 基础镜像 docker pull ubuntu:22.04 hello-world 能跑出 “Hello from Docker!”、 ubuntu:22.04 能拉下来就成。 三、准备目录和 ISO # 宿主机上的目录规划 sudo mkdir -p /opt/comsol64 # COMSOL 安装目标 sudo chown -R $USER:$USER /opt/comsol64 mkdir -p ~/comsol_iso # 放 ISO mkdir -p ~/comsol_work # 工作文件(.mph) mkdir -p ~/comsol-docker # Dockerfile 目录 # 把你的 COMSOL ISO 文件放到 ~/comsol_iso/ # 比如从你 Mac 上传: # scp ~/Downloads/COMSOL.6.4.293.x64_dvd.iso zzz@<服务器IP>:~/comsol_iso/ ls -lh ~/comsol_iso/ # 应该看到几个 GB 的 ISO 文件 目录名 comsol64 只是个标签,里面装什么版本都行。装 6.3 也可以叫 comsol64 ,纯习惯问题。 四、写 Dockerfile 并构建镜像 cd ~/comsol-docker cat > Dockerfile << 'EOF' FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Asia/Shanghai # COMSOL 运行需要的所有依赖 RUN apt-get update && apt-get install -y --no-install-recommends \ # X11 基础 libx11-6 libxext6 libxrender1 libxrandr2 libxi6 \ libxcursor1 libxinerama1 libxft2 libxtst6 libxss1 \ libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 \ # OpenGL libgl1 libglu1-mesa libegl1 libgles2 \ # 字体 libfontconfig1 libfreetype6 fontconfig \ fonts-dejavu fonts-liberation fonts-noto-cjk \ # GTK / WebKit (COMSOL 帮助系统) libgtk-3-0 libwebkit2gtk-4.0-37 \ libatk1.0-0 libatk-bridge2.0-0 libnss3 libcups2 \ # 音频和常用库 libasound2 libgomp1 \ # 消除 GTK 警告(可选但推荐) libcanberra-gtk-module libcanberra-gtk3-module \ librsvg2-common shared-mime-info \ # 工具 xauth ca-certificates locales \ && rm -rf /var/lib/apt/lists/* \ && fc-cache -fv RUN locale-gen en_US.UTF-8 zh_CN.UTF-8 ENV LANG=en_US.UTF-8 # 创建和宿主机一致的用户(避免文件权限问题) ARG USER_ID=1000 ARG GROUP_ID=1000 ARG USER_NAME=zzz RUN groupadd -g ${GROUP_ID} ${USER_NAME} && \ useradd -m -u ${USER_ID} -g ${GROUP_ID} -s /bin/bash ${USER_NAME} USER ${USER_NAME} WORKDIR /home/${USER_NAME} CMD ["/bin/bash"] EOF 构建: cd ~/comsol-docker docker build \ --build-arg USER_ID=$(id -u) \ --build-arg GROUP_ID=$(id -g) \ --build-arg USER_NAME=$(whoami) \ -t comsol-runtime . $(id -u) 、 $(id -g) 、 $(whoami) 自动取当前用户的 UID/GID/用户名。这一步 至关重要 ——让容器内的用户和宿主机一致,避免后续保存的文件权限混乱(不然 .mph 文件会归属 root,宿主机用户改不动)。 构建大概 3–5 分钟,看到 Successfully tagged comsol-runtime:latest 即成功。 五、在容器里安装 COMSOL(要在 VNC 桌面里操作) 这一步必须在 VNC 桌面的终端里跑 ,因为 COMSOL 安装器是 GUI,需要图形界面。 5.1 连进 VNC 桌面 用 RealVNC Viewer / TurboVNC Viewer 连到服务器的 VNC 桌面。桌面里打开终端(XFCE 的 Terminal Emulator)。 验证你在对的地方: echo $DISPLAY # 应该是 :1 或类似,不能是空、不能是 localhost:10.0(那是 SSH 转发) 5.2 挂载 ISO sudo mkdir -p /mnt/comsol_iso sudo mount -o loop ~/comsol_iso/COMSOL.6.4.293.x64_dvd.iso /mnt/comsol_iso # 验证 ls /mnt/comsol_iso/ # 应该看到 setup、setupgui、bin/ 等 文件名按你实际的 ISO 改。挂载时提示 WARNING: device write-protected, mounted read-only 是 正常 的,ISO 本来就是只读的。 5.3 允许容器访问 X11 xhost +local: # 输出 "non-network local connections being added to access control list" 5.4 启动安装容器 docker run --rm -it \ --name comsol-installer \ -e DISPLAY=$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix:rw \ -v /mnt/comsol_iso:/iso:ro \ -v /opt/comsol64:/usr/local/comsol/multiphysics:rw \ -v $HOME/comsol_work:/home/$(whoami)/comsol_work \ --net=host \ --shm-size=4g \ comsol-runtime \ bash 进入容器后提示符可能看起来和宿主机一样(因为我们用了同样的用户名),验证一下: # 容器内 ldd --version | head -1 # ldd (Ubuntu GLIBC 2.35-...) 2.35 ← 容器里是 2.35,证明在容器里 cat /etc/os-release | grep VERSION # VERSION="22.04 ..." 5.5 运行安装器 cd /iso ./setup GUI 窗口会弹到你的 VNC 桌面上。 安装时的关键设置 : 步骤 选什么 License 按你的许可证类型选 Install Directory 手动改成 /usr/local/comsol/multiphysics Components 按需勾选模块 Install Directory 这一步特别重要 COMSOL 默认建议 /usr/local/comsol64/multiphysics 这种带版本号的路径,但 容器里这个目录不存在 ,而 /usr/local/ 是 root 拥有的,容器里的用户没权限创建——会报"目标文件夹无效"。 必须手动改成 /usr/local/comsol/multiphysics (去掉 comsol64 那一层),这个路径已经是我们挂载好的目录,可以写。 填好后 Next,安装 10–30 分钟,看你勾了多少模块。 5.6 安装完成 # 容器内验证 ls /usr/local/comsol/multiphysics/bin/ # 应该看到 comsol 文件 exit # 退出容器 宿主机验证: ls /opt/comsol64/bin/ # 同样应该看到 comsol,证明文件持久化了 卸载 ISO(不再需要): sudo umount /mnt/comsol_iso 六、写启动脚本 6.1 GUI 模式启动脚本 cat > ~/run_comsol.sh << 'SCRIPT_EOF' #!/bin/bash # COMSOL 6.4 GUI 启动脚本 if [ -z "$DISPLAY" ]; then echo "错误:DISPLAY 变量为空,请在 VNC 桌面终端里运行" exit 1 fi xhost +local: > /dev/null docker run --rm -it \ --name comsol \ -e DISPLAY=$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix:rw \ -v /opt/comsol64:/usr/local/comsol/multiphysics \ -v $HOME/comsol_work:/home/$(whoami)/comsol_work \ --net=host \ --shm-size=8g \ comsol-runtime \ /usr/local/comsol/multiphysics/bin/comsol -3drend sw "$@" SCRIPT_EOF chmod +x ~/run_comsol.sh -3drend sw 是软件渲染参数,适合无 GPU 的服务器。如果有显卡用硬件加速,去掉这个参数(或换成 -3drend ogl )。 6.2 Batch 模式启动脚本 cat > ~/run_comsol_batch.sh << 'SCRIPT_EOF' #!/bin/bash # COMSOL 6.4 Batch 模式启动脚本 # 用法: ./run_comsol_batch.sh input.mph output.mph [其他 comsol batch 参数] if [ $# -lt 2 ]; then echo "用法: $0 <input.mph> <output.mph> [其他参数]" echo "示例: $0 model.mph result.mph -np 48" exit 1 fi INPUT="$1" OUTPUT="$2" shift 2 EXTRA_ARGS="$@" INPUT_NAME=$(basename "$INPUT") OUTPUT_NAME=$(basename "$OUTPUT") LOG_NAME="${OUTPUT_NAME%.mph}.log" docker run --rm \ --name comsol-batch-$$ \ -v /opt/comsol64:/usr/local/comsol/multiphysics \ -v $HOME/comsol_work:/home/$(whoami)/comsol_work \ --net=host \ --shm-size=16g \ comsol-runtime \ /usr/local/comsol/multiphysics/bin/comsol batch \ -inputfile /home/$(whoami)/comsol_work/$INPUT_NAME \ -outputfile /home/$(whoami)/comsol_work/$OUTPUT_NAME \ -batchlog /home/$(whoami)/comsol_work/$LOG_NAME \ $EXTRA_ARGS SCRIPT_EOF chmod +x ~/run_comsol_batch.sh 6.3 设置别名(可选,避免影响其他用户) 如果服务器多人共用一个账号,老用户可能还在用 COMSOL 6.1(不要碰他们的环境)。只给自己加新版本的快捷命令: echo 'alias comsol64="$HOME/run_comsol.sh"' >> ~/.bashrc source ~/.bashrc 效果: 老用户 comsol → 继续走 PATH 里的老版本(不受影响) 你 comsol64 → 走 Docker 跑新版本 七、日常使用 7.1 GUI 交互模式 每次工作流: Mac 打开 RealVNC Viewer,连服务器 :1 VNC 桌面里打开 XFCE 终端 跑: ~/run_comsol.sh # 或用 alias comsol64 COMSOL 启动,正常用,保存的 .mph 文件存到 ~/comsol_work/ 打开已有文件: comsol64 -open /home/zzz/comsol_work/mymodel.mph 注意路径用容器内的( /home/zzz/comsol_work/ ),等于宿主机的 ~/comsol_work/ 。 7.2 Batch 模式(推荐用于大模型) 不需要 GUI,可以 SSH 直接跑、扔后台、关掉终端继续算: # 文件先放进 ~/comsol_work/ cp mymodel.mph ~/comsol_work/ # 前台跑 ~/run_comsol_batch.sh mymodel.mph result.mph -np 48 # 扔后台跑(推荐) nohup ~/run_comsol_batch.sh mymodel.mph result.mph -np 48 \ > batch.out 2>&1 & 7.3 监控正在跑的 batch # 看容器状态 docker ps # 看 COMSOL 求解日志 tail -f ~/comsol_work/result.log # 看容器资源占用 docker stats # 判断是否完成(recovery 文件消失 = 任务结束) ls ~/comsol_work/result.mph.recovery 2>/dev/null || echo "完成" 7.4 batch 常用参数 ~/run_comsol_batch.sh model.mph result.mph \ -np 48 \ # 使用 48 核 -study std1 \ # 只跑名为 std1 的研究 -paramfile params.txt # 参数扫描 # 看全部参数 docker run --rm comsol-runtime \ /usr/local/comsol/multiphysics/bin/comsol batch -help 实测在 Ubuntu 18.04 上跑 COMSOL 6.4, 性能和原生安装基本无差异 (容器化的性能损耗对计算密集型应用几乎可以忽略)。唯一的代价是首次启动多 1-2 秒(加载共享库),后续启动就秒开。 希望这篇能帮到同样卡在 glibc 版本问题上的同学。有补充欢迎反馈。 1 个帖子 - 1 位参与者 阅读完整话题

LinuxDo 最新话题 · 2026-05-06 21:53:55+08:00 · tech

glibc动态内存分配 写C语言时,如果想申请或释放内存,就需要 malloc 和 free 函数。从linux内核层面来看,往往会用到这两个系统调用: mmap brk 其中, brk 会将进程的heap段向内存高位扩充,可以理解为把堆顶抬高了。而 mmap 则在heap段和stack段之间申请匿名内存(不用担心这片区域被填满,64位系统下,你的电脑内存加起来都没它大)。 结合linux进程的简化内存分布,更加直观一些: 低地址 [text] [data] [bss] [heap] … [mmap region] … [stack] 高地址 释放内存的系统调用也有两个: munmap brk (没错, brk 也可以收缩堆空间) 这两种分配方式的区别在于, mmap 一般用来申请大块内存(默认值大约是128KB以上) 但是呢,问题出现了。每次进行系统调用时,都要从ring3态切换到ring0态,中间还要进行一堆安全检查,完事了还得再换回来,极其消耗系统资源。为此,glibc开局就申请了一大块内存,根据进程的需要来细细分割,管理这些内存的管理器就是ptmalloc2。接下来我们详细说说它。 结构初识 为了弄清楚这玩意,我查了不少资料。刚开始读源码不容易看懂;ctf wiki的内容全面,可是一上来就是一堆细节,看了头晕;相应的视频和书籍虽然权威,但基本都停留在2.29之前的版本。所以嘛…我想写一点新的东西,就比如这篇教程。 下面按照由浅入深的顺序,介绍一些概念: malloc : 申请内存 free : 释放内存 chunk : 申请的内存块,每次 malloc 都会申请到一个 chunk bin : 垃圾桶, chunk 被 free 释放后会进入 bin arena : 一个 arena 相当于一个堆,有自己的一套 bins main_arena : 主线程使用的 arena ,目前我们只研究单线程程序,也就是只有 main_arena 一个 arena malloc 分两种情况 申请大块内存:使用 mmap 系统调用 释放普通内存:正常流程(从 bin 获取,没有的话再进行分配) free 也分两种情况 释放 mmap 申请的内存:使用 munmap 系统调用 申请小块内存:正常流程(进 bin ) chunk 每一个 chunk 都是有自己的结构滴,这个结构的全称叫做 malloc_chunk ,好在不复杂。 // malloc/malloc.c:1153 struct malloc_chunk { INTERNAL_SIZE_T mchunk_prev_size; /* 前一个 chunk 大小 */ INTERNAL_SIZE_T mchunk_size; /* 当前 chunk 大小 */ struct malloc_chunk* fd; /* 前向指针 */ struct malloc_chunk* bk; /* 后向指针 */ struct malloc_chunk* fd_nextsize; /* large bin 专用 */ struct malloc_chunk* bk_nextsize; /* large bin 专用 */ }; 讲解一下字段的含义 INTERNAL_SIZE_T 就是 size_t ,前两个变量存放 chunk 大小,它们被称为 chunk 头部。因为 malloc 申请完内存之后,用户可以使用的部分是从第三个字段开始的,也就是说,后面的四个指针变量,正常情况下是没用的,会被用户写入的数据覆盖。 用户申请内存时,必须是16字节的整数倍(32位是8字节),就算你申请了1字节,ptmalloc2也会给你16字节。而 chunk 头部就占据了16字节(以后默认64位系统),所以一个 chunk 最小也有32字节。 这时,细心的你会发现,既然每个 chunk 都是16的倍数,那么 mchunk_size (简称 size )字段的低4位就处于未使用的状态,那就拿来当标志位吧: 位 宏 含义 bit 0 PREV_INUSE 前一个 chunk 是否被分配 bit 1 IS_MMAPPED 是否通过 mmap 分配 bit 2 NON_MAIN_ARENA 是否不属于 main arena 只用3个比特位,是为了兼容32位的版本。 chunk 进入 bin 后, fd 指针就指向前一个进入该 bin 的 chunk , bk 指针指向后一个进入该 bin 的 chunk 。这样的话,在 bin 中形成链表结构,方便存取。至于 fd_nextsize 和 bk_nextsize ,现在并不重要,待以后慢慢细讲。 先说这么多吧,多了不好消化。 知识库 how2heap :一个学习堆利用技术的绝佳资源库,包含了针对不同glibc版本的各种利用方法(如fastbin_dup, unsafe unlink, house of系列等)的教学代码。 CTF Wiki :由社区持续维护的CTF知识库,PWN部分涵盖了从入门基础到高级利用的系统性教程。 ctf pwn之glibc堆利用 : 我的教程(混进了什么奇怪的东西) 本人第一次写教程,求佬友批评指正,我会不断改进的。 1 个帖子 - 1 位参与者 阅读完整话题

linux.do · 2026-04-29 22:55:09+08:00 · tech

简单聊聊堆管理器 众所周知,内存分配方面的策略有很多。不同的操作系统、不同的C语言库甚至应用程序都有不同的堆管理器。就比如: 堆管理器 平台 ptmalloc2 现代Linux (glibc 2.3+) Windows Heap Windows (NT 内核) tcmalloc Google自研,多线程速度快 mallocng musl libc,轻量级 而我们主要研究的,就是linux下最常用C语言标准库glibc的堆管理器——ptmalloc2 在pwn区,glibc可谓是令人闻风丧胆,每隔几个版本就整波大的,机制还复杂得离谱。 linux环境下,我们可以运行 /lib/libc.so.6 查看当前系统的glibc版本。 glibc重大版本 2.23 及之前 除了 safe_unlink 之外,几乎没有值得一提的防护机制。可以理解机制,但过于古老,现在几乎见不到。 2.26 引入万恶之源 tcache ,虽说在多线程方面性能有所增加,但是安全防护堪称一坨shit。属于堆利用的事故多发地。 2.29 一个密集的“打补丁”版本。Unsorted Bin Attack、House of Force 等经典手法被集中修复, tcache 的 double‑free 检测也进一步增强,利用难度上了一个台阶。 2.32 PWN 选手的噩梦——Safe Linking 横空出世。tcache 和 fastbin 的 fd 指针现在会被加密存储,直接篡改变得非常困难,必须配合地址泄露才能绕过。同时,对齐检查的引入也让构造精密的假堆块变得更加容易失败。 2.34 移除了 __malloc_hook 和 __free_hook 。以前靠堆溢出覆盖这两个钩子就能轻松接管控制流,现在这条路被彻底堵死,攻击者被迫转向 _IO_FILE 结构的攻击。 2.36 运行时强制为 GOT 表施加只读保护(等效 Full RELRO)。直接在 GOT 里改写函数指针的方式彻底失效,篡改 _IO_FILE 虚表(如 House of Apple 系列)成为绝对主流。 2.40+ 开始重构核心函数,进入现代化阶段。单纯利用堆getshell已经不太可能。 何处启程 古代 : 学习无防护的经典利用——glibc 2.23; 学习tcache的经典利用——glibc 2.27。现代环境中,这些手段都有相应的防护措施。 近代 :glibc 2.35,Safe-Linking是一个难点,并尝试 tcache stashing unlink attack 等高阶技巧。 现代 :glibc 2.37 - 2.43,堆利用与glibc其他模块( IO_FILE 等)结合。 根据 how2heap 的利用教程,这次我们先从glibc2.35的基础环境开始,进行一些机制以及著名利用手段(off-by-one, house-of系列等)。 常用工具 Docker 环境 (如 pwndocker ) :强烈推荐用于搭建本地环境,可以轻松切换不同版本的glibc,确保与远程服务器环境完全一致。另外,虚拟环境也方便与主机隔离,防止自己给自己RCE了 。 pwndbg : 直观查看堆内存布局与各种结构,本地进行调试 pwntools : 每一个pwn大手子的专武,用于编写脚本(更多功能等待发现)。 知识库 how2heap :一个学习堆利用技术的绝佳资源库,包含了针对不同glibc版本的各种利用方法(如fastbin_dup, unsafe unlink, house of系列等)的教学代码。 CTF Wiki :由社区持续维护的CTF知识库,PWN部分涵盖了从入门基础到高级利用的系统性教程。 ctf pwn之glibc堆利用 : 我的教程(混进了什么奇怪的东西) 本人第一次写教程,求佬友批评指正,我会不断改进的。 2 个帖子 - 2 位参与者 阅读完整话题

linux.do · 2026-04-22 00:45:18+08:00 · tech

CVE-2024-2961 完整 RCE 链详解:1字节 glibc 溢出如何秒杀 PHP 这里算是我为数不多接触到pwn的一次,但是我这次机会想到,我有必要进行对pwn的学习 当然,这次是PHP的多链引发的想法,涉及了一个PWN相关影响深远的CVE,CVE-2025-2961. 我会用WEB手也通俗易懂的语言来讲解,这个CVE网上相关文章也很多,我也就阐述一下自己观点 这个CVE的是发生在iconv函数中,并且基本上是在转义函数可控的情况下发生的, 这里先说说前置概念,这个缓冲区溢出是因为ISO-2022-CN-EXT的SS2/SS3切换缺乏缓冲区校验引起的 ISO-2022-CN-EXT是一个中文字符编码形式,假设这个编码是一个柜子,那么SS2,SS3就是抽屉,用来存个别字符的柜子 我们先看一看简化的关键源码, else if ((used & SS2_mask) != 0 && (ann & SS2_ann) != (used << 8)) { const char *escseq; assert (used == CNS11643_2_set); /* XXX */ escseq = "*H"; *outptr++ = ESC; // 0x1B *outptr++ = '$'; // 0x24 *outptr++ = *escseq++; // 0x2A *outptr++ = *escseq++; // 0x48 ann = (ann & ~SS2_ann) | (used << 8); } else if ((used & SS3_mask) != 0 && (ann & SS3_ann) != (used << 8)) { const char *escseq; assert ((used >> 5) >= 3 && (used >> 5) <= 7); escseq = "+I+J+K+L+M" + ((used >> 5) - 3) * 2; *outptr++ = ESC; *outptr++ = '$'; *outptr++ = *escseq++; *outptr++ = *escseq++; ann = (ann & ~SS3_ann) | (used << 8); } 当识别是SS2/SS3的时候在缓存区写入我注视的这一串命令,而这里并没有校验缓冲区是否够存放, 所以会造成缓冲区溢出,这个理念倒是非常清晰易懂,但是难点是如何利用进行RCE。 在PHP中控制了拥有读文件等操作,在漏洞版本范围内,便可上升为RCE 这里要先从PHP的内存管理机制说起,读文件可以用过滤器php://filter 而filter支持dechunk自定义块,在一块内存中,如果不停发送dechunk也就可以自定义每块的大小, file_get_contents("php://filter/ dechunk| convert.iconv.latin1.latin1| convert.iconv.latin1.latin1| (重复多次堆喷) convert.iconv.UTF-8.ISO-2022-CN-EXT /resource=/etc/passwd"); 也就是空闲内存,然后当中插入受害块,也就是ISO-2022-CN-EXT的SS2/SS3 进行编码转换命令时分到的块,而有几个特殊的汉字会使其溢出,这样就会溢出到相邻块, 也就会污染相邻块的最低位,往往是0x48,也就是说最低位0000变为0x48,这样内存释放时 free_solts的这部分内存指向就变了,链表被毒化之后, 攻击者利用堆喷(简略概括),将许多空闲内存都挂上string对象,而其中是有len属性的, 当正常执行一次构造字符串时,地址偏移让正常写字符串的操作写入了len字段 这样len的长度可以被改的超级大,这样就泄漏了大量系统函数的地址 接下来就是RCE PHP的内存管理器是ZENDMM,他是一个大内存池, 它控制分配这些内存的供给与释放,而其中_zend_mm_heap结构的子结构custom_heap控制内存释放的指针 它控制着内存释放的时候指向的函数指针 如此一来我调用dechunk结合之前泄漏的地址,任意写入custom_heap,将指针覆盖为system()的地址 //简化结构 typedef struct _zend_mm_custom_heap { void (*_free)(void *ptr); // ← 要改的目标 // ... } custom_heap; _zend_mm_heap->custom_heap._free = system(); // 攻击者通过 overlap 实现 也就是每次内存释放都会调用 当然PHP的内存调用是很频繁的, 最后将要执行的命令放入一个可控的zend_string对象中,也就是一个挂载其的对象,然后触发一次内存释放, 命令也就执行了。 1 个帖子 - 1 位参与者 阅读完整话题