2021年3月16日~3月30日(日本時間では3月17日~3月31日)に開催された中高生向けのCTF大会、picoCTFの[Reversing]分野のwriteupです。
その他のジャンルについてはこちらを参照
Transformation
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)])
enc
ファイルが配布されます。
開いてみると
灩捯䍔䙻ㄶ形楴獟楮獴㌴摟潦弸弰摤捤㤷慽
わーお。全然わからん。問題文のスクリプトの逆をするコードを書いて動かしてみます。
enc = '灩捯䍔䙻ㄶ形楴獟楮獴㌴摟潦弸弰摤捤㤷慽' for c in enc: print(chr(int('{:016b}'.format(ord(c))[:8],2)), end='') print(chr(int('{:016b}'.format(ord(c))[8:],2)), end='')
実行結果
picoCTF{16_bits_inst34d_of_8_0ddcd97a}
keygenme-py
keygenme-trial.py
keygenme-trial.py
が配布されます。
keygenme-trial.py (長いので折りたたみ)
#============================================================================#
#============================ARCANE CALCULATOR===============================#
#============================================================================#
import hashlib
from cryptography.fernet import Fernet
import base64
# GLOBALS --v
arcane_loop_trial = True
jump_into_full = False
full_version_code = ""
username_trial = "MORTON"
bUsername_trial = b"MORTON"
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
star_db_trial = {
"Alpha Centauri": 4.38,
"Barnard's Star": 5.95,
"Luhman 16": 6.57,
"WISE 0855-0714": 7.17,
"Wolf 359": 7.78,
"Lalande 21185": 8.29,
"UV Ceti": 8.58,
"Sirius": 8.59,
"Ross 154": 9.69,
"Yin Sector CL-Y d127": 9.86,
"Duamta": 9.88,
"Ross 248": 10.37,
"WISE 1506+7027": 10.52,
"Epsilon Eridani": 10.52,
"Lacaille 9352": 10.69,
"Ross 128": 10.94,
"EZ Aquarii": 11.10,
"61 Cygni": 11.37,
"Procyon": 11.41,
"Struve 2398": 11.64,
"Groombridge 34": 11.73,
"Epsilon Indi": 11.80,
"SPF-LF 1": 11.82,
"Tau Ceti": 11.94,
"YZ Ceti": 12.07,
"WISE 0350-5658": 12.09,
"Luyten's Star": 12.39,
"Teegarden's Star": 12.43,
"Kapteyn's Star": 12.76,
"Talta": 12.83,
"Lacaille 8760": 12.88
}
def intro_trial():
print("\n===============================================\n\
Welcome to the Arcane Calculator, " + username_trial + "!\n")
print("This is the trial version of Arcane Calculator.")
print("The full version may be purchased in person near\n\
the galactic center of the Milky Way galaxy. \n\
Available while supplies last!\n\
=====================================================\n\n")
def menu_trial():
print("___Arcane Calculator___\n\n\
Menu:\n\
(a) Estimate Astral Projection Mana Burn\n\
(b) [LOCKED] Estimate Astral Slingshot Approach Vector\n\
(c) Enter License Key\n\
(d) Exit Arcane Calculator")
choice = input("What would you like to do, "+ username_trial +" (a/b/c/d)? ")
if not validate_choice(choice):
print("\n\nInvalid choice!\n\n")
return
if choice == "a":
estimate_burn()
elif choice == "b":
locked_estimate_vector()
elif choice == "c":
enter_license()
elif choice == "d":
global arcane_loop_trial
arcane_loop_trial = False
print("Bye!")
else:
print("That choice is not valid. Please enter a single, valid \
lowercase letter choice (a/b/c/d).")
def validate_choice(menu_choice):
if menu_choice == "a" or \
menu_choice == "b" or \
menu_choice == "c" or \
menu_choice == "d":
return True
else:
return False
def estimate_burn():
print("\n\nSOL is detected as your nearest star.")
target_system = input("To which system do you want to travel? ")
if target_system in star_db_trial:
ly = star_db_trial[target_system]
mana_cost_low = ly**2
mana_cost_high = ly**3
print("\n"+ target_system +" will cost between "+ str(mana_cost_low) \
+" and "+ str(mana_cost_high) +" stone(s) to project to\n\n")
else:
# TODO : could add option to list known stars
print("\nStar not found.\n\n")
def locked_estimate_vector():
print("\n\nYou must buy the full version of this software to use this \
feature!\n\n")
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
def decrypt_full_version(key_str):
key_base64 = base64.b64encode(key_str.encode())
f = Fernet(key_base64)
try:
with open("keygenme.py", "w") as fout:
global full_version
global full_version_code
full_version_code = f.decrypt(full_version)
fout.write(full_version_code.decode())
global arcane_loop_trial
arcane_loop_trial = False
global jump_into_full
jump_into_full = True
print("\nFull version written to 'keygenme.py'.\n\n"+ \
"Exiting trial version...")
except FileExistsError:
sys.stderr.write("Full version of keygenme NOT written to disk, "+ \
"ERROR: 'keygenme.py' file already exists.\n\n"+ \
"ADVICE: If this existing file is not valid, "+ \
"you may try deleting it and entering the "+ \
"license key again. Good luck")
def ui_flow():
intro_trial()
while arcane_loop_trial:
menu_trial()
# Encrypted blob of full version
full_version = \
b"""
gAAAAABgT_nv3JrW2AMPOanzoOatT8tWrZSH9V2-H_-sY8RFTh1Vr9guvCw3iIUq7eVs4IhR2u6bI_HkJm6u5VK99vYIKXfthUYjQRMNmo6uMwqdK8ZYm3wH3Z2BlFNmGxMSXKmFhDQujSexuuMg5ZFIb5VhyZeUY4R9KubbghdUIlZ2hBeCpxef_ioNjM7VUKJEyJgxiAtPdMKSgT8y-FJsikUsw9Scbe8nH-bC856u_mqk6AKQnORLmxfsJykFMFm-wOh-unFnUvg9HiT6lYXCrFnXNEFEnq5djwM9H4iRXYwyO4XdHEqVcodEyE3HoPFOh24R9ATElafkxty16jECSabI7k608v6sk2Pxd-EAI0XEtjlVE5Qz-qdnoTIXEXXbwK8Bpw33JwRGJs-WduRmF2G5qVVAGONjaAc9CPXhawUkf2_sNMlnq6lqn7sBb2K7BiqW2Efc88pzfsOYvGQC94CBBhIUQTePRcZ_bUbsDLR8PrWlNQ28Vt6P45URpPDtxkUGQtixiQi9QlPThCFJs28XmApvRQQfJHdje45t2ELeVgn5YMi_FHQ98qgMudwXHXprigHAMmcB2rFPWWhAQn4sIrdL2In424RWO0qOhd9IJrU7DldcrmOMyvpHV87HzVgsRnI97Hn-jiVi_FemDWAsDGFwjaGbrxWUfr01ienDjlrXc-NC2x1Tt6jTHiFb2LbUqzGyjJ-m-5eCuqr1zh8_oCypnBB79XuKMGlngql0NQVEReZ9pDZd3Iax0TJVM55luvuCpjolarAJXz28Ynt5et1DHfnAQ1gQ4laW3ZtRL6TTp1vRpfllOURLpAxNrY03a7cMS5iIS7nXMgllh-uV9S1O3Ww4z_nPSh3q-1yKUilHfuXwdazGjBvfvwDSD3ZgvzgDmvQ-eCNV4I-oEHlAjqAiuwMvClU3Pr-mmBg257A8LHQlJJ7Rl6oYpW-B43esP6sQ2G66ofHbx4ZyUhqH5WC6jjoSuxyRFs6Z6wiNDztgkSdqbSc7iTxtF8oHBqPI_boBwr7YskpWmgwNFhWPzxGwyhMI2H38WGx1klLhNKsa4Ep9vcTuj_ltCL_QZkIgfgklf3QYGWBNLjIPBPkv1c0GqYMPkhnc4Libqdr8LXZJug2ry1s-H16d2U7_cjo9d8usNG2nNhUQvJ8d5I58r3IlioEUWjkUfWy4t3Aw8UN43iZ62MyzE7yzNIYMIwIxgB9qXmrFkh3pFnoZKlPHh7rjVBlbsIgPl64Ewi70P2AdkuDe7BXQ6kX4rdWm3i18ePWIX8n0WYz4eyGz41eRXU2hjyJeIz4Wi0rcjx39LS9BXWq4qHYnT3WNGzONy1ArEiNNiXYk61CyJiaCmFX2BZ5NL85O0ePD-bf7cn7KIWvTGqUoXIP-j1FAYvJy47myTITkurZ7YwEHIAk2sejWMltN-JeI6Jqu8nFKTl1h1sbNpCvAnJmSFczKN9fTJONndpK_PpPbUIilA1UHeqtsQfhNpyx1KK6xeFYyCa0V2sLDnHIHWREXMgcB99TUYSV0_DpbIvwQIaINt7cTDQWWvD7tyFXWltw7VqGAUCgtWYAOIoXj9ZxyOCwuIBHXGD8h8r_kBWx8GlN2Mn70ALuVpPWfR_Y55SHGy3KEUtgig92ms5758hdTQjbvb9wk9byAXvAwpltWQG86XlCIIZSM2vpDqpQ6l-8j_BBir_oXjjuaHQKEw3A3ISpLTP9ZKqcRuDAyeiNNNO_HuPK8hxNFzy7Zvu46tH3waLaPW_v_OivUOx7rP8YxocSv6ON_lwR4sRj3mvAk_WPbXpGQaRaNL813pp5aPzOkUAuod25H6esLw0Io7nCirZ3ZhPKfhtY0bakpTl2o5JJGe_2eEm_D6afBnhnkpEaJOsJhkPj7BXBZH9VJJlBWsHOV8WM3sgy1dBQjCP4C0ThSBhEUwq1mPIFKsvffKJgianCnk0fdmpFOLDJci9i5LDAB-sra6RM4y-HhV2gt3PSvyB_H0WDcdQhSZHVz6U_yNMucIW8-4YwdRwec6HGrNneiyIlacHlKO15PCwWBogndUApfLYrxKHHbxIU1sDybMlIEgBBu9_3gOStqXZu4zBDR93P-qYGz-0jrEHq3YVN9olBLx9yoOVI2AZ9m0PJwpKHYc2uZsciFy__dckefZ79zycUzeSKuKbhaZozQOvdLKwmtcB8y5n3_nwwN6-FQu9APoLqsZoaq3wT5QM6ax-_FS9XwzhqEunognjINxAXu660db73L710noV3CBT76aDY3hiV7fnYhQTfysI4SHQtnAzHXMbxYmNfbMnOALTAkQt7Yaiy_NGAr7jmNOAQwhHIW8eaOsCfhRPl7XjdM7pX2N4EM5cFAEphKWQ62f_3RhnyvaTDNmDXkde-_2FjfBMGirouVJL2rSA-icdD1pfkCKO7U0An5V7AUKuYDTZXTXE4DFWUIq4K36jFmHJ9fwllHoe1_A22RuP4DHm4o7TYEJIeo5UvaVc9Lfd6edWIn8KqDzhTz6TZLWlSbK_RnZlpCv3rdibpHzE-epOv1y5pDv08R8jWnVzHv0c3DgfJzWknRd-S5hIIOd5FEyCAx4NpOqYJjpD109ijZLzDMxuvml8QJmX2tplAp-5xGGiVAFPLse1Vo95f5Wsh8KXwnbNjFQSYf8AgskPp_2spP7rBpVvDrn6HJp7TvQY6mNZMXgLOWy8WHynKESedYkD7iQSHwZ5iMvJTVLDHjYbll0PZyI-Tsy7Q9ZP4ysdzkL5TwGwHa53UhUsiLQmhpkht4dKg3kqq-pA-f5PwKOWKCsKwtUyGFvz9gL7yDNNQwNpSaN2vBy5U8ulVA8SooM9SwmelNHOKvF2rmy6nOQ8mzt-vnnKqCTnc_4VgNakpw86IQVsw7VGQEGTNLD8ULL8bFkSlfICvDQKSLOPONExox17G4YuXFRpDCGoK3KLbM4oKQCi9QWPOL0jhovl0zu1j-XIY7IA2SX4C_Ie9MV27amQi7mwFHYpj3081isJOZd_cUxszijjqOFI71IBeHwSHj5wOnWRUHVLk-aOCT_rKKpae6mPyL0uJsIS1s0B62scEcn0wFa90pjuARSPhc35VCA9TFDcq6W0tAi4aNNzT-vgv4NkqfR6hh_jqs0irQZj7jtUh4OGbkaZljMovt7UYBKd9L4m5gmh1w672_Oif5pDU3YXMdc4FSp39f1wWDvHz0Uw7grUTlvLL3ge6f-Elxe2c0n56nWxs4qo4rqGY9c5BuIFxly_6iymzec33Yo8r0_L5VIRIoWl6lcZq-uIRdTl8mzbVDEoU7r1P6mvg3xUfKfF-Q-XnlfV23XNLSuIF3JF78DkjkNxdOP9dM4dCHbexa5yKeNqAan6Rq7LCnlxTGqP_lxpJO4GvU6npINZ1qjp9ydUveK-Ya7o6rfJqN1GbITvUdaMOFq00OfOsKdZXn7gB_I6el_O-YKJcx3W2X_im81FwT_8P-zW_jEdO-lmztiEyVEvUOFJJfAm-K_G4-HEYzROMPrgof708fnnxDaWGiUi0AtJeEk4sHvf2PTiE0517FcKadlELd_4R-eLKk9Wwu24faJ_R0Ej8NinIbeqCUTb0LkIKa5ChJbL4nXs9f019rUFewPDJhpVLy4nJywzVou9_7-DCMxUT3ld1PjfumXvAbYejzN4Ubue-yfBwPFe_U2YoLM-CyCPNXCbP8WKzCzzNwCcLf8OcQ6gbsyhi0Ul9urHnqWK93qVmPZLEVG9QBEmKPtN6FNWtNIomFiLnl6C9dwbSCWxUJWU_dP7tmVzundLgQhiZhgQz2T7ffSf1aNGSR5hklFayb-MyfOJm46EPx1hVVgqIcdxg7Ko4gt6rpjwAYRQ==
"""
# Enter main loop
ui_flow()
if jump_into_full:
exec(full_version_code)
めっちゃ長い。とにかく動かしてみます。
$ python keygenme-trial.py =============================================== Welcome to the Arcane Calculator, MORTON! This is the trial version of Arcane Calculator. The full version may be purchased in person near the galactic center of the Milky Way galaxy. Available while supplies last! ===================================================== ___Arcane Calculator___ Menu: (a) Estimate Astral Projection Mana Burn (b) [LOCKED] Estimate Astral Slingshot Approach Vector (c) Enter License Key (d) Exit Arcane Calculator What would you like to do, MORTON (a/b/c/d)?
4つのメニューから選べるみたい。cのライセンスキー入力部分のコードはこちら。
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")
check_key
の処理。
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
key
は入力したライセンス、username_trial
はglobalに定義されてるMORTON
で固定なので、keyが計算できそう。
import hashlib bUsername_trial = b"MORTON" user_key = 'picoCTF{1n_7h3_|<3y_of_' arry = [4,5,3,6,2,7,1,8] for i in arry: user_key += hashlib.sha256(bUsername_trial).hexdigest()[i] user_key += '}' print(user_key)
実行結果
$ python solve.py picoCTF{1n_7h3_|<3y_of_75fc1081}
念の為、enter_licenseに突っ込んで正しいか見てもらう。
$ python keygenme-trial.py =============================================== Welcome to the Arcane Calculator, MORTON! This is the trial version of Arcane Calculator. The full version may be purchased in person near the galactic center of the Milky Way galaxy. Available while supplies last! ===================================================== ___Arcane Calculator___ Menu: (a) Estimate Astral Projection Mana Burn (b) [LOCKED] Estimate Astral Slingshot Approach Vector (c) Enter License Key (d) Exit Arcane Calculator What would you like to do, MORTON (a/b/c/d)? c Enter your license key: picoCTF{1n_7h3_|<3y_of_75fc1081} Full version written to 'keygenme.py'. Exiting trial version... =================================================== Welcome to the Arcane Calculator, tron! ===================================================
よさそう!ということで、これがflagでした。
crackme-py
crackme.py
crackme.py
が配布されます。
# Hiding this really important number in an obscure piece of code is brilliant! # AND it's encrypted! # We want our biggest client to know his information is safe with us. bezos_cc_secret = "A:4@r%uL`M-^M0c0AbcM-MFE0g4dd`_cgN" # Reference alphabet alphabet = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ \ "[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" def decode_secret(secret): """ROT47 decode NOTE: encode and decode are the same operation in the ROT cipher family. """ # Encryption key rotate_const = 47 # Storage for decoded secret decoded = "" # decode loop for c in secret: index = alphabet.find(c) original_index = (index + rotate_const) % len(alphabet) decoded = decoded + alphabet[original_index] print(decoded) def choose_greatest(): """Echo the largest of the two numbers given by the user to the program Warning: this function was written quickly and needs proper error handling """ user_value_1 = input("What's your first number? ") user_value_2 = input("What's your second number? ") greatest_value = user_value_1 # need a value to return if 1 & 2 are equal if user_value_1 > user_value_2: greatest_value = user_value_1 elif user_value_1 < user_value_2: greatest_value = user_value_2 print( "The number with largest positive magnitude is " + str(greatest_value) ) choose_greatest()
最初にmainからcallされるようになっている choose_greatest()
は、ただ入力値の大きい方を返すだけのプログラムなのでダミー。その上にある decode_secret()
で bezos_cc_secret
をdecodeするための関数呼び出しを一行追加すればOK。
#元の関数の最後を下記のように変更 #choose_greatest() decode_secret(bezos_cc_secret)
実行結果
$ python crackme.py picoCTF{1|\/|_4_p34|\|ut_8c551048}
ARMssembly 0
What integer does this program print with arguments
4004594377
and4110761777
? File: chall.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb})
chall.S
が配布されます。アセンブリだ。
…ちょっと読むのが面倒だったので、ヒントの "Simple compare" から、入力値のどちらか大きい値がflagに違いないと推測。
value1 = 4004594377 value2 = 4110761777 print(hex(value1)[2:]) print(hex(value2)[2:])
実行結果
$ python solvee.py eeb142c9 f5053f31
下の値をflag formatに合わせたやつが通りました。(全然想定解ではないけど、解けてる人が多かったのでそういうことかな、と…。アセンブリ読めない。)
speeds and feeds
There is something on my shop network running at
mercury.picoctf.net:59953
, but I can't tell what it is. Can you?
指定されたホストに接続してみます。
$ nc mercury.picoctf.net 59953 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 G0Z0.1 G0X1.1034Y3.0345 G1Z0.1 G1X1.6552Y3.5862 G1X2.2069Y3.8621 G1X2.7586Y3.8621 G1X3.5862Y3.5862 G1X4.1379Y3.0345 G1X4.4138Y2.2069 G1X4.4138Y1.6552 G1X4.1379Y0.8276 ...
こんなのがばーっっとでてきます。全然わからん。
ヒントを見てみます。
What language does a CNC machine use?
CNC machine というのがキーワードらしい。
これっぽい。Gコードとな。
下記に、日本語の基本的なGコード紹介サイト。
Gコードの基礎知識と使用時の注意点【機能がわかる一覧表付き】 | CAD/CAMコラム | CAD/CAMに関する資料 | 株式会社フアクト
これ自分でplotする関数角のめっちゃ大変そう…。と思ってぐぐってみると、plotしてくれるサイトがいくつかありました!今回は下記を利用。めっちゃ良い。
GCode Fileに降ってきたやつを貼り付けると、flagが描かれました!
Shop
Best Stuff - Cheap Stuff, Buy Buy Buy... Store Instance: source. The shop is open for business at
nc mercury.picoctf.net 3952
.
source
が配布されます。
$ file source source: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, Go BuildID=Wq2z6hkBrrAovu6w8dMb/chyUPt3_NgRB5zVfMpf8/99tYvdYHy3xNdHp4wuxA/ClEGFX9e3WU6qjzPxg9K, with debug_info, not stripped
実行ファイルだ。
まずは指定されたところにつないでみます。きっとここで使われているやつが配布されているに違いない。
$ nc mercury.picoctf.net 3952 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:
なるほど。最初は40 coinsしかもって無くて、Flagを買いたいんだけどお金が足りないと。何とかしてお金を稼がないといけないわけだ。
コードを解析する前に適当にチートしてたら通った。
Choose an option: 1 How many do you want to buy? -10000000 You have 150000040 coins Item Price Count (0) Quiet Quiches 10 12 (1) Average Apple 15 10000008 (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 57 99 49 49 56 98 98 102 125]
りんごを負の数購入すると、計算がバグっているようでお金が増えた。あとは普通にflagを買うと、10進数のflagが降ってきた。
CyberChefでFrom DecimalしたらFlagがでました🙌
ARMssembly 1
For what argument does this program print
win
with variables83
,0
and3
? File: chall_1.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb})
chall_1.S
が配布されます。
.arch armv8-a .file "chall_1.c" .text .align 2 .global func .type func, %function func: sub sp, sp, #32 str w0, [sp, 12] mov w0, 83 str w0, [sp, 16] str wzr, [sp, 20] mov w0, 3 str w0, [sp, 24] ldr w0, [sp, 20] ldr w1, [sp, 16] lsl w0, w1, w0 str w0, [sp, 28] ldr w1, [sp, 28] ldr w0, [sp, 24] sdiv w0, w1, w0 str w0, [sp, 28] ldr w1, [sp, 28] ldr w0, [sp, 12] sub w0, w1, w0 str w0, [sp, 28] ldr w0, [sp, 28] add sp, sp, 32 ret .size func, .-func .section .rodata .align 3 .LC0: .string "You win!" .align 3 .LC1: .string "You Lose :(" .text .align 2 .global main .type main, %function main: stp x29, x30, [sp, -48]! add x29, sp, 0 str w0, [x29, 28] str x1, [x29, 16] ldr x0, [x29, 16] add x0, x0, 8 ldr x0, [x0] bl atoi str w0, [x29, 44] ldr w0, [x29, 44] bl func cmp w0, 0 bne .L4 adrp x0, .LC0 add x0, x0, :lo12:.LC0 bl puts b .L6 .L4: adrp x0, .LC1 add x0, x0, :lo12:.LC1 bl puts .L6: nop ldp x29, x30, [sp], 48 ret .size main, .-main .ident "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0" .section .note.GNU-stack,"",@progbits
また結構長い。
これに問題文の入力を入れるとどういう出力になるか、という問題っぽい。
このあたりを読みながら解読。
白目むきながら解いたので、何処か違っているところあったらご指摘ください。
.arch armv8-a .file "chall_1.c" .text .align 2 .global func .type func, %function func: sub sp, sp, #32 w0 = 8, wzr = 3, str w0, [sp, 12] # [sp+12]のアドレスに、 w0を書き込む 8 mov w0, 83 # w0 <- 83 w0 = 83 str w0, [sp, 16] # [sp + 16] のアドレスに、w0を書き込む 83 str wzr, [sp, 20] # [sp + 20] のアドレスに、wzrを書き込む 3 mov w0, 3 # w0 <- 3 w0 = 3 str w0, [sp, 24] # [sp + 24] <- w0 [sp + 24] = 3 ldr w0, [sp, 20] # w0 = [sp+20] のアドレスの値 (== wzr) w0 = 3 ldr w1, [sp, 16] # w1 = [sp+16] のアドレスの値(== 83) w1 = 83 lsl w0, w1, w0 # w0 = w1 << w0 w0 = 83 << 3 = 664 str w0, [sp, 28] # [sp + 28] <- w0 w0 ldr w1, [sp, 28] # w1 = [sp + 28] (==w0) w1 = 664 ldr w0, [sp, 24] # w0 = [sp + 24] (==3) w0 = 3 sdiv w0, w1, w0 # w0 = w1 // w0 w0 = w1 // 3 = 83 str w0, [sp, 28] # [sp + 28] <- w0 w0 ldr w1, [sp, 28] # w1 = [sp + 28] (=w0) w1 = w0 = 83 ldr w0, [sp, 12] # w0 = [sp + 12] w0 = 8 sub w0, w1, w0 # w0 = w1 // w0 w0 = 83 // 8 = 0x1b str w0, [sp, 28] # sp + 28 = w0 ldr w0, [sp, 28] # w0 = sp + 28 add sp, sp, 32 # sp = sp + 32 ret .size func, .-func .section .rodata .align 3 .LC0: .string "You win!" .align 3 .LC1: .string "You Lose :(" .text .align 2 .global main .type main, %function main: stp x29, x30, [sp, -48]! # x29, x30 を stack pointer から48上にpush add x29, sp, 0 # x29 = sp + 0 str w0, [x29, 28] # [x29+28]のアドレスに、 w0を書き込む [x29+28] <- 83 str x1, [x29, 16] # [x29+16]のアドレスに、 x1を書き込む [x29+16] <- 0 ldr x0, [x29, 16] # x0 = [x29+16]のアドレスの値 (== x1) [x29+16] <- 0 add x0, x0, 8 # x0 = x0 + 8 x0 = 8 ldr x0, [x0] # x0 = x0 x0 = 8 bl atoi str w0, [x29, 44] # [x29+44] に w0 を書き込む [x29+44] <- 8 ldr w0, [x29, 44] # w0 = [x29+44] のアドレスの値 w0 <- 8 bl func # func呼び出し cmp w0, 0 # cmp w0 0 bne .L4 # w0 != 0 -> you lose adrp x0, .LC0 # w0 == 0 -> you win add x0, x0, :lo12:.LC0 bl puts b .L6 .L4: adrp x0, .LC1 add x0, x0, :lo12:.LC1 bl puts .L6: nop ldp x29, x30, [sp], 48 ret .size main, .-main .ident "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0" .section .note.GNU-stack,"",@progbits
ということで、答えは picoCTF{0000001b}
となりました。
ちなみに ARMssembly 2 は終了間際に同じように解読してガチャガチャフラグ候補を突っ込んでみたけど通らなかった…。