0x00
下載下來是一個apk,拖進模擬器運行一下,是一個輸入框,輸入flag然後檢查flag是否正確。
改後綴爲zip後使用dex2jar反編譯得到一個classes-dex2jar.jar。
0x01
將這個jar拖進jd-gui查看一波反編譯出來的java代碼,得到如下核心代碼:
protected static boolean checkflag(String paramString) {
String[] arrayOfString = paramString.split("_");
System.out.println("xxx1"+":"+String.format("%d", new Object[] { Integer.valueOf(arrayOfString.length) }));
if (arrayOfString.length == 4 && paramString.length() == 27) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < arrayOfString.length; i++) {
String str2 = reChange(arrayOfString[i]);
System.out.println(str2);
String str1 = str2.substring(0, i + 1);
str2 = str2.substring(i + 1);
str1 = reChange(str1);
str2 = reChange(str2);
stringBuilder.append(str1);
stringBuilder.append(str2);
}
BigInteger bigInteger1 = new BigInteger(stringBuilder.toString(), 16);
BigInteger bigInteger2 = new BigInteger("A3332C65844CC6F5E5DABE8DD42FDE39", 16);
if (bigInteger1.pow(65537).mod(bigInteger2).toString(16).compareTo("6d14c92d4ae244b583227f1e870f57bc") == 0)
return true;
}
return false;
}
public static String reChange(String paramString) {
char[] arrayOfChar = paramString.toCharArray();
int j = arrayOfChar.length;
for (int i = 0; i < j / 2; i++) {
char c = arrayOfChar[i];
int k = j - 1 - i;
arrayOfChar[i] = arrayOfChar[k];
arrayOfChar[k] = c;
}
return String.valueOf(arrayOfChar);
}
0x02邏輯分析:
首先是一系列的字符變換,這個先不管,看最後一句
bigInteger1.pow(65537).mod(bigInteger2).toString(16).compareTo("6d14c92d4ae244b583227f1e870f57bc"
RSA實錘了。大體就是將所給的字符串經過字符變換之後轉換爲16進制大整數通過RSA加密。
所以需要先將RSA密文還原爲明文,再根據字符變換逆回flag。
0x03攻擊RSA
在這裏,我開始的思路是猜測私鑰不會太大,使用已知明文攻擊找私鑰,但跑了一個小時還是沒出來,所以我估計私鑰不會太小。
此處RSA的n:"A3332C65844CC6F5E5DABE8DD42FDE39"和e:"65537"已經給出,故嘗試分解n得到p,q,再根據p,q和e得到私鑰d。
0x04暴力分解N
此處可以使用在線站點,也可以使用RSAtools:
此處使用RSA-tool2:在N處輸入模數N,如果是16進制,右上角base選擇16,然後FactorN,得到p:12004517743563056963
q:18070686016358995667
0x05根據p,q,e計算私鑰d
此處給出已知p,q,e求d的python3代碼:
#已知p,q,e,求解密密鑰d
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise Exception('模數分解失敗')
else:
return x % m
def main():
e=65537
p=12004517743563056963
q=18070686016358995667
d = modinv(e,(p-1)*(q-1))
print(d)
if __name__ == '__main__':
main()
運行之後得到:d=72585793360129405920426096641020292037
0x06根據d與密文c計算明文m
RSA的解密數學公式爲:
明文m=c^d (mod N)
這裏^指求冪。
其他博客用的是大整數計算工具big Integer Calculator,這個工具我沒找到,直接使用python3的pow函數:
pow(c,d,N):
計算得到明文m=62823249404463802755091465448,轉爲16進制:cafe2c86588df9d0798304e8
0x07回到java,根據字符變換逆回flag。
在checkflag處打斷點,輸入"abcd_ef_ghijk",使用F5步進查看變量變化情況以觀察其邏輯,得到如下邏輯:
1.根據下劃線將字符串分爲3組
2.分別對每個分組根據組號作爲長度做字符串反轉
3.分別對每個分組根據組號作爲長度做字符串切割
4.拼接操作得到結果:
d abc ef ijk gh
0x08根據字符變換邏輯寫出逆向算法:
根據上述邏輯,該字符串分爲4組,且第 i 組長度至少爲 i , 否則會發生數組下標越界。
且明文長度爲24,推測每組6個字符。如果每一組的長度不等,或許還有其他flag也能通過驗證。
根據上述邏輯,將cafe2c86588df9d0798304e8分爲4組,第一組取第一個字符拼接到第一組末尾,第二組取前兩個字符拼接到第二組末尾,3,4同理。最終得到flag:afe2cc_588d86_079f9d_e88304