๋ฌธ์ : DreamHack โ [CodeEngn] MobileApp L02 ๋ถ๋ฅ: Reversing / Mobile (Android) ๋์ด๋: ๐ฅ Bronze 2 FLAG:
the key is november rain
๊ฐ์ ์๋ฆฌ์ฆ์ L01์ ํ๊ณ ๋์ ์ด์ด์ ๋ฐ์ ๋ฌธ์ ๋ค. ๊ตฌ์กฐ๋ ๊ฑฐ์ ๋์ผํ๋ค โ SEED-128 CBC๋ก ํ๋์ฝ๋ฉ๋ ๋ฌธ์์ด์ ๋ณตํธํด ๊ทธ ๊ฐ์ด "ํค๊ฐ"์ด ๋๋ ํํ. ๋ค๋ง ์ด๋ฒ์ ๋ ๊ฐ์ง๊ฐ ๋ฌ๋๋ค. ์ ํจํ SEED ๋ธ๋ก์ด ํ๋๊ฐ ์๋๋ผ ๋์ด๋ผ CBC ์ฒด์ด๋์ ์ค์ ๋ก ์ด์ด์ผ ํ๊ณ , ์ฑ์ด ๋ฒํผ์ ๋๋ฌ๋ ๋ต์ ๋ณด์ฌ์ฃผ์ง ์๋๋ก ์กฐ๊ฑด์ ์ธ ๊ฒน์ผ๋ก ๋ง์๋๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | [CodeEngn] MobileApp L02 |
| ๋์ด๋ | ๐ฅ Bronze 2 |
| ๋ถ๋ฅ | Reversing / Mobile |
| ์ ๊ณต ํ์ผ | SmartApp L02.apk |
| ์คํ | Android (Dalvik), ๋ค์ดํฐ๋ธ libKISACrypto.so |
| ํต์ฌ ๊ธฐ๋ฒ | SEED-128 2๋ธ๋ก CBC ๋ณตํธ + BigInteger ๋ถํธ ์ธ์ฝ๋ฉ + ์คํ ์์ฒด๊ฐ ๋งํ UI |
๐ฌ ์ฝ๋ ์ ์ฐฐ
APK ์์ ๋จผ์ ๋ณธ๋ค.
file "SmartApp L02.apk" && unzip -l "SmartApp L02.apk" | grep -E "dex|\.so"
classes.dex์ ํ์์คํฌํ๊ฐ 2013-11-02 12:42 โ ๋ค์์ ๋ณผ ์กฐ๊ฑด๋ฌธ์ ๋ฐํ ์๋ "2013-11-02-12:35:03"๊ณผ 7๋ถ ์ฐจ์ด๋ค. ๊ฐ๋ฐ์๊ฐ ๊ทธ ์์๋ฅผ ์ฑ์ ๋น๋ํ๋ ๊ทธ ์์ ๊ทผ์ฒ๋ก ๋ฐ์๋ ํ์ ์ผ๋ก ๋ณด์ธ๋ค.
jadx๋ก ๋์ปดํ์ผํด์ Security ํด๋์ค๋ถํฐ ๋ณธ๋ค. L01๊ณผ ์์ ํ ๊ฐ์ ๊ตฌ์กฐ๋ค.
jadx -q -r -d jadx_out "SmartApp L02.apk"
DecryptStr์ L01๊ณผ ํ ๊ธ์๋ ๋ค๋ฅด์ง ์๋ค. hex ๋ฌธ์์ด์ BigInteger๋ก ๋ฐ์ดํธ๋ก ๋ฐ๊พผ ๋ค SEED๋ก ๋ณตํธํ๋ค. System.loadLibrary("KISACrypto")๊ฐ ๋์ ๋์ง๋ง L01์์ ์ด๋ฏธ ํ์ธํ๋ฏ ํ์ค SEED-128์ด๋ผ .so๋ฅผ ๋ฆฌ๋ฒ์ฑํ ํ์๋ ์๋ค.
๊ทธ๋ผ ์ด ๊ฐ์ ์ด๋์ ํธ์ถํ๋์ง MainActivity๋ฅผ ๋ณธ๋ค.
cat jadx_out/sources/com/namdaehyeon/findkey2/MainActivity.java
์ฌ๊ธฐ์ L01๊ณผ ๊ฐ๋ฆฐ๋ค. L01์ ๋ฒํผ์ ๋๋ฅด๋ฉด ๊ณง๋ฐ๋ก DecryptStr ๊ฒฐ๊ณผ๊ฐ ๋ํ์์์ ๋จ๋๋ฐ, L02๋ onCreate() ์์ ์กฐ๊ฑด๋ฌธ์ด ํ๋ ๋ ์๋ค.
if (makeDate() == "2013-11-02-12:35:03" && Volume() == 53) {
this.aView.setText(keyString());
}์ด ํ ์ค์ด ์ธ ๊ฐ์ง ์ด์ ๋ก ์ ๋ ์ฐธ์ด ๋ ์ ์๋ค.
==๋ Java์์ String ๋ด์ฉ ๋น๊ต๊ฐ ์๋๋ผ ์ฐธ์กฐ ๋น๊ต๋ค.makeDate()๊ฐ ๋งค๋ฒSimpleDateFormat.format()์ผ๋ก ์๋ก ๋ง๋ ๋ฌธ์์ด ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํ๋ฏ๋ก, ๋ด์ฉ์ด ์ฐ์ฐํ ๋๊ฐ์๋==๋ ๋ค๋ฅธ ๊ฐ์ฒด๋ผ ๊ฑฐ์ง์ด๋ค..equals()๋ฅผ ์ผ์ด์ผ ํ ์๋ฆฌ๋ค.- ์ค๋ น
==๊ฐ.equals()์๋๋ผ๋makeDate()๋ ํ์ฌ ์๊ฐ์ ๋ด์ผ๋ฏ๋ก"2013-11-02-12:35:03"๊ณผ ๊ฐ์์ง ์๊ฐ์ ์คํ ์ค ๋จ 1์ด, ๊ทธ๊ฒ๋ ๊ธฐ๊ธฐ ์๊ฐ์ ๊ทธ๋ ๊ฒ ๋ง์ถฐ์ผ๋ง ์จ๋ค. Volume()์ ํ๋์ฝ๋ฉ๋ ์์ 33์ ๋ฆฌํดํ๋ค. ์กฐ๊ฑด์Volume() == 53์ด๋ ์ด ๋น๊ต๋ ์ฝ๋๊ฐ ๋๋ ํ ํญ์ ๊ฑฐ์ง์ด๋ค.
์
์ค ํ๋๋ง ๊ฑธ๋ ค๋ ๋ต์ ์ ๋จ๋๋ฐ ์ธ ๊ฐ๊ฐ ๋์์ ๋งํ ์๋ค. ์ฆ ์ด ์ฑ์ ์ค์ ๋ก ์คํํด์๋ ์ ๋ ๋ต์ ๋ณผ ์ ์๊ฒ ์ค๊ณ๋๋ค. keyString()์ด ๋๊ธฐ๋ ๋ฆฌํฐ๋ด์ ์ง์ ๋ณตํธํ๋ ๊ฒ ๋ง๊ณ ๋ ๊ธธ์ด ์๋ค.
public String keyString() {
return Security.DecryptStr("-1c776f3a2fa678cae27879e87a74553c61009eefb037bbe2abdbd5e4314407e60000000000000000");
}๐ฃ ํต์ฌ โ BigInteger ์ธ์ฝ๋ฉ, ๊ทธ๋ฆฌ๊ณ ์ด๋ฒ์ ๋ธ๋ก์ด ๋ ๊ฐ
L01์์ ๋ค๋ฃฌ ํจ์ ๋ ๊ฐ์ง(BigInteger 2์ ๋ณด์ ์ธ์ฝ๋ฉ, keyโ iv)๋ ์ฌ๊ธฐ์๋ ๊ทธ๋๋ก ์ ์ฉ๋๋ค. Security ์ ์ ๋ฐฐ์ด์ ๋ถํธ ์๋ ๋ฐ์ดํธ๋ก ๋ค์ ์ฝ์ผ๋ฉด:
key = 33d24f8f082279f1e9f394370ad40589
iv = 268d66a735a81a815fbad9fa36192513keyString()์ ๋ฆฌํฐ๋ด์ -๋ก ์์ํ๋ ์์ hex๋ค. Java new BigInteger(hex, 16).toByteArray()๋ ์ด ๊ฐ์ ์ต์ ๊ธธ์ด์ ๋ถํธ ์๋ 2์ ๋ณด์ big-endian์ผ๋ก ๋๋ ค์ฃผ๋๋ฐ, ์ด๋ฒ์ ๊ทธ ๊ฒฐ๊ณผ๊ฐ 40๋ฐ์ดํธ๋ก, L01(30๋ฐ์ดํธ, ์ ํจ ๋ธ๋ก 1๊ฐ)๋ณด๋ค ๊ธธ๋ค. 16๋ฐ์ดํธ์ฉ ๋ธ๋ก์ผ๋ก ๋๋๋ฉด ์์ 32๋ฐ์ดํธ(2๊ฐ ๋ธ๋ก)๊ฐ ์ ํจ SEED ๋ฐ์ดํฐ๊ณ , ๋๋จธ์ง 8๋ฐ์ดํธ๋ ์๋ณธ ๋ฆฌํฐ๋ด ๋์ 0000000000000000์ด ๊ทธ๋๋ก ๋์ด์จ zero-fill ์์ฌ๋ค โ 16๋ฐ์ดํธ๋ฅผ ๋ชป ์ฑ์์ ๋ฒ๋ ค์ง๋ค.
๋ธ๋ก์ด ๋์ด๋ผ๋ ๊ฒ L01๊ณผ์ ์ค์ง์ ์ธ ์ฐจ์ด๋ค. CBC ๋ณตํธ๋ ์ด๋ ๊ฒ ์ฒด์ด๋๋๋ค.

