STMCTF18-Final Mobil Sorulari

basitmobil

jadxte açtığımda flag zaten karşımda.

basit

Flag : STMCTF{yazdan_kalan_s1caklarla_1s1nal1m}

benibul

Apkyı jeb’de açıyorum görüyorumki .flag.txt diye bir dosya oluşturuyor. İçine de binary->octal->decimal dönüşümünü yapıp değeri yazıyor.

jeb

Dosyaya bakmadan direk datayı alıp binary -> asci değerini de bakabilirsiniz. Dosyaya adb shell ile emulatore bağlanıp bakalım. (v24 istiyor ona göre vm açmak lazım)

sahte

Bu binary değerleri sahteymiş demekki apknın başka bir yerinde başka binary değerleri var.

cp benibul.apk benibul.zip
unzip benibul.zip
cd lib/x86

Komutları ile apknın native librarysine gidiyorum. Tahminimce yine 0101 gibi değer olduğu için grepliyorum.
Bulduğum değerleri asciye çevirdiğimde flag çıkıyor

gercek

Flag: STMCTF{?_evet_taraf1nd4n_!}

terslebeni

jadxte apkyı açıyorum. Görüyorumki bir karşılaştırma var. Uygulama bu karşılaştırmaya göre apkdeki bir componente setText ile text basıyor.

ters

R.string değerlerinin ne olduğunu Resources/resoueces.arsc/values/strings.xml dosyasından görebiliriz.

R.string.a UmxUbYYSllDKA1n+ZMR/sZXJ5ZglWOSfYGzfuVauT+o=
R.string.b R09IT01FIQ==
R.string.d Tebrikler, doğru bayrağı buldun
R.string.e Denemeye devam

Karşılatırmanın yapıldığı kod parçası şu şekilde

Base64.encodeToString( Util.a( obj,Util.a( new String( Base64.decode( MainActivity.this.getString(R.string.b),0)))), 0).trim().equals( MainActivity.this.getString(R.string.a)) != null

Kodu kısaltırsak

Util.a(new String(Base64.decode(MainActivity.this.getString(R.string.b), 0)))
first = Util.a(base64decode(R.string.b))
second = Util.a(obj,first)

Yani if’i içinde şu karşılatırma yapılıyor.
base64encode(second) == R.string.a

R.string.a değerini biliyoruz : UmxUbYYSllDKA1n+ZMR/sZXJ5ZglWOSfYGzfuVauT+o=

first ve second için Util classına bakalım

util

Birden fazla a fonksiyonu var. first ve second için hangi a fonksiyonun çalışacağını parametre sayısı belirleyecek. First için tek değerli yani md5li fonksiyon, second için alttaki aes encryption fonksiyonu çalışacak.
first’ün gittiği fonksiyon gelen değerin md5ini alıp dönen değerin 16 karaterini alıyor.

second’ın gittiği a fonksiyonun ilk parametrisi str, ikinci parametresi key. Bu parametrelerle ECB modunda aes encryption yapıyor.

yani first değerimiz md5(R.string.b)[:16] , bu değer de aes encryptiona key olarak gidiyor.

second’ın gittiği fonksiyonun ilk parametresi apkdan gelen input değeri. Bu değeri alıp constant key ile encrypt edip base64ünü aldığımızda R.string.a değeriyle aynı değeri bulmamız gerek. Tersten gidersek

base64decode(R.string.a) yı md5(R.string.b)[:16] ile decrypt edince flage ulaşabiliriz.

import base64
from Crypto.Cipher import AES
from hashlib import md5
a = "UmxUbYYSllDKA1n+ZMR/sZXJ5ZglWOSfYGzfuVauT+o="
b = "R09IT01FIQ=="
ciphertext = base64.b64decode(a)
key = base64.b64decode(b)
m = md5()
m.update(key)
key = m.hexdigest()[:16]
cipher = AES.new(key,AES.MODE_ECB)
cipher.decrypt(ciphertext)

Flag : STMCTF{!ben_evdey1m_zaten}

muslukcu

apkyı jadxte açınca görünüyorki sürekli bir log.d akışı var. STMCTF{ stringini arayınca tek bir yerde geçtiğini görüyorum ve koda gidiyorum.

muslukcu

skor değişkenine baktığımda aslında constant oldugunu görüyorum. Oyundaki skor score değişkeninde tutuluyor. Geriye touchx değeri kalıyor. Bu değer ekrana tıklandığında değişiyor aslında. Ama flagi hesaplarken ilk değeri olan 1000’i almak gerekiyor.
Bu bilgilerden sonra 2 yol kullanabiliriz.

  • Elle hesaplama
  • Gerekli condition’ı sağlayıp flagi log.d ye bastırabiliriz.

Elle hesaplarsanız .

>>> 48*13*5000*1000*999
3116880000000

STMCTF{} içine koydugunuzda yarışmadaki flage ulaşıyorsunuz.

ANCAK

Bu değer bir integer değişkeninin tutabilceği değerden yani 0xffffffff ‘den fazla. Yani aslında i değeri 5000 olduğunda apk’nın log.d ye basacağı değer farklı bir değer.
Bunu frida ile deneyebiliriz.

Kodum şu şekilde :

setImmediate(function() { //prevent timeout
console.log("[*] Starting script");
Java.performNow(function() {
Java.choose("mario.Board", {
onMatch: function (instance) {
console.log("Changed score")
instance.i.value = 5000
console.log(instance.touchx.value)
console.log(instance.skor.value)
console.log(instance.i.value)
},
onComplete: function (){

}
})
})
})

i değerini 5000 yapıyorum. Kodun olduğu fonksiyon sürekli çağırıldığı için trigger etmeme gerek yok. Çağırılmasaydı instance.checkLife() ekleyerek bunu sağlayabilirdik.
Değer değiştikten sonra condition sağlandığı için bana log.d den flagi vermesi lazım.

frida
Evet istenen değerler olmasına rağmen farklı bir değer çıktı.
Bu değere şu şekilde ulaşıp emin olabiliriz.

>>> 48*13*5000*1000*999 & 0xffffffff
3028710400
>>> 3028710400-4294967295 -1
-1266256896

cokzararli

Bu soruda frida için bi check beklerdim açıkcası. Çünkü kontrol yapılmazsa gerçekten çok kolay bir şekilde geliyor soru. (Intented yolda çok zor değil tabi)

Apkda keylogger classındaki kod şu şekilde :

uret

Eğer istenen conditionlar sağlanırsa native kütüphaneden x , y ve z fonksiyonları çağırılarak flag üretiliyor. E o zaman çağıralım bu fonksiyonları :D

setImmediate(function() { //prevent timeout
console.log("[*] Starting script");
Java.performNow(function() {
Java.choose("tr.com.stm.cokzararli.Keylogger", {
onMatch: function (instance) {
console.log("Found instance")
console.log(instance.x())
console.log(instance.y())
console.log(instance.z())

},
onComplete: function (){

}
})
})
})

urett