LoginSignup
1
0

More than 1 year has passed since last update.

picoCTF 2022 Write Up

Posted at

2022年3月に開催されたpicoCTF 2022に参加しました。
昨年は個人で出場していましたが、今年はTeam TaruTaruとして出場しています。TaruTaruは9200点で595位でした。もっと精進せねば...

自分が解いた問題についてWrite Upを記しました。チームメイトと結構重複はしています。
実際に使用したコードはGitHubに置いてあります。可読性は皆無なので、気になるところがあったら気軽に尋ねていただけると幸いです。

Binary Exploitation

buffer overflow 0 (100pts)

配布されたvuln.c には

vuln.c
int main(int argc, char **argv){
  //略
  signal(SIGSEGV, sigsegv_handler);
  //略
}

という関数が存在し、sigsegv_handlerの内容は

vuln.c
void sigsegv_handler(int sig) {
  printf("%s\n", flag);
  fflush(stdout);
  exit(1);
}

となっています。
したがって、実行中にSegmentation Faultを発生させればsigsegv_handlerが実行されflagが出力されるようです。
main関数内で実行されるvuln関数の中身は

vuln.c
void vuln(char *input){
  char buf2[16];
  strcpy(buf2, input);
}

とinputが16文字未満であることを想定していますが、このinputはmain関数内で

vuln.c
int main(int argc, char **argv){
  //略
  char buf1[100];
  gets(buf1); 
  vuln(buf1);
  //略
}

の形で最大100文字(Null文字含む)まで渡すことが可能です。
したがって、プログラムに大量の文字を送りつければSegmentation Faultが発生してflagが表示されます。

CVE-XXXX-XXXX (100pts)

「Windows Print Spooler Service CVE」でGoogle検索をすると、IPAのウェブサイトが最初にヒットします。
CVE番号はCVE-2021-34527です。
この問題のジャンルはBinary Explotionであってますか???

RPS (200pts)

サーバーとじゃんけんをして、5連勝したらフラグが入手できるようです。勝敗の判定は

game-redacted.c
char* loses[3] = {"paper", "scissors", "rock"};
//中略
if (strstr(player_turn, loses[computer_turn])) {
  puts("You win! Play again?");
  return true;
} else {
  puts("Seems like you didn't win this time. Play again?");
  return false;
}

で実装されています。
palyer_turnはユーザー入力の文字列で、computer_turnは0~2の乱数です。
strstr(const char *s1, const char *s2)は文字列s1が文字列s2を 含む ときその開始位置を返すので、入力としてrockscissorspaperを入力すると相手の手がなんであったとしても勝利として判定されます。
これを5回繰り返せばフラグが表示されます。

Cryptography

basic-mod1 (100pts)

message.txt に記載された数のmod37を計算し、その値が0-25であればA-Zに、26-35であれば0-9に、36ならば_に置き換えればいいようです。
スクリプトを書いて実行します。

solve.py
print("picoCTF{", end="")
with open("message.txt", "r") as fp:
	for n in fp.read().strip().split(" "):
		n = int(n)
		n = n%37
		if n==36:
			print("_", end="")
		elif n>=26:
			print(n-26, end="")
		else:
			print(chr(ord('a')+n), end="")

print("}")

basic-mod2 (100pts)

message.txt に記載された数のmod41での逆元を計算し、その値が1-26であればA-Zに、27-36であれば0-9に、37であれば_に置き換えます。

x*y \equiv 1\ (mod\ n)

が成立するとき、yをxの(mod nにおける)逆元と呼びます。逆元を効率的に計算するアルゴリズムは存在しますが、mod 41なら全探索で計算してしまえばOKです。

solve.py
def get_inv(i):
    for j in range(1, 41):
        if (i*j)%41==1:
            return j
    return 999


