
polkit CVE-2021-4034를 codeql로 탐색하는 모든 단계
** 개요 이 저장소는 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 범위 분석 라이브러리로 살펴볼 수 있습니다.
polkit/src/programs/pkexec.c의 pkexec의 main() 함수는 다음과 같은 구조를 가집니다: #+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").
/a/ 데이터베이스를 [[https://lgtm.com/projects/g/freedesktop/polkit/ci/#ql][lgtm에서]] 얻을 수 있으며, 현재 <2022-02-11 금> 버전은 =...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 DB 구축 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는 하드웨어, VM (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
# /usr/local/bin에 이전 meson 설치
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
# 등.
# ---------------------------------
# codeql 설정 -- 여전히 root
# 가져오기 -- codeql cli 및 라이브러리 검색 및 추출
# 사용법: grab 버전 url 접두사
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"
# lib 가져오기
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용 codeql 데이터베이스 빌드 (여전히 mozjs-78 사용)
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
db를 호스트의 영구 위치에 복사 #+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][Build polkit and codeql db]]에서 수행한 대로 빌드합니다.
다음에서는 데이터베이스에 대해 다음 디렉토리 구조를 가정합니다: #+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 > 0=으로 =n=을 사용하는 모든 위치를 올바르게 식별합니다.
이 쿼리는 취약한 버전의 코드인 =polkit-0.119.db=에서 결과를 보고합니다. 다음으로, 패치된 버전인 =polkit-a2bf5c9c.db=에서 아무것도 보고하지 않도록 확인하고 개선해야 합니다.