# 2023 TPPCTF
## Chôn vùi bộ nhớ
**Note:** Khi mà có tổng lũy thừa các số mà cần tìm hệ số ta có thể dùng Newton identify
### Source
```python=
from secret import flag
assert flag[:6] == 'TPCTF{' and flag[-1] == '}'
flag = flag[6:-1]
assert len(set(flag)) == len(flag)
xs = []
for i, c in enumerate(flag):
xs += [ord(c)] * (i + 1)
p = 257
print('output =', [sum(pow(x, k, p) for x in xs) % p for k in range(1, len(xs) + 1)])
#output = [125, 31, 116, 106, 193, 7, 38, 194, 186, 33, 180, 189, 53, 126, 134, 237, 123, 65, 179, 196, 99, 74, 101, 153, 84, 74, 233, 5, 105, 32, 75, 168, 161, 2, 147, 18, 68, 68, 162, 21, 94, 194, 249, 179, 24, 60, 71, 12, 40, 198, 79, 92, 44, 72, 189, 236, 244, 151, 56, 93, 195, 121, 211, 26, 73, 240, 76, 70, 133, 186, 165, 48, 31, 39, 3, 219, 96, 14, 166, 139, 24, 206, 93, 250, 79, 246, 256, 199, 198, 131, 34, 192, 173, 35, 0, 171, 160, 151, 118, 24, 10, 100, 93, 19, 101, 15, 190, 74, 10, 117, 4, 41, 135, 45, 107, 155, 152, 95, 222, 214, 174, 139, 117, 211, 224, 120, 219, 250, 1, 110, 225, 196, 105, 96, 52, 231, 59, 70, 95, 56, 58, 248, 171, 16, 251, 165, 54, 4, 211, 60, 210, 158, 45, 96, 105, 116, 30, 239, 96, 37, 175, 254, 157, 26, 151, 141, 43, 110, 227, 199, 223, 135, 162, 112, 4, 45, 66, 228, 162, 238, 165, 158, 27, 18, 76, 36, 237, 107, 84, 57, 233, 96, 72, 6, 114, 44, 119, 174, 59, 82, 202, 26, 216, 35, 55, 159, 113, 98, 4, 74, 2, 128, 34, 180, 191, 8, 101, 169, 157, 120, 254, 158, 97, 227, 79, 151, 167, 64, 195, 42, 250, 207, 213, 238, 199, 111, 149, 18, 194, 240, 53, 130, 3, 188, 41, 100, 255, 158, 21, 189, 19, 214, 127]
```
### Ý tưởng
Như đã note ở trên mình sẽ dùng New Identify. Link doc:
https://zhuanlan.zhihu.com/p/146243974
Mình sẽ giải thích sơ sơ qua như sau:

Giả sử ta có đa thức P như ảnh trên với k nghiệm từ alpha 1 -> alpha k và Pi là tổng lũy thừa i chạy thừ alpha 1 -> alpha k

Và dùng lý thuyết của Newton Identify thì ta có thể khôi phục lại các hệ số của phương trình tổng thành 1 cái duy nhất và nghiệm của phương trình này chính là các ký tự của flag.