print("picoCTF{", end="")
with open("message.txt", "r") as fp:
    for n in fp.read().strip().split(" "):
        n = int(n)
        n = get_inv(n)
        if n==37:
            print("_", end="")
        elif n>=27:
            print(n-27, end="")
        else:
            print(chr(ord('a')+n-1), end="")

print("}")

rail-fence (100pts)

CyberChefでデコードできます。

substitution0 (100pts)

単一換字式暗号です。
文章の最後にある
Pmj tuec xg: fxslSPT{...}
は明らかにThe flag is: picoCTF{...}を置き換えたものでしょう。
フラグは大文字で構成されていますが、それ以外の文章は小文字ばかりであるので、大文字と小文字で置換が異なる場合は大文字の置換先の特定が不可能そうに見えるので、多分大文字と小文字の置換先は同じである...気がします。というかそうであってほしいです。
あとはそれっぽい単語を見つけて1文字1文字置換先を特定していくだけです。

substitution1 (100pts)

同じく単一換字式暗号です。
eqs coxa dj: zdifIEC{...}the flag is: picoCTF{...}を置き換えたものでしょう。 残りは気合で置き換えていきます。

substitution2 (100pts)

暗号文を見た瞬間にブチギレそうになりますが、ぐっとこらえてコーヒーを一杯飲みます。これまでの流れからして単一換字式暗号でしょうし、gvjnxqceupemzMGN{...}theflagispicoCTF{...}を置換したものなのでしょう。
あとは気合です。気合なのです。
頻度解析なんてのは知らない子なのです。

Vigenere (100pts)

CyberChefでデコードできました。

diffie-hellman (200pts)

AliceとBobは、ディフィーヘルマン鍵交換で秘密の値を共有し、その値を用いてCaesar暗号で平文を暗号化したそうです。ディフィーヘルマン鍵交換に使われた値が軒並み小さいので、簡単に復号ができそうですが、そんな小難しいことを考えなくても最後がCaesar暗号ですので力業で複合すればOKです。
平文として意味を持ちそうだったのがC4354R_C1PH3R_15_4_817_0U7D473D_84AA1DA8であったので、これをpicoCTF{}でwrapすればフラグになりました。

Forensics

Enhance! (100pts)

配布されたsvgファイルをテキストエディタで開くと

drawing.flag.svg
    <text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:0.00352781px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
       x="107.43014"
       y="132.08501"
       id="text3723"><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.08501"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3748">p </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.08942"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3754">i </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.09383"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3756">c </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.09824"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3758">o </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.10265"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3760">C </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.10706"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3762">T </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.11147"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3764">F { 3 n h 4 n </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.11588"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3752">c 3 d _ a a b 7 2 9 d d }</tspan></text>

flagが記入されているのが見えます。

File types (100pts)

配布されたファイルの拡張子は.pdfですが、fileコマンドでファイルタイプを確認すると

% file Flag.pdf 
Flag.pdf: shell archive text

と表示されます。これは、

% sh Flag.pdf

とすることで解凍できます。
このように、fileコマンドでファイルタイプを調べる→対応したアプリケーションで解凍する を繰り返すと最終的に

7069636f4354467b66316c656e406d335f6d406e3170756c407431306e5f
6630725f3062326375723137795f33633739633562617d0a

という中身のテキストファイルが得られます。どうやらHexのようなので、CyberChefでデコードしたらフラグが表示されました。

Lookey here (100pts)

% cat anthem.flag.txt | grep pico
      we think that the men of picoCTF{gr3p_15_@w3s0m3_4c479940}

Packets Primer (100pts)

配布されたpcapファイルをWiresharkで読み込むとフラグが表示されました。
スクリーンショット 2022-03-18 2.03.16.png

Redaction gone wrong (100pts)

書類の一部が黒塗りにされていますが、ドラッグしてコピーしたあとテキストエディタにペーストすれば読むことができます。

Sleuthkit Intro (100pts)

mmlsを実行すると

% mmls disk.img
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000   0000002048   0000204799   0000202752   Linux (0x83)

