ほぼ過去のpicoCTFからの流用でしたが、当時の問題を解いたことはなかったのでまあ..
結構解いたんですけど開催期間中にwriteupを書ききる気力がなかったので途中までです。



General Skills

2Warm (50)

237(10進法)を2進数に変換できますか?

変換するだけ

$ python3
>>> bin(237)
'0b11101101'
          

FLAG: CognitiveCTF{11101101}


Bases (100)

bDNhcm5fdGgzX3IwcDM1XzgwM2MwMTZk は何を意味しますか?これには基数(base)が関係していそうです。

base64でデコード

$ python3
>>> import base64
>>> base64.b64decode("bDNhcm5fdGgzX3IwcDM1XzgwM2MwMTZk")
b'l3arn_th3_r0p35_803c016d'
          

FLAG: CognitiveCTF{l3arn_th3_r0p35_803c016d}


First Grep (100)

ファイルの中にあるフラグを見つけられますか?手動で調べるのは本当に面倒ですが、どうやらもっと良い方法があるみたいです。

grepコマンド

$ grep "CTF" file
CognitiveCTF{grep_is_good_to_find_things_83b1c474}
          

FLAG: CognitiveCTF{grep_is_good_to_find_things_83b1c474}


strings it (100)

実行せずにこのファイルにあるフラグを見つけることができますか?

stringsコマンド

$ strings strings | grep "CTF"
YzOejwCTF3GVzbdb8PkOKp1cKvAwEUvRSOLLm1yFFETiT
CognitiveCTF{5tRIng5_1T_ce414d29}
7Oqu9T7p8SAoQcOcQVHM46k1xpt1M6Iu2ag4dw1OFCTFRbv6
          

FLAG: CognitiveCTF{5tRIng5_1T_ce414d29}


what's a net cat? (100)

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}
          

FLAG: CognitiveCTF{nEtCat_Mast3ry_df97c511}


Based (200)

真の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"
          

FLAG: CognitiveCTF{learning_about_converting_values_c5ff9833}


plumbing (200)

ファイルの外で処理データを扱わなければならないときもあります。プログラムの出力を保持したまま、フラグを検索する方法を見つけてください。shell1.production.cognitivectf.com 48465に接続してください。

ncコマンドで接続すると大量に出力されるのでgrepします。

$ nc shell1.production.cognitivectf.com 48465 | grep "CTF"
CognitiveCTF{digital_plumb3r_6159603d}
          

FLAG: CognitiveCTF{digital_plumb3r_6159603d}


whats-the-difference (200)

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}
          

FLAG: CognitiveCTF{th3yr3_a5_d1ff3r3nt_4s_bu77r_4nd_j311y_28d5bdce30002966e7d80509641e0f09}


where-is-the-file (200)

このファイルを隠すためにスーパーシークレットなマインド・トリックを使いました。おそらく何かが /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: CognitiveCTF{w3ll_that_d1dnt_w0RK_86e54c22}


flag_shop (300)

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_costaccount_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
          

FLAG: CognitiveCTF{m0n3y_bag5_787b7396}


mus1c (300)

私はあなたに歌を書きました。フラグは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
          

FLAG: CognitiveCTF{rrrocknrn0113r_e5a2}


1_wanna_b3_a_r0ck5tar (350)

私はあなたに別の歌を書きました。フラグは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
          

FLAG: CognitiveCTF{BONJOVI_98AC}


Forensics

Glory of the Garden (50)

この庭園には見た目以上のものが含まれています。

バイナリエディタで開くと最後に"Here is a flag ..."と書いてあります。

FLAG: CognitiveCTF{more_than_m33ts_the_3y3_392ff9d1}


So Meta (150)

この画像からフラグを探し出してください。

exif情報の中にあります。

$ exiftool pico_img.png
...
Artist                          : CognitiveCTF{s0_m3ta_71d23127}
...
          

FLAG: CognitiveCTF{s0_m3ta_71d23127}


What Lies Within (150)

この建物の中に何かあります。フラグを復元できますか?

こちらのサイトを利用すればすぐ。
詳しくはわからないけど LSB steganography という方法で、RGB成分を表すビット列のうちそれぞれ最下位ビットを使ってデータを隠すらしいです。
そのぐらいの変化では人間の目で色の違いを識別できないそうで。

FLAG: CognitiveCTF{h1d1ng_1n_th3_b1t5_ed5ae361}


shark on wire 1 (150)

このパケットキャプチャを手に入れました。フラグを復元してください。

wiresharkの "分析"->"追跡"->"UDPストリーム" で次々みてくと6番目に出てきます。
なんとなくUDPのパケット多いし..でやりましたがこういうやり方でいいんでしょうか

FLAG: CognitiveCTF{StaT31355C0NN}


like1000 (250)

この.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
          

FLAG: CognitiveCTF{l0t5_0f_TAR5}


Investigative Reversing 2 (350)

バイナリと画像を復元しました。何ができるか見極めてください。どこかにフラグがあるはずです。

実行ファイルを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}
           

FLAG: CognitiveCTF{n3xt_0n3_231bd73666dc755894efc2886e4}


reverse_cipher (300)

バイナリとテキストファイルを復元しました。フラグを手に入れることができますか?

実行ファイルを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}
          

FLAG: CognitiveCTF{r3v3rs34a8b1204}