]> git.somenet.org - pub/jan/ctf-seminar.git/blob - writeups/smashing/seccon19.md
Add write-up for OTW day-25: Lost in Maze
[pub/jan/ctf-seminar.git] / writeups / smashing / seccon19.md
1 # Retrospective
2 The CTF was quite fun and used some vulnerabilities which were revealed not that long ago. 
3
4 Time spent: ~ 13 hours
5
6 # Option-Cmd-U (solved)
7 ## Overview
8 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.
9
10 ## Approach
11 The source for the webpage is provided and listed below:
12 ```
13 <?php
14 if (isset($_GET['url'])){
15     $url = filter_input(INPUT_GET, 'url');
16     $parsed_url = parse_url($url);              
17     if($parsed_url["scheme"] !== "http"){
18         // only http: should be allowed. 
19         echo 'URL should start with http!';
20     } else if (gethostbyname(idn_to_ascii($parsed_url["host"])) === gethostbyname("nginx")) {
21         // local access to nginx from php-fpm should be blocked.
22         echo 'Oops, are you a robot or an attacker?';
23     } else {
24         // file_get_contents needs idn_to_ascii(): https://stackoverflow.com/questions/40663425/
25         highlight_string(file_get_contents(idn_to_ascii($url),
26                                             false,
27                                             stream_context_create(array(
28                                                 'http' => array(
29                                                     'follow_location' => false,
30                                                     'timeout' => 2
31                                                 )
32                                             ))));
33     }
34 }
35 ?>
36 ```
37
38 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.
39
40 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`.
41 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. 
42
43 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. 
44
45 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.
46
47 And finally: `SECCON{what_a_easy_bypass_314208thg0n423g}`
48
49 [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)
50 # Web-SPA (almost solved :()
51 ## Overview
52 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.
53
54  ## Approach
55  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. 
56 ```
57 methods: {
58   async fetchContest(contestId) {
59     this.contest = await $.getJSON(`/${contestId}.json`)
60   },
61   async fetchContests() {
62     this.contests = await $.getJSON('/contests.json')
63   },
64   async onHashChange() {
65     const contestId = location.hash.slice(1);
66     if (contestId) {
67       if (contestId === 'report') {
68         this.goReport();
69       } else {
70         await this.goContest(contestId);
71       }
72     } else {
73       this.goHome();
74     }
75   },
76 ```
77
78 Another fact which was revealed by the source code was the usage of jQuery to load data which will become important later on.
79 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.
80
81 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.
82
83 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.
84
85 ## Conclusion
86 So close, yet so far away - never discard an idea just because you do not know the technology used for it.