[NUAACTF-2017] [Reverse]nuaactf解法

tty 2024-07-26 10:51:28 732 0


下载附件后解压得到 nuaactf.jar 使用 jdgui反编译


查看入口Main类


密码校验的逻辑只是简单做了字符比对 加密结果 caf4cbafdf72ce0f2f2eadc4309916e8c96f0de8


密码加密的逻辑比较复杂 但是由于长度有限且提示中有爆破 把这段逻辑copy出来后使用字典遍历执行 爆破出密码

下图为加密主逻辑


爆破代码如下

  // scala 脚本平台不支持选的java 将就看 
  private def dynamicClassLoader(): Unit = {
    val pass = "caf4cbafdf72ce0f2f2eadc4309916e8c96f0de8"
    val table = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    val tLen = table.length.toLong
    val len = Math.pow(tLen.toDouble,4).toLong
    println(len)
    0L.until(len).foreach(i=> {
      val key = table(((i/(tLen*tLen*tLen))%tLen).toInt).toString +
        table(((i/(tLen*tLen))%tLen).toInt).toString +
        table(((i/tLen)%tLen).toInt).toString +
        table((i%tLen).toInt).toString
      val passStr = dynamicClassLoaderPassDigest(key.getBytes).map(b=>String.format("%02x", b&0xff)).mkString
      if(pass.equals(passStr)){
        println(key + ":" + passStr)
      }
    })
}

  private def dynamicClassLoaderPassDigest(x: Array[Byte]): Array[Byte] = {
    val blks = new Array[Int](((x.length + 8 >> 6) + 1) * 16)
    var i = 0
    i = 0
    while (i < x.length) {
      blks(i >> 2) = blks(i >> 2) | x(i) << 24 - i % 4 * 8
      i += 1
    }
    blks(i >> 2) = blks(i >> 2) | 128 << 24 - i % 4 * 8
    blks(blks.length - 1) = x.length * 8
    val w = new Array[Int](80)
    var a = 1732584193
    var b = -271733879
    var c = -1732584194
    var d = 271733878
    var e = -1009589776
    i = 0
    while (i < blks.length) {
      val olda = a
      val oldb = b
      val oldc = c
      val oldd = d
      val olde = e
      for (j <- 0 until 80) {
        w(j) = if (j < 16) blks(i + j)
        else dynamicClassLoaderPassRol(w(j - 3) ^ w(j - 8) ^ w(j - 14) ^ w(j - 16), 1)
        val t = dynamicClassLoaderPassRol(a, 5) + e + w(j) + (if (j < 20) 1518500249 + (b & c | (b ^ 0xFFFFFFFF) & d)
        else if ((j < 40)) (1859775393 + (b ^ c ^ d))
        else (if ((j < 60)) (-(1894007588) + (b & c | b & d | c & d))
        else -(899497514) + (b ^ c ^ d)))
        e = d
        d = c
        c = dynamicClassLoaderPassRol(b, 30)
        b = a
        a = t
      }
      a += olda
      b += oldb
      c += oldc
      d += oldd
      e += olde

      i += 16
    }
    val digest = new Array[Byte](20)
    dynamicClassLoaderPassFill(a, digest, 0)
    dynamicClassLoaderPassFill(b, digest, 4)
    dynamicClassLoaderPassFill(c, digest, 8)
    dynamicClassLoaderPassFill(d, digest, 12)
    dynamicClassLoaderPassFill(e, digest, 16)
    digest
  }

  private def dynamicClassLoaderPassRol(num: Int, cnt: Int) = num << cnt | num >>> 32 - cnt

  private def dynamicClassLoaderPassFill(value: Int, arr: Array[Byte], off: Int): Unit = {
    arr(off + 0) = (value >> 24 & 0xFF).toByte
    arr(off + 1) = (value >> 16 & 0xFF).toByte
    arr(off + 2) = (value >> 8 & 0xFF).toByte
    arr(off + 3) = (value >> 0 & 0xFF).toByte
  }


执行结果如下


得到密码 mdzz 这个密码后面还有用所以这个爆破过程不能跳过


再看回主类里的flag逻辑部分 可以看到是通过 a14b64a0683594003b4efe8a2285acd8 类来加载其他类执行方法

a14b64a0683594003b4efe8a2285acd8类是重写的classloader 


使用爆破的密码作为key 使用固定字串的iv通过AES算法解码加密类数据还原为真实类

复制这段逻辑将其他无法反编译的类还原之后再反编译

具体代码如下

    // scala 脚本平台不支持选的java 将就看 
val passByte = "mdzz".getBytes val passStr = new String(passByte) println(passStr) val keyByte = Md5Utils.md5(passByte) val data = FileUtils.readStream(new FileInputStream("目录\\旧类名.class")) val target = AesUtils.decodeCbcPkcs5(data, keyByte, "****************".getBytes) FileUtils.writeStream(target, new File("目录\\旧类名_new.class"))


反编还原类译结果为


根据调用链路依次为


校验目标


加工步骤1


加工步骤2


原地返回

逆向整个过程得到flag值

代码如下

    // scala 脚本平台不支持选的java 将就看    val checkflag: Array[Byte] = Array(100, 106, 55, 53, 80, 48, 66, 0, 95, 81, 2, 55, 110, 108, 67, 54, 119, 51)
    val checkflag2: Array[Byte] = checkflag.zipWithIndex.map(b=>{
      if(b._2 % 3 == 0){
        (b._1 ^ 0x6).toByte
      }else{
        b._1
      }
    })
    val checkflag3: Array[Byte] = checkflag2.zipWithIndex.map(b=>{
      if((b._2+2) % 3 == 0){
        (b._1 ^ 0x33).toByte
      }else{
        b._1
      }
    })
    println(new String(checkflag3))


运行结果为 bY73c0D3_W17h_C0D3

按代码格式提交 nuaactf{bY73c0D3_W17h_C0D3} 返回失败 直接提交 bY73c0D3_W17h_C0D3 还是失败

提交 flag{bY73c0D3_W17h_C0D3} 返回成功

分类:Reverse
image
作者:tty

2

提交

100

收入

相关WriteUP

  • 2018网鼎杯3-babyre

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 3年前
  • EasyXor

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 1年前
  • EasyReverse

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 1年前
  • [NUAACTF-2017] [Reverse]robots解法

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 1年前
  • 2018-网鼎杯-advance

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 1年前
问题反馈