となります。
サーバーにアクセスすると What is the size of the Linux partition in the given disk image?と尋ねられるので、202752を送信するとフラグが入手できます。

Eavesdrop (300pts)

工事中...

SideChannel (400pts)

pin_checkerというバイナリが配布され、実行すると "Please enter your 8-digit PIN code:"と尋ねられます。 試しに"00000000"を入力したところ、"Access denied."と表示されました。おそらく適切な8桁のPINコードを入力するとAccessに成功するのでしょう。
問題タイトルが"SideChannel"であり、ヒントには"Read about timing-based side-channel attacks."と書かれていることから、PINコード(の一部?)が正解と一致しているか否かで実行時間に変化が出るのだと思われます。思われるのですが、正解のPINコードは8桁の数字と考えられるので、候補は00000000から99999999のわずか$10^8$通りしかありません。もう全部試してしまいましょう。
まず、stringsでバイナリを調べると、"Access granted. You may use your PIN to log into the master server."というメッセージが用意されていることがわかります。正しいPINコードを入力すると、このメッセージが表示されるのでしょう。
あとは適当なシェルを組んで、正解メッセージが出てくるまで全探索を行います。

% seq 0 99999999 | xargs -t -P1024 -r -I @ sh -c 'echo @ && printf "%08d\n" "@" | ./pin_checker ' > log.txt

このシェルスクリプトは、「"0から99999999までの数を表示したあと、(8桁に0埋めして)pin_checkerに渡す" 処理を1024並列で行う」スクリプトです。32コア64スレッドのAMD Ryzen ThreadRipper 3970Xで並列実行をぶん回したところ、4日ほどあれば終わりそうな雰囲気がしていました。実際には2日経過した時点で上記の正解メッセージが表示されました。
注意点として、上記のスクリプトはlog.txtへの書き込みの際に排他制御のようなものを一切行っていないので、正しいPINコードがファイルに書き込まれてから正解メッセージが書き込まれるまでの間に他のスレッドが大量の書き込みを行っています。とはいえ、正解メッセージの近くに正解のPINコードも書き込まれていると考えて問題は無いでしょう。

% cat log.txt | grep -3 granted
Please enter your 8-digit PIN code:
8
Checking PIN...
Access granted. You may use your PIN to log into the master server.
48390594
Please enter your 8-digit PIN code:
8

どうやら、正しいPINコードは48390594の周辺であるようです。改めて、並列処理をせずにこの付近を探索します。

% seq 48389600 48390600 | xargs -t -r -I@ sh -c 'echo @ && printf "%08d\n" "@" | ./pin_checker ' > log2.txt
% cat log2.txt | grep -5 granted
Access denied.
48390513
Please enter your 8-digit PIN code:
8
Checking PIN...
Access granted. You may use your PIN to log into the master server.
48390514
Please enter your 8-digit PIN code:
8
Checking PIN...
Access denied.

正しいPINコードは48390513でした。あとはこれをサーバーに送ればフラグが入手できます。
去年もこんなことばかりしてた気がします。真面目にやれ。

Revers Engineering

file-run1 (100pts)

% strings run | grep pico
picoCTF{U51N6_Y0Ur_F1r57_F113_9bc52b6b}

file-run2 (100pts)

% strings run | grep pico
picoCTF{F1r57_4rgum3n7_be0714da}

実行なんて必要なかったんだ...

GDB Test Drive (100pts)

配布されたbinaryをダウンロード後、問題文に記されたとおりにコマンドを実行するとフラグが表示されました。
スクリーンショット 2022-03-18 0.57.05.png

patchme.py (100pts)

配布されたpythonファイルを実行するとPlease enter correct password fo flag:と尋ねられます。ファイルの中身を見ると

patchme.flag.py
    if( user_pw == "ak98" + \
                   "-=90" + \
                   "adfjhgj321" + \
                   "sleuth9000"):

