[2022强网杯] polydiv和gamemaster

目录

polydiv

re_gamemaster


polydiv

题目先需要先爆破一个4字节的sha256再求40次 模二多项式,成功以后就直接给flag

import socketserver
import os, sys, signal
import string, random
from hashlib import sha256

from secret import flag
from poly2 import *

pad = lambda s:s + bytes([(len(s)-1)%16+1]*((len(s)-1)%16+1))
testCases = 40

class Task(socketserver.BaseRequestHandler):
    def _recvall(self):
        BUFF_SIZE = 2048
        data = b''
        while True:
            part = self.request.recv(BUFF_SIZE)
            data += part
            if len(part) < BUFF_SIZE:
                break
        return data.strip()

    def send(self, msg, newline=True):
        try:
            if newline:
                msg += b'\n'
            self.request.sendall(msg)
        except:
            pass

    def recv(self, prompt=b'> '):
        self.send(prompt, newline=False)
        return self._recvall()

    def close(self):
        self.send(b"Bye~")
        self.request.close()

    def proof_of_work(self):
        random.seed(os.urandom(8))
        proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)])
        _hexdigest = sha256(proof.encode()).hexdigest()
        self.send(f"sha256(XXXX+{proof[4:]}) == {_hexdigest}".encode())
        x = self.recv(prompt=b'Give me XXXX: ')
        if len(x) != 4 or sha256(x+proof[4:].encode()).hexdigest() != _hexdigest:
            return False
        return True

    def guess(self):
        from Crypto.Util.number import getPrime
        a,b,c = [getPrime(i) for i in [256,256,128]]
        pa,pb,pc = [PP(bin(i)[2:]) for i in [a,b,c]]
        r = pa*pb+pc
        self.send(b'r(x) = '+str(r).encode())
        self.send(b'a(x) = '+str(pa).encode())
        self.send(b'c(x) = '+str(pc).encode())
        self.send(b'Please give me the b(x) which satisfy a(x)*b(x)+c(x)=r(x)')
        #self.send(b'b(x) = '+str(pb).encode())
        
        return self.recv(prompt=b'> b(x) = ').decode() == str(pb)


    def handle(self):
        #signal.alarm(1200)

        if not self.proof_of_work():
            return

        for turn in range(testCases):
            if not self.guess():
                self.send(b"What a pity, work harder.")
                return
            self.send(b"Success!")
        else:
            self.send(b'Congratulations, this is you reward.')
            self.send(flag)
        
        

class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

#class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
class ForkedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

if __name__ == "__main__":
    
    HOST, PORT = '0.0.0.0', 10000
    server = ForkedServer((HOST, PORT), Task)
    server.allow_reuse_address = True
    server.serve_forever()

运算式子很简单,已知r,a,c求b,其实有个除法就行了

r = pa*pb+pc

题目给定了一个 模二多项式环的运算 的类(有删减),里边用到除的部分被删掉了,从网上找原版,发现原版被删了,只有搜索引擎的缓存,但python程序在缓存时压缩了空格并删掉了回车换行这样的程序基本无法读。并且把 [0] 也丢掉。拿到程序根据意思理解也很难,然后用搜狗搜了一下发现一个正常版的。搜狗平常啥都搜不到,但偶尔还会有点用。

    def div(self,other): 
        r,b = self.param[::-1],other.param[::-1] 
        if len(r) < len(b): 
            return Polynomial2(),self 
        q= [0]*(len(r) - len(b) + 1) 
        for i in range(len(q)): 
            if len(r)>=len(b): 
                index = len(r) - len(b) + 1 # 确定所得商是商式的第index位 
                q[-index] = int(r[0] / b[0]) # 更新被除多项式 
                b_=b.copy() 
                b_.extend([0]*(len(r) - len(b))) 
                b_ = [t*q[i] for t in b_] 
                r = [(r[t] - b_[t])%2 for t in range(len(r))] 
                for j in range(len(r)): #除去列表最左端无意义的0 
                    if r[0]==0: 
                        r.remove(0) 
                    else: 
                        break
            else: 
                break 
        return Polynomial2(q),Polynomial2(r) 

同时由于给出的数据是多项式,需要改为可处理的方式(第三种)

def p3(s):
    return eval( b'['+s.replace(b'+', b',').replace(b', 1', b', 0').replace(b'x^', b'').replace(b'x', b'1')+ b']' )

然后就可以连后台了

from pwn import *
import string
from hashlib import sha256
from poly2 import *

def get_proof(last, shav):
    s = string.ascii_letters+string.digits
    for v1 in s:
        for v2 in s:
            for v3 in s:
                for v4 in s:
                    t = (v1+v2+v3+v4).encode() + last
                    #print(t, sha256(t).hexdigest(), shav)
                    #return
                    if sha256(t).hexdigest() == shav:
                        return (v1+v2+v3+v4).encode()

context.log_level = 'debug'

p = remote('59.110.212.61', 26882)

#爆破sha256
p.recvuntil(b'sha256(XXXX+')
last = p.recvuntil(b') == ', drop=True)
shav = p.recvline()[:-1]
print('last:', last)
print('sha256:', shav)
found = get_proof(last, shav.decode())
p.sendlineafter(b'Give me XXXX: ', found)

