这题作为这次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
然后这题比较蛋疼的是,最开始主办方题目弄错了点啥,搞得一直过不了,各种无语,最关键的是,当时题目有问题的情况下有队过了,所以无奈啊……