鸣谢大佬joker帮忙分析,真大佬!
前言:
论坛未开启Markdown语法支持,如果想要更好的阅读,可以下载文末附件,使用浏览器打开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。
使用道具 举报