🍗 Wiki

libc

libc

libc is a short of the term 'Standard C Library'. It is a library of standard functions that can be used by all C programs. It is usually refers the glibc(GNU C Library) in Linux, but the term libc is simply refers 'standard C Library', so it refers any C language libraries by any vendors.

These are famous LibCs that are widely used.

  • glibc: GNU C Library. Used in GNU/Linux and GNU Hurd

  • BSD libc: It was/is used in BSDs

  • (The Apache) stdcxx: Apache C++ Standard Library, it has been retired.

  • Bionic: A libc by Google to be used in Android.

Of course Windows also provide C libraries. These libraries are shipped with Visual C++, or Visual C++ Redistributables.

1. A story of C libraries

If you are familiar with Linux, you may also feel the term glibc familiar. Because whenever you run an application in Linux, almost every executables require the glibc. Sometimes to run another application that is not really well made, you may heard that term while fixing the application.

But you should aware that the glibc is not the only one libc. If you have heard of the Alpine Linux, which is very lightweight Linux environment, you also might heard of the musl libc.

In BSD-derived operating system, the BSD libc is used.

For small systems like embedded devices, sometimes you can see the uClibc. There was the original uClibc by Erik Andersen, the lastest release was in 2012. If you see the uClibc library is in an embedded system, it is likely to be uClibc-ng, the 'ng' is short of 'Next Generation'.

2. In CTF Challenge

When you solve a Linux pwnable challenge, version of glibc is important. Sometimes it can be a hassle and you could waste your time just to fix it.

2.1. Finding version of the libc with leaked address

In CTF challenge, some challenge includes libc. You can patch the challenge to run it with the given libc, which is described in here, so it will make your exploit code run against the CTF challenge server and solve the challenge.

But some other challenges does not have libc, it can cause your exploit code not work if you target the remote CTF server, even if it runs in your local Linux system and successfully get a root shell.

But if you leaked the address of libc functions like printf or malloc, you can query it and guess which version of libc is used in the remote server.

Let’s assume you’ve got address of the glibc functions. The code here will print the address of glibc functions.

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>

#include <dlfcn.h>  // gcc -ldl


int main() {
  printf("write() : %p\n", dlsym(RTLD_DEFAULT, "write"));
  printf("read() : %p\n", dlsym(RTLD_DEFAULT, "read"));
  printf("printf() : %p\n", dlsym(RTLD_DEFAULT, "printf"));
  printf("system() : %p\n", dlsym(RTLD_DEFAULT, "system"));

  return 0;
}

After compiling and executing it, these were printed.

$ ./a.out
write() : 0x7fe75f952870
read() : 0x7fe75f9527d0
printf() : 0x7fe75f89e6f0
system() : 0x7fe75f88ed70

The Linux system has enabled the ASLR. So every time I run the compiled executable, I can see the address changes.

But the key is the bottom three hexadecimals. There is the libc-database, you can query it and guess the version of glibc. Let’s visit https://libc.rip, a website based on the project.

Then fill the forms by typing 'read' in Symbol name, and the last three hexadecimals in Address. The more Symbol name and Address pairs are given, the more likely to find the correct libc version.

read

Results show these symbols with that address should be 'libc6_2.35-0ubuntu3.x_amd64'. I compiled and run the executable in the WSL Ubuntu 22.04.

When you click one of the results, you can see other address of functions. And there is a download link. You can download libc.so.6 and patch the challenge file.

2.2. Get precompiled glibcs

Some CTF challenge requires you to use old version of the glibc. For example, you might want to solve a challenge that requires you to use the House of Force technique, which requires the version of glibc to be older than 2.29.

There are awesome projects to download precompiled glibcs, I prefer https://github.com/matrix1001/glibc-all-in-one personally.

It is a pack of a python script and shell scripts, so you don’t need to care about how to build and install, and so on. Start cloning the Github repository first.

$ # `--depth 1` to clone only the latest revision(commit) of the repository
$ git clone --depth 1 https://github.com/matrix1001/glibc-all-in-one

To get a list of available versions of glibc, run update_list first. Sometimes the script won’t work,

exec: Failed to execute process './update_list': The file specified the interpreter '/usr/bin/python', which is not an executable command.

Simply fix the first line of the script or python3 update_list. In my case, I replaced python to python3.

After running the script, you will see there are list file and old_list file.

$ cat list
2.23-0ubuntu11.3_amd64
2.23-0ubuntu11.3_i386
2.23-0ubuntu3_amd64
2.23-0ubuntu3_i386
2.27-3ubuntu1.5_amd64
2.27-3ubuntu1.5_i386
2.27-3ubuntu1.6_amd64
2.27-3ubuntu1.6_i386
...

