From 4968332255f1655467a157f6e245e09c60d94036 Mon Sep 17 00:00:00 2001 From: homic <michael.hoschek@gmx.at> Date: Sat, 18 Jan 2020 17:35:20 +0100 Subject: [PATCH] added writeups --- writeups/smashing/asis19.md | 20 ++++++++ writeups/smashing/ctfzone19.md | 25 ++++++++++ writeups/smashing/seccon19.md | 86 ++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 writeups/smashing/asis19.md create mode 100644 writeups/smashing/ctfzone19.md create mode 100644 writeups/smashing/seccon19.md diff --git a/writeups/smashing/asis19.md b/writeups/smashing/asis19.md new file mode 100644 index 0000000..affb680 --- /dev/null +++ b/writeups/smashing/asis19.md @@ -0,0 +1,20 @@ +# Retrospective +Sadly i was not able to spend that much time on this CTF and therefore i was only able to work on one challenge actively. This challenge was somewhat okay-ish due to the fact that some guessing was needed to get the structure of the filesystem without the second version. Due to his the challenge took way longer that it should have been. + +Time spent: ~10 hours +# protected area (solved) +## Overview +The website shows a welcome text and another line of text which looks like a todo list. + +## Approach +First we checked the source of the website which revealed some methods to interact with an api to read files from a server. The two most important functions for us in this case are `file_check(file)` which checks if a file exists and `file_read(file)` which eventually reads a file and returns its contents. + +Since `file_check` internally checks if the returned value equals `True` we assumed that the backend was written in Python which turned out to be true later. + +Trying some stuff we found out that it is possible to traverse the path. The backend seems to replace `../` only once so we can bypass this by nesting another `../` resulting in `....//`. Also the backends response was `security` if the file did not end with `.txt` which later turned out to be wrong since its only checking if the whole query ends with `.txt`. + +Unfortunately we were still missing the structure of the filesystem therefore we did not know where we should look for our flag. After some time a second version of the challenge was revealed which provided the used docker template and we were able to extract the source code of the backend. + +Knowing the basic structure of flask apps we were able to find an endpoint called `/protected_area_0098` which hold the flag. This page was protected with a password which we were able to extract from the config. + +Eventually we were able to access the endpoint which revealed the flag: `ASIS{f70a0203d638a0c90a490ad46a94e394}` \ No newline at end of file diff --git a/writeups/smashing/ctfzone19.md b/writeups/smashing/ctfzone19.md new file mode 100644 index 0000000..339e6a3 --- /dev/null +++ b/writeups/smashing/ctfzone19.md @@ -0,0 +1,25 @@ +# Retrospective +The goal of this CTF was to be as realistic as possible. I think they met their goal somehow but the web challenges were really not enjoyable due to a lot of guesswork. Especially the `bathhouse` which was a multi-stage challenge lead from one guessing part to another. + +Time spent: ~15 hours +# bathhouse (solved) +## Overview +A user can book an appointment for a bathhouse and needs to provide some data like the phone number and the amount of time to be there. + +## Approach +The form was vulnerable to SQLi which allowed us to dump the database. The dumped database contained an `username` and `password` which is needed to login under `/set_price` found in the `robots.txt`. There we were able to change the price of a receipt which was generated with `wkhtmltopdf 0.12.1`. This version of `wkhtmltopdf` was vulnerable to file inclusion and therefore we were able to include data from the system. After messing around with different inclusion techniques we found that `wkhtmltopdf` executes JS which allowed us to read files from the system and include its content into the generated pdf. +``` +<script> + x=new XMLHttpRequest; + x.onload=function() { + document.write(this.responseText) + }; + x.open("GET","file:///etc/passwd"); + x.send(); +</script> +``` +From this one an unnecessary search for the flag begun since the flag was not in an usual location like e.g. `/flag` and the structure of the project was unknown. After digging around on the server and looking for anything which could give a hint @lavish tried to find additional routes with `DirBuster` and eventually found another endpoint `/status/` containing the path to the app and used modules. + +Afterwards we were able to extract the source code of the app which and one of the views yielded another hint to `http://syncdata/sync.html`. Unfortunately this path was not fetchable with our previous approach and again some time passed looking for other possible hints. +Eventually `syncdata` actually contained the flag but was only accessible via an iframe which did not work for the other files on the filesystem. +Finally `<div><p>Report Heading</p><iframe src=http://syncdata/sync.html height="500" width="500">` as payload revealed the flag. \ No newline at end of file diff --git a/writeups/smashing/seccon19.md b/writeups/smashing/seccon19.md new file mode 100644 index 0000000..0a32918 --- /dev/null +++ b/writeups/smashing/seccon19.md @@ -0,0 +1,86 @@ +# Retrospective +The CTF was quite fun and used some vulnerabilities which were revealed not that long ago. + +Time spent: ~ 13 hours + +# Option-Cmd-U (solved) +## Overview +There are two servers one host for the webpage and another one hosting the flag which is only accessible on the local network. The webpage allows to make requests to other pages and is eventually used to request the flag. + +## Approach +The source for the webpage is provided and listed below: +``` +<?php +if (isset($_GET['url'])){ + $url = filter_input(INPUT_GET, 'url'); + $parsed_url = parse_url($url); + if($parsed_url["scheme"] !== "http"){ + // only http: should be allowed. + echo 'URL should start with http!'; + } else if (gethostbyname(idn_to_ascii($parsed_url["host"])) === gethostbyname("nginx")) { + // local access to nginx from php-fpm should be blocked. + echo 'Oops, are you a robot or an attacker?'; + } else { + // file_get_contents needs idn_to_ascii(): https://stackoverflow.com/questions/40663425/ + highlight_string(file_get_contents(idn_to_ascii($url), + false, + stream_context_create(array( + 'http' => array( + 'follow_location' => false, + 'timeout' => 2 + ) + )))); + } +} +?> +``` + +Quickly one can see that the address of the provided `host` must not match the address of `nginx` in the first check while still being the same when requesting the file. On a closer look the different usages of `idn_to_ascii` stand out and hint a potential vulnerability. + +First we need to trigger the `Oops, are you a robot or an attacker?` message to ensure we are requesting to the correct server which can be done with the following input: `http://nginx`. +First we tried to insert arbitrary UTF-8 characters. Sadly this lead to an URL containing punycode (i.e. `xn--http://nginx/flag-5zb`) which already invalidated the needed scheme. After searching time through the deeps of the internet eventually i found a presentation [1] about HostSplitting and normalization of unicode characters. This presentation shows, that not every unicode character is transformed to punycode which helps us a lot. + +At the end of the presentation a list containing some characters which can be transformed to regular ascii is provided. This list contains a special version of `@` which is transformed to its regular ascii version. + +Knowing this special `﹫` the final payload (`http://﹫nginx/flag.php`) can be constructed. Since the first check only normalizes the given host (`﹫nginx`) and `@nginx` obviously wont have the same address like `nginx` the first check is successfully bypassed. In the next step the initial input is transformed resulting in `http://@nginx/flag.php`. Since `@` is used to provide a username and password to pass authentication the URL is valid and the flag can be retrieved. + +And finally: `SECCON{what_a_easy_bypass_314208thg0n423g}` + +[1] [https://i.blackhat.com/USA-19/Thursday/us-19-Birch-HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization.pdf](https://i.blackhat.com/USA-19/Thursday/us-19-Birch-HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization.pdf) +# Web-SPA (almost solved :() +## Overview +An SPA was given showing all challenges of SECCON CTFs. The file from which the data of the challenges should be loaded was given as URL parameter which we were able to use to load data from arbitrary URLs. + + ## Approach + Since it was an Vue app the source code was available and we quickly realized that user controlled data (`contestId`) was used to load json files. +``` +methods: { + async fetchContest(contestId) { + this.contest = await $.getJSON(`/${contestId}.json`) + }, + async fetchContests() { + this.contests = await $.getJSON('/contests.json') + }, + async onHashChange() { + const contestId = location.hash.slice(1); + if (contestId) { + if (contestId === 'report') { + this.goReport(); + } else { + await this.goContest(contestId); + } + } else { + this.goHome(); + } + }, +``` + +Another fact which was revealed by the source code was the usage of jQuery to load data which will become important later on. +But first we need to find a way to load from any domain and not just the current one. This one can easily be achieved by providing an absolute path (e.g. `evil.com/evil.json`) instead of a relative one. Sadly a regular request to an user controlled server does not reveal anything about the admin and therefore we needed to find another way to exfiltrate the admins cookie. + +After searching in the deeps of the web again the jQuery documentation revealed that `$.getJSON` treats a request as `JSONP` as soon as `callback=` is provided in the URL to request. With JSONP JavaScript code can be returned which is then executed on the client side. Sadly none of us had any prior experience with JSONP and therefore we were not able to trigger this vulnerability. + +After the end of the CTF the writeups showed that JSONP was indeed the solution and the with the help of the returned JS the cookie could have been extracted and sent in another request tous. + +## Conclusion +So close, yet so far away - never discard an idea just because you do not know the technology used for it. -- 2.43.0