文章目录
- WEB
- ezHTTP
- Bypass it
- Select Courses
- 2048*16
- jhat
- RE
- ezASM
- ezPYC
- ezUPX
- ezIDA
- PWN
- EzSignIn
- CRYPTO
- 奇怪的图片
- ezRSA
- ezMath
- ezPRNG
- MISC
- SignIn
- 来自星尘的问候
- simple_attack
- 希儿希儿希尔
- 签到
放假比较闲,打打比赛
WEB
ezHTTP
来自vidar.club、UA要求阿巴阿巴阿巴、来自本地(提示不是XFF,其他的都试一下发现是X-Real-IP)、最后解jwt得到flag
Bypass it
有个注册按钮但是无法跳转过去,查看一下注释发现里面有个JavaScript写的alert('很抱歉,当前不允许注册');top.location.href='login.html'
那么禁用一下浏览器的js注册一个账号,再开启js即可,火狐浏览器的方法为
url框输入about:config搜索javascript.enabled,把true改成false即可
Select Courses
最迷惑的一集
然后想着里面有个full,源码里面还看到了is_full这些
接着就一直这样发包:
full还是1对吧,但是如果左边我故意让右边400几次,再发正常的包,多次重复这个步骤,莫名其妙full=0了
(例如下面这张图期间发了1次id=4+4,id=441,发了3次id=4)
复现不出来,纯看运气,拿当时出交flag时截的截图了
2048*16
卡死惹
有个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")
jhat
之前问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())
RE
ezASM
与0x22异或即可
ezPYC
刚开始用着几年前一直用的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
ezUPX
upx -d ezUPX
然后IDA打开即可
xor 0x32
VIDAR{Wow!Y0u_kn0w_4_l1ttl3_0f_UPX!}
ezIDA
IDA直接打开就能看到
PWN
EzSignIn
nc即可
CRYPTO
奇怪的图片
不是很懂密码为什么会放这个题,感觉还是考的misc,要说密码的话总感觉有点像OTP的样子
简单分析函数
import timefrom PIL import Image, ImageDraw, ImageFont
import threading
import random
import secretsflag = "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, ImageFontdef 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_imageimport 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}
ezRSA
观察到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 binasciie = 65537
p = 149127170073611271968182576751290331559018441805725310426095412837589227670757540743929865853650399839102838431507200744724939659463200158012469676979987696419050900842798225665861812331113632892438742724202916416060266581590169063867688299288985734104127632232175657352697898383441323477450658179727728908669
q = 116122992714670915381309916967490436489020001172880644167179915467021794892927977272080596641785569119134259037522388335198043152206150259103485574558816424740204736215551933482583941959994625356581201054534529395781744338631021423703171146456663432955843598548122593308782245220792018716508538497402576709461
c=10529481867532520034258056773864074017027019578041866245400647840230251661652999709715919620810933437191661180003295923273655675729588558899592524235622728816065501918076120812236580344991140980991532347991252705288633014913479970610056845543523591324177567061948922552275235486615514913932125436543991642607028689762693617305246716492783116813070355512606971626645594961850567586340389705821314842096465631886812281289843132258131809773797777049358789182212570606252509790830994263132020094153646296793522975632191912463919898988349282284972919932761952603379733234575351624039162440021940592552768579639977713099971
n = p*qL = (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}
ezMath
不懂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, denomreturn None, Nonex,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 = 9037815138660369922198555785216162916412331641365948545459353586895717702576049626533527779108680key_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'
ezPRNG
不是很懂密码学,我只知道lfsr和伪随机数那个库,但是改一改就不是很会了,这个题我选择的是
c语言多线程爆破(爆破完约6小时,针对此题耗时约2小时)
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>#define NUM_THREADS 8 // CPU线程
#define SEARCH_SPACE 4294967295Uuint32_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}
MISC
SignIn
笑鼠了,刚开赛太着急看换个方式签到,以为说的是满足'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!},正确
simple_attack
明文攻击,不过第一次用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) & 0xffffffffif 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.closefile.close()
得到的图片存在LSB,里面是key
hgame{DISAPPEARINTHESEAOFBUTTERFLY}