这题作为这次defcon预选赛中唯一的一道coding题,还是1分,很显然是送分题了,直接连上服务器可以得到如下返回:
$ nc catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me 9999 ****Initial Register State**** rax=0xfcf7659c7a4ad096 rbx=0x1df0e8dfe8f70b53 rcx=0x55004165472b9655 rdx=0x1aa98e77006adf1 rsi=0x949a482579724b11 rdi=0x1e671d7b7ef9430 r8=0x3251192496cee6a6 r9=0x278d01e964b0efc8 r10=0x1c5c8cca5112ad12 r11=0x75a01cef4514d4f5 r12=0xe109fd4392125cc7 r13=0xe5e33405335ba0ff r14=0x633e16d0ec94137 r15=0xb80a585e0cd42415 ****Send Solution In The Same Format**** About to send 74 bytes: hŒråRI‡ÔA]HÿÊIÇ¢éNhIÿÊHÿÃHÎIÇ^…6H¤Ã MÃI÷ëH)ðHÆQØ8eHÿÀIÁÕH5Œm'Ã^C
即这里是给出了一些寄存器的初始状态,让我们执行下面给的机器指令,那显然,相比于写个解释器模拟执行,我们写个程序直接能运行这段指令是最好的。这里如果用汇编写可能会方便点吧,不过鉴于好久没写过汇编的了,用C++写了个内嵌汇编的:
#!/usr/bin/env python #encoding:utf-8 import zio import pwn import commands TARGET = ('catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me', 9999) io = zio.zio(TARGET) io.readline() regs = io.read_until(['****Send Solution In The Same Format****']).split('n')[:-1] while True: io.read_until('bytes: n') ops = io.read_until_timeout() if ops[-1] != 'xc3': print "Error, not end with xc3." exit(1) print '' print len(ops) regs = [x.split('=') for x in regs] ops = 'x48xb8' + zio.l64(int(regs[0][1], 16)) + ops # headers cpp = open('run.cpp', 'w') cpp.write(''' #include <stdio.h> using namespace std; int main() { char *ops = "%s"; ''' % ''.join(['\x%02x' % ord(x) for x in ops])) for x in regs: # if int(x[1], 16) > int('0xffffffff', 16): # x[1] = str(int(x[1], 16) & 0xffffffff) cpp.write('__asm__("mov $%s, %%%s");n' % (x[1], x[0])) # pwn.o # codes = pwn.disasm(ops).split('n') # for x in codes: # cpp.write('__asm__("%s");n' % x.split(' ' * 6)[-1].strip()) cpp.write("((void (*)())ops)();n") for x in regs: cpp.write('__asm__("push %%%s");n' % x[0]) i = 6 for x in regs: # cpp.write('printf("%%%d$p\n");n' % i) cpp.write('printf("0x%%%d$llx\n");n' % i) # cpp.write('printf("0x%%%d$016llx\n");n' % i) i += 1 # footers cpp.write(''' return 0; } ''') cpp.close() commands.getstatusoutput('g++ -z execstack -fno-stack-protector -Wall -o ./run ./run.cpp') status, output = commands.getstatusoutput('./run') output = output.split('n')[::-1] i = 0 for x in regs: output[i] = x[0] + '=' + output[i] i += 1 io.writelines(output) io.readline() # print io.readline() # print io.read_until_timeout()
这里中间产生的C++代码类似:
#include <cstdio> using namespace std; int main() { char *ops = "……"; __asm__("mov $0x365a6103367515ed, %rax"); __asm__("mov $0x3cb1a6fff5b038b6, %rbx"); __asm__("mov $0x2a5e4d432e3810e7, %rcx"); __asm__("mov $0x3fc79069d112d35e, %rdx"); __asm__("mov $0xe9b6862f68ddc08b, %rsi"); __asm__("mov $0xc9bb5638f34a4ed2, %rdi"); __asm__("mov $0x1eea6b2bcfa19f55, %r8"); __asm__("mov $0xb48d0fe2a6217fcd, %r9"); __asm__("mov $0x7ff063bcf81b25d7, %r10"); __asm__("mov $0x7ecfd1750b003532, %r11"); __asm__("mov $0x2e45b91b25474403, %r12"); __asm__("mov $0xb9661518b82c711, %r13"); __asm__("mov $0x3f397cafd35489db, %r14"); __asm__("mov $0x1d0dc0438a2a9b1f, %r15"); ((void (*)())ops)(); __asm__("push %rax"); __asm__("push %rbx"); __asm__("push %rcx"); __asm__("push %rdx"); __asm__("push %rsi"); __asm__("push %rdi"); __asm__("push %r8"); __asm__("push %r9"); __asm__("push %r10"); __asm__("push %r11"); __asm__("push %r12"); __asm__("push %r13"); __asm__("push %r14"); __asm__("push %r15"); printf("%6$pn"); printf("%7$pn"); printf("%8$pn"); printf("%9$pn"); printf("%10$pn"); printf("%11$pn"); printf("%12$pn"); printf("%13$pn"); printf("%14$pn"); printf("%15$pn"); printf("%16$pn"); printf("%17$pn"); printf("%18$pn"); printf("%19$pn"); return 0; }
即先将寄存器初始设置好,然后运行所给指令,然后将所有寄存器压栈,通过printf输出。然后这里要注意的是,所给指令最后一句几乎可以肯定说是ret,所以可以很方便的直接call上去就好,但是由于编译出来call的时候,是先将地址移到rax中,然后再call,所以说我们需要在所给指令前面加上一段指令重新设置rax的值,也即上面Python代码中的:
ops = 'x48xb8' + zio.l64(int(regs[0][1], 16)) + ops
然后这题比较蛋疼的是,最开始主办方题目弄错了点啥,搞得一直过不了,各种无语,最关键的是,当时题目有问题的情况下有队过了,所以无奈啊……