Misc

Verifier

一个用 ply.yacc 实现的语法分析器,要写一段代码 print 一个小于 0 的数。但是他会先进行预处理,并求出每个变量可能的最小值和最大值,当 print 的输入的最小可能值小于 0 时会退出。
在预处理 while 时,他只会尝试运行 3 次,那么一个在第 4 次或之后才被修改的值就不会被预处理考虑到。

  1. T=10;A=1;B=1;[T>1{T=T+-1;A==5?{B=-1}:{A=A};A==-1?{A=5}:{A=A};A==0?{A=-1}:{A=A};A==4?{A=0}:{A=A};A==3?{A=4}:{A=A};A==2?{A=3}:{A=A};A==1?{A=2}:{A=A};!T}];!B

Crypto

halffeed

程序每轮会把一个 tag 和明文异或得到密文,然后 tag=cipher[:8]+plain[8:] ,然后用 key 对 tag 加密,同时 key 本身也会被加密(相当于一个固定的 key 生成器)。
需要得到一个包含 ;cat flag; 的字符串,但是不能直接对包含 cat flag 的字符串加密。
Tag 可以直接由明密文异或得到,考虑 randchar*8 + ;cat flarandchar*16+g;+…..,他们处理后的tag有概率前两位相同(生日攻击),这样就可以拼出一个完整的 cat flag。

  1. from pwn import *
  2. import random
  3. def getconn():
  4. #return process(['python3','prob.py'])
  5. return remote('110.10.147.44',7777)
  6. def encrypt(s):
  7. if type(s) is str:
  8. s=s.encode()
  9. print(s.hex())
  10. r=getconn()
  11. r.send('1\n')
  12. r.send(s.hex()+'\n')
  13. r.recvuntil('ciphertext = ')
  14. cipher=bytes.fromhex(r.recv(len(s.hex())).decode())
  15. r.recvuntil('tag = ')
  16. tag=bytes.fromhex(r.recv(32).decode())
  17. r.send('4\n')
  18. r.close()
  19. return cipher,tag
  20. def getflag(nonce,cipher,tag):
  21. r=getconn()
  22. r.send('3\n')
  23. r.send(nonce.hex()+'\n')
  24. r.send(cipher.hex()+'\n')
  25. r.send(tag.hex()+'\n')
  26. r.interactive()
  27. def xor(a,b):
  28. return bytes(b1 ^ b2 for b1, b2 in zip(a,b))
  29. def getrnd(n):
  30. return bytes([random.randint(0,255)for i in range(n)])
  31. #print(encrypt(b'\0'*128))
  32. a=b'\0'*8+b';cat fla'
  33. b=b'\0;'+b'\0'*14
  34. br=b'g;'+b'\0'*14
  35. mapa={}
  36. mapb={}
  37. res=None
  38. #mapa[b'\x99(']=b'\xa3\xab\xcd\xa6W\xbaT\xc8;cat fla'
  39. #mapb[b'\x99(']=b'\x93\xf0\x0f\xd9m\xb7\xa1gy\x07\nX\nY\xed\xe3'
  40. #res=b'\x99('
  41. while 1:
  42. at=getrnd(8)+a[8:]
  43. ci,ta=encrypt(at+b)
  44. ac=ci[:16]
  45. bc=ci[16:]
  46. tb=xor(b,bc)
  47. bc2=xor(br,tb)
  48. nt=bc2[:8]+b[8:]
  49. #print(nt[:2])
  50. mapa[nt[:2]]=at
  51. ut=getrnd(16)
  52. ci,ta=encrypt(ut+b'\0'*32)
  53. xc=ci[:16]
  54. yc=ci[16:32]
  55. zc=ci[32:]
  56. #print(yc[:2])
  57. mapb[yc[:2]]=ut
  58. for i in mapa:
  59. if i in mapb:
  60. res=i
  61. if res is not None:
  62. break
  63. #print(res,mapa[res],mapb[res])
  64. at=mapa[res]
  65. ci,ta=encrypt(at+b)
  66. #print('A',(at+b).hex())
  67. ac=ci[:16]
  68. bc=ci[16:]
  69. tb=xor(b,bc)
  70. bc2=xor(br,tb)
  71. nt=bc2[:8]+b[8:]
  72. ut=mapb[res]
  73. ci,ta=encrypt(ut+b'\0'*32)
  74. xc=ci[:16]
  75. yc=ci[16:32]
  76. nt2=yc[:8]+b'\0'*8
  77. #print(nt2.hex())
  78. zc=ci[32:]
  79. #print(yc[:2])
  80. #print(ci,ta)
  81. #print(xor(yc,nt))
  82. #print(xor(nt2,nt))
  83. #print((xor(nt2,tb)[:8]+nt2[8:]).hex())
  84. #src= at+xor(nt2,tb)[:8]+nt2[8:]+b'\0'*16
  85. getflag(b'\0'*16,ac+xor(xor(nt2,tb)[:8]+nt2[8:],tb)+zc,ta)

