来自vidar.club、UA要求阿巴阿巴阿巴、来自本地(提示不是XFF,其他的都试一下发现是X-Real-IP)、最后解jwt得到flag
有个注册按钮但是无法跳转过去,查看一下注释发现里面有个JavaScript写的alert('很抱歉,当前不允许注册');top.location.href='login.html'
那么禁用一下浏览器的js注册一个账号,再开启js即可,火狐浏览器的方法为
url框输入about:config搜索javascript.enabled,把true改成false即可
最迷惑的一集
然后想着里面有个full,源码里面还看到了is_full这些
接着就一直这样发包:
full还是1对吧,但是如果左边我故意让右边400几次,再发正常的包,多次重复这个步骤,莫名其妙full=0了
(例如下面这张图期间发了1次id=4+4,id=441,发了3次id=4)
复现不出来,纯看运气,拿当时出交flag时截的截图了
卡死惹
有个index-_wkhdPNY.js,存的整个游戏的逻辑,大概率flag的输出在这里面
随便找个格式化一下https://willnode.github.io/deobfuscator/
盲猜flag被base64加密过,不过搜hga的base64字符串没搜到,继续往下翻
猜测这个可能跟flag有关,先保留
找到获胜的逻辑,会执行s0(n(439), "V+g5LpoEej/fy0nPNivz9SswHIhGaDOmU8CuXb72dB1xYMrZFRAl=QcTq6JkWK4t3")
随便打开个网页,在控制台把整个js输入进去
然后alert一下s0(n(439), "V+g5LpoEej/fy0nPNivz9SswHIhGaDOmU8CuXb72dB1xYMrZFRAl=QcTq6JkWK4t3")
之前问gpt问了老半天,直到hint3已出现就恍然大悟怎么问了
select new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream("/flag"))).readLine()
接着问了一下GPT,读取根目录:select java.util.Arrays.toString(new java.io.File("/").list())
与0x22异或即可
刚开始用着几年前一直用的pyinstxtractor发现就是逆不出pyc头,才发现这玩意早更新了
然后找个在线的pyc逆一下即可
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]
#后略
一眼xor
upx -d ezUPX
然后IDA打开即可
xor 0x32
VIDAR{Wow!Y0u_kn0w_4_l1ttl3_0f_UPX!}
IDA直接打开就能看到
nc即可
不是很懂密码为什么会放这个题,感觉还是考的misc,要说密码的话总感觉有点像OTP的样子
简单分析函数
import time
from PIL import Image, ImageDraw, ImageFont
import threading
import random
import secrets
flag = "hgame{fake_flag}"
#生成随机RGB图片
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
#图像上画flag
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
#异或两张图RGB
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
#生成与随机token当文件名
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()
简单看了一下,生成了一个随机RGB的image和一个key_image,然后在image上面生成一个flag的其中一个字符就异或一下一直到生成完。能够发现current_image是用的同一张,意思是生成了字符之后还会在上面生成,就会产生覆盖(不过不要紧)
简单来说,就是
key^a1 = a2
key^b1 = b2a2^b2 = a1^b1
背景都是相同的,那么a1^b1就是异或不同的字符
基于这一点,我想到如果我选中的图是第一张图或者最后一张图,那么用这张图片去异或其他图片,得到的信息是绝对递增的,不会出现信息个数相同的情况(即假设全为flag{123}选中的为f,再往后异或会出现f^fl = l,f^fla = la……;反之}^3} = 3,}^23} = 23)
因此尝试爆破
from PIL import Image, ImageDraw, ImageFont
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
import os
DL = os.listdir('./')
NDL = [file for file in DL if ".png" in file]
print(NDL)
print(len(NDL))
for j in range(len(NDL)):
image1 = Image.open(NDL[j])
dr = NDL[j].split(".png")[0]
os.mkdir(f'../{dr}')
for i in range(len(NDL)):
image2 = Image.open(NDL[i])
image3 = xor_images(image1,image2)
image3.save(f'../{dr}/{i}.png')
image3.close()
image2.close()
这里获得了很多文件夹,当我看到18ef202a的时候发觉不简单,有一张单独出现的{
注意到出现{
的个数正好为5个,说明这张原图上面写的字符是hgame{
,那么后面按照顺序递增就是flag的正向顺序
最后排序得到hgame{1adf_17eb_803c}
观察到leak1和leak2是
leak1=pow(p,q,n)
leak2=pow(q,p,n)
不是很懂密码,所以只能百度这个了,找到一个用了leak1=pow(p,q,n)
的题https://www.cnblogs.com/U-L-G-A-N-O-Y/articles/17866487.html
可知leak1和leak2其实就是p和q,写常规解RSA即可
import gmpy2
import binascii
e = 65537
p = 149127170073611271968182576751290331559018441805725310426095412837589227670757540743929865853650399839102838431507200744724939659463200158012469676979987696419050900842798225665861812331113632892438742724202916416060266581590169063867688299288985734104127632232175657352697898383441323477450658179727728908669
q = 116122992714670915381309916967490436489020001172880644167179915467021794892927977272080596641785569119134259037522388335198043152206150259103485574558816424740204736215551933482583941959994625356581201054534529395781744338631021423703171146456663432955843598548122593308782245220792018716508538497402576709461
c=10529481867532520034258056773864074017027019578041866245400647840230251661652999709715919620810933437191661180003295923273655675729588558899592524235622728816065501918076120812236580344991140980991532347991252705288633014913479970610056845543523591324177567061948922552275235486615514913932125436543991642607028689762693617305246716492783116813070355512606971626645594961850567586340389705821314842096465631886812281289843132258131809773797777049358789182212570606252509790830994263132020094153646296793522975632191912463919898988349282284972919932761952603379733234575351624039162440021940592552768579639977713099971
n = p*q
L = (p-1)*(q-1)
d = gmpy2.invert(e,L)
m = gmpy2.powmod(c,d,n)
print(binascii.unhexlify(hex(m)[2:]))
#hgame{F3rmat_l1tt1e_the0rem_is_th3_bas1s}
不懂crypto,搜一下特征assert x**2 - D * y**2 == 1
https://www.wxjk.net/other/23395136.html
#sage
numTry = 1500
def solve_pell(N, numTry):
cf = continued_fraction(sqrt(N))
for i in range(numTry):
denom = cf.denominator(i)
numer = cf.numerator(i)
if numer^2 - N * denom^2 == 1:
return numer, denom
return None, None
x,y = solve_pell(114514,numTry)
print(y)
得到
9037815138660369922198555785216162916412331641365948545459353586895717702576049626533527779108680
解一下就行
from Crypto.Cipher import AES
from libnum import n2s as long_to_bytes
def pad(x):
return x+b'\x00'*(16-len(x)%16)
y = 9037815138660369922198555785216162916412331641365948545459353586895717702576049626533527779108680
key_bytes = long_to_bytes(y)
key_padded = pad(key_bytes)[:16]
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"
cipher = AES.new(key_padded, AES.MODE_ECB)
decrypted_flag = cipher.decrypt(enc)
print(decrypted_flag)
#b'hgame{G0od!_Yo3_k1ow_C0ntinued_Fra3ti0ns!!!!!!!}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
不是很懂密码学,我只知道lfsr和伪随机数那个库,但是改一改就不是很会了,这个题我选择的是
c语言多线程爆破(爆破完约6小时,针对此题耗时约2小时)
#include
#include
#include
#include
#define NUM_THREADS 8 // CPU线程
#define SEARCH_SPACE 4294967295U
uint32_t PRNG(uint32_t R, uint32_t mask) {
uint32_t nextR = (R << 1) & 0xffffffff;
uint32_t i = (R & mask) & 0xffffffff;
uint32_t nextbit = 0;
while (i != 0) {
nextbit ^= (i % 2);
i = i / 2;
}
nextR ^= nextbit;
return nextR;
}
void *search_thread(void *arg) {
int thread_id = *((int *)arg);
uint32_t mask = 0b10001001000010000100010010001001;
char* outputs[4] = {
"1111110110111011110000101011010001000111111001111110100101000011110111",
"0010000000001010111100001100011101111101111000100100111010101110010110",
"1110110110010001011100111110111110111001111101010011001111100100001000",
"0001101010101010100001001001100010000101010100001010001000100011101100"
};
char buffer[71];
uint32_t start = thread_id * (SEARCH_SPACE / NUM_THREADS);
uint32_t end = (thread_id == NUM_THREADS - 1) ? SEARCH_SPACE : (thread_id + 1) * (SEARCH_SPACE / NUM_THREADS);
for (int i = 0; i < 4; i++) {
for (uint32_t R = start; R <= end; R++) {
uint32_t currentR = R;
for (int j = 0; j < 70; j++) {
currentR = PRNG(currentR, mask);
buffer[j] = (currentR & 1) + '0';
}
buffer[70] = '\0';
if (strncmp(buffer, outputs[i], 70) == 0) {
printf("Found matching R: %08x in thread %d\n", R, thread_id);
break;
}
if (R % 100000000 == 0) {
printf("Progress: %u\n", R);}
}
}
return NULL;
}
int main() {
pthread_t threads[NUM_THREADS];
int thread_ids[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
thread_ids[i] = i;
pthread_create(&threads[i], NULL, search_thread, (void *)&thread_ids[i]);
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
printf("Search completed.\n");
return 0;
}
总之最后得到flag:hgame{fbbbee82-3f43-4f91-9337-907880e4191a}
笑鼠了,刚开赛太着急看换个方式签到,以为说的是满足'hgame\{[A-Z_]+\}'
就行,过了半分多钟才发现不对旁边有个附件。
发到手机上从充电口网上看就行了
hgame{WOW_GREAT_YOU_SEE_IT_WONDERFUL}
当时stegbreak没爆出来就没去单独试steghide了,后来单独试了发现是steghide,不知道stegbreak咋回事,下次还是stegseek吧。
密码123456,然后图片进行谷歌搜图
搜字母表https://www.bilibili.com/read/cv14514692/
手动转换一下得到hgame{wel??me!}
没找到问号,试了一下welcome不对,感觉是数字但是一下子没找到,去搜leet发现字母c没有对应的数字表示,就试了下hgame{welc0me!},正确
明文攻击,不过第一次用winrar压缩发现不能被攻击,我猜出题人又是用的bandzip,用bandzip果不其然能被攻击,而且为什么不用store;还有为什么不用winrar,感觉好像bandzip跟其他压缩软件挺格格不入的
解压出来的photo直接浏览器输入即可自动转base64的图片
一眼希尔密码,文件尾找到密文,看图片就是恢复高宽了,百度找一个爆破CRC的即可
这个代码直接换原新的图片
import binascii
import struct
import sys
file = input("图片地址:")
fr = open(file,'rb').read()
data: bytearray = bytearray(fr[0x0c:0x1d])
crc32key = eval('0x'+str(binascii.b2a_hex(fr[0x1d:0x21]))[2:-1])
n = 4095
for w in range(n):
width = bytearray(struct.pack('>i', w))
for h in range(n):
height = bytearray(struct.pack('>i', h))
for x in range(4):
data[x+4] = width[x]
data[x+8] = height[x]
crc32result = binascii.crc32(data) & 0xffffffff
if crc32result == crc32key:
print(width,height)
newpic = bytearray(fr)
for x in range(4):
newpic[x+16] = width[x]
newpic[x+20] = height[x]
fw = open(file+'.png','wb')
fw.write(newpic)
fw.close
file.close()
得到的图片存在LSB,里面是key
hgame{DISAPPEARINTHESEAOFBUTTERFLY}