You can choose the version and download what you desired. I will download 2.27-3ubuntu1_amd64 for example. If you want to download the version not listed in the list file, it might be there at the old-list file.

$ ./download 2.27-3ubuntu1_amd64
Getting 2.27-3ubuntu1_amd64
  -> Location: https://mirror.tuna.tsinghua.edu.cn/ubuntu/pool/main/g/glibc/libc6_2.27-3ubuntu1_amd64.deb
  -> Downloading libc binary package
  -> Extracting libc binary package
x - debian-binary
x - control.tar.xz
x - data.tar.xz
/tmp/glibc-all-in-one
  -> Package saved to libs/2.27-3ubuntu1_amd64
  -> Location: https://mirror.tuna.tsinghua.edu.cn/ubuntu/pool/main/g/glibc/libc6-dbg_2.27-3ubuntu1_amd64.deb
  -> Downloading libc debug package
  -> Extracting libc debug package
x - debian-binary
x - control.tar.xz
x - data.tar.xz
/tmp/glibc-all-in-one
  -> Package saved to libs/2.27-3ubuntu1_amd64/.debug

If there were no errors, there should be both libc-2.27.so and ld-2.27.so.

$ file libs/2.27-3ubuntu1_amd64/ld-2.27.so
libs/2.27-3ubuntu1_amd64/ld-2.27.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=64df1b961228382fe18684249ed800ab1dceaad4, stripped
$ file libs/2.27-3ubuntu1_amd64/libc-2.27.so
libs/2.27-3ubuntu1_amd64/libc-2.27.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0, for GNU/Linux 3.2.0, stripped

As you downloaded the ld interpreter and libc shared object, you can patch the challenge with patchelf.

See README.md for the detail.

2.3. Finding the glibc version if you know the sha256 of Docker image

Docker is an essential part of Linux computing, even in CTF challenges. Some CTF Pwnable challenge ships with Dockerfile, it makes you reproduce the pwnable server environment in your desktop or laptop.

But I already have my own pwnable environment based on Docker, so when a pwn challenge gives me a Dockerfile, it makes me itch. I have to build the Docker environment again, and it consumes a lot of time and disk space.

But you can see the version of glibc of remote challenge server if the Dockerfile is provided. What you need is just a hash of the base image. This tip can be useful if you want (almost) exactly the same environment as the remote CTF server.

$ head -1 Dockerfile
FROM ubuntu:22.04@sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac

We’re going to extract library files in the image. To extract library files in the image, a container from the image should be run.

$ docker create ubuntu:22.04@sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac
4730858291d177f0cb97773d6633777ed296de2144ccc99ae533d0dcfcdb23ca

Then, copy files in the directory /lib/ to host.

$ docker cp 4730858291d177f0cb97773d6633777ed296de2144ccc99ae533d0dcfcdb23ca:/lib/x86_64-linux-gnu ./lib
Successfully copied 47.2MB to /home/ch1keen/project/wiki.ch1keen.xyz/lib
$ file lib/libc.so.6
lib/libc.so.6: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=89c3cb85f9e55046776471fed05ec441581d1969, for GNU/Linux 3.2.0, stripped
$ file lib/ld-linux-x86-64.so.2
lib/ld-linux-x86-64.so.2: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=aa1b0b998999c397062e1016f0c95dc0e8820117, stripped

If you don’t need to be too platform-specific, listing packages inside the image might be useful.

If you’re using Docker Desktop newer than 4.7.0, you can use docker sbom command. It will print out which packages are included in a Docker image. It includes version of glibc, too.

$ docker sbom ubuntu:22.04@sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac
Syft v0.43.0
 ✔ Pulled image
 ✔ Loaded image
 ✔ Parsed image
 ✔ Cataloged packages      [101 packages]
NAME                 VERSION                                  TYPE
adduser              3.118ubuntu5                             deb
apt                  2.4.5                                    deb
...
libc-bin             2.35-0ubuntu3                            deb
libc6                2.35-0ubuntu3                            deb
...

If you find docker sbom command does not work,

$ docker sbom ubuntu:22.04
docker: 'sbom' is not a docker command.
See 'docker --help'
$ docker scout ubuntu:22.04
docker: 'scout' is not a docker command.
See 'docker --help'

You can install syft to do exactly the same thing. Because the docker sbom command is just a Docker plugin, a wrapper of the syft.

curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

3. Trivia

  • musl-libc has an issue about the locale. It may not really good at developing the multi-language, especially you need to deal with the setlocale() and getlocale().

  • Normally the libc files are stored in the /lib/<architecture>/libc.so.6, for example /lib/x86_64-linux-gnu/libc.so.6. Historically there were libc.so.1…​libc.so.5, and libc.so.6 is known not to compatible with the previous libc.so.xs.

4. See Also