という表記があるので、pwとしてak98-=90adfjhgj321sleuth9000を入力するとフラグが入手できます。

Safe Opener (100pts)

配布されたJavaファイルには

SafeOpener.java
String encodedkey = "cGwzYXMzX2wzdF9tM18xbnQwX3RoM19zYWYz";

と書かれており、またSafeOpenerクラス内には

SafeOpener.java
Base64.Encoder encoder = Base64.getEncoder();

という表記も見えます。どうやらb64 encodeした入力とencodedkeyを比較しているようなので、これをdecodeしたところpl3as3_l3t_m3_1nt0_th3_saf3となりました。これをpicoCTF{}で囲ったものがフラグです。

unpackme.py (100pts)

unpackme.pyplain = f.decrypt(payload)exec(plain.decode())の間にprint(plain)と挿入して、何がexec()で実行されているのかを確かめます。
実行したところ、plainの中にフラグが含まれていました。

bloat.py (100pts)

配布されたpythonファイルを実行すると
Please enter correct password for flag:と言われます。
中身は微妙な難読化が施されていてイライラしますが、どうやら

bloat.flag.py
if arg432 == a[71]+a[64]+a[79]+a[79]+a[88]+a[66]+a[71]+a[64]+a[77]+a[66]+a[68]:

の部分でパスワードの判定をしているようです。
なので、適当な位置にprint(a[71]+a[64]+a[79]+a[79]+a[88]+a[66]+a[71]+a[64]+a[77]+a[66]+a[68])と差し込んで中身を表示させると、この値がhappychanceであることがわかります。
あとは改めてこの値を入力することでフラグが表示されます。

Fresh Java (200pts)

Javaのデコンパイラを用いて配布されたバイナリをリバースします。私はDockerを用いて

% docker run -it --rm -v $PWD:/mnt kwart/jd-cli /mnt/KeygenMe.class > decode.java

としてリバース結果を取得しました。
中身を見ると、

decode.java
//略
if (str.charAt(2) != 'c') {
  System.out.println("Invalid key");
  return;
}
if (str.charAt(1) != 'i') {
  System.out.println("Invalid key");
  return;
}
if (str.charAt(0) != 'p') {
  System.out.println("Invalid key");
  return;
} 

とフラグを1文字ずつ正解と比較しているので、これを繋ぎ合わせればフラグが入手できます。

Bbbbloat (300pts)

与えられたBinaryを実行するとWhat's my favorite number?と尋ねられ、適当な数字を入れるとSorry, that's not it!と言われました。
GhidraでBinaryをデコンパイルしたところ

decompile.c
printf("What\'s my favorite number? ");
__isoc99_scanf();
if (local_48 == 0x86187) {
  __s = (char *)FUN_00101249(0,&local_38);
  fputs(__s,stdout);
  putchar(10);
  free(__s);
 }
else {
  puts("Sorry, that\'s not it!");
}

という結果が見えたので、549255(=0x86187)を入力したところフラグが表示されました。

Web Exploitation

Includes (100pts)

ウェブページのstyle.cssにフラグの前半が、script.jsに後半が書かれていました。
スクリーンショット 2022-03-20 18.00.54.png スクリーンショット 2022-03-20 18.00.56.png

Inspect HTML (100pts)

ウェブページのHTMLファイルにフラグがコメントで記載されています。
スクリーンショット 2022-03-20 18.05.12.png

Local Authority (100pts)

ログインフォームが表示されるので、適当なidとpwでログインするとlogin.phpに飛ばされました。このページではsecure.jsというファイルを読み込んでおり、その中身は

secure.js
function checkPassword(username, password)
{
  if( username === 'admin' && password === 'strongPassword098765' )
  {
    return true;
  }
  else
  {
    return false;
  }
}

という激ヤバ実装なので、このidとpwで改めてログインしたところフラグが表示されました。

Search source (100pts)

本質的にはIncludesと変わりませんが、文量が多いので目で探すのは辛いです。
まず、

