前言:
坛友们,年轻就是资本,和我一起逆天改命吧,我的学习过程全部记录及学习资源:https://www.52pojie.cn/thread-1582287-1-1.html
立帖为证!--------记录学习的点点滴滴
0x1 题目来源
1.攻防世界reserve练习区XSCTF联合招新赛(初赛):easyEZbaby_app。
2.题目描述
3. 看题目描述应该很适合我这样的菜鸟,锁定目标就可以开始分析了,拖进模拟器看看运行结果。
4.通过简单观察可以知道我需要输入正确的用户名和密码才能拿到flag。
0x2 静态分析
1.使用jadx-1.3.2工具反编译apk文件,找到onclick函数,可以看到是将我输入的用户名调用checkUsername校验,密码调用checkPass校验。
public void onClick(View view) {
String obj = this.username.getText().toString();
String obj2 = this.password.getText().toString();
if (!checkUsername(obj) || !checkPass(obj2)) {
Toast.makeText(this, "登录失败", 0).show();
return;
}
Toast.makeText(this, "登录成功", 0).show();
Toast.makeText(this, "flag{" + obj + obj2 + "}", 0).show();
}
2.先去看用户名校验函数,似乎使用了md5加密,加密的数据是zhishixuebao,然后执行了一个for循环,对加密后的数据取奇数位i+=2,1,3,5,7......这样,然后调用equals函数判断是否等于我输入的字符串。
public boolean checkUsername(String str) {
if (str != null) {
try {
if (!(str.length() == 0 || str == null)) {
MessageDigest instance = MessageDigest.getInstance("MD5");
instance.reset();
instance.update("zhishixuebao".getBytes());
String hexString = toHexString(instance.digest(), "");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hexString.length(); i += 2) {
sb.append(hexString.charAt(i));
}
String sb2 = sb.toString();
return (sb2).equals(str);
}
return false;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
return false;
}
3.再去看看校验密码函数,判断长度是不是等于15,后面也是一个for循环,这里给它化简255-i+2-98-未知数x需要等于'0'对应的ASCII值48,那么求x的值,x=111-i,而i的值就是从0到14,这样便可以计算出15位的密码。
public boolean checkPass(String str) {
if (str == null) {
return false;
}
char[] charArray = str.toCharArray();
if (charArray.length != 15) {
return false;
}
for (int i = 0; i < charArray.length; i++) {
charArray[i] = (char) ((((255 - i) + 2) - 98) - charArray[i]);
if (charArray[i] != '0' || i >= 15) {
return false;
}
}
return true;
}
4.基本上整个程序的逻辑就是这样的,只要想明白了,确实很简单。
0x3 动态调试
1.接下来动态调试一下,打开jeb,找到onclicl函数,ctrl+B下断点,输入admin和123456运行,可以看到经过md5加密后“zhishixuebao”变成了"7da5fec345fecde5fdcd641f68e0b6d1",经过和md5在线加密网站对比可以知道是标准的md5 32位加密。
2.然后继续单步走,可以看到v3就是for循环的下标,从0开始每次加2,取md5对应下标值存储,然后直接和我输入的用户名admin比较是否相等。
3.正常再单步走肯定跳转向失败了,所以强行修改返回值为true,继续往下,这里因为我输入的123456长度为6,长度必须为15,我这里直接修改,让他继续走。
4.继续看,v1就是for循环变量i,首先v3 = 255-i,然后再-98,然后再将我输入的123456第一个字符1给到v4,再v3=v3-v4,然后给v4赋值0x30,最后比较v3和v4是否相等。
5.接下来的跳转我强制改值让他跳转成功,是不是也能得到flag?flag{7afc4fcefc616ebdonmlkjihgfedcba}
0x4总结
1.通过前面的分析可以知道本题有两种解法,一种是简单直接的爆破,另一种就是计算用户名和密码,得到flag。
2.方法一:爆破,直接强制修改跳转,可参考0x3动态分析那里。
3.方法二:计算flag,用户名可照搬算法,密码按照刚刚的计算方程用代码表示即可。
package ctf;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class test01 {
public static void main(String[] args) {
//搬运原算法得到用户名
try {
MessageDigest instance = MessageDigest.getInstance("MD5");
instance.reset();
instance.update("zhishixuebao".getBytes());
String hexString = toHexString(instance.digest(), "");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hexString.length(); i += 2) {
sb.append(hexString.charAt(i));
}
String user = sb.toString();
System.out.println("用户名:"+user);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
//计算密码
StringBuilder pwd = new StringBuilder();
for (int i = 0; i < 15; i++) {
pwd.append((char) ((((255 - i) + 2) - 98) - 48));
}
System.out.println("密码:"+pwd);
}
private static String toHexString(byte[] bArr, String str) {
StringBuilder sb = new StringBuilder();
for (byte b : bArr) {
String hexString = Integer.toHexString(b & 255);
if (hexString.length() == 1) {
sb.append('0');
}
sb.append(hexString);
sb.append(str);
}
return sb.toString();
}
}
输出:
用户名:7afc4fcefc616ebd
密码:onmlkjihgfedcba
4.输入正确的用户名和密码也可以得到flag,和爆破的方式拿到的一样。
5.建议:可以让flag与前面的我输入的用户名或者密码一起参与计算,这样就没办法通过爆破得到flag。