CyBRICS CTF 2019 Writeup

CyBRICS CTF 2019 について

CyBRICS CTF 2019 が開催されました。
2019年7月20日 午後7時~7月21日 午後7時(24時間)

cybrics.net

CyBRICS CTF 2019は、1位~3位に賞金の出る大会です。 なんと、1位は、10,000 USDももらえるそうです。 ただし、参加制限があり、残念ながら国籍フィルターで日本人は除外されてしまいます。

BRICS — that you're from Brazil, Russia, India, China or South Africa;

今回は、「バラバラで参加しよう」という話になり、一人で参加しました。 結果は、92/775位で266点でした。 幅広いジャンルでの問題が多かった印象です。 難易度も易しいものから高難易度の問題まで幅広く用意されていました。 一人では全然時間が足りなかったので、次回は複数人で参加したいです。

f:id:tsalvia:20190721203801p:plain

CyBRICS CTF 2019 Writeup(10問)

Mic Check (Cyber, Baby, 10 pts)

問題

Have you read the game rules? There's a flag there.

解答例

Welcome問題、ゲームルールのリンク先にあるフラグを投入するだけ。

f:id:tsalvia:20190721213218p:plain

FLAG

cybrics{W3lc0M3_t0_t3h_G4M#}

Warmup (Web, Baby, 10 pts)

問題

E_TOO_EASY
Just get the flag

解答例

問題のリンクをクリックすると、304で別のページにリダイレクトされてしまいます。 curlで直接リンク先を確認すると、末尾にBase64エンコードされたデータが確認できました。

$ curl http://45.32.148.106

<html>
        <script language="JavaScript">
                function func() {
                  document.location.href = 'final.html'
                }
                </script>
<body onload=func()>
<p>
But at this moment to take its place?<br/>
It’s getting cold,” said Zametov. “Only the other end, waiting for her brother, but she said that he had come to that, nervous irritability from hunger, she falls to beating them at once, and warmly pressed his head was clear that he was talking nonsense, Sonia,” he muttered bitterly.<br/>

省略

And what if I hear any rumours, I’ll take it back in time,” struck him like a chicken in the stinking, dusty town air.<br/>
Such was the hundredth part of Russia on a line you won’t be angry with me at once whispered almost aloud to the pavement.<br/>
She was a lie at first?”<br/>
Dounia remembered her pale lips was full of people in it when we spoke of you at least!<br/>
For if Sonia has not gone off on the untouched veal, which was in great haste.<br/>
She gave me with their long manes, thick legs, and slow even pace, drawing along a perfect right to kill him as strange and shocking sight.<br/>
Here is your base64-encoded flag: Y3licmljc3s0YjY0NmM3OTg1ZmVjNjE4OWRhZGY4ODIyOTU1YjAzNH0=
</p></body></html>

Base64でデコードすると、フラグを取得することができました。

$ echo "Y3licmljc3s0YjY0NmM3OTg1ZmVjNjE4OWRhZGY4ODIyOTU1YjAzNH0=" | base64 -d
cybrics{4b646c7985fec6189dadf8822955b034}

FLAG

cybrics{4b646c7985fec6189dadf8822955b034}

Sender (Network, Baby, 10 pts)

問題

We've intercepted this text off the wire of some conspirator, but we have no idea what to do with that.
intercepted_text.txt
Get us their secret documents

添付ファイル(intercepted_text.txt)

220 ugm.cybrics.net ESMTP Postfix (Ubuntu)
EHLO localhost
250-ugm.cybrics.net
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
AUTH LOGIN
334 VXNlcm5hbWU6
ZmF3a2Vz
334 UGFzc3dvcmQ6
Q29tYmluNHQxb25YWFk=
235 2.7.0 Authentication successful
MAIL FROM: <fawkes@ugm.cybrics.net>
250 2.1.0 Ok
RCPT TO: <area51@af.mil>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
From: fawkes <fawkes@ugm.cybrics.net>
To: Area51 <area51@af.mil>
Subject: add - archive pw
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0

=62=74=77=2E=0A=0A=70=61=73=73=77=6F=72=64 =66=6F=72 =74=68=65 =61=72=63=
=68=69=76=65 =77=69=74=68 =66=6C=61=67=3A =63=72=61=63=6B=30=57=65=73=74=
=6F=6E=38=38=76=65=72=74=65=62=72=61=0A=0A=63=68=65=65=72=73=21=0A
.
250 2.0.0 Ok: queued as C4D593E8B6
QUIT
221 2.0.0 Bye

解答例

末尾の方に Quoted-printable でエンコードされたデータがあるので、とりあえずデコードしてみました。

=62=74=77=2E=0A=0A=70=61=73=73=77=6F=72=64 =66=6F=72 =74=68=65 =61=72=63=
=68=69=76=65 =77=69=74=68 =66=6C=61=67=3A =63=72=61=63=6B=30=57=65=73=74=
=6F=6E=38=38=76=65=72=74=65=62=72=61=0A=0A=63=68=65=65=72=73=21=0A

デコードするとパスワードが手に入りました。 フラグは、アーカイブファイルの中にあるようです。

btw.

password for the archive with flag: crack0Weston88vertebra

cheers!

次に、問題文の先頭にあるドメインを足掛かりに調べます。 とりあえず、nmapで調査をしてみます。 sshpop3のポートが開いていることが分かりました。

PS> nmap ugm.cybrics.net
Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-20 20:17 ???? (?W???)
Nmap scan report for ugm.cybrics.net (136.244.67.129)
Host is up (0.24s latency).
rDNS record for 136.244.67.129: 136.244.67.129.vultr.com
Not shown: 995 closed ports
PORT    STATE    SERVICE
22/tcp  open     ssh
25/tcp  filtered smtp
110/tcp open     pop3
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds

Nmap done: 1 IP address (1 host up) scanned in 13.71 seconds

問題文に書かれているものは、pop3のコマンド履歴だったようです。 また、問題文の「AUTH LOGIN」付近を見ると、ログインユーザとパスワードがBase64エンコードされていました。

$ echo "ZmF3a2Vz" | base64 -d
fawkes
$ echo "Q29tYmluNHQxb25YWFk=" | base64 -d
Combin4t1onXXY

ユーザ名、パスワードが分かったので、telnetで接続します。 接続できたら、「LIST」コマンドでメール一覧を確認し、「RETR <メール番号>」コマンドでメール内容を確認します。

$ telnet ugm.cybrics.net 110
USER fawkes
PASS Combin4t1onXXY
LIST
RETR 1

メールを確認すると、zipファイルが添付されているようです。 Base64エンコードされているので、デコードすると暗号付きzipファイルが出てきました。

Return-Path: <fawkes@ugm.cybrics.net>
X-Original-To: fawkes@ugm.cybrics.net
Delivered-To: fawkes@ugm.cybrics.net
Received: by sender (Postfix, from userid 1000)
        id B83843EBFF; Thu, 18 Jul 2019 16:41:23 +0000 (UTC)
Date: Thu, 18 Jul 2019 16:41:23 +0000
From: fawkes <fawkes@ugm.cybrics.net>
To: Area51 <area51@af.mil>, fawkes <fawkes@ugm.cybrics.net>
Subject: interesting archive
Message-ID: <20190718164123.GA9631@ugm.cybrics.net>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="J2SCkAp4GZ/dPZZf"
Content-Disposition: inline
User-Agent: Mutt/1.5.24 (2015-08-30)


--J2SCkAp4GZ/dPZZf
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

take a look. dont share. secret.

--J2SCkAp4GZ/dPZZf
Content-Type: application/zip
Content-Disposition: attachment; filename="secret_flag.zip"
Content-Transfer-Encoding: base64

UEsDBBQACQBjAMua8k6A+vIXUogBAA+iAQAPAAsAc2VjcmV0X2ZsYWcucGRmAZkHAAEAQUUD
CAC1GtwFWQRy7mwXUpknBhOJ3hpnDv1ei1Kf+knOhoW61yeyPdnML4vSrff+GUxQYCGKz6SB
txgPjLvcWjoZokQBxFczx5575Z8Wv6dcwrX2X5A4WUFP+vpXBeq7E/c1Q7T87mR2WJnrhqLs
253Zoz1KC2kq+Gs/KXZILyxSzFWW6h7YLlozE6ru/f8WGtlLZzw5CXMdTcaZFGBjJX9jsqqY
以下省略

冒頭で取得したパスワードを使って展開すると、PDFファイルが出てきました。 PDFファイルを開くと、フラグが書かれていました。

f:id:tsalvia:20190720205358p:plain

FLAG

cybrics{Y0uV3_G0T_m41L}

Honey, Help! (rebyC, Baby, 10 pts)

問題

Added at 10:50 UTC: there was a typo in the flag. Please re-submit.
HONEY HELP!!!
I was working in my Kali MATE, pressed something, AND EVERYTHING DISAPPEARED!

I even copied the text from terminal

f:id:tsalvia:20190721222037p:plain

リンク先のファイル(text from terminal)

root@myLOVELYcomputer:~/cybrics# ls -la
total 12
drwxr-xr-x  2 root root 4096 Jul 22  2019 .
drwxr-xr-x 21 root root 4096 Jul 22  2019 ..
-rw-r--r--  1 root root   44 Jul 22  2019 flag
root@myLOVELYcomputer:~/cybrics# echo $'\e(0'

⎼⎺⎺├@└≤LOVELY␌⎺└⎻┤├␊⎼:·/␌≤␉⎼␋␌⎽# ┌⎽ -┌▒
├⎺├▒┌ 12
␍⎼┬│⎼-│⎼-│  2 ⎼⎺⎺├ ⎼⎺⎺├ 4096 J┤┌ 22  2019 .
␍⎼┬│⎼-│⎼-│ 21 ⎼⎺⎺├ ⎼⎺⎺├ 4096 J┤┌ 22  2019 ..
-⎼┬-⎼--⎼--  1 ⎼⎺⎺├ ⎼⎺⎺├   44 J┤┌ 22  2019 °┌▒±
⎼⎺⎺├@└≤LOVELY␌⎺└⎻┤├␊⎼:·/␌≤␉⎼␋␌⎽# ␌▒├ °┌▒± 
␌≤␉⎼␋␌⎽π␤0┌≤_␌⎼4⎻_1⎽_├␤␋⎽_▒┌13┼␋$␤_0⎼_┬4├?£
⎼⎺⎺├@└≤LOVELY␌⎺└⎻┤├␊⎼:·/␌≤␉⎼␋␌⎽# 

解答例

自分のPCで実際に echo $'\e(0' を実行して、気合で置換していきました。
もっとスマートな解き方が知りたい。

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
と入力すると、以下のようになります。

f:id:tsalvia:20190721223218p:plain

FLAG

cybrics{h0ly_cr4p_1s_this_al13ni$h_0r_w4t?}

Tone (Forensic, Baby, 10 pts)

問題

Ha! Looks like this guy forgot to turn off his video stream and entered his password on his phone!
youtu.be/11k0n7TOYeM

解答例

リンクを踏むと、限定公開のYoutubeの動画に飛ばされます。 よく耳を澄ますと、電話の入力音(DTMF)が聞こえてきます。

Youtubeの音声を録音し、Audacityで少しずつ再生しながら耳コピしました。 個人的には、再生速度を上げると違いが分かりやすかったです。

f:id:tsalvia:20190721224435p:plain

音声の比較対象として、こちらのサイトも参考にしました。

onlinetonegenerator.com

耳コピした結果が以下の通りとなります。

222 999 22 777 444 222 7777 7777 33 222 777 33 8 8 666  66  2 555 333 555 2 4

これらの番号は、携帯電話のキーパッドと対応しています。 割り当てると、以下の通りになります。

c y b r i c s s e c r e t t o n a l f l a g

フラグの形式に合わせて整形したものが、フラグとなります。

FLAG

cybrics{secret tonal flag}

Oldman Reverse (Reverse, Baby, 10 pts)

問題

I've found this file in my grandfather garage. Help me understand what it does
oldman.asm

添付ファイル(oldman.asm)

.MCALL  .TTYOUT,.EXIT
START:
    mov   #MSG r1 
    mov #0d r2
    mov #32d r3
loop:       
    mov   #MSG r1 
    add r2 r1
    movb    (r1) r0
    .TTYOUT
    sub #1d r3
    cmp #0 r3
    beq     DONE
    add #33d r2
    swab r2
    clrb r2
    swab r2    
    br      loop      
DONE: 
    .EXIT

MSG:
    .ascii "cp33AI9~p78f8h1UcspOtKMQbxSKdq~^0yANxbnN)d}k&6eUNr66UK7Hsk_uFSb5#9b&PjV5_8phe7C#CLc#<QSr0sb6{%NC8G|ra!YJyaG_~RfV3sw_&SW~}((_1>rh0dMzi><i6)wPgxiCzJJVd8CsGkT^p>_KXGxv1cIs1q(QwpnONOU9PtP35JJ5<hlsThB{uCs4knEJxGgzpI&u)1d{4<098KpXrLko{Tn{gY<|EjH_ez{z)j)_3t(|13Y}"
.end START

解答例

見慣れない命令(swabやclrb)を調べていると、PDP-11と呼ばれる古いコンピュータのアセンブリだということが分かりました。

ja.wikipedia.org

アセンブリを読んでいくと、33文字毎にMSGをローテートして表示するプログラムのようです。 C言語に読み換えて実装してみました。このプログラムを実行すると、フラグが取得できます。

#include <stdio.h>
#include <string.h>

int main(void)
{
    char msg[] = "cp33AI9~p78f8h1UcspOtKMQbxSKdq~^0yANxbnN)d}k&6eUNr66UK7Hsk_uFSb5#9b&PjV5_8phe7C#CLc#<QSr0sb6{%NC8G|ra!YJyaG_~RfV3sw_&SW~}((_1>rh0dMzi><i6)wPgxiCzJJVd8CsGkT^p>_KXGxv1cIs1q(QwpnONOU9PtP35JJ5<hlsThB{uCs4knEJxGgzpI&u)1d{4<098KpXrLko{Tn{gY<|EjH_ez{z)j)_3t(|13Y}";

    int i;
    for (i = 0; i < 32; i++) {
        char tmp[100] = {};
        putchar(*msg);
        memcpy(tmp, msg, 33);
        memmove(msg, msg + 33, strlen(msg) - 33);
        memcpy(msg + strlen(msg) - 33, tmp, 33);
    }
    putchar('\n');
    return 0;
}

FLAG

cybrics{pdp_gpg_crc_dtd_bkb_php}

Matreshka (Reverse, Easy, 50 pts)

問題

Matreshka hides flag. Open it
matreshka.zip

解答例

zipファイルを展開すると、クラスファイルとdataファイルが入っていました。

$ file *
Code2.class: compiled Java class data, version 54.0
data.bin:    data

とりあえず実行してみると、Noと表示されます。

PS> java.exe Code2
No

jadを使ってデコンパイルしてコードを確認してみます。

PS> jad.exe .\Code2.class
Parsing .\Code2.class... Generating Code2.jad

デコンパイル結果を確認すると、どうやらユーザ名が正しくないと「No」となってしまうようです。

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   2.java

import java.io.*;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

class Code2
{

    Code2()
    {
    }

    public static byte[] decode(byte abyte0[], String s)
        throws Exception
    {
        SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES");
        byte abyte1[] = s.getBytes();
        DESKeySpec deskeyspec = new DESKeySpec(abyte1);
        javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(2, secretkey);
        byte abyte2[] = cipher.doFinal(abyte0);
        return abyte2;
    }

    public static byte[] encode(byte abyte0[], String s)
        throws Exception
    {
        SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES");
        byte abyte1[] = s.getBytes();
        DESKeySpec deskeyspec = new DESKeySpec(abyte1);
        javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(1, secretkey);
        byte abyte2[] = cipher.doFinal(abyte0);
        return abyte2;
    }

    public static void main(String args[])
        throws Exception
    {
        String s = "matreha!";
        byte abyte0[] = encode(System.getProperty("user.name").getBytes(), s);
        byte abyte1[] = {
            76, -99, 37, 75, -68, 10, -52, 10, -5, 9, 
            92, 1, 99, -94, 105, -18
        };
        for(int i = 0; i < abyte1.length; i++)
            if(abyte1[i] != abyte0[i])
            {
                System.out.println("No");
                return;
            }

        File file = new File("data.bin");
        FileInputStream fileinputstream = new FileInputStream(file);
        byte abyte2[] = new byte[(int)file.length()];
        fileinputstream.read(abyte2);
        fileinputstream.close();
        byte abyte3[] = decode(abyte2, System.getProperty("user.name"));
        FileOutputStream fileoutputstream = new FileOutputStream("stage2.bin");
        fileoutputstream.write(abyte3, 0, abyte3.length);
        fileoutputstream.flush();
        fileoutputstream.close();
    }
}

上記のコードを書き換えて、本来のユーザ名を特定します。

import java.io.*;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

class Matreshka
{

    Matreshka()
    {
    }

    public static byte[] decode(byte abyte0[], String s)
        throws Exception
    {
        SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES");
        byte abyte1[] = s.getBytes();
        DESKeySpec deskeyspec = new DESKeySpec(abyte1);
        javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(2, secretkey);
        byte abyte2[] = cipher.doFinal(abyte0);
        return abyte2;
    }

    public static void main(String args[])
        throws Exception
    {
        String s = "matreha!";
        byte abyte1[] = {
            76, -99, 37, 75, -68, 10, -52, 10, -5, 9, 
            92, 1, 99, -94, 105, -18
        };
        byte dec_abyte1[] = decode(abyte1, s);
        System.out.println(new String(dec_abyte1));
    }
}

上記のプログラムを実行して確認すると、「lettreha」が本来のユーザ名だと分かりました。

PS> javac .\Matreshka.java
PS> java Matreshka
lettreha

元のソースコードSystem.getProperty("user.name") のところを「lettreha」に置き換えました。

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   2.java

import java.io.*;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

class Code2
{

    Code2()
    {
    }

    public static byte[] decode(byte abyte0[], String s)
        throws Exception
    {
        SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES");
        byte abyte1[] = s.getBytes();
        DESKeySpec deskeyspec = new DESKeySpec(abyte1);
        javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(2, secretkey);
        byte abyte2[] = cipher.doFinal(abyte0);
        return abyte2;
    }

    public static byte[] encode(byte abyte0[], String s)
        throws Exception
    {
        SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES");
        byte abyte1[] = s.getBytes();
        DESKeySpec deskeyspec = new DESKeySpec(abyte1);
        javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(1, secretkey);
        byte abyte2[] = cipher.doFinal(abyte0);
        return abyte2;
    }

    public static void main(String args[])
        throws Exception
    {
        String s = "matreha!";
        String username = "lettreha";
        // byte abyte0[] = encode(System.getProperty("user.name").getBytes(), s);
        byte abyte0[] = encode(username.getBytes(), s);
        byte abyte1[] = {
            76, -99, 37, 75, -68, 10, -52, 10, -5, 9, 
            92, 1, 99, -94, 105, -18
        };
        for(int i = 0; i < abyte1.length; i++)
            if(abyte1[i] != abyte0[i])
            {
                System.out.println("No");
                return;
            }

        File file = new File("data.bin");
        FileInputStream fileinputstream = new FileInputStream(file);
        byte abyte2[] = new byte[(int)file.length()];
        fileinputstream.read(abyte2);
        fileinputstream.close();
        // byte abyte3[] = decode(abyte2, System.getProperty("user.name"));
        byte abyte3[] = decode(abyte2, username);
        FileOutputStream fileoutputstream = new FileOutputStream("stage2.bin");
        fileoutputstream.write(abyte3, 0, abyte3.length);
        fileoutputstream.flush();
        fileoutputstream.close();
    }
}

上記のプログラムを実行すると、「stage2.bin」というファイルが生成されました。

PS> javac .\Code2.java
PS> java Code2

2つ目のファイルは、ELFファイルのようです。

$ file stage2.bin
stage2.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

とりあえず、実行してみましたが、やはり「Fail」と表示されるようです。

$ ./stage2.bin
Fail

まずは、gdb-pedaで「Fail」の表示箇所を見つけます。 writeシステムコールでbreakさせて、少しステップ実行してみます。

gdb-peda$ catch syscall 1
gdb-peda$ r

すると、エラーを表示していそうな箇所を見つけることができました。

[----------------------------------registers-----------------------------------]
RAX: 0xc000000300 --> 0xc00002a000 --> 0xc00002a800 --> 0xc00002b000 --> 0xc00002b800 --> 0xc00002c000 (--> ...)
RBX: 0x427890 (<runtime.printunlock+96>:        jmp    0x427830 <runtime.printunlock>)
RCX: 0xc0000003b1 --> 0x100000000000001
RDX: 0x0
RSI: 0xc000000300 --> 0xc00002a000 --> 0xc00002a800 --> 0xc00002b000 --> 0xc00002b800 --> 0xc00002c000 (--> ...)
RDI: 0x2
RBP: 0xc00002a788 --> 0xc00002a790 --> 0x428737 (<runtime.main+519>:    mov    eax,DWORD PTR [rip+0x121b93]        # 0x54a2d0 <runtime.runningPanicDefers>)
RSP: 0xc00002a6d0 --> 0x4a3f9c ("Fail\nGreekKhmerLatinLimbuNushuOghamOriyaOsageRunicSTermTakriTamilargp=arraycasp1casp2casp3closefalsefaultfunc(gcingint16int32int64panicsleepslicesse41sse42ssse3uint8write (MB)\n Value addr= base  code="...)
RIP: 0x47630d (<main.main+861>: mov    rbp,QWORD PTR [rsp+0xb8])
R8 : 0x5
R9 : 0x5
R10: 0x5
R11: 0x206
R12: 0xc ('\x0c')
R13: 0xb ('\x0b')
R14: 0x200
R15: 0x200
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4762fa <main.main+842>:    mov    QWORD PTR [rsp+0x8],0x5
   0x476303 <main.main+851>:    call   0x4280d0 <runtime.printstring>
   0x476308 <main.main+856>:    call   0x427830 <runtime.printunlock>
=> 0x47630d <main.main+861>:    mov    rbp,QWORD PTR [rsp+0xb8]
   0x476315 <main.main+869>:    add    rsp,0xc0
   0x47631c <main.main+876>:    ret
   0x47631d <main.main+877>:    call   0x44ea40 <runtime.morestack_noctxt>
   0x476322 <main.main+882>:    jmp    0x475fb0 <main.main>
[------------------------------------stack-------------------------------------]
0000| 0xc00002a6d0 --> 0x4a3f9c ("Fail\nGreekKhmerLatinLimbuNushuOghamOriyaOsageRunicSTermTakriTamilargp=arraycasp1casp2casp3closefalsefaultfunc(gcingint16int32int64panicsleepslicesse41sse42ssse3uint8write (MB)\n Value addr= base  code="...)
0008| 0xc00002a6d8 --> 0x5
0016| 0xc00002a6e0 --> 0x4
0024| 0xc00002a6e8 --> 0x4
0032| 0xc00002a6f0 --> 0xc000014088 --> 0x746f6f72 ('root')
0040| 0xc00002a6f8 --> 0x4
0048| 0xc00002a700 --> 0x8
0056| 0xc00002a708 --> 0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
main.main () at /home/awengar/Distr/Hacks/ORG/SPBCTF/CyBrics/rev100_matreshka/2.go:25
25      /home/awengar/Distr/Hacks/ORG/SPBCTF/CyBrics/rev100_matreshka/2.go: No such file or directory.
gdb-peda$

アドレスが分かったので、Ghidraで対象のアドレス(0x47630d)を見てみます。

f:id:tsalvia:20190722001000p:plain

エラー表示をしていそうな関数(runtime.printstring())が見つかりました。 その少し上には、io/ioutil.WriteFile() や crypto/rc4.() などがあり、そこがメインのコードになっていそうです。 その場所(0x476181)にgdb-pedaで無理やりjumpしてみます。

gdb-peda$ b *0x476126
gdb-peda$ b *0x476181
gdb-peda$ r
gdb-peda$ jump *0x476181
gdb-peda$ c

すると、また新たに「result.pyc」という名前のファイル生成されました。

$ file result.pyc
result.pyc: data

また、今回はそのまま実行することができませんでした。

$ python3 result.pyc
RuntimeError: Bad magic number in .pyc file

拡張子的には、pycファイルとなっているので、とりあえずデコンパイルしてみます。 今回は、下記のサイトで行いました。

python-decompiler.com

デコンパイル結果は、以下の通りとなります。

def decode(data, key):
    idx = 0
    res = []
    for c in data:
        res.append(chr(c ^ ord(key[idx])))
        idx = (idx + 1) % len(key)

    return res


flag = [
 40, 11, 82, 58, 93, 82, 64, 76, 6, 70, 100, 26, 7, 4, 123, 124, 127, 45, 1, 125, 107, 115, 0, 2, 31, 15]
print('Enter key to get flag:')
key = input()
if len(key) != 8:
    print('Invalid len')
    quit()
res = decode(flag, key)
print(''.join(res))

どうやらフラグは、8文字の文字列をキーとしたxor暗号で暗号化されているようです。 ただし、今回のフラグの初めの8文字は、cybrics{ であることが分かっています。

cybrics{ と 暗号化されたフラグでxorを取れば、キーが求められそうです。

CyberChefを使ってキーを求めてみます。

f:id:tsalvia:20190722003340p:plain

以上の結果から、今回のxorキーは、「Kr0H4137」だということが分かりました。 「Kr0H4137」を使って、フラグを復号することができました。

f:id:tsalvia:20190722003540p:plain

FLAG

cybrics{M4TR35HK4_15_B35T}

Disk Data (Forensic, Easy, 56 pts)

問題

Disk dump hides the flag. Obtain it
data2.zip.torrent

解答例

添付されたtorrentファイルからzipファイルをダウンロードし、展開するとExt4ファイルシステムのディスクイメージが出てきました。

$ file data2.bin
data2.bin: Linux rev 1.0 ext4 filesystem data, UUID=a795f441-a210-45b1-885b-ff53d3ca0a61 (extents) (huge files)

「data2.bin」をAutopsyで開いてみました。 しばらく漁っていると、「.bash_history」を見つけました。 コマンド履歴を見てみると、どこからかwgetで画像ファイルをダウンロードし、 ImageMagickのconvertコマンドで画像の一部を白く塗りつぶしているのが確認できました。

ls
ls -anl
bash
su rev
read -r URL
cd Downloads/
wget $URL
eog kTd0T9g.png 
convert kTd0T9g.png -fill white -draw "rectangle 0,0 300,35" kTd0T9g.png 
eog kTd0T9g.png 
ync
sync
ls -anl
cd Downloads/
wget https://www.torproject.org/dist/torbrowser/8.5.4/tor-browser-linux64-8.5.4_en-US.tar.xz
wget https://github.com/geohot/qira/archive/v1.3.zip
unzip v1.3.zip 
ls
tar xvf tor-browser-linux64-8.5.4_en-US.tar.xz

編集された画像を見てみると、コマンド履歴の通り左上が白く塗りつぶされています。 画像を直接編集しているので復元できそうにありません。

f:id:tsalvia:20190721115838p:plain

さらに調査を進めると、ファイルのメタデータであるファイル拡張属性(user.xdg.origin.url)にURL(https://i.imgur.com/kTd0T9g.png)が残っていることに気が付きました。

f:id:tsalvia:20190721115735p:plain

実際にアクセスしてみると、白く塗りつぶされる前の画像が出てきました。 左上にフラグも確認できます。

f:id:tsalvia:20190722005044p:plain

FLAG

cybrics{A11W4Y5_D1G_D33P3R}

QShell (Cyber, Easy, 50 pts)

問題

QShell is running on
nc spbctf.ppctf.net 37338

Grab the flag

解答例

nc spbctf.ppctf.net 37338 で接続してみると、以下のようにQRコードが表示されます。

f:id:tsalvia:20190722005716p:plain

QRコードを読み取ると、プロンプトが出てきました。

sh-5.0$

. を入力してみると、「tile cannot extend outside image」とエラーが出てきました。 エラーの文章から推測すると、こちらも同じ形式でコマンドを送信しなけらばならないようです。 また、 . 以外の文字列だと反応しないことから、 . がコマンドの終端となっているようです。

QRコードをいちいち作るのは面倒くさいので、下記のサイトを参考し、qrencodeを使ってQRコードを生成させました。

orebibou.com

qrencodeのインストールは、下記コマンドでできます。

$ sudo apt-get install qrencode

以下のコマンドで、自由に文字列をQRコードに変換することができます。

$ qrencode -t ASCIIi "ls"
##########################################################
##########################################################
##########################################################
##########################################################
########              ######  ##  ##              ########
########  ##########  ##  ##  ##  ##  ##########  ########
########  ##      ##  ##  ##    ####  ##      ##  ########
########  ##      ##  ##########  ##  ##      ##  ########
########  ##      ##  ##          ##  ##      ##  ########
########  ##########  ##      ######  ##########  ########
########              ##  ##  ##  ##              ########
########################  ################################
########    ##  ####    ####      ##      ##    ##########
########          ######  ####  ##  ##  ##  ####  ########
############  ######  ####  ##  ####        ####  ########
##############    ####  ####  ##          ####    ########
##########  ####  ##            ##      ####    ##########
########################    ######    ##  ##  ##  ########
########              ##          ##  ##    ##  ##########
########  ##########  ####  ########  ######      ########
########  ##      ##  ####  ####      ######      ########
########  ##      ##  ##  ##  ####      ######    ########
########  ##      ##  ######    ##      ##    ##  ########
########  ##########  ##  ####    ####  ##  ##############
########              ##      ####  ##  ####    ##########
##########################################################
##########################################################
##########################################################
##########################################################

qrencodeの実行結果をpythonスクリプトに貼り付けて、QR送信用のプログラムを作成しました。 まずは、lsコマンド送信用のプログラムを作成しました。

from pwn import *

qr_ls = ["##########################################################", \
         "##########################################################", \
         "##########################################################", \
         "##########################################################", \
         "########              ######  ##  ##              ########", \
         "########  ##########  ##  ##  ##  ##  ##########  ########", \
         "########  ##      ##  ##  ##    ####  ##      ##  ########", \
         "########  ##      ##  ##########  ##  ##      ##  ########", \
         "########  ##      ##  ##          ##  ##      ##  ########", \
         "########  ##########  ##      ######  ##########  ########", \
         "########              ##  ##  ##  ##              ########", \
         "########################  ################################", \
         "########    ##  ####    ####      ##      ##    ##########", \
         "########          ######  ####  ##  ##  ##  ####  ########", \
         "############  ######  ####  ##  ####        ####  ########", \
         "##############    ####  ####  ##          ####    ########", \
         "##########  ####  ##            ##      ####    ##########", \
         "########################    ######    ##  ##  ##  ########", \
         "########              ##          ##  ##    ##  ##########", \
         "########  ##########  ####  ########  ######      ########", \
         "########  ##      ##  ####  ####      ######      ########", \
         "########  ##      ##  ##  ##  ####      ######    ########", \
         "########  ##      ##  ######    ##      ##    ##  ########", \
         "########  ##########  ##  ####    ####  ##  ##############", \
         "########              ##      ####  ##  ####    ##########", \
         "##########################################################", \
         "##########################################################", \
         "##########################################################", \
         "##########################################################"]

context(arch='amd64', os='linux')
p = remote('spbctf.ppctf.net', 37338)
ret = p.readuntil('.')
ret = p.readline().strip()

white = "\xe2\x96\x88"
black = "\x20"
for i in range(len(qr_ls)):
    qr = qr_ls[i].replace("##", white).replace("  ", black)
    p.sendline(qr)
p.sendline(".")
p.interactive()

上記のプログラムを実行すると、新たにQRコードが返ってきました。 少しバグっているようですが、問題なく読み取れました。

f:id:tsalvia:20190721125114p:plain

QRコードを読み取ると、以下の通りになっていました。

1.py
2.py
docker-compose.yml
Dockerfile
flag.txt
log.txt
qweqwe.png
rex.txt
runserver.sh
run.sh

次に、「cat flag.txt」という文字列をQRコードに変換し、送信用のプログラムを調整しました。

from pwn import *

qr_cat_flag = ["##########################################################", \
               "##########################################################", \
               "##########################################################", \
               "##########################################################", \
               "########              ####  ####  ##              ########", \
               "########  ##########  ##  ####  ####  ##########  ########", \
               "########  ##      ##  ####  ########  ##      ##  ########", \
               "########  ##      ##  ##  ####  ####  ##      ##  ########", \
               "########  ##      ##  ######      ##  ##      ##  ########", \
               "########  ##########  ##      ##  ##  ##########  ########", \
               "########              ##  ##  ##  ##              ########", \
               "############################      ########################", \
               "########          ##        ####    ##  ##  ##  ##########", \
               "########  ##    ########    ####    ##    ##  ##  ########", \
               "##########  ####  ##    ##      ##    ####      ##########", \
               "########  ##    ##  ####  ##  ############        ########", \
               "########  ####  ####  ######    ####    ##  ##############", \
               "########################  ######              ##  ########", \
               "########              ##  ######  ##  ######    ##########", \
               "########  ##########  ####  ####    ##  ####  ############", \
               "########  ##      ##  ##  ##          ##  ####    ########", \
               "########  ##      ##  ##  ##    ##  ##    ##  ############", \
               "########  ##      ##  ##    ##    ##########  ############", \
               "########  ##########  ##  ######    ##        ############", \
               "########              ##  ##  ####    ########  ##########", \
               "##########################################################", \
               "##########################################################", \
               "##########################################################", \
               "##########################################################"]

context(arch='amd64', os='linux')
p = remote('spbctf.ppctf.net', 37338)
ret = p.readuntil('.')
ret = p.readline().strip()

white = "\xe2\x96\x88"
black= "\x20"
for i in range(len(qr_ls)):
    qr = qr_cat_flag[i].replace("##", white).replace("  ", black)
    p.sendline(qr)
p.sendline(".")
p.interactive()

上記のプログラムを実行すると、またQRコードが返ってきました。

f:id:tsalvia:20190721125027p:plain

QRコードを読み取ると、フラグとなっていました。

FLAG

cybrics{QR_IS_MY_LOVE}

Bitkoff Bank (Web, Easy, 50 pts)

問題

Need more money! Need the flag!
http://45.77.201.191/index.php
Mirror: http://95.179.148.72:8083/index.php

解答例

リンクにアクセスすると、ユーザ名とパスワードの登録画面に飛ばされます。

f:id:tsalvia:20190722014224p:plain

登録するとメニュー画面に遷移します。

f:id:tsalvia:20190722014304p:plain

「MINE BTC」ボタンを押すと、自分のBTCが少しだけ増えます。

f:id:tsalvia:20190722014408p:plain

「change」ボタンでBTCとUSDの交換をすることができます。 最終的に、$1をどうにかして集めて、フラグを購入することが目標となるようです。

色々調査を進めてみると、USDとBTCの何度かの交換で、少しづつ増えていくことに気が付きました。

そこで、nodejsで以下の動作をするプログラムを作成しました。

  1. BTC(0.00001)をUSDに変換する
  2. USD(0.1)をBTCに変換する。
  3. USDが1以上になるまで、上記を繰り返す。
'use strict';
const request = require('request');

function send(error, response, body) {
    if (error) {
        console.log(error);
        return;
    }

    const usd = body.split('<b>')[1].split('</b>')[0];
    const btc = body.split('<b>')[2].split('</b>')[0];

    console.log('usd: ' + usd + '\tbtc: ' + btc);

    if (usd > 1)
        process.exit(0);
}

const options = {
    url: 'http://95.179.148.72:8083/index.php',
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Cookie': 'name=bitcoin_user; password=bitcoin_pass'
    },
    form: { 'from_currency': '', 'to_currency': '', 'amount': 0 }
}

setInterval(() => {
    options.form = { 'from_currency': 'btc', 'to_currency': 'usd', 'amount': 0.00001 };
    request(options, send);
    options.form = { 'from_currency': 'usd', 'to_currency': 'btc', 'amount': 0.1 };
    request(options, send);
}, 100);

上記のプログラムを実行すると、下記の通りになります。

$ node bitcoin.js

省略

usd: 0.784832   btc: 0.000024
usd: 0.684832   btc: 0.0000332
usd: 0.793888   btc: 0.0000232
usd: 0.793888   btc: 0.0000232
usd: 0.902944   btc: 0.0000132
usd: 0.902944   btc: 0.0000132
usd: 1.012      btc: 0.0000032

$1を越えたので、「buy flag ($1)」で購入すると、フラグが表示されました。

f:id:tsalvia:20190721200608p:plain

FLAG

cybrics{50_57R4n93_pR3c1510n}