ほぼ過去のpicoCTFからの流用でしたが、当時の問題を解いたことはなかったのでまあ..
結構解いたんですけど開催期間中にwriteupを書ききる気力がなかったので途中までです。
237(10進法)を2進数に変換できますか?
変換するだけ
$ python3 >>> bin(237) '0b11101101'
bDNhcm5fdGgzX3IwcDM1XzgwM2MwMTZk は何を意味しますか?これには基数(base)が関係していそうです。
base64でデコード
$ python3 >>> import base64 >>> base64.b64decode("bDNhcm5fdGgzX3IwcDM1XzgwM2MwMTZk") b'l3arn_th3_r0p35_803c016d'
ファイルの中にあるフラグを見つけられますか?手動で調べるのは本当に面倒ですが、どうやらもっと良い方法があるみたいです。
grepコマンド
$ grep "CTF" file CognitiveCTF{grep_is_good_to_find_things_83b1c474}
実行せずにこのファイルにあるフラグを見つけることができますか?
stringsコマンド
$ strings strings | grep "CTF" YzOejwCTF3GVzbdb8PkOKp1cKvAwEUvRSOLLm1yFFETiT CognitiveCTF{5tRIng5_1T_ce414d29} 7Oqu9T7p8SAoQcOcQVHM46k1xpt1M6Iu2ag4dw1OFCTFRbv6
netcat (nc) を使うことはかなり重要になってきます。 shell1.production.cognitivectf.comのポート番号48436に接続してフラグを獲得できますか?
ncコマンド
$ nc shell1.production.cognitivectf.com 48436 You're on your way to becoming the net cat master CognitiveCTF{nEtCat_Mast3ry_df97c511}
真の1337になるためには、16進数や2進数などの異なるデータエンコーディングを理解しなければなりません。このプログラムからフラグを取得して、1337になりつつあることを証明できますか?nc shell1.production.cognitivectf.com 48437で接続してください。
nc shell1.production.cognitivectf.com 48437
で接続すると2進数,8進数,16進数に変換された文字列が与えられるので文字列に戻して入力します。
$ nc shell1.production.cognitivectf.com 48437 Let us see how data is stored container Please give me the 01100011 01101111 01101110 01110100 01100001 01101001 01101110 01100101 01110010 as a word. ... you have 45 seconds..... Input: Please give me the 163 154 165 144 147 145 as a word. Input: Please give me the 737472656574 as a word. Input:
45秒あるようなので手動でも間に合いますが..
import re from pwn import * def t(l,b): text = "" for c in l: text += chr(int(c,b)) return text p = remote('shell1.production.cognitivectf.com', 48437) ret = p.recvuntil('Input:\n') str = re.search(r'Please give me the (.*) as a word.',ret.decode('utf-8')) p.sendline(t(str.group(1).split(" "),2)) ret = p.recvuntil('Input:\n') str = re.search(r'Please give me the (.*) as a word.',ret.decode('utf-8')) p.sendline(t(str.group(1).split(" "),8)) ret = p.recvuntil('Input:\n') str = re.search(r'Please give me the (.*) as a word.',ret.decode('utf-8')) l = re.split('(..)',str.group(1))[1::2] p.sendline(t(l,16)) ret = p.recv() print(ret)
実行結果
$ python3 solve.py [+] Opening connection to shell1.production.cognitivectf.com on port 48437: Done b"You've beaten the challenge\n\nFlag: CognitiveCTF{learning_about_converting_values_c5ff9833}\n"
ファイルの外で処理データを扱わなければならないときもあります。プログラムの出力を保持したまま、フラグを検索する方法を見つけてください。shell1.production.cognitivectf.com 48465に接続してください。
ncコマンドで接続すると大量に出力されるのでgrepします。
$ nc shell1.production.cognitivectf.com 48465 | grep "CTF" CognitiveCTF{digital_plumb3r_6159603d}
2つの画像の違いを見つけられますか? このファイルはshell1.production.cognitivectf.comの以下ディレクトリに置かれています。
cmpコマンドで2つのファイルの差分を出力できます
$ cmp -bl cattos.jpg kitters.jpg | awk '{print $3}' | tr -d "\n" CognitiveCTF{th3yr3_a5_d1ff3r3nt_4s_bu77r_4nd_j311y_28d5bdce30002966e7d80509641e0f09}
このファイルを隠すためにスーパーシークレットなマインド・トリックを使いました。おそらく何かが /problems/where-is-the-file_2_2ff4ede8b2b7115cabad9aa409c5c7a9 にあるでしょう。
隠しファイルがありました
$ ls -al total 36 drwxr-xr-x 2 root root 4096 Jul 29 13:27 . drwxr-x--x 277 root root 24576 Aug 6 18:08 .. -rw-rw-r-- 1 hacksports hacksports 44 Jul 29 13:27 .cant_see_me $ cat .cant_see_me CognitiveCTF{w3ll_that_d1dnt_w0RK_86e54c22}
flag shopに物が売られていますが、flagは買えますか? nc shell1.production.cognitivectf.com 15583で接続してください。
ソースコード
#include#include int main() { setbuf(stdout, NULL); int con; con = 0; int account_balance = 1100; while(con == 0){ printf("Welcome to the flag exchange\n"); printf("We sell flags\n"); printf("\n1. Check Account Balance\n"); printf("\n2. Buy Flags\n"); printf("\n3. Exit\n"); int menu; printf("\n Enter a menu selection\n"); fflush(stdin); scanf("%d", &menu); if(menu == 1){ printf("\n\n\n Balance: %d \n\n\n", account_balance); } else if(menu == 2){ printf("Currently for sale\n"); printf("1. Defintely not the flag Flag\n"); printf("2. 1337 Flag\n"); int auction_choice; fflush(stdin); scanf("%d", &auction_choice); if(auction_choice == 1){ printf("These knockoff Flags cost 900 each, enter desired quantity\n"); int number_flags = 0; fflush(stdin); scanf("%d", &number_flags); if(number_flags > 0){ int total_cost = 0; total_cost = 900*number_flags; printf("\nThe final cost is: %d\n", total_cost); if(total_cost <= account_balance){ account_balance = account_balance - total_cost; printf("\nYour current balance after transaction: %d\n\n", account_balance); } else{ printf("Not enough funds to complete purchase\n"); } } } else if(auction_choice == 2){ printf("1337 flags cost 100000 dollars, and we only have 1 in stock\n"); printf("Enter 1 to buy one"); int bid = 0; fflush(stdin); scanf("%d", &bid); if(bid == 1){ if(account_balance > 100000){ FILE *f = fopen("flag.txt", "r"); if(f == NULL){ printf("flag not found: please run this on the server\n"); exit(0); } char buf[64]; fgets(buf, 63, f); printf("YOUR FLAG IS: %s\n", buf); } else{ printf("\nNot enough funds for transaction\n\n\n"); }} } } else{ con = 1; } } return 0; }
ソースコードを確認するとaccount_balance > 100000
でフラグが表示されることがわかります。
account_balance
の初期値は1,100ですが、42行目で値を更新する処理があります。
入力された値number_flags
に900をかけた値total_cost
をaccount_balance
から引いています。
total_cost
が負の値であればaccount_balance
を100000以上にできます。
整数オーバーフローを起こせば良さそうです。
int型の最大値は2147483647らしいので、total_cost = 900*number_flags
がこれを超えるような数値になるようにnumber_flags
の値を入力します。
2500000くらいでいいでしょう。
$ nc shell1.production.cognitivectf.com 15583 Welcome to the flag exchange We sell flags 1. Check Account Balance 2. Buy Flags 3. Exit Enter a menu selection 2 Currently for sale 1. Defintely not the flag Flag 2. 1337 Flag 1 These knockoff Flags cost 900 each, enter desired quantity 2500000 The final cost is: -2044967296 Your current balance after transaction: 2044968396 Welcome to the flag exchange We sell flags 1. Check Account Balance 2. Buy Flags 3. Exit Enter a menu selection 1 Balance: 2044968396 Welcome to the flag exchange We sell flags 1. Check Account Balance 2. Buy Flags 3. Exit Enter a menu selection 2 Currently for sale 1. Defintely not the flag Flag 2. 1337 Flag 2 1337 flags cost 100000 dollars, and we only have 1 in stock Enter 1 to buy one 1 YOUR FLAG IS: CognitiveCTF{m0n3y_bag5_787b7396} Welcome to the flag exchange We sell flags 1. Check Account Balance 2. Buy Flags 3. Exit Enter a menu selection 3
私はあなたに歌を書きました。フラグはCognitiveCTF{}の形式にしてください。
lyrics.txt
Cognitive's a CTFFFFFFF my mind is waitin It's waitin Put my mind of Cognitive into This my flag is not found put This into my flag put my flag into Cognitive shout Cognitive shout Cognitive shout Cognitive My song's something put Cognitive into This Knock This down, down, down put This into CTF shout CTF my lyric is nothing Put This without my song into my lyric Knock my lyric down, down, down shout my lyric Put my lyric into This Put my song with This into my lyric Knock my lyric down shout my lyric Build my lyric up, up ,up shout my lyric shout Cognitive shout It Cognitive CTF is fun security is important Fun is fun Put security with fun into Cognitive CTF Build Fun up shout fun times Cognitive CTF put fun times Cognitive CTF into my song build it up shout it shout it build it up, up shout it shout Cognitive The chord is a directory The directory is blank put the chord of the directory into Cognitive shout Cognitive The shell is flower channel is a emotion put the shell of channel into rainbow knock it down shout it The future is freedom put the future of the future into lyric build it up, up build it up, up shout it knock it down, down, down, down freedom is so put lyric of freedom into the way knock it down shout it build lyric up shout lyric
歌詞みたいですが、ヒントからRockstarというプログラミング言語らしいです。
kaiser-rubyというツールで実行や.rbファイルへの変換が行えるらしいです。
実行すると10進数が表示されるので文字列に変換します。
$ gem install kaiser-ruby $ kaiser-ruby execute lyrics.txt 114 114 114 111 99 107 110 114 110 48 49 49 51 114 95 101 53 97 50 $ kaiser-ruby execute lyrics.txt | awk '{printf "%c",$0}'; echo rrrocknrn0113r_e5a2
私はあなたに別の歌を書きました。フラグはCognitiveCTF{}の形式にしてください。
上の問題と同じくRockstarという言語の問題です。
ただし今回はエラーで実行できません。
kaiser-ruby transpile
でrubyのコードへ変換ができるため、変換して読んでみました。
$ kaiser-ruby execute lyrics2.txt (eval): (eval):39: Can't escape from eval with break (SyntaxError) $ $ kaiser-ruby transpile lyrics.txt > output.rb
output.rb
@rocknroll = true @silence = false @a_guitar = 19 @tommy = 44 @music = 160 print '> ' __input = $stdin.gets.chomp @the_music = Float(__input) rescue __input if @the_music == @a_guitar puts ("Keep on rocking!").to_s print '> ' __input = $stdin.gets.chomp @the_rhythm = Float(__input) rescue __input if @the_rhythm - @music == nil @tommy = 66 puts (@tommy).to_s @music = 79 @jamming = 78 puts (@music).to_s puts (@jamming).to_s @tommy = 74 puts (@tommy).to_s @tommy = 79 puts (@tommy).to_s @rock = 86 puts (@rock).to_s @tommy = 73 puts (@tommy).to_s @music = 95 puts (@music).to_s @performance = 57 puts (@performance).to_s @tale = 56 puts (@tale).to_s @poetry = 65 puts (@poetry).to_s @patience = 67 puts (@patience).to_s break puts ("Bring on the rock!").to_s else break end end
output.rbは入力を受け付けて正しい入力だった場合に出力が発生するようです。
正しい入力をした場合に出力される箇所は2回目のif文内と読めるので、ここだけ残してあとは消してしまえば問題なく実行できます。
実行すると上の問題と同じように数字が出てきます。
$ cat output.rb @tommy = 66 puts (@tommy).to_s @music = 79 @jamming = 78 puts (@music).to_s puts (@jamming).to_s @tommy = 74 puts (@tommy).to_s @tommy = 79 puts (@tommy).to_s @rock = 86 puts (@rock).to_s @tommy = 73 puts (@tommy).to_s @music = 95 puts (@music).to_s @performance = 57 puts (@performance).to_s @tale = 56 puts (@tale).to_s @poetry = 65 puts (@poetry).to_s @patience = 67 puts (@patience).to_s $ $ ruby output.rb 66 79 78 74 79 86 73 95 57 56 65 67 $ $ ruby output.rb | awk '{printf"%c",$0}'; echo BONJOVI_98AC
この庭園には見た目以上のものが含まれています。
バイナリエディタで開くと最後に"Here is a flag ..."と書いてあります。
この画像からフラグを探し出してください。
exif情報の中にあります。
$ exiftool pico_img.png ... Artist : CognitiveCTF{s0_m3ta_71d23127} ...
この建物の中に何かあります。フラグを復元できますか?
こちらのサイトを利用すればすぐ。
詳しくはわからないけど LSB steganography という方法で、RGB成分を表すビット列のうちそれぞれ最下位ビットを使ってデータを隠すらしいです。
そのぐらいの変化では人間の目で色の違いを識別できないそうで。
このパケットキャプチャを手に入れました。フラグを復元してください。
wiresharkの "分析"->"追跡"->"UDPストリーム" で次々みてくと6番目に出てきます。
なんとなくUDPのパケット多いし..でやりましたがこういうやり方でいいんでしょうか
この.tarファイルは何度もtarがかけられています。
与えられた1000.tarファイルを開くと999.tarが現れます。
1.tarになるまで展開すれば良さそうです。
手動では面倒なのでシェルスクリプトを書きます。
#!/bin/bash tmp="" tar=".tar" for ((i=1000;i>=1;i--)) do tmp=$i$tar tar -xvf $tmp rm $tmp done
実行するとflag.pngが得られました。
$ ./solve.sh x 999.tar x filler.txt x 998.tar x filler.txt .... x 2.tar x filler.txt x 1.tar x filler.txt x flag.png
バイナリと画像を復元しました。何ができるか見極めてください。どこかにフラグがあるはずです。
実行ファイルをghidraで解析します。
main関数のデコンパイル結果を見るとざっくりと以下のようなことをしています。
・original.bmpの先頭2000バイトを読み込みそのままencoded.bmpに書き込む。
・flag.txtから0x32(50)バイトを読み込み暗号化処理を施してencoded.bmpに書き込む。
・original.bmpの先頭から2050バイト目以降を読み込みそのままencoded.bmpに書き込む。
肝心の暗号化処理のところはヒントにもあるLSBエンコーディングをしているようです。
該当部分のデコンパイル結果は以下です。
original.bmpの各バイトの最下位ビットをflag.txtのビット(ただし-5してから渡している)に置き換えている感じですかね。
//main関数内 local_6c = 0; while (local_6c < 0x32) { local_68 = 0; while (local_68 < 8) { uVar2 = codedChar(local_68,flag_byte[local_6c] - 5,original_image_byte); local_75 = (byte)uVar2; fputc((uint)local_75,encoded_image); fread(&original_image_byte,1,1,original_image); local_68 = local_68 + 1; } local_6c = local_6c + 1; } //codedChar関数 ulong codedChar(int counter,byte flag_byte,byte original_byte) { byte local_9; local_9 = flag_byte; if (counter != 0) { local_9 = (byte)((int)(uint)flag_byte >> ((byte)counter & 0x1f)); } return (ulong)((uint)original_byte & 0xfffffffe | (uint)local_9 & 1); }
逆の処理を書きます。
flag = "" with open("encoded.bmp", "rb") as f: f.seek(2000) for i in range(50): b = "" for _ in range(8): data = f.read(1) b = str(int.from_bytes(data, 'big') & 1) + b flag += chr(int(b,2)+5) print(flag)
実行結果
$ python3 solve.py CognitiveCTF{n3xt_0n3_231bd73666dc755894efc2886e4}
バイナリとテキストファイルを復元しました。フラグを手に入れることができますか?
実行ファイルをghidraで見るとフラグの文字列を1文字ずつ置き換える処理をしていることがわかります。
フラグのCognitiveCTF{と}の部分はそのまま出力し、中身の部分を置き換えています。
該当する場所のデコンパイル結果は以下です。
local_50 = 0xd; while ((int)local_50 < 0x1c) { if ((local_50 & 1) == 0) { local_55 = flag[(int)local_50] + '\x05'; } else { local_55 = flag[(int)local_50] + -2; } fputc((int)local_55,output_file); local_50 = local_50 + 1; }
4行目のデコンパイル結果が若干変ですがアセンブリを見ればただの+5
であることがわかります。
したがってこの処理の逆の処理を書きます。
enc = "CognitiveCTF{p8t8px19_=`6052}" i = 0 while i < 0xd: print(enc[i],end="") i += 1 i = 0xd while i < 0x1c: if i & 1 == 0: a = ord(enc[i]) - 5 else: a = ord(enc[i]) + 2 print(chr(a),end="") i += 1 print("}")
実行結果
$ python3 solve.py CognitiveCTF{r3v3rs34a8b1204}