Chúng ta sẽ gộp 253 phương trình tổng trên thành 1 đa thức P quả công thức của Newton quả Pi. Cộng tổng cái Pi này vào là ta được đa thức P và giải bằng roots là xong.
### Code
```python=
from Crypto.Util.number import *
output = [125, 31, 116, 106, 193, 7, 38, 194, 186, 33, 180, 189, 53, 126, 134, 237, 123, 65, 179, 196, 99, 74, 101, 153, 84, 74, 233, 5, 105, 32, 75, 168, 161, 2, 147, 18, 68, 68, 162, 21, 94, 194, 249, 179, 24, 60, 71, 12, 40, 198, 79, 92, 44, 72, 189, 236, 244, 151, 56, 93, 195, 121, 211, 26, 73, 240, 76, 70, 133, 186, 165, 48, 31, 39, 3, 219, 96, 14, 166, 139, 24, 206, 93, 250, 79, 246, 256, 199, 198, 131, 34, 192, 173, 35, 0, 171, 160, 151, 118, 24, 10, 100, 93, 19, 101, 15, 190, 74, 10, 117, 4, 41, 135, 45, 107, 155, 152, 95, 222, 214, 174, 139, 117, 211, 224, 120, 219, 250, 1, 110, 225, 196, 105, 96, 52, 231, 59, 70, 95, 56, 58, 248, 171, 16, 251, 165, 54, 4, 211, 60, 210, 158, 45, 96, 105, 116, 30, 239, 96, 37, 175, 254, 157, 26, 151, 141, 43, 110, 227, 199, 223, 135, 162, 112, 4, 45, 66, 228, 162, 238, 165, 158, 27, 18, 76, 36, 237, 107, 84, 57, 233, 96, 72, 6, 114, 44, 119, 174, 59, 82, 202, 26, 216, 35, 55, 159, 113, 98, 4, 74, 2, 128, 34, 180, 191, 8, 101, 169, 157, 120, 254, 158, 97, 227, 79, 151, 167, 64, 195, 42, 250, 207, 213, 238, 199, 111, 149, 18, 194, 240, 53, 130, 3, 188, 41, 100, 255, 158, 21, 189, 19, 214, 127]
p = 257
P = output
e = []
#get e
for i in range(253):
temp = 0
for j in range(i):
temp += (-1)**j * e[j] * P[i-j-1]
temp %= p
temp = (P[i] - temp) % p
ei = temp * inverse((-1)^i*(i+1), p) % p
e.append(ei)
#get a
a = [1]
for i in range(len(e)):
a.append((-1)^(i+1) * e[i] % p)
#find roots
PR.<x> = PolynomialRing(Zmod(p))
f = 0
for i in range(253):
f += x^(253-i)*a[i]
f += a[-1]
res = f.roots()
#get flag
flag = [0 for i in range(22)]
for i in res:
flag[i[1]-1] = chr(i[0])
print("TPCTF{" + "".join(flag) + "}")
```
a = (hệ số để nhân với ei)
## sort-teaser
### Source
```python!
import re
from Crypto.Util.number import bytes_to_long
import random
letters=set(bytes(range(65,91)).decode())
class Command:
def __init__(self, target_var, op, l, r=0):
self.target_var = target_var
self.op = op
self.l = l if l in letters else int(l)
self.r = r if r in letters else int(r)
def __str__(self):
return self.target_var+"="+str(self.l)+((self.op+str(self.r)) if self.op!="=" else "")
__repr__=__str__
class Computation:
def __init__(self):
self.vars={x:0 for x in letters}
def resolve_val(self,symbol):
return self.vars[symbol] if type(symbol)==str else symbol
def run(self,cmd):
if cmd.op=='+':
self.vars[cmd.target_var]=self.resolve_val(cmd.l)+self.resolve_val(cmd.r)
if self.vars[cmd.target_var].bit_length()>100000:
raise OverflowError
elif cmd.op=='-':
self.vars[cmd.target_var]=self.resolve_val(cmd.l)-self.resolve_val(cmd.r)
if self.vars[cmd.target_var].bit_length()>100000:
raise OverflowError
elif cmd.op=='*':
self.vars[cmd.target_var]=self.resolve_val(cmd.l)*self.resolve_val(cmd.r)
if self.vars[cmd.target_var].bit_length()>100000:
raise OverflowError
elif cmd.op=='/':
self.vars[cmd.target_var]=self.resolve_val(cmd.l)//self.resolve_val(cmd.r)
elif cmd.op=='%':
self.vars[cmd.target_var]=self.resolve_val(cmd.l)%self.resolve_val(cmd.r)
elif cmd.op=='&':
self.vars[cmd.target_var]=self.resolve_val(cmd.l)&self.resolve_val(cmd.r)
elif cmd.op=='|':
self.vars[cmd.target_var]=self.resolve_val(cmd.l)|self.resolve_val(cmd.r)
elif cmd.op=='^':
self.vars[cmd.target_var]=self.resolve_val(cmd.l)^self.resolve_val(cmd.r)
elif cmd.op=='<':
self.vars[cmd.target_var]=int(self.resolve_val(cmd.l)<self.resolve_val(cmd.r))
elif cmd.op=='>':
self.vars[cmd.target_var]=int(self.resolve_val(cmd.l)>self.resolve_val(cmd.r))
elif cmd.op=='<=':
self.vars[cmd.target_var]=int(self.resolve_val(cmd.l)<=self.resolve_val(cmd.r))
elif cmd.op=='>=':
self.vars[cmd.target_var]=int(self.resolve_val(cmd.l)>=self.resolve_val(cmd.r))
elif cmd.op=='!=':
self.vars[cmd.target_var]=int(self.resolve_val(cmd.l)!=self.resolve_val(cmd.r))
elif cmd.op=='==':
self.vars[cmd.target_var]=int(self.resolve_val(cmd.l)==self.resolve_val(cmd.r))
elif cmd.op=='<<':
if self.resolve_val(cmd.l).bit_length()+self.resolve_val(cmd.r)>100000:
raise OverflowError
self.vars[cmd.target_var]=int(self.resolve_val(cmd.l)<<self.resolve_val(cmd.r))
elif cmd.op=='>>':
self.vars[cmd.target_var]=int(self.resolve_val(cmd.l)>>self.resolve_val(cmd.r))
elif cmd.op=='=':
self.vars[cmd.target_var]=self.resolve_val(cmd.l)
def parse_command(cmdstr):
cmdstr=re.sub("\s","",cmdstr)
m=re.match("^([A-Z])=([A-Z]|-?\d+)$",cmdstr)
if m:
return Command(m[1],"=",m[2])
m=re.match("^([A-Z])=([A-Z]|-?\d+)([+\-*/%&|^><]|[><!=]=|<<|>>)([A-Z]|-?\d+)$",cmdstr)
if m:
return Command(m[1],m[3],m[2],m[4])
m=re.match("^([A-Z])=-([A-Z])$",cmdstr)
if m:
return Command(m[1],"-",0,m[2])
raise SyntaxError
def run_commands(fun, init_state):
comp=Computation()
comp.vars.update(init_state)
try:
for i in fun:
comp.run(i)
except Exception as e:
pass # exceptions are suppressed
return comp.vars
def input_function(line_limit, cmd_limit):
fun=[]
while True:
line = input().strip()
if line == "EOF":
break
if len(line)>line_limit:
assert False, "command too long"
fun.append(parse_command(line))
if len(fun)>cmd_limit:
assert False, "too many commands"
return fun
flag=open(f"flag.txt").read().strip()
assert len(flag)<32
print("Enter your function A:")
fun_A=input_function(100,30)
flag_array=list(flag.encode()) # the flag is static
A=[]
B=[]
for i in range(100):
cur_A=bytes_to_long(bytes(flag_array))
cur_res=run_commands(fun_A,{"A":cur_A})
A.append(cur_A)
B.append(cur_res["B"])
random.shuffle(flag_array)
assert len(set(B))==1, "results are not same"
target_B=bytes_to_long(bytes(sorted(flag_array)))
if target_B==B[0]:
print(flag)
else:
print("You did not sort correctly")
exit(0)
```