Hoppa yfir í efnið

Óörugg afraðgreining

Forrit þurfa oft að vista eða senda gögn sem lifa lengur en ein keyrsla. Python-orðabók getur orðið að JSON-texta, veflota að köku og forritshlutur að tvíundarunu. Að breyta gildi í slíkt flutningsform kallast röðgreining. Að byggja gildið aftur kallast afraðgreining.

Sum snið endursmíða aðeins einföld gögn. Önnur geta endursmíðað hluti og kallað föll sem hluta ferlisins. Ef ótraust inntak fær slíkan afraðgreini getur gögnunum fylgt hegðun sem forritarinn bjóst ekki við.

Verkefnið Stillingarpakkinn tekur við Base64-kóðuðu Python-pickle. Það afhendir sérstakt hjálparfall sem má aðeins nota til að sækja fánann inni í sandkassanum. Við skoðum pickle án þess að keyra það á eigin tölvu og búum síðan til einn afmarkaðan hlut sem vísar á þetta fall.

Aldrei afraðgreina óþekkt pickle á eigin tölvu

pickle.loads() getur kallað föll við afraðgreiningu. Notaðu pickletools.dis() til að skoða ótraust pickle án keyrslu. Sendu aðeins verkefnissértæka hjálparfallið í einangraða Örvangursþjóninn; ekki nota skel, skráaraðgang, netbeiðnir eða almenn kerfisföll.

Röðgreining og afraðgreining

Röðgreining (e. serialization) breytir gildi í runu sem má geyma eða senda:

Python-gildi → raðgreinandi → bæti eða texti

Afraðgreining (e. deserialization) fer til baka:

bæti eða texti → afraðgreinandi → gildi eða hlutur

Með JSON gæti ferlið verið:

import json

settings = {"theme": "dark", "page_size": 20}
encoded = json.dumps(settings)
decoded = json.loads(encoded)

decoded verður ný orðabók með streng og tölu. JSON hefur afmarkaðar gagnategundir: hluti, lista, strengi, tölur, sanngildi og null. JSON-textinn segir ekki Python að flytja inn tiltekið fall eða smíða handahófskenndan klasa.

Python-pickle hefur annað markmið:

import pickle

encoded = pickle.dumps(settings)
decoded = pickle.loads(encoded)

Pickle getur varðveitt fleiri Python-eiginleika en JSON. Það getur meðal annars vísað á klasa og föll sem Python notar við endursmíði. Þessi viðbótargeta er ástæðan fyrir að ótraust pickle er sérstakt CTF-viðfang.

Gagnasnið eða hlutur með hegðun

Skoðum skaðlausan punktaklasa:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

Pickle þarf að vita hvernig á að smíða Point aftur. Hlutur getur skilgreint __reduce__() og skilað callable-gildi ásamt viðföngum:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __reduce__(self):
        return (Point, (self.x, self.y))

Við afraðgreiningu notar pickle þessar upplýsingar til að kalla Point(x, y). Þetta er eðlileg endursmíði, en sýnir mikilvægu mörkin: pickle-lýsing getur valið kallanlegt gildi sem afraðgreinandinn notar.

Ef lýsingin kemur frá ótraustum aðila getur hann reynt að velja annað fall sem er tiltækt í þjónustunni. Veikleikinn liggur ekki í Base64 eða HTTP sjálfu heldur í því að pickle.loads() treystir hegðun sem er kóðuð í rununni.

Base64 breytir aðeins framsetningu

Pickle er tvíundarsnið og hentar ekki alltaf beint í JSON eða eyðublað. Þess vegna er það oft Base64-kóðað:

import base64
import pickle

package = base64.b64encode(pickle.dumps(settings)).decode()

HTTP-beiðni gæti sent:

POST /settings/import HTTP/1.1
Host: nordurmarkadur.example
Content-Type: application/x-www-form-urlencoded

package=gASV...

Base64 veitir hvorki leynd né heilleika. Biðlari getur afkóðað strenginn, breytt bætunum og kóðað hann aftur. Fyrsta skref í CTF er því að greina lögin:

HTTP-reitur → Base64 → pickle → Python-gildi

Ekki kalla Base64-streng „dulkóðaðan stillingapakka“. Hann er aðeins kóðaður til flutnings.

Að þekkja sniðið

Nokkur snið hafa auðþekkjanleg upphafsgildi, þótt þau séu aðeins vísbendingar:

