DDCTF 2018 Android WriteUp

RSA

垃圾代码比较多 
动态调试,跟随输入值进入,发现将输入放在了一个结构体中,然后用后八字节作为指针来控制 
要求len为31字节 
将输入逐字节异或了一个数组以后,遍历结果,要求满足一下关系式: 
a[i] == a[i+10] 
即一个10字节长度的字符串循环3遍 
通过校验以后将a[10]赋0,然后atoi转换为整型 
用一个大数5889412424631952987 去除这个整数,要求余数为0并且该整数小于商 
进行大数分解后发现有两个因数:1499419583<10> · 3927794789<10>

取较小的那个数,重复四遍后异或数组即可得到flag

a = [73, 90, 75, 10, 67, 92, 65, 80, 65, 75, 85, 93, 67, 13, 70, 64, 65, 1, 92, 6, 1, 89, 91, 14, 90, 82, 65, 93, 8, 94, 6]
r = "1499419583"*4
for i in range(31):
    print(chr(ord(r[i])^a[i]), end='')12345


Hello Baby Dex

jeb反编译后发现了大量第三方库,提取关键词robust搜索发现是热补丁技术 
查找assets文件夹发现一个BMP,拖出来查看发现是zip结构,解压得到DEX文件,反编译之,在MainActivity$1Patch类中发现了Onclick函数的热补丁 
与Invoke方法很类似,查找EnhancedRobustUtils.invokeReflectMethod 的详细说明可以获知各个参数的意义 
构造了一个String,然后将

DDCTF{ + Joseph(3,4) + Joseph(5, 6) + } 
通过append方法连接起来,最后通过equals进行比较

刚开始用原DEX中的Joseph方法算了半天,翻过来覆过去python java试了个遍就是不对,最后才发现也被热补丁了,气爆

Joseph的热方法在MainActivityPatch类中,调用了一堆add方法,懒得搞了直接动态调试,在最后equals方法处拦截得到flag

Robust的各个方法介绍和原理在https://juejin.im/post/58e4ce652f301e006227ab40有比较详细的说明,包括xxPatch类,xxPatchControl类的作用等等


Diffie-Hellman

也是垃圾代码比较多,跟第一题如出一辙 
动态调试找到几个核心关系

long long通过两个int型来保存,计算会麻烦一点,需要找到他们的真正关系

p 0xB4948 7B06AA40

mod_residual 0x1D026 744B3680

找到两个数p和mod_residual 
程序中对2进行了左移的死循环,每左移一位进行一次模p的比较,要求余数为mod_residual 
即满足下列关系式2

^n % 0xB49487B06AA40 == 0x1d026744b368012

爆破n,得到 208603


ECC

反编译发现被混淆的非常非常严重,简单逆向可知是ECC算法的secp256k1曲线,通过输入的私钥来计算公钥,要求公钥的两个整数拼接起来为00AF576186553C**B9224B738D89162F723BCFBF589CEF072A2C0ADA7B3443B5DC21D75144B89C87E3AC0BE030A1F5CE90E86F635D3E86271FB71375F5F581E9A2

为了爆破速度,找了一下午的C++的ECC实现,跑了一下发现计算出来的公钥不一样 
用Python的ECC库加密得到的公钥数也不一样,无奈只好回头继续逆APP

多次动态调试无果,跟踪混淆过后的程序太痛苦了 
代码导出到java中,也因为包名和类名的冲突而无法运行

纠结了许久乖乖静态分析,发现在org的某个库中有一些字符串,谷歌之终于找到该第三方库bouncycastle 
一个一个类去比对,最后完全还原整个函数调用过程,一运行发现公钥得到的两个数还是不同,心态爆炸 
突然发现IDE给了提示,这个函数被废弃了 

 1524403439524043209.png

于是找到getXCoord,结果终于相同 
开始爆破,安心睡觉 
第二天起来发现结果43458080

package me.company;
import java.math.BigInteger;
import java.security.spec.ECParameterSpec;
//import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;

import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X962NamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.asn1.sec.SECNamedCurves;
public class Main {

    public static void main(String[] args)
    {
        long n = 43450000;
        while (true) {
            if (c(n)) {
                break;
            }
            else{
                n++;
                if(n%10000==0)System.out.println(n);
            }
        }
        System.out.println("find it");
        System.out.println(n);
         // 43458080

    }

    public static boolean c(long i)
    {
        String m = "00AF576186553C**B9224B738D89162F723BCFBF589CEF072A2C0ADA7B3443B5DC21D75144B89C87E3AC0BE030A1F5CE90E86F635D3E86271FB71375F5F581E9A2";
        //getParameterSpec("secp256k1").;
        String input = String.valueOf(i);
        BigInteger test = new BigInteger(input.getBytes());
        //BigInteger test = new BigInteger("1");
        //System.out.println(test);
        X9ECParameters ecP = SECNamedCurves.getByName("secp256k1");
        ECPoint g = ecP.getG();
        //System.out.println(g);
        ECPoint p = g .multiply(test);
        p.getX();
        BigInteger x = p.getXCoord().toBigInteger();
        BigInteger y = p.getYCoord().toBigInteger();
        //System.out.println(x);
        //System.out.println(y);
        byte[] v3 = x.toByteArray();
        byte[] v4 = y.toByteArray();
        byte[] v5 = new byte[v3.length + v4.length];
        int v0_3;
        for(v0_3 = 0; v0_3 < v5.length; ++v0_3) {
            byte v2_1 = v0_3 < v3.length ? v3[v0_3] : v4[v0_3 - v3.length];
            v5[v0_3] = v2_1;
        }

        StringBuilder v2_2 = new StringBuilder();
        int v3_1 = v5.length;
        for(v0_3 = 0; v0_3 < v3_1; ++v0_3) {
            v2_2.append(String.format("%02X", Byte.valueOf(v5[v0_3])));
        }
        return v2_2.toString().equals(m);
    }
}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970


破解密钥

反而比前面的几个题目都简单=。= 
逆向发现so中对输入和一个数组进行了异或,结果要求和另一个数组相等 
关键在于异或的那个数组是动态生成的,静态看了一下SHA256乱七八糟的调用了一大堆很麻烦,干脆动态调试得了 
第一遍取到结果不对,回头看发现有取TraceID的地方 
通过动态从Libc中取到的open和read函数取到TraceID,然后根据TraceID的字符串来取SHA256的值

调试的时候将read出来的值改为TraceID:\t0 即可得到正确的数组,异或得flag


------------W&P CTF战队纳新------------

W&P名字来自于Whitecap100 CTF of Web&Pwn的缩写。而把Pwn放进战队名字是因当时参加了百度杯总决赛但因没有Pwn选手(当时我们只有三个Web选手)使我们从稳稳的第三一路掉到第八。希望能够牢记Pwn的重要性,几个团队创始人一拍即合决定以原先参加百度杯的人为基础组建一支属于自己的CTF战队。

现战队特向广大CTF爱好者,白帽子招收MISC、PPC、CRYPTO、PWN、REVERSE、WEB、STEGA各类赛题的选手。

当然最重要的是福利了

1.成为白帽100安全攻防实验室核心成员拥有各项特权

2.参加各类比赛,让你一战成名

3.提供就业机会,与多家公司有良好的合作关系。

4.团队专属徽章,彰显你的独特

5.奖金!!

6.过年过节小礼品什么的还是有的


有意加入的请将简历投至 #ctf@whitecap100.org#

本文作者:白帽100安全攻防实验室

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/70949.html

Tags:
评论  (0)
快来写下你的想法吧!

白帽100安全攻防实验室

文章数:22 积分: 52

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号