% wget -r http://saturn.picoctf.net:58133/

としてウェブページを手元に保存します。-rオプションを付けることで、jsやcssなどのリンク先も同時に保存することができます。
つづいて、

% grep -rl pico
./saturn.picoctf.net:58133/css/style.css

としてpicoという文字列が含まれているファイルを探します。どうやら./saturn.picoctf.net:58133/css/style.cssにフラグが含まれていそうです。
最後に

% cat ./saturn.picoctf.net:58133/css/style.css | grep pico
/** banner_main picoCTF{1nsp3ti0n_0f_w3bpag3s_587d12b8} **/

としてフラグを取得します。

Forbidden Paths (200pts)

Filenameとして../../../../flag.txtを入力するとフラグが表示されます。

Power Cookie (200pts)

isAdmin: 0というCookieがセットされているので、これを1に書き換えたところフラグが表示されました。
スクリーンショット 2022-03-20 16.04.07.png

Cookieの書き換えにはEditThisCookieというChrome拡張が便利です。

Secrets (200pts)

問題文にWe have several pages hidden.とあるので、おそらくリンクが貼られていないページが存在するのでしょう。dirbを用いてbrute forceを実行します。

% dirb http://saturn.picoctf.net:61481/

-----------------
DIRB v2.22    
By The Dark Raver
-----------------

START_TIME: Sun Mar 20 07:40:05 2022
URL_BASE: http://saturn.picoctf.net:61481/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

GENERATED WORDS: 4612                                                          

---- Scanning URL: http://saturn.picoctf.net:61481/ ----
+ http://saturn.picoctf.net:61481/index.html (CODE:200|SIZE:1023)              
==> DIRECTORY: http://saturn.picoctf.net:61481/secret/                         
                                                                               
---- Entering directory: http://saturn.picoctf.net:61481/secret/ ----
==> DIRECTORY: http://saturn.picoctf.net:61481/secret/assets/                  
==> DIRECTORY: http://saturn.picoctf.net:61481/secret/hidden/                  
+ http://saturn.picoctf.net:61481/secret/index.html (CODE:200|SIZE:468)        
                                                                               
---- Entering directory: http://saturn.picoctf.net:61481/secret/assets/ ----
                                                                               
---- Entering directory: http://saturn.picoctf.net:61481/secret/hidden/ ----
+ http://saturn.picoctf.net:61481/secret/hidden/index.html (CODE:200|SIZE:2118)
                                                                               
-----------------
END_TIME: Sun Mar 20 08:39:18 2022
DOWNLOADED: 18448 - FOUND: 3

dribはhttp://saturn.picoctf.net:61481/secret/hidden/index.htmlというページを発見しました。なんかそれっぽいのでアクセスしてみます。
すると、http://saturn.picoctf.net:61481/secret/hidden/superhidden/login.cssというリソースをロードしていることがわかります。
そこでhttp://saturn.picoctf.net:61481/secret/hidden/superhidden/にアクセスしたところ、ページ内に背景と同じ色でフラグが記載されているのを発見しました。

SQL Direct (200pts)

指定されたSQLサーバーに接続し、テーブル一覧を調べ、テーブルの中身を表示します。

pico=# \dt
         List of relations
 Schema | Name  | Type  |  Owner   
--------+-------+-------+----------
 public | flags | table | postgres
(1 row)

pico=# select * from flags;
 id | firstname | lastname  |                address                 
----+-----------+-----------+----------------------------------------
  1 | Luke      | Skywalker | picoCTF{L3arN_S0m3_5qL_t0d4Y_31fd14c0}
  2 | Leia      | Organa    | Alderaan
  3 | Han       | Solo      | Corellia
(3 rows)

SQLiLite (300pts)

Usernameを' or 1=1 ;-- として、pwに適当な値を設定するとログインができます。あとは、開発者ツールでページを見るとフラグが記載されていました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0