Most of the binaries in the system are coming from distribution packages so building patches for them is different from the above. Here is how to do it.
This example builds glibc
patch for an old fashioned CVE-2015-0235 GHOST
vulnerability for RHEL7. The build is done using `scripts/pkgbuild`_ and
package files are stored in packages/rhel7/glibc/glibc-2.17-55.el7
.
First, we need the exact versions of tools and libs. Let's build a Docker image and a container for it:
$ docker build -t kernelcare/centos7:gcc-4.8.2-16.el7 \
docker/kernelcare/centos7/gcc-4.8.2-16.el7
...
$ docker run -v $PWD:/libcare --cap-add SYS_PTRACE -it \
kernelcare/centos7:gcc-4.8.2-16.el7 /bin/bash
[root@... /]#
Now, from inside the container let's install vulnerable version of glibc:
[root@... /]# yum downgrade -y --enablerepo=C7.0.1406-base \
glibc-2.17-55.el7 glibc-devel-2.17-55.el7 \
glibc-headers-2.17-55.el7 glibc-common-2.17-55.el7
...
Also we have to downgrade elfutils since newer versions of eu-unstrip
fail to work with glibc utilities:
[root@... /]# yum downgrade -y --enablerepo=C7.0.1406-base \
elfutils-devel-0.158-3.el7.x86_64 elfutils-0.158-3.el7.x86_64 \
elfutils-libs-0.158-3.el7.x86_64 elfutils-libelf-0.158-3.el7.x86_64 \
elfutils-libelf-devel-0.158-3.el7.x86_64
...
Build the libcare
tools:
[root@... /]# make -C /libcare/src clean all && make -C /libcare/tests/execve
...
Now build and run the sample GHOST app that runs 16 threads to constantly check
whether the glibc
is vulnerable to GHOST and prints a dot every time it
detects a buffer overflow in the gethostbyname_r
function.
The downgraded glibc
is vulnerable:
[root@... /]# cd /libcare/samples/ghost
[root@... ghost]# make
...
[root@... ghost]# ./GHOST
............^C
Press Ctrl+C to get your console back and let's start building the patch for
glibc
.
The build is done in two stages.
First, the original package build is repeated with all the `intermediate
assembly files`_ stored and saved for later. This greatly helps to speed up
builds against the same base code. Run the following from inside our docker
container to pre-build glibc
package:
[root@... /]# cd /libcare/
[root@... /libcare]# ./scripts/pkgbuild -p packages/rhel7/glibc/glibc-2.17-55.el7
...
This should download the package, do a regular RPM build with kpatch_cc
wrapper substituted for GCC and store the pre-built data into the archive under
/kcdata
directory:
[root@... /libcare]# ls /kcdata
build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz glibc-2.17-55.el7.src.rpm
Now let's build the patch, the output will be verbose since it contains tests run
by the kp_patch_test
defined in packages/rhel7/glibc/glibc-2.17-55.el7/info
:
[root@... /libcare]# ./scripts/pkgbuild packages/rhel7/glibc/glibc-2.17-55.el7
...
[root@... /libcare]# ls /kcdata/kpatch*
/kcdata/kpatch-glibc-2.17-55.el7.x86_64.tgz
Unwrap patches and run the GHOST sample:
[root@... /libcare]# cd /kcdata
[root@... /kcdata]# tar xf kpatch*
[root@... /kcdata]# /libcare/samples/ghost/GHOST 2>/dev/null &
[root@... /kcdata]# patient_pid=$!
And, finally, patch it. All the threads of the sample must stop when the GHOST vulnerability is patched:
[root@... /kcdata]# /libcare/src/libcare-ctl -v patch -p $patient_pid \
root/kpatch-glibc-2.17-55.el7.x86_64
...
1 patch hunk(s) have been successfully applied to PID '...'
(Press Enter again)
[1]+ Done /libcare/samples/ghost/GHOST 2> /dev/null
You can patch any running application this way:
[root@... /kcdata]# sleep 100 &
[root@... /kcdata]# patient_pid=$!
[root@... /kcdata]# /libcare/src/libcare-ctl -v patch -p $patient_pid \
root/kpatch-glibc-2.17-55.el7.x86_64
...
1 patch hunk(s) have been successfully applied to PID '...'
Congratulations on finishing this rather confusing sample!