P1 = D(C1) โ IV๋ L01๊ณผ ๊ฐ์ ๊ณ์ฐ์ด์ง๋ง, P2 = D(C2) โ C1์์ C1(์ฒซ ๋ธ๋ก ์ํธ๋ฌธ)์ด ๋ค์ XOR ํค๋ก ์ฐ์ธ๋ค. ์ด๊ฑธ ๋์น๊ณ ๋ ๋ธ๋ก์ ๊ฐ๊ฐ IV๋ก๋ง ๋ณตํธํ๋ฉด ๋ ๋ฒ์งธ ๋ธ๋ก์ ๊นจ์ง ๋ฐ์ดํธ๊ฐ ๋์จ๋ค. ๋คํํ openssl seed-cbc๋ ์ฌ๋ฌ ๋ธ๋ก์ ํ ๋ฒ์ ๋๊ธฐ๋ฉด ์ด ์ฒด์ด๋์ ์๋์ผ๋ก ์ฒ๋ฆฌํด์ค๋ค โ ์ง์ XOR์ ์งค ํ์ ์์ด 32๋ฐ์ดํธ๋ฅผ ํต์งธ๋ก ๋๊ธฐ๋ฉด ๋๋ค.
๐ Full Exploit
smali์์ keyยทivยท์ํธ๋ฌธ ๋ฆฌํฐ๋ด์ ์ง์ ํ์ฑํด ์์ฒด์๊ฒฐ๋ก ๋ง๋ค์๋ค. L01์ solve.py์ ๋ผ๋๋ ๊ฐ๊ณ , ๋ธ๋ก ์๋ฅผ ๋ฆฌํฐ๋ด ๊ธธ์ด์์ ๊ณ์ฐํ๋ ๋ถ๋ถ๋ง ๋ค๋ฅด๋ค.
import re
import subprocess
from pathlib import Path
BASE = Path(__file__).resolve().parent / "decoded" / "smali" / "com" / "namdaehyeon" / "findkey2"
SEC = BASE / "Security.smali"
MAIN = BASE / "MainActivity.smali"
def parse_smali_byte_array(text, label):
m = re.search(r":%s\s*\n\s*\.array-data 1\n(.*?)\.end array-data" % label, text, re.S)
vals = re.findall(r"(-?0x[0-9a-fA-F]+)t", m.group(1))
return bytes(int(v, 16) & 0xFF for v in vals)
def java_biginteger_hex_to_bytes(hexstr):
sign = -1 if hexstr.startswith('-') else 1
value = sign * int(hexstr.lstrip('-'), 16)
n = 1
while True:
try:
return value.to_bytes(n, 'big', signed=True)
except OverflowError:
n += 1
def pkcs7_strip(b):
pad = b[-1]
return b[:-pad] if 1 <= pad <= 16 and b[-pad:] == bytes([pad]) * pad else b
sec_text = SEC.read_text()
key = parse_smali_byte_array(sec_text, "array_0")
iv = parse_smali_byte_array(sec_text, "array_1")
print(f"[*] key = {key.hex()}")
print(f"[*] iv = {iv.hex()}")
ct_hex = re.search(r'const-string v1, "(-?[0-9a-f]+)"', MAIN.read_text()).group(1)
ct_bytes = java_biginteger_hex_to_bytes(ct_hex)
print(f"[*] cipherText (BigInteger.toByteArray) = {ct_bytes.hex()} ({len(ct_bytes)} bytes)")
n_blocks = len(ct_bytes) // 16
block = ct_bytes[:n_blocks * 16]
print(f"[*] {n_blocks} valid SEED block(s), trailing {len(ct_bytes) - len(block)} bytes are zero-fill artifact")
out = subprocess.run(
["openssl", "enc", "-d", "-seed-cbc", "-K", key.hex(), "-iv", iv.hex(),
"-nopad", "-provider", "legacy", "-provider", "default"],
input=block, capture_output=True)
pt = pkcs7_strip(out.stdout)
print(f"[+] decrypted = {pt.decode()!r}")
print(f"[+] flag submit = {pt.decode().lower()!r}")
๋ณตํธ๋ ๋ฌธ์ฅ์ The Key is November Rain. ์ด๊ฒ ์ฑ์ด (์คํ์ด ๋ถ๊ฐ๋ฅํ ์กฐ๊ฑด ๋ค์ ์จ๊ฒจ์) ๋ณด์ฌ์ฃผ๋ ค๋ ๊ฐ์ด๋ค.
โถ๐ ์ ์ถ ์ฝ์ง โ ๋ณตํธ๋ ๋ฌธ์ฅ ๊ทธ๋๋ก๋ ์ค๋ต
๋ณตํธ๋ ๋ฌธ์์ด์ ๊ทธ๋๋ก ์ ์ถํ๋ค๊ฐ ์ค๋ต ์ฒ๋ฆฌ๋ฅผ ๋ฐ์๋ค. ๋ฌธ์ฅ์์ "ํค ์์ฒด"๋ง ๋ฝ์ November Rain์ผ๋ก๋ ์๋ํ์ง๋ง ์ด ์ญ์ ์ค๋ต์ด์๋ค. ์๋ฌธ์๋ก ๋ฐ๊พผ ์ ์ฒด ๋ฌธ์ฅ the key is november rain์ ๋ฃ์ผ๋ ์ ๋ต์ผ๋ก ์ฒ๋ฆฌ๋๋ค.
$ dreamhack.py wg 377 --flag "The Key is November Rain"
RESULT: ์ค๋ต/์คํจ
$ dreamhack.py wg 377 --flag "November Rain"
RESULT: ์ค๋ต/์คํจ
$ dreamhack.py wg 377 --flag "the key is november rain"
RESULT: ์ ๋ต! ํด๊ฒฐ ์ฒ๋ฆฌ๋จ๋ณตํธ ๋ก์ง ์์ฒด(ํคยทIVยท๋ธ๋ก ์ฒด์ด๋)๋ ์ฒ์๋ถํฐ ๋ง์๋ค๋ ๋ป์ด๋ผ, ๋งํ ์ง์ ์ ์ํธ๊ฐ ์๋๋ผ ์ ์ถ ๋ฌธ์์ด์ ํํ์๋ค. ์ ํํ ๋์๋ฌธ์ ๊ตฌ๋ถ ๋๋ฌธ์ธ์ง ๋ค๋ฅธ ์ ๊ทํ ๊ท์น์ด ์๋์ง๋ ํ๋ซํผ ๋ด๋ถ ๋ก์ง์ด๋ผ ๋จ์ ํ ์ ์์ง๋ง, ์ค์ ๋ก ํต๊ณผํ ๊ฑด ์๋ฌธ์ ๋ฒ์ ์ด์๋ค.
๐ ๊ฒฐ๋ก
ํด๋ผ์ด์ธํธ ๋ก์ง์ด "์คํ ๋ชป ํ๊ฒ" ๋ง์๋๋ ์ ์ ๋ถ์ ์์์๋ ์๋ฏธ๊ฐ ์๋ค. == ์ฐธ์กฐ๋น๊ต, ํ๋์ฝ๋ฉ๋ ์์, ๋๋ฌ ๋ถ๊ฐ๋ฅํ ๊ณผ๊ฑฐ ์๊ฐ โ ์ด ์ฑ์ ์ธ ๊ฐ์ง ์๋ก ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก UI ๊ฒฝ๋ก๋ฅผ ๋ด์ธํ๋ค. ํ์ง๋ง ๊ทธ ๋ด์ธ์ ์ ๋ถ "๋ฐํ์์ ๊ทธ ์ฝ๋๊ฐ ์คํ๋๋๊ฐ"์ ๊ดํ ๊ฒ์ด๋ผ, ์ฝ๋๋ฅผ ์คํํ์ง ์๊ณ ๊ทธ๋ฅ ์ฝ์ด์ ๊ฐ์ ๊ณ์ฐ์ ์ฐ๋ฆฌ ์์ผ๋ก ์ฌํํ๋ฉด ์๋ฌด ์๋ฏธ๊ฐ ์์ด์ง๋ค. ์ฑ์ ์ค์ ๋ก ์ค์นํด์ ๋๋ฌ๋ณผ ํ์ ์์ฒด๊ฐ ์์๋ค.
CBC๋ ๋ธ๋ก์ด ๋์ด๋๋ฉด ์ฒด์ด๋๋ ๊ฐ์ด ๋์ด๋๋ค. L01์ฒ๋ผ ์ ํจ ๋ธ๋ก์ด ํ๋๋ฟ์ด๋ฉด D(C) โ IV ํ ๋ฒ์ผ๋ก ๋๋์ง๋ง, ๋ธ๋ก์ด ์ฌ๋ฌ ๊ฐ๋ฉด ๋ ๋ฒ์งธ ๋ธ๋ก๋ถํฐ๋ ์ด์ ๋ธ๋ก์ ์ํธ๋ฌธ์ด XOR ํค๊ฐ ๋๋ค. ์ด ์ฌ์ค์ ๋์น๊ณ ๋ชจ๋ ๋ธ๋ก์ IV๋ก๋ง ๋ณตํธํ๋ฉด ์ฒซ ๋ธ๋ก๋ง ๋ง๊ณ ๋๋จธ์ง๋ ์ ๋ถ ๊นจ์ง๋ค.
์ ์ถ ํ์์ ๋ณตํธ ๊ฒฐ๊ณผ์ ๋ค๋ฅผ ์ ์๋ค. ์ํธ๋ฅผ ์ ํํ ํ์๋ค๊ณ ๋์ด ์๋๋ค. ๋์๋ฌธ์ยท๊ณต๋ฐฑยท๋ฌธ์ฅ ์ ์ฒด๋ ์ผ๋ถ๋์ฒ๋ผ, ๋ณตํธ๋ "๊ฐ"๊ณผ ํ๋ซํผ์ด ๊ธฐ๋ํ๋ "์ ์ถ ๋ฌธ์์ด"์ด ์ ํํ ๊ฐ์ ํํ๊ฐ ์๋ ์ ์๋ค๋ ๊ฑธ ์ด๋ฒ ๋ฌธ์ ์์ ํ์ธํ๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