Vísbending Mögulegt snið
{ eða [ í læsilegum texta JSON
Python-pickle með tvíundarhaus Pickle, útgáða og protocol ráða nákvæmum bætum
Java-serialization með þekktum töfrabætum Java object serialization
Texti eins og a: eða O: með lengdum PHP serialization

Base64 felur upphafsbætin þar til strengurinn er afkóðaður. Vistaðu þau í skrá og skoðaðu með file, xxd eða sniðsértæku greiningartóli. Ekki keyra almennan afraðgreini aðeins til að „sjá hvað kemur út“.

Villur gefa líka vísbendingar. invalid load key bendir til að Python hafi reynt að lesa pickle sem passar ekki. Can't get attribute eða ModuleNotFoundError getur sagt að pickle vísi á Python-heiti sem þjónustan finnur ekki. Slík villa sýnir endursmíði hluta en gefur ekki sjálfkrafa gilt kall.

pickletools.dis() skoðar án afraðgreiningar

Python fylgir tólið pickletools, sem sundurgreinir pickle-opcodes án þess að framkvæma venjulega pickle.loads():

import base64
import pickletools

package = "<Base64-pakki úr verkefninu>"
raw = base64.b64decode(package, validate=True)
pickletools.dis(raw)

Skaðlaus stillingaorðabók gæti sýnt aðgerðir fyrir orðabók, strengi og heiltölur. Pickle sem vísar á fall getur innihaldið opcodes sem nefna global-heiti og REDUCE, sem biður afraðgreinandann að kalla smíðafall með viðföngum.

pickletools.dis() er greining, ekki full sönnun á því hvað þjónninn gerir. Sama global-heiti getur vísað á aðra útfærslu í öðru umhverfi og innflutningur getur mistekist. En tólið sýnir bygginguna án þess að keyra ótrausta hegðun á vinnuvélinni þinni.

Ekki prófa með pickle.loads() eftir sundurgreiningu

Að opcodes líti einföld út er ekki næg ástæða til að afraðgreina óþekktan pakka. Haltu greiningunni óvirkri og keyrðu aðeins þitt eigið, afmarkaða CTF-inntak á sandkassaða verkefnið.

Verkefnissértækt hjálparfall

Stillingarpakkinn afhendir Python-einingu sem heitir challenge_models. Hún hefur fallið:

def reveal_flag():
    return "staðgengill utan þjónsins"

Í þjónustunni er sama import-heiti til, en fallið skilar fánanum sem appið geymir í minni. Þetta gerir pickle kleift að vísa á eitt fyrirfram skilgreint, skaðlaust mark án eval, skeljar eða skráarlesturs.

Afmarkaður CTF-hlutur getur lýst endursmíði sinni svona:

import challenge_models


class RevealRequest:
    def __reduce__(self):
        return (challenge_models.reveal_flag, ())

Þetta segir pickle að afraðgreining noti challenge_models.reveal_flag() án viðfanga. Á eigin tölvu skilar handout-útgáfan aðeins staðgengli; við köllum hana samt ekki með því að afraðgreina pakkann. Í jail notar þjónninn sína eigin útgáfu.

Þessi leið er viljandi þrengri en almenn Python-græjukeðja. Þegar hjálparfallið skilar fánanum er verkefninu lokið.

Smíðum pakkann án þess að keyra hann

Þú mátt raðgreina eigin hlut; hættan er að afraðgreina ótraustan hlut. Búðu til pakkann og Base64-kóðaðu hann:

import base64
import pickle

from solution_object import RevealRequest

raw = pickle.dumps(RevealRequest())
package = base64.b64encode(raw).decode()
print(package)

solution_object.py er þín eigin skrá með afmarkaða klasanum hér að ofan. Kóðinn kallar ekki pickle.loads() og keyrir því ekki reveal_flag().

Þú getur sundurgreint þitt eigið raw til að staðfesta að það vísi aðeins á rétt hjálparfall:

import pickletools

pickletools.dis(raw)

Leitaðu að import-heiti challenge_models, fallinu reveal_flag og einni REDUCE-aðgerð. Ef þú sérð óvænt module- eða fallheiti skaltu ekki senda pakkann.

Göngudæmi: Stillingarpakkinn

Sæktu fyrst venjulegan pakka frá /settings/export. Base64-afkóðaðu hann og skoðaðu með pickletools.dis(). Staðfestu að hann lýsi aðeins skaðlausri stillingaorðabók.

Lestu síðan afhenta challenge_models.py og staðfestu nákvæmt module- og fallheiti. Búðu til RevealRequest sem skilar aðeins þeirri callable-tvennd sem verkefnið leyfir, raðgreindu hlutinn og Base64-kóðaðu niðurstöðuna.

Sendu strenginn í sama reit og venjulegi pakkinn notar:

BASE_URL='https://slod-verkefnisins.example'
PACKAGE='<Base64-pakki sem þú bjóst til>'

curl -i -X POST "$BASE_URL/settings/import" \
  --data-urlencode "package=$PACKAGE"

Ef svarið birtir fánann hefur afraðgreinandinn kallað verkefnissértæka hjálparfallið. Kaflinn gefur ekki upp fullbúinn pickle-streng eða fána.

Ef þjónninn skilar ModuleNotFoundError skaltu athuga að global-heitið í pickletools.dis() sé nákvæmlega það sama og handout-skráin notar. Ef Base64 er hafnað skaltu nota base64.b64encode() og --data-urlencode frekar en að handbreyta strengnum.

Ekki skipta yfir í os.system, subprocess, eval eða skrár ef hjálparfallið finnst ekki. Slík breyting fer út fyrir verkefnið og felur líklega aðeins innsláttar- eða import-villu.

Undirrituð gögn

Forrit getur sent undirskrift eða HMAC með raðgreindum gögnum. Rétt sannreynd undirskrift getur sýnt að pakkinn hafi ekki breyst eftir að traustur útgefandi bjó hann til.

Þetta breytir ekki eiginleikum pickle-sniðsins. Ef þjónninn afraðgreinir pakkann áður en undirskrift er sannreynd gerist hættulega skrefið of snemma. Ef önnur leið undirritar hvaða pakka sem notandi sendir og sá pakki er síðan samþykktur hefur notandinn enn leið til að fá gilt, hættulegt snið undirritað.

Í CTF skaltu rekja röðina:

Base64-afkóðun → undirskriftarpróf → afraðgreining

eða:

Base64-afkóðun → afraðgreining → undirskriftarpróf

Röðin er mikilvægari en það eitt að undirskriftarreitur sé til. Ef breyting á einum bita fær stöðuga höfnun áður en nokkur pickle-villa birtist er rétt heilleikapróf sterk vísbending gegn einfaldri pakkabreytingu.

Við reynum ekki að giska á raunverulega undirskriftarlykla. Ef verkefni gefur lítið, afmarkað gervilyklamengi getur staðbundinn samanburður verið sérstakt dulritunarverkefni, en það er ekki hluti Stillingarpakkans.

Java, PHP og önnur snið

Java object serialization getur endursmíðað hluti og kallað sérstakar líftímaaðferðir. Veikleikar nota oft græjukeðju (e. gadget chain): röð þegar tiltækra klasa sem saman valda óvæntri hegðun. Hvaða keðja er möguleg ræðst af nákvæmum bókasöfnum og útgáfum þjónsins.

PHP unserialize() getur á svipaðan hátt smíðað hluti og kallað svokallaðar töfraaðferðir. YAML- og önnur snið geta einnig haft útfærslur sem smíða tungumálssértæka hluti í stað einfaldra gagna.

Við förum ekki yfir tilbúnar græjukeðjur eða verkfæri til að búa þær til. Fyrir byrjanda er mikilvægara að þekkja sniðið, finna afraðgreiningarsökkulinn og spyrja hvort sniðið endursmíði aðeins gögn eða einnig hegðun. Stillingarpakkinn notar eitt sýnilegt Python-hjálparfall svo niðurstaðan sé skýr og endurtakanleg.

Vísbendingar gegn veikleikum

JSON sem er lesið í orðabók með föstum reitum er ekki pickle aðeins vegna þess að reitur heitir object. Gagnaskema sem samþykkir aðeins strengi, tölur og lista mælir gegn almennri hlutasmíði.

Base64-strengur er heldur ekki sjálfkrafa raðgreindur hlutur. Afkóðaðu og skoðaðu töfrabæti eða texta. Hann gæti verið mynd, þjappað skjal eða einfalt JSON.

Ef frumkóði notar json.loads(), staðfestir skema og smíðar síðan föst eigin forritsgildi er __reduce__() ekki viðeigandi. Ef undirskrift er sannreynd áður en pickle.loads() er kallað og engin leið er að fá breyttan pakka undirritaðan er einföld breyting ólíkleg.

Aftur á móti eru pickle.loads() á beiðnigögnum, Java ObjectInputStream á ótraustum straumi eða PHP unserialize() á breytanlegri köku sterkar vísbendingar um að hegðun geti fylgt gögnunum. Staðfestu alltaf áhrif með afmarkaða hjálparmarkinu sem verkefnið gefur, ekki almennri kerfisskipun.

Samantekt

Röðgreining breytir gildi í flutningsform og afraðgreining byggir það aftur. JSON lýsir afmörkuðum gagnategundum, en Python-pickle og sambærileg hlutasnið geta vísað á klasa og föll sem taka þátt í endursmíði.

Base64 er aðeins ytra kóðunarlag. pickletools.dis() leyfir okkur að skoða opcodes án þess að keyra ótraust pickle. Í lifandi verkefninu búum við til eigin hlut sem vísar aðeins á fyrirfram skilgreint reveal_flag()-hjálparfall og sendum hann í nsjail-einangraða þjónustu.

Undirskrift getur sýnt heilleika pakka en gerir hegðunarmikið snið ekki að einföldu gagnasniði; röð sannprófunar og afraðgreiningar skiptir máli. Java- og PHP-verkefni geta notað græjukeðjur, en þær eru framhaldsatriði og ráðast af nákvæmu umhverfi.

Í næsta kafla færum við okkur aftur yfir í vafrann og skoðum vefkóðainnskoti. Þar verður notandastýrt gildi að virku HTML eða JavaScript í uppruna vefsins.