streamgame124最大的特点在于明文空间很小,例如1,密钥长度是19位二进制数,即有52万个可能的结果,对于现代计算机来说,完全可以在短时间内爆破完成,相比思考问题的解法,直接爆破显得更加经济。

stream3就不一样了,一打开发现密钥是18位十六进制,这时候任何爆破都不能奏效了,就得考虑更合适的解法。

这里用的是correlation attack,当一串密钥流是通过多个lfsr独立生成后再通过代数操作结合在一起的时候就可以用这种攻击方式。本质上是一种分治的操作,即把各个密钥单独算出来,首先得观察三个密钥流的结合操作:

  1. def single_round(R1, R1_mask, R2, R2_mask, R3, R3_mask):
  2. (R1_NEW, x1) = lfsr(R1, R1_mask)
  3. (R2_NEW, x2) = lfsr(R2, R2_mask)
  4. (R3_NEW, x3) = lfsr(R3, R3_mask)
  5. return R1_NEW, R2_NEW, R3_NEW, (x1*x2)^((x2^1)*x3)
  6. # x1 x2 x3 out
  7. # 0 0 0 0
  8. # 0 0 1 1
  9. # 0 1 0 0
  10. # 0 1 1 0
  11. # 1 0 0 0
  12. # 1 0 1 1
  13. # 1 1 0 1
  14. # 1 1 1 1
  15. # out is more related to x1 and x3, but not x2

从下面的表格中可以看出,x1、x3和out更加有关联,表现在x1和out同时相等的频率有6/8,而x2只有4/8,约等于随机的二进制组合。因此,r1和r3可以单独通过correlation attack求出来,最后再用brute force找出r2。

  1. # from flag import flag
  2. # assert flag.startswith("flag{")
  3. # assert flag.endswith("}")
  4. # assert len(flag)==24
  5. def lfsr(R, mask):
  6. output = (R << 1) & 0xfffff

直接爆破

  1. # from flag import flag
  2. # assert flag.startswith("flag{")
  3. # assert flag.endswith("}")
  4. # assert len(flag) == 27
  5. def lfsr(R, mask):
  6. output = (R << 1) & 0xffffff
  7. i = R & mask & 0xffffff
  8. lastbit = 0
  9. while i != 0:
  10. lastbit ^= (i & 1)
  11. i = i >> 1
  12. output ^= lastbit
  13. return (output, lastbit)
  14. # R = int(flag[5:-1], 2)
  15. mask = 0x100002
  16. def encrypt(R):
  17. ret = ''
  18. for i in range(12):
  19. tmp = 0
  20. for j in range(8):
  21. (R, out) = lfsr(R, mask)
  22. tmp = (tmp << 1) ^ out
  23. ret += chr(tmp)
  24. return ret
  25. def brute_force():
  26. with open('key', 'rb') as r:
  27. cipher = r.read()
  28. for i in range(2**20, 2**21):
  29. print i
  30. if encrypt(i) == cipher:
  31. raw_input()

flag{110111100101001101001}

StreamGame4

1.本题给了一个加密脚本和一个加密过后的key。

2.脚本将flag做为二进制读入,做为密钥。然后循环1024*1024次,每次循环8次nlfsr函数,将结果变换写入key中。所以可以进行爆破,每次检测key的一位,从第一位开始,如果正确在检测下一位。脚本如下:

  1. def nlfsr(R,mask):
  2. output = (R << 1) & 0xffffff
  3. i=(R&mask)&0xffffff
  4. lastbit=0
  5. changesign=True
  6. while i!=0:
  7. if changesign:
  8. lastbit &= (i & 1)
  9. changesign=False
  10. else:
  11. lastbit^=(i&1)
  12. i=i>>1
  13. output^=lastbit
  14. return (output,lastbit)
  15. f=open("key","rb").read()
  16. mask=0b110110011011001101110
  17. flag = False
  18. for x in range(0,pow(2,27-6)-1):
  19. R=x
  20. for i in range(1024*1024): #此处可以把1024*1024改成一个较小的数
  21. tmp=0
  22. for j in range(8):
  23. (R,out)=nlfsr(R,mask)
  24. tmp=(tmp << 1)^out
  25. if f[i]!=tmp:
  26. break
  27. elif i==1024*1024-1: #此处也要同时更改
  28. flag=True
  29. break
  30. if x%100000==0:
  31. print('x =',x)
  32. if flag==True:
  33. flag=x
  34. break
  35. print("flag{"+bin(x)[2:]+"}"