** 概述 本仓库使用 CodeQL 研究 polkit pkexec 漏洞。 包含
已完成事项:
待办事项:
** 漏洞详情 Polkit pkexec 漏洞 [[https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034][(CVE-2021-4034)]] 源于与 argv 相关的数组越界错误,并在此基础上进一步利用。越界部分的问题 我们可以通过 codeql 范围分析库来研究。
pkexec 的 main() 函数位于 polkit/src/programs/pkexec.c,其结构如下 #+begin_src text 435 main (int argc, char *argv[]) 436 { ... 534 for (n = 1; n < (guint) argc; n++) 535 { ... 568 } ... 610 path = g_strdup (argv[n]); ... #+end_src
主要思路:
需要检查的版本:
自 2009 年起的所有 Polkit 版本均存在漏洞;第一个版本发布于 2009 年 5 月(提交 c8c3d83,“Add a pkexec(1) command”)。
我们可以从 [[https://lgtm.com/projects/g/freedesktop/polkit/ci/#ql][lgtm]] 获取 /a/ 数据库,当前版本 <2022-02-11 Fri> 为 =...srcVersion_a6bedfd...= 但该版本已经包含了 polkit 补丁: #+BEGIN_SRC text commit a6bedfd09b7bba753de7a107dc471da0db801858 (origin/master, origin/HEAD, master) Author: Xi Ruoyao [email protected] Date: Thu Jan 27 10:16:32 2022 +0000
jsauthority: port to mozjs-91
commit a2bf5c9c83b6ae46cbd5c779d3055bff81ded683 Author: Jan Rybar [email protected] Date: Tue Jan 25 17:21:46 2022 +0000
pkexec: local privilege escalation (CVE-2021-4034)
#+END_SRC 我们可以看到问题已被修复: #+BEGIN_SRC text commit a2bf5c9c83b6ae46cbd5c779d3055bff81ded683 Author: Jan Rybar [email protected] Date: Tue Jan 25 17:21:46 2022 +0000
pkexec: local privilege escalation (CVE-2021-4034)
diff --git a/src/programs/pkcheck.c b/src/programs/pkcheck.c index f1bb4e1..768525c 100644 --- a/src/programs/pkcheck.c +++ b/src/programs/pkcheck.c @@ -363,6 +363,11 @@ main (int argc, char *argv[]) local_agent_handle = NULL; ret = 126;
exit(126);
因此我们需要 [[https://gitlab.freedesktop.org/polkit/polkit.git][源代码]] 并自行构建数据库,一个为补丁前版本,一个为补丁后版本。
下一节将介绍使用 Docker 容器的构建步骤。
** 构建 polkit 和 CodeQL 数据库 在获取 codeql 数据库之前,我们需要先完成 polkit 的构建环境设置。
构建的操作系统选项:
macOS 值得一试,但很快就会遇到麻烦。使用 =brew= 获取 依赖项在一定程度内可行,但 =mozjs-78= 依赖项是 spidermonkey 的特定版本,构建 /该/ 版本并不现实。 #+BEGIN_SRC sh # autoconf... 在 mac 上有点棘手 brew install autoconf automake libtool gtk-doc export PATH="/usr/local/opt/libtool/libexec/gnubin:$PATH" ./autogen.sh
# 使用 meson?
brew install meson ninja intltool glib gobject-introspection
#+END_SRC
Linux 是 polkit 的原生环境,但选择哪个发行版?mozjs-78 依赖项是 spidermonkey 的特定版本;此外,并非所有发行版都使用 polkit:
Ubuntu 22.04 可以通过多种方式运行,包括物理机、虚拟机(vmware、 virtualbox、multipass 等),或另一台主机上的 docker 容器。对于此问题,我们可以使用 Docker 容器,并同时包含 codeql 命令行工具。
容器的定义位于 ./Dockerfiles 中,以下是构建 序列: #+BEGIN_SRC shell # 用于设置 qlbuild 容器的基础镜像 docker pull ubuntu:jammy docker images docker run --cpus 4 -m 8GB -ti ubuntu:jammy
# 待定制的镜像
docker build -t qlbuild .
#+END_SRC 注意:在 Windows 和 Mac 上使用 docker desktop 时,内存和 CPU 限制必须 在那里调高。设置完成后,容器运行序列很简单 #+BEGIN_SRC sh # 以守护进程方式运行,以便断开连接时仍保持运行。 docker run -d -p 127.0.0.1:2020:22 --cpus 8 -m 16GB qlbuild
# 然后连接
ssh -p 2020 test@localhost
#+END_SRC
在 Ubuntu 22.04 上构建
#+BEGIN_SRC sh
# ---------------------------------
# 系统设置/安装,以 root 身份:
echo "deb-src http://archive.ubuntu.com/ubuntu/ jammy main restricted" >> /etc/apt/sources.list
apt-get update
apt-get install -y zile build-essential git cmake
meson ninja-build
libmozjs-78-0 libmozjs-78-dev
libdbus-1-3 libdbus-1-dev
apt-get build-dep -y policykit-1
apt install unzip
# polkit 版本 a2bf5c9c 还需要一些额外的包
apt install duktape duktape-dev
# 将旧版 meson 安装到 /usr/local/bin
pip3 install meson==0.60.3
# 或者获取源码并使用:
# wget https://github.com/mesonbuild/meson/archive/refs/tags/0.60.3.tar.gz
# tar zxf 0.60.3.tar.gz
# etc.
# ---------------------------------
# codeql 设置 -- 仍为 root
# 获取 -- 下载并解压 codeql cli 和库
# 用法:grab 版本 平台 前缀
grab() {
version=$1; shift
platform=$1; shift
prefix=$1; shift
mkdir -p $prefix/codeql-$version &&
cd $prefix/codeql-$version || return
# 获取 cli
wget "https://github.com/github/codeql-cli-binaries/releases/download/$version/codeql-$platform.zip"
# 获取库
wget "https://github.com/github/codeql/archive/refs/tags/codeql-cli/$version.zip"
# 修复属性
if [ `uname` = Darwin ] ; then
xattr -c *.zip
fi
# 解压
unzip -q codeql-$platform.zip
unzip -q $version.zip
# 重命名库目录以供 VS Code 使用
mv codeql-codeql-cli-$version/ ql
# 删除压缩包
rm codeql-$platform.zip
rm $version.zip
}
grab v2.7.6 linux64 /opt
grab v2.6.3 linux64 /opt
# ---------------------------------
# 以用户 test 身份:
# 获取 polkit 源码
cd /tmp && git clone https://gitlab.freedesktop.org/polkit/polkit.git
# 构建版本 0.119
cd /tmp/polkit
git checkout 0.119
git clean -fxd
meson setup builddir
meson compile -C builddir
find builddir -name pkexec -ls
: 139269 76 -rwxr-xr-x 1 test root 76696 Feb 12 03:06 builddir/src/programs/pkexec
# ---------------------------------
# 为版本 0.119 构建 codeql 数据库
cd /tmp/polkit
git checkout 0.119
git clean -fxd
# 照常运行配置步骤,不使用 codeql
cd /tmp/polkit && rm -fR builddir
meson setup builddir
# 在 codeql 下运行构建步骤
export CODEQL=/opt/codeql-v2.7.6/codeql/codeql
$CODEQL --version
$CODEQL database create --language=cpp -s . -j 8 -v \
polkit-0.119.db \
--command='meson compile -C builddir'
# 等待
# TRAP import complete (10.2s).
# Successfully created database at /tmp/polkit/polkit-0.119.db.
# 快速检查以确保 pkexec 已被识别:
unzip -v polkit-0.119.db/src.zip |grep pkexec
: 29713 Defl:N 8477 72% 2022-02-14 20:12 bb39f235 tmp/polkit/src/programs/pkexec.c
# ---------------------------------
# 为版本 a2bf5c9c(已修补版本,仍使用 mozjs-78)构建 codeql 数据库
cd /tmp/polkit
git checkout a2bf5c9c
git clean -fxd
# 照常运行配置步骤,不使用 codeql
cd /tmp/polkit && rm -fR builddir
/usr/local/bin/meson setup builddir
# 使用 meson 0.61 时,配置会遇到错误
# actions/meson.build:3:5: ERROR: Function does not take positional arguments.
# 快速搜索可找到
# https://lore.kernel.org/all/20220111222135.693a88f2@windsurf/T/
# 进而找到
# [1/1] package/gobject-introspection: bump to version 1.70.0
# 在 codeql 下运行构建步骤
export CODEQL=/opt/codeql-v2.7.6/codeql/codeql
$CODEQL --version
$CODEQL database create --language=cpp -s . -j 8 -v \
polkit-a2bf5c9c.db \
--command='/usr/local/bin/meson compile -C builddir'
# 等待
# TRAP import complete (7.2s).
# Successfully created database at /tmp/polkit/polkit-a2bf5c9c.db.
# 快速检查以确保 pkexec 已被识别:
unzip -v polkit-a2bf5c9c.db/src.zip |grep pkexec
: 30136 Defl:N 8647 71% 2022-02-14 21:27 6af18604 tmp/polkit/src/programs/pkexec.c
#+END_SRC
将数据库复制到主机上的永久位置 #+BEGIN_SRC sh # 从容器中复制 mkdir -p ~/local/polkit && cd ~/local/polkit scp -rq -P 2020 test@localhost:/tmp/polkit/polkit-0.119.db . scp -rq -P 2020 test@localhost:/tmp/polkit/polkit-a2bf5c9c.db .
# 保留原始文件
zip -rq polkit-0.119.zip polkit-0.119.db
zip -rq polkit-a2bf5c9c.zip polkit-a2bf5c9c.db
#+END_SRC
接下来,进行查询开发环境的设置。
** 查询开发环境设置 查询可以通过 codeql cli 单独探索,或使用 codeql cli + VS Code 插件。对于这两种情况,都需要安装 cli(参见上面的 =grab()= 函数), 并从 [[./db]] 解压数据库,或按照 [[*Build polkit and codeql db][构建 polkit 和 codeql 数据库]] 中的方法自行构建。
下面,我们假设数据库采用以下目录结构: #+BEGIN_SRC text . ├── polkit-0.119.db │ ├── codeql-database.yml │ ├── db-cpp │ ├── log │ └── src.zip ├── polkit-0.119.zip ├── polkit-a2bf5c9c.db │ ├── codeql-database.yml │ ├── db-cpp │ ├── log │ └── src.zip └── polkit-a2bf5c9c.zip #+END_SRC
** 查询 查询在 [[./argv-out-of-bounds-*.ql]] 中逐步开发。
[[./argv-out-of-bounds-0.ql]] 中的第一步使用 AST 和变量 引用将结果缩小到问题的已知部分,如下所示。
#+BEGIN_SRC text declaration | 435 main (int argc, char *argv[]) | 436 { | ... init other var; | 534 for (n = 1; compare to argc | n < (guint) argc; update other var | n++) | 535 { | ... | 568 } | ... indexed read | 610 path = g_strdup (argv[n]); | ... | 629 if (path[0] != '/') | 630 { | ... | 632 s = g_find_program_in_path (path); | ... indexed write | 639 argv[n] = path = s; | 640 } #+END_SRC
值的探索从 [[./argv-out-of-bounds-1.ql]] 开始,尝试 通过以下方式使用 =SimpleRangeAnalysis= 库 #+begin_src javascript lowerBound(cmp.getLeftOperand().getFullyConverted()), "left lower bound", lowerBound(cmp.getRightOperand().getFullyConverted()), "right lower bound" #+end_src 这些边界对于类型而言是正确的,但过于宽泛——它们包含了 迭代的可能结果。我们只对静态确定的初始边界感兴趣, 即任何迭代发生之前的边界。
换句话说,这不是一个通用的数据流问题;我们只想检查 特定执行路径上的初始值传播。=for= 循环 使这个问题复杂化,循环内的操作也是如此。
我们真正想要的是查看完全绕过循环的执行路径。这 在 [[./argv-out-of-bounds-1.ql]] 的后半部分通过使用 =SsaDefinition= 完成。
查询 [[./argv-out-of-bounds-2.ql]] 清理了 [[./argv-out-of-bounds-1.ql]] 中的探索,并正确识别了所有使用 =n= 且已知索引值 =n > 0= 的位置。
此查询在易受攻击的代码版本 =polkit-0.119.db= 上报告结果。 接下来,需要对其进行检查和增强,使其在已修补版本 =polkit-a2bf5c9c.db= 上不报告任何结果。