[2022HWS硬件安全冬令营 X DASCTF Jan][CRYPTO] Accelerate your time
[CRYPTO] Accelerate your time
flag{80d0169d22da3c35}
Android 逆向分析
-
拿到apk首先解压反编译
dex
,此步骤省略。其登录流程很简单,也可以从源码轻易的读出来:首先验证用户名和密码,接下来验证时间。首先对时间进行
md5
编码,之后加上用户名密码再进行一次编码然后对产生的第二个信息摘要进行判断。首先要解决登陆的账户问题。(笔者用大量的实验证实了乱填账户密码是不对的) -
首先破解账户和密码
然后与不出所料我试了好长时间差点开始穷举的时候,我看到了data
模块。其中含有一个关键的,看上去很可疑的一段代码:即
public final Result<LoggedInUser> login(String paramString1, String paramString2, String paramString3) { Intrinsics.checkParameterIsNotNull(paramString1, "username"); Intrinsics.checkParameterIsNotNull(paramString2, "password"); Intrinsics.checkParameterIsNotNull(paramString3, "trandmark"); //... if (Intrinsics.areEqual(paramString1, paramString3)) { paramString3 = new int[7]; int i = 0; int k; for (int j = 0; ; j++) { k = i; if (j > 6) break; paramString3[j] = (paramString1.charAt(j) ^ paramString2.charAt(j)); } while (k <= 6) { if (new int[] { 6, 28, 1, 19, 27, 5, 29 }[k] != paramString3[k]) break; if (k == 6) //return success..
随即可以先把这部分逆向出来。这段代码的逻辑很简单,就是一个简单的异或加密。只是这里的
trandmark
字串的值要从反编译的包中找到,是Android
。注意到满足success
的返回值代码段上的if
构成的断言,这暗示我们用户名就是Android
,原因是areEqual(paramString1, paramString3)
,即trandmark
和username
的值相同。写一个小脚本就可以获得password:
int main(int argc, char** argv) { int code[] = { 6, 28, 1, 19, 27, 5, 29 }; char trandmark[] = { "Android" }; for (int i = 0; i < 7; i++) { printf("%d | %c\n", code[i] ^ trandmark[i], code[i] ^ trandmark[i]); /* 输出 * 71 | G 114 | r 101 | e 97 | a 116 | t 108 | l 121 | y */ } return 0; }
那么知道了
password
字段的值是Greatly
。实验表明这个登录凭据是被接受的,因此这里我们猜对了。(事实上笔者没有那么幸运,我只是把整个com.flag.reverse.c
的源码都大致看了一遍,找到了调用栈,这才顺藤摸瓜解出来了) -
暴力穷举时间。这里的时间是通过两次
md5
加密生成的,换句话说,由于可能加了盐,因此碰撞失效。观察关键代码:private final void updateUiWithUser(LoggedInUserView paramLoggedInUserView) { Intrinsics.checkExpressionValueIsNotNull(getString(2131558465), "getString(R.string.welcome)"); paramLoggedInUserView.getDisplayName(); paramLoggedInUserView = new StringBuilder(); paramLoggedInUserView.append(String.valueOf(this.hour)); paramLoggedInUserView.append(String.valueOf(this.minute)); paramLoggedInUserView.append(String.valueOf(this.second)); paramLoggedInUserView = LoginActivityKt.encodeMD5(paramLoggedInUserView.toString()); StringBuilder localStringBuilder = new StringBuilder(); localStringBuilder.append("flag{"); localStringBuilder.append(paramLoggedInUserView); localStringBuilder.append("}"); localStringBuilder.append((EditText)_$_findCachedViewById(R.id.username)); localStringBuilder.append((EditText)_$_findCachedViewById(R.id.password)); if (Intrinsics.areEqual(LoginActivityKt.encodeMD5(localStringBuilder.toString()), getString(2131558446))) Toast.makeText(getApplicationContext(), (CharSequence)"Congulations, You got the secert code", 1).show(); else Toast.makeText(getApplicationContext(), (CharSequence)"Yeah, you are logged in but the code is still hidden under the mist", 1).show(); }
前面首先通过控件
Calender
获取当前的时间,并且不补零,24小时制生成一个时间拼接字符串计算md5。然后使用localStringBuilder.append("flag{"); localStringBuilder.append(paramLoggedInUserView); localStringBuilder.append("}"); localStringBuilder.append((EditText)_$_findCachedViewById(R.id.username)); localStringBuilder.append((EditText)_$_findCachedViewById(R.id.password));
进行拼接。这里前三行很好懂,后两行事实上进行了一定的强制转换。
_$_findCachedViewById
可以说是一个取代findById
的语法糖,他获取一个id并且返回一个View。这里的EditText
强制转换读取了这个控件里的值,并且接到字符串的尾部,像flag{xxx}AndroidGreatly
一样因此知道了基本的操作,又知道了md5,由于穷举的成本只有时间,也就是
24\*60\*60 = 145440
次,因此是可以接受的,甚至可以加一个好看的格式化。脚本如下:import hashlib def md5value(key): input_name = hashlib.md5() input_name.update(key.encode()) #input_name.update(key.encode("utf-8")) return (input_name.hexdigest())[8:24].lower() for i in range(0,24,1): for j in range(0,60,1): for k in range(0,60,1): targetstr = str(i)+str(j) +str(k) str1 = md5value(targetstr) str2 = 'flag{' + str1 + '}' + 'AndroidGreatly' #Username = Android #Password = Greatly str3 = md5value(str2) print(targetstr,' # ',str1,' # ',str2,' # ',str3) if(str3 == '1a9852e856816224'): print("found time:" +targetstr) input()
最终生成
然后提交对应的时间加密的
flag
即可
作者发布、转载的任何文章中所涉及的技术、思路、工具仅供以安全目的的学习交流,并严格遵守《中华人民共和国网络安全法》、《中华人民共和国数据安全法》等网络安全法律法规。
任何人不得将技术用于非法用途、盈利用途。否则作者不对未许可的用途承担任何后果。
本文遵守CC BY-NC-SA 3.0协议,您可以在任何媒介以任何形式复制、发行本作品,或者修改、转换或以本作品为基础进行创作
您必须给出适当的署名,提供指向本文的链接,同时标明是否(对原文)作了修改。您可以用任何合理的方式来署名,但是不得以任何方式暗示作者为您或您的使用背书。
同时,本文不得用于商业目的。混合、转换、基于本作品进行创作,必须基于同一协议(CC BY-NC-SA 3.0)分发。
如有问题, 可发送邮件咨询.