#计算guess
for i in range(40):
    p.recvuntil(b'(x) = ')
    pr = Poly(p3(p.recvline()[:-1]))
    p.recvuntil(b'(x) = ')
    pa = Poly(p3(p.recvline()[:-1]))
    p.recvuntil(b'(x) = ')
    pc = Poly(p3(p.recvline()[:-1]))
    pb = (pr - pc).div(pa)
    
    p.sendlineafter(b'> b(x) = ', str(pb[0]))
    p.recvuntil(b'Success!\n')

p.recv()
p.recv()
p.recv()
p.recv()

pause()

re_gamemaster

这是个逆向题,结了一个.net写和游戏和一个gamemessage文件

用dnSpy打开找到以下几段

1,BlackjackConsole Program.Main() 里将gamemessage读入memory变量

			FileStream fileStream = File.OpenRead("gamemessage");
			int num = (int)fileStream.Length;
			Program.memory = new byte[num];
			fileStream.Position = 0L;
			fileStream.Read(Program.memory, 0, num);

2,BlackjackConsole Game.goldFunc 567 将memory的内容按字节与34异或

												for (int i = 0; i < Program.memory.Length; i++)
												{
													byte[] array = Program.memory;
													int num = i;
													array[num] ^= 34;
												}

3,BlackjackConsole Game.goldFunc() 538  将memory 用AES,ECB 解密生成ExploitClass.dll

													byte[] key = new byte[]
													{
														66,
														114,
														97,
														105,
														110,
														115,
														116,
														111,
														114,
														109,
														105,
														110,
														103,
														33,
														33,
														33
													};
													ICryptoTransform cryptoTransform = new RijndaelManaged
													{
														Key = key,
														Mode = CipherMode.ECB,
														Padding = PaddingMode.Zeros
													}.CreateDecryptor();
													Program.m = cryptoTransform.TransformFinalBlock(Program.memory, 0, Program.memory.Length);

这样就可以恢复这个动态库了

data = open('gamemessage', 'rb').read()
data = bytes([v^0x22 for v in data])

from Crypto.Cipher import AES

key = bytes([0x42, 0x72, 0x61, 0x69, 110, 0x73, 0x74, 0x6f, 0x72, 0x6d, 0x69, 110, 0x67, 0x21, 0x21, 0x21])
aes = AES.new(key,AES.MODE_ECB)
m = aes.decrypt(data)

pos = 0x13eb
m = m[pos:]+m[:pos]
open('temp.dll', 'wb').write(m)

然后用dnSpy再打开这个动态库就可以找到加密部分了

主要函数是check1 在程序中会对3个数字赋值(当然不知道是多少了),然后进行检查,查检通过后把这3个数作成12字节的key与密文异或就是flag

check1把这3个数向左移位并填充尾位(用其它位运算得到)共320次循环,最终这320个1位结果作成40个字节去校验。

private static void Check1(ulong x, ulong y, ulong z, byte[] KeyStream)
{
	int num = -1;
	for (int i = 0; i < 320; i++)
	{
		x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1UL) | x << 1);
		y = (((y >> 30 ^ y >> 27) & 1UL) | y << 1);
		z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1UL) | z << 1);
		bool flag = i % 8 == 0;
		if (flag)
		{
			num++;
		}
		KeyStream[num] = (byte)((long)((long)KeyStream[num] << 1) | (long)((ulong)((uint)((z >> 32 & 1UL & (x >> 30 & 1UL)) ^ (((z >> 32 & 1UL) ^ 1UL) & (y >> 31 & 1UL))))));
	}
}

解法想来想去最后想到z3,1000多个变量,不过运算比较快1分钟之内就解决。

from z3 import *

first = [101,5,80,213,163,26,59,38,19,6,173,189,198,166,140,183,42,247,223,24,106,20,145,37,24,7,22,191,110,179,227,5,62,9,13,17,65,22,37,5]
array5 = [60,100,36,86,51,251,167,108,116,245,207,223,40,103,34,62,22,251,227]

sec = [0]*320
for i in range(40):
    for j in range(8):
        sec[i*8+j] = (first[i] >>(7-j))&1

x = [BitVec(f'x{i}',1) for i in range(360+30)]
y = [BitVec(f'y{i}',1) for i in range(360+31)]
z = [BitVec(f'z{i}',1) for i in range(320+32)]
s = Solver()
s.add([x[0]==0,x[1]==0,y[0]==0])
for i in range(320):
    x[i+32] = x[i+2]^x[i+3]^x[i+6]^x[i+8]
    y[i+32] = y[i+1]^y[i+4]
    z[i+32] = z[i]^z[i+1]^z[i+2]^z[i+3]^z[i+5]^z[i+7]
    s.add(sec[i] == (z[i]&x[i+2])^((z[i]^1)&y[i+1]))

if s.check() == sat:
    d = s.model()
    print('x=', end='')
    for i in range(32):
        print(str(d[x[i]]), end='')
    print('\ny=', end='')
    for i in range(32):
        print(str(d[y[i]]), end='')
    print('\nz=', end='')
    for i in range(32):
        print(str(d[z[i]]), end='')

#x=00001001010100010101010001100101
#y=00110011110000101000100101110011
#z=10111010101000101100011000110011
#156324965,868387187,3131229747     

最后通过这3个数字得到flag

n1 = 156324965
n2 = 868387187
n3 = 3131229747

from pwn import p32
key = (p32(n1)+p32(n2)+p32(n3))*2
array5 = [60,100,36,86,51,251,167,108,116,245,207,223,40,103,34,62,22,251,227]

for i,v in enumerate(array5):
    print(chr(v^key[i]), end='')
    
#Y0u_@re_G3meM3s7er!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值