没时间作,只作了一道题。不过有时间估计也是只作这一个题。
题目看上去很简单:
- 选过一个proof就是爆破一个sha256,4个字节
- 生成17字节的随机数,后边的东西就都是自己输入的了
- 输入p(头部为随机数),e,data
- 由data生成sha384,唯一的要求:
pow(int(data, 16), E, P) == int(sha384(data).hexdigest(), 16) % P
由于sha384不能逆,所以也就别想这东西了。
#!/usr/bin/env python3
import random
import signal
import socketserver
import string
from Crypto.Util.number import *
from hashlib import sha256, sha384
from os import urandom
from secret import flag
LEN = 17
class Task(socketserver.BaseRequestHandler):
def __init__(self, *args, **kargs):
super().__init__(*args, **kargs)
def proof_of_work(self):
random.seed(urandom(8))
proof = ''.join([random.choice(string.ascii_letters + string.digits + '!#$%&*-?') for _ in range(20)])
digest = sha256(proof.encode()).hexdigest()
self.dosend('sha256(XXXX + {}) == {}'.format(proof[4: ], digest))
self.dosend('Give me XXXX:')
x = self.request.recv(10)
x = (x.strip()).decode('utf-8')
if len(x) != 4 or sha256((x + proof[4: ]).encode()).hexdigest() != digest:
return False
return True
def dosend(self, msg):
try:
self.request.sendall(msg.encode('latin-1') + b'\n')
except:
pass
def timeout_handler(self, signum, frame):
raise TimeoutError
def recv_fromhex(self, l):
passwd = self.request.recv(l).strip()
passwd = bytes.fromhex(passwd.decode('latin-1'))
return passwd
def handle(self):
try:
signal.signal(signal.SIGALRM, self.timeout_handler)
signal.alarm(50)
if not self.proof_of_work():
self.dosend('You must pass the PoW!')
return
signal.alarm(60)
magic = urandom(LEN)
magic_num = bytes_to_long(magic)
self.dosend(magic.hex())
self.dosend('P:>')
P = int(self.request.recv(100).strip(), 16)
self.dosend('E:>')
E = int(self.request.recv(100).strip(), 16)
self.dosend('data:>')
data = self.request.recv(100).strip()
num1 = int(data, 16)
if P >> (384 - LEN * 8) == magic_num and isPrime(P):
data2 = sha384(data).hexdigest()
num2 = int(data2, 16)
if pow(num1, E, P) == num2 % P:
self.dosend(flag)
else:
self.dosend('try harder!!!')
except TimeoutError:
self.dosend('Timeout!')
self.request.close()
except:
self.dosend('Wtf?')
self.request.close()
class ThreadedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 15555
server = ThreadedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()
最可恶的就是有时间限制:60秒。
由于sha384不能逆,所以data也就基本是定值了,可以操作的就是P和E,实际上就是求离散对数。由于有时间限制,就要想办法弄个数可以很容易的求出离散对数。
离散对数的安全性要求P-1的最大素因子足够大,所以解这个题就要爆破一个最大素因子很小的P-1。又由于头部固定,我的P爆破从邻接头部后边爆破,这样P-1后尾部全是2可以快速缩小。
#sagemath
def aaa(p0):
c = int(sha384(b'11').hexdigest(), 16)
p0 = p0<<(384-17*8)
for i in range(0x10000):
tp = p0 + (i<<(384-17*8 - 16)) + 1
if is_prime(tp):
l = int(factor(tp-1)[-1][0]).bit_length()
if l<48:
print('p = ', hex(tp)[2:])
print('e = ', hex(int(discrete_log(mod(c,tp),mod(17,tp))))
return
aaa(0x46b3d5cb67ecabdd14ed8997f372d4e911)
然后组装就有个问题了,我机子上sage和pwntools组合不上,只能先把取得的东西放sage运算再把结果复制回去。经过几次手抖的过程后终于解决。
from pwn import *
from Crypto.Util.number import *
from hashlib import sha256, sha384
import string
def proof(tail, s):
tab = string.ascii_letters + string.digits + '!#$%&*-?'
for i in tab:
for j in tab:
for k in tab:
for l in tab:
msg = i+j+k+l+tail
r = sha256(msg.encode()).hexdigest()
if r == s:
print(" "+i+j+k+l)
return i+j+k+l
p = remote('202.120.7.219', 15555)
context.log_level = 'debug'
'''
sha256(XXXX + Q6quUUnOKw693Z*k) == 8c0452ca7ca220652104d4bd0dd72f6fc25f0bee622b9b3f53c97741b52bfcd6
Give me XXXX:
'''
p.recvuntil(b'sha256(XXXX + ')
tail = p.recvuntil(b') == ', drop=True).decode()
s = p.recvuntil(b'\n', drop=True).decode()
m = proof(tail, s)
p.sendlineafter(b'Give me XXXX:\n', m.encode())
#b'1adb38ceb59d18365bc9aa199955af2da1\n'
data = p.recvline()[:-1]
print(data)
p0 = int(data, 16)
print('p0 = ', hex(p0))
sp = input("P:")
p.sendlineafter(b'P:>\n', sp)
se = input("E:")
p.sendlineafter(b'E:>\n', se)
p.sendlineafter(b'data:>\n', b'11') #0x11 = 17
p.interactive()
另外,由于要运算离散对数,g不能大,一般如果没特殊情况可以取2,但是2,3经常会有无法求解的情况(含因子)所以这里用的17,其实3有时候也行。最大因子当然越小越好,但太小不容易遇到测试两次后发现一个44位的,就随便写了48,因为这块也需要节省时间。
最后看看交互的过程:
$ py a.py
[+] Opening connection to 202.120.7.219 on port 15555: Done
[DEBUG] Received 0x64 bytes:
b'sha256(XXXX + cRZm&a!8X3i$28YJ) == b23f429bace252093acf4b3ab0d059fa8a52102e30cb495e7b912560903209a9\n'
[DEBUG] Received 0xe bytes:
b'Give me XXXX:\n'
[DEBUG] Sent 0x5 bytes:
b'C6u7\n'
[DEBUG] Received 0x23 bytes:
b'779d13f0f1ffda1735928284fd3f240801\n'
[DEBUG] Received 0x4 bytes:
b'P:>\n'
[DEBUG] Sent 0x62 bytes:
b'779d13f0f1ffda1735928284fd3f24080111790000000000000000000000000000000000000000000000000000000001\n'
b'\n'
[DEBUG] Received 0x4 bytes:
b'E:>\n'
[DEBUG] Sent 0x61 bytes:
b'3fb7c5754190aa6425ba81af4b68a09154d708572f7491c56999fdae75a7787e83a90409311c2f7502c0779ebfb5e98\n'
b'\n'
[DEBUG] Received 0x7 bytes:
b'data:>\n'
[DEBUG] Sent 0x3 bytes:
b'11\n'
[*] Switching to interactive mode
[DEBUG] Received 0x31 bytes:
b'flag{Hope_you_can_solve_by_smoothness_this_time}\n'
flag{Hope_you_can_solve_by_smoothness_this_time}