MUNCH

需要分解 1024 位的 n。其中 p 给出了 从 0,146,292,438 bit 开始的 111bit。

给出的方法是,另外钦定一个质数 mod,然后给出若干对 k_i*px%mod 的高几十位。这些 k_i 基本都是随机的。

考虑 LLL 算法可以求出若干个向量的较小的线性组合,可以构造下面这些向量:

  1. (每个 k_i*px 的有效值) + (若干 0)
  2. (每个 k_i) + 一个奇怪的常数 C + (若干 0)
  3. 接下来若干行,每行是
  4. (前若干个里,第 i 行的第 i 个位置是 mod) + 0 + (后若干个里,第 i 行的第 i 个位置是 1)

这样的话,一种可能较小的线性组合就是第一个向量加上若干个后面的向量再减若干个第二个向量。这时那个奇怪的常数就是求出的 px 了。至于这个常数应该取多少,以及这为啥能奏效,我就不知道了(试出来的结果是 1<<100 能求出解

  1. s=open('output').readlines()
  2. n,seed=map(int,s[4].strip().split(' '))
  3. t=[]
  4. for i in range(200):
  5. t.append(int(s[7+i]))
  6. seeds=[]
  7. for i in range(200):
  8. seeds.append(seed)
  9. seed=seed**2%n
  10. for i in t:
  11. assert (i<<460)<n
  12. u=list(range(2,200,4))
  13. M=[]
  14. c=len(u)
  15. V=100
  16. M.append([t[i]<<460 for i in u]+[0]*(c+1))
  17. M.append([seeds[i] for i in u]+[1<<V]+[0]*c)
  18. for i in range(c):
  19. M.append([n if i==j else 0 for j in range(c)]+[0]+[1 if i==j else 0 for j in range(c)])
  20. M=Matrix(M)
  21. print(M[0][c])
  22. M2=M.LLL()
  23. print(M2[0][c])
  24. for i in range(5):
  25. if not M2[i][c]: continue
  26. assert M2[i][c]%(1<<V)==0
  27. print(i,M2[i][0],M2[i][c]//(1<<V))

剩下的可以参考 这个
这个

Polynomials

NTRUEncrypt,给了私钥里 -1 0 1 的个数。

不过实际上并没有用到,直接参考下面论文里的 LLL 做法就能解出。

https://sci-hub.tw/https://link.springer.com/chapter/10.1007%2F978-3-540-74143-5_9#

  1. import random
  2. n=60
  3. p=3
  4. q=1499
  5. h=[314, 1325, 1386, 176, 369, 1029, 877, 1255, 111, 1226, 117, 0, 210, 761, 938, 273, 525, 751, 1085, 372, 1333, 898, 780, 44, 649, 1463, 326, 354, 116, 1080, 1065, 1109, 358, 275, 1209, 964, 101, 950, 415, 1492, 1197, 921, 1000, 1028, 1400, 43, 1003, 914, 447, 360, 1171, 1109, 223, 1134, 1157, 1383, 784, 189, 870, 565]
  6. c=(20,20,20)
  7. ks={}
  8. while True:
  9. rnds=[random.randint(1,10)for i in range(n*2)]
  10. #rnds=[8]*n+[1]*n
  11. M=Matrix(n*2,n*2)
  12. for i in range(n):
  13. M[i,i]=q*rnds[i]
  14. for i in range(n):
  15. for j in range(n):
  16. M[i+n,j]=h[(j-i)%n]*rnds[j]
  17. for i in range(n):
  18. M[i+n,i+n]=1*rnds[i+n]
  19. M2=M.LLL()
  20. #print(M2[0])
  21. #print(M2[1])
  22. #ts=[]
  23. rcnt=0
  24. for i in range(n*2):
  25. tl=list(M2[i])[:n]
  26. tr=list(M2[i])[n:]
  27. for j in range(n):
  28. tl[j]=tl[j]//rnds[j]%q
  29. tr[j]=tr[j]//rnds[j+n]%q
  30. if tr[0]==q-2:
  31. tr[0]-=1
  32. elif tr[0]==2:
  33. tr[0]+=1
  34. cntl=0
  35. for j in tl:
  36. if j%q==3 or j%q==0 or j%q==q-3:
  37. cntl+=1
  38. cntr=0
  39. for j in tr:
  40. if j%q==3 or j%q==0 or j%q==q-3:
  41. cntr+=1
  42. if cntl==n and cntr==n and sum(tl):
  43. print(tl,tr)
  44. exit()
  45. if cntl>=n-1 or cntr>=n-1:
  46. if str(tr) not in ks:
  47. print(cntl,cntr,tr)
  48. ks[str(tr)]=1
  49. rcnt+=1
  50. print(rcnt,len(ks))

Reverse

SimpleMachine

一个虚拟机,把和 flag 有关的跳转钦定为不跳转,然后丢进 z3,就好了。(太懒了不想手解)

  1. def get2b(a,b):
  2. return a[b]+(a[b+1]<<8)
  3. def set2b(a,b,c):
  4. a[b]=c&255
  5. a[b+1]=c>>8&255
  6. get_2b=get2b
  7. def read_bytes(a,b,c):
  8. #print('read:',b,c)
  9. global str_pos
  10. for i in range(c):
  11. #print(str_pos)
  12. if str_pos==len(str_to_read):
  13. a[b+i]=255
  14. else:
  15. a[b+i]=str_to_read[str_pos]
  16. str_pos+=1
  17. def write_bytes(a,b,c):
  18. global str_res
  19. for i in range(c):
  20. str_res+=chr(a[b+i])
  21. def step1():
  22. #print('step1')
  23. v1=stat[58]
  24. if v1==2:
  25. set2b(mem,get2b(stat,60),get2b(stat,62))
  26. else:
  27. assert v1==0
  28. v2=get2b(stat,60)
  29. if v2!=65535:
  30. set2b(stat,2*v2+28,get2b(stat,62))
  31. def step2():
  32. global cons
  33. op=stat[48]
  34. #print('step2',op)
  35. if op==0:
  36. set2b(stat,62,get2b(stat,52))
  37. elif op==1:
  38. set2b(stat,62,get2b(stat,52)+get2b(stat,54))
  39. elif op==2:
  40. set2b(stat,62,get2b(stat,52)*get2b(stat,54))
  41. elif op==3:
  42. set2b(stat,62,get2b(stat,52)^get2b(stat,54))
  43. elif op==4:
  44. #print('#',get2b(stat,52),get2b(stat,54))
  45. #set2b(stat,62,get2b(stat,52)<get2b(stat,54))
  46. #print(get2b(stat,52),get2b(stat,54))
  47. rt=get2b(stat,52)<get2b(stat,54)
  48. if type(rt) is bool:
  49. set2b(stat,62,rt)
  50. else:
  51. set2b(stat,62,0)
  52. #assert get2b(stat,52)==0
  53. if get2b(stat,52)==0: cons.append(get2b(stat,54)==0)
  54. elif op==5:
  55. #cons.append(get2b(stat,52)==0)
  56. if get2b(stat,52):
  57. stat[46]=0
  58. stat[56]=0
  59. stat[64]=0
  60. set2b(stat,34,get2b(stat,54))
  61. return
  62. pass
  63. elif op==6:
  64. read_bytes(mem,get2b(stat,52),get2b(stat,54))
  65. elif op==7:
  66. write_bytes(mem,get2b(stat,52),get2b(stat,54))
  67. elif op==8:
  68. stat[46]=0
  69. stat[56]=0
  70. stat[64]=0
  71. for i in range(4):
  72. stat[24+i]=0
  73. return
  74. stat[64]=1
  75. stat[58]=stat[49]
  76. set2b(stat,60,get2b(stat,50))
  77. def step3():
  78. #print('step3')
  79. v1=stat[64]
  80. v2=stat[38]
  81. v3=stat[39]
  82. do_label15=True
  83. if v1 and stat[58]==v2 and get2b(stat,60)==get2b(stat,42):
  84. set2b(stat,52,get2b(stat,62))
  85. #goto label 15
  86. elif v2==2:
  87. set2b(stat,52,get_2b(mem,get2b(stat,42)))
  88. v2=stat[58]
  89. else:
  90. if v2==1:
  91. set2b(stat,52,get2b(stat,42))
  92. if v1==0:
  93. do_label15=False
  94. #goto label 8
  95. v2=stat[58]
  96. #goto label 15
  97. else:
  98. if v2: assert False
  99. set2b(stat,52,get2b(stat,2*get2b(stat,42)+28))
  100. v2=stat[58]
  101. if do_label15 and v3==v2 and get2b(stat,60)==get2b(stat,44):
  102. set2b(stat,54,get2b(stat,62))
  103. #goto label 12
  104. elif v3==2:
  105. set2b(stat,54,get_2b(mem,get2b(stat,44)))
  106. elif v3==1:
  107. set2b(stat,54,get2b(stat,44))
  108. elif v3:
  109. assert False
  110. stat[56]=1
  111. stat[48]=stat[36]
  112. stat[49]=stat[37]
  113. set2b(stat,50,get2b(stat,40))
  114. def step4():
  115. global res_pos,res_naive
  116. #print('step4')
  117. a=get2b(stat,34)
  118. #if a==416:
  119. # res_naive=True
  120. #if not res_naive:
  121. # res_pos=a
  122. #print(a)
  123. v1=get2b(mem,a)
  124. #print(v1,hex(v1))
  125. stat[36]=v1&0x7f
  126. stat[37]=v1>>7&7
  127. stat[38]=v1>>10&7
  128. stat[39]=v1>>13&7
  129. for i in range(6):
  130. stat[40+i]=mem[a+2+i]
  131. stat[46]=1
  132. set2b(stat,34,a+8)
  133. #print(stat[34:46])
  134. pass
  135. from z3 import *
  136. mem=[]
  137. stat=[]
  138. mem=[0]*0x10000
  139. t=open('target','rb').read()
  140. for i in range(len(t)):
  141. mem[i]=t[i]
  142. stat=[0]*72
  143. stat[24]=1
  144. #str_to_read=[0]*36
  145. str_to_read=[]
  146. cons=[]
  147. for i in range(36):
  148. t=BitVec('s'+str(i), 16)
  149. cons.append((t&255)==t)
  150. str_to_read.append(t)
  151. str_pos=0
  152. str_res=''
  153. res_pos=0
  154. res_naive=False
  155. while stat[24]:
  156. if stat[64]:
  157. step1()
  158. if stat[56]:
  159. step2()
  160. if stat[46]:
  161. step3()
  162. step4()
  163. print(str_res)
  164. #solve(cons)
  165. so=Solver()
  166. so.add(cons)
  167. print(so.check())
  168. m=so.model()
  169. res=''
  170. for i in range(36):
  171. res+=chr(m.evaluate(str_to_read[i]).as_long())
  172. print(res)

malicious

一个病毒(?)程序,会获取一个网址的信息解密一段代码。虽然这个网址无法访问,但是他把这个信息求了 md5,反解得到 activate。代入运行,解密的代码会向硬盘直接写入一个 dos 扇区。

这个 dos 扇区又混淆了一次代码,但是我还没逆到那,比赛就结束了,后来看别的 wp 知道是我的世纪 < 0x30 所以看不到 flag。