
Toutes les étapes de l'exploration de la CVE-2021-4034 de polkit à l'aide de codeql
** Vue d'ensemble Ce dépôt examine le bug pkexec de polkit en utilisant CodeQL. Il contient
Ce qui est fait :
Ce qui reste à faire :
** Le Bug Le bug pkexec de Polkit [[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)]] commence par une erreur de bornes de tableau concernant argv et s'appuie sur cela. La partie hors bornes du problème est quelque chose que nous pouvons examiner avec la bibliothèque d'analyse de plages de codeql.
La fonction main() de pkexec dans polkit/src/programs/pkexec.c a la structure #+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
Idées principales :
Versions à vérifier :
Toutes les versions de Polkit à partir de 2009 sont vulnérables ; première version en mai 2009 (commit c8c3d83, « Add a pkexec(1) command »).
nous pouvons obtenir /une/ base de données [[https://lgtm.com/projects/g/freedesktop/polkit/ci/#ql][depuis lgtm]], celle actuelle <2022-02-11 ven> est =...srcVersion_a6bedfd...= mais celle-ci est déjà après le correctif 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 Et nous pouvons voir que le problème est corrigé : #+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);
Nous avons donc besoin du [[https://gitlab.freedesktop.org/polkit/polkit.git][code source]] et de construire nos propres bases de données, une avant correctif, une après.
La section suivante détaille les étapes de construction, en utilisant un conteneur Docker.
** Construire polkit et la base de données CodeQL Nous avons besoin de la configuration de construction pour polkit avant de pouvoir obtenir une base de données codeql.
Options de systèmes d'exploitation pour la construction :
macOS vaut la peine d'être essayé, mais cela devient délicat rapidement. Utiliser =brew= pour obtenir les dépendances fonctionne jusqu'à un certain point, mais la dépendance =mozjs-78= est une version spécifique de spidermonkey et construire /cela/ n'est pas pratique. #+BEGIN_SRC sh # autoconf... un peu délicat sur un mac brew install autoconf automake libtool gtk-doc export PATH="/usr/local/opt/libtool/libexec/gnubin:$PATH" ./autogen.sh
# Utiliser meson ?
brew install meson ninja intltool glib gobject-introspection
#+END_SRC
Linux est l'environnement natif pour polkit, mais lequel ? La dépendance mozjs-78 est une version spécifique de spidermonkey ; de plus, polkit n'est pas utilisé par toutes les distributions :
Ubuntu 22.04 peut être exécuté de plusieurs manières, sur du matériel, une VM (vmware, virtualbox, multipass, etc.), ou un conteneur docker sur un autre hôte. Pour ce problème, nous pouvons utiliser un conteneur Docker et inclure également les outils en ligne de commande codeql.
La définition du conteneur est dans ./Dockerfiles, voici la séquence de construction : #+BEGIN_SRC shell # Image de base pour configurer le conteneur qlbuild docker pull ubuntu:jammy docker images docker run --cpus 4 -m 8GB -ti ubuntu:jammy
# Image à personnaliser
docker build -t qlbuild .
#+END_SRC Remarque : lors de l'utilisation de docker desktop sur windows et mac, les limites de mémoire et de cpu doivent être augmentées là-bas. Une fois définies, la séquence d'exécution du conteneur est simplement #+BEGIN_SRC sh # Exécuter en tant que démon pour qu'il reste actif même en cas de déconnexion. docker run -d -p 127.0.0.1:2020:22 --cpus 8 -m 16GB qlbuild
# Et se connecter
ssh -p 2020 test@localhost
#+END_SRC
Construction sur Ubuntu 22.04
#+BEGIN_SRC sh
# ---------------------------------
# Configuration/installation du système, en tant que 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
# la version polkit a2bf5c9c nécessite également quelques extras
apt install duktape duktape-dev
# meson plus ancien dans /usr/local/bin
pip3 install meson==0.60.3
# Ou obtenir la source et l'utiliser :
# wget https://github.com/mesonbuild/meson/archive/refs/tags/0.60.3.tar.gz
# tar zxf 0.60.3.tar.gz
# etc.
# ---------------------------------
# configuration codeql -- toujours en tant que root
# récupérer -- télécharger et extraire le cli et la bibliothèque codeql
# Utilisation : grab version url prefix
grab() {
version=$1; shift
platform=$1; shift
prefix=$1; shift
mkdir -p $prefix/codeql-$version &&
cd $prefix/codeql-$version || return
# Obtenir le cli
wget "https://github.com/github/codeql-cli-binaries/releases/download/$version/codeql-$platform.zip"
# Obtenir la bibliothèque
wget "https://github.com/github/codeql/archive/refs/tags/codeql-cli/$version.zip"
# Corriger les attributs
if [ `uname` = Darwin ] ; then
xattr -c *.zip
fi
# Extraire
unzip -q codeql-$platform.zip
unzip -q $version.zip
# Renommer le répertoire de la bibliothèque pour VS Code
mv codeql-codeql-cli-$version/ ql
# Supprimer les archives
rm codeql-$platform.zip
rm $version.zip
}
grab v2.7.6 linux64 /opt
grab v2.6.3 linux64 /opt
# ---------------------------------
# En tant qu'utilisateur test :
# Obtenir la source polkit
cd /tmp && git clone https://gitlab.freedesktop.org/polkit/polkit.git
# Construire la version 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
# ---------------------------------
# Construire la base de données codeql pour la version 0.119
cd /tmp/polkit
git checkout 0.119
git clean -fxd
# Exécuter l'étape de configuration comme d'habitude, sans codeql
cd /tmp/polkit && rm -fR builddir
meson setup builddir
# Exécuter l'étape de construction sous 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'
# Attendre
# TRAP import complete (10.2s).
# Successfully created database at /tmp/polkit/polkit-0.119.db.
# Et une vérification rapide pour s'assurer que pkexec a été vu :
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
# ---------------------------------
# Construire la base de données codeql pour la version a2bf5c9c, la version corrigée (et utilisant toujours
# mozjs-78)
cd /tmp/polkit
git checkout a2bf5c9c
git clean -fxd
# Exécuter l'étape de configuration comme d'habitude, sans codeql
cd /tmp/polkit && rm -fR builddir
/usr/local/bin/meson setup builddir
# Avec meson 0.61, la configuration rencontre l'erreur
# actions/meson.build:3:5: ERROR: Function does not take positional arguments.
# une recherche rapide mène à
# https://lore.kernel.org/all/20220111222135.693a88f2@windsurf/T/
# et de là à
# [1/1] package/gobject-introspection: bump to version 1.70.0
# Exécuter l'étape de construction sous 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'
# Attendre
# TRAP import complete (7.2s).
# Successfully created database at /tmp/polkit/polkit-a2bf5c9c.db.
# Et une vérification rapide pour s'assurer que pkexec a été vu :
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
Copier la base de données vers un emplacement permanent sur l'hôte #+BEGIN_SRC sh # Copier depuis le conteneur 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 .
# Conserver les originaux
zip -rq polkit-0.119.zip polkit-0.119.db
zip -rq polkit-a2bf5c9c.zip polkit-a2bf5c9c.db
#+END_SRC
Ensuite, la configuration pour le développement de requêtes.
** Configuration du développement de requêtes Les requêtes peuvent être explorées via le cli codeql seul, ou en utilisant le cli codeql + le plugin VS Code. Dans les deux cas, installer le cli (voir la fonction =grab()= ci-dessus), et extraire les bases de données de [[./db]] ou les construire comme fait dans [[*Build polkit and codeql db][Construire polkit et la base de données codeql]]
Dans ce qui suit, nous supposons cette structure de répertoires pour les bases de données : #+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
** La requête La requête est développée de manière incrémentale dans [[./argv-out-of-bounds-*.ql]].
Les premières étapes dans [[./argv-out-of-bounds-0.ql]] utilisent l'AST et les références de variables pour réduire les résultats aux parties connues du problème, comme dans ce qui suit.
#+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
L'exploration des valeurs commence dans [[./argv-out-of-bounds-1.ql]] avec une tentative d'utilisation de la bibliothèque =SimpleRangeAnalysis= via #+begin_src javascript lowerBound(cmp.getLeftOperand().getFullyConverted()), "left lower bound", lowerBound(cmp.getRightOperand().getFullyConverted()), "right lower bound" #+end_src Les bornes sont correctes pour les types, mais trop générales -- elles incluent les résultats possibles de l'itération. Nous ne sommes intéressés que par les bornes initiales qui sont statiquement déterminées, celles avant toute itération.
Autrement dit, ce n'est pas un problème général de flux de données ; nous voulons seulement vérifier la propagation des valeurs initiales le long de certains chemins d'exécution. La boucle =for= complique cela, tout comme les opérations qu'elle contient.
Nous voulons vraiment voir les chemins d'exécution qui contournent complètement la boucle. Cela est fait dans les dernières parties de [[./argv-out-of-bounds-1.ql]], en utilisant une =SsaDefinition=.
La requête [[./argv-out-of-bounds-2.ql]] nettoie l'exploration de [[./argv-out-of-bounds-1.ql]] et identifie correctement tous les emplacements utilisant =n= avec une valeur d'index connue =n > 0=.
Cette requête rapporte des résultats sur la version vulnérable du code, =polkit-0.119.db=. Ensuite, elle doit être vérifiée et améliorée pour ne rien rapporter sur la version corrigée, =polkit-a2bf5c9c.db=.