5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...

admin 2022年4月1日11:19:59评论56 views字数 7724阅读25分44秒阅读模式


小花吐槽:


这是特意为成都沙龙设立的现场CTF。


拿到题目之后我问麦香,难么?

他邪魅一笑,不难啊,就是脑洞有点大

天真的我以为真的不难,为此设立了闯关成功的10名大奖,希望大家开开心心满载而归。


没想到,整整五小时,没人能够拿到唯一的FLAG。

千元大奖又被我邮寄回了北京。。。


厉害了我的麦香同学。

恭喜你在跨次元CTF夺得第一。

下次出题我们脑洞可以再小点哦:)


5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...



题目地址如下

http://btctf.secbox.cn/



这套简单的CTF主要由三部分组成。Web、Misc、Apk,每个部分都有一个小坑~

0x01Web

思路很简单,就是一个PHP弱类型 php在不加strict的情况下,不会对类型进行严格比较,比较坑的点就是加了Salt,需要写脚本去爆破下。

脚本如下

import hashlib

import string

import itertools

import sys

 

defmd5(string):

    m1 = hashlib.md5()

    m1.update(string)

    return m1.hexdigest()

prefix= "0e"

 

fori in itertools.product(string.ascii_letters,repeat = int(sys.argv[1])):

    pw = "".join(i)

    password1 = md5(pw) + "SALT"

    password = md5(password1)

 

    if password[:2] == prefix:

        if password[2:].isdigit():

            print pw

            print password1

print password

 

或者

import string

import hashlib

import itertools

 

def md5(strs):

    m1 = hashlib.md5()

    m1.update(strs)

    return m1.hexdigest()

 

def ver(passwd):

    pwd = md5(passwd) + "SALT"

    pwd_1 = md5(pwd)

 

    if pwd_1[:2] == '0e':

        if pwd_1[2:].isdigit():

            return True

 

def go(num):

    for i initertools.product(string.ascii_letters, repeat=int(num)):

        if ver(''.join(i)):

            print ''.join(i)

            exit()

 

go(9)

最后过关,拿到下一关的地址。5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...0x02 Misc


Misc首先查看源代码,可以看到两个提示,tieba&机器人。

仔细推测可能是贴吧&robots

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...


我们使用贴吧的高级搜索

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...

http://tieba.baidu.com/p/4827665506?pid=99279354524&cid=0#99279354524

找到了一个有关系的帖子,帖子上提示了一个二维码,扫描发现是一串无意义的字符串,但是同时帖子里还给出了一张鸟叔的照片,这时候就可以尝试搜索psy(鸟叔的英文名)+QRCODE(二维码英文)。搜索到了一个二维码隐写软件,Psytec QR Code,尝试解密二维码发现需要密码。于是继续从之前的提示下手。

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...


在robots.txt中发现passwd文件夹,访问后发现了一串64位的字符串:7c43bc96790717a772d50754f9f1f7e6a8db1d82db78ed452ba0882fb9554fc9

明显是两个Md5,解出来拼凑成butianctf.

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...

顺利拿到第三关的apk地址。
0x03 Apk
下面主要说下Apk的解题思路。

运行apk,点击屏幕中央的按钮,提示“Youneed to press tha button harder”。使用jeb打开apk,找到MainActivity类的onCreate函数中,按钮设立的监听器类为b,并且传入了textview对象和MainActivity类本身。

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...

跟踪b类的onClick函数,如下,如果步骤中的任何一部出现异常,就会输出“You need to press tha button harder”

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...


我们一步一步看,首先调用了“private byte[] a(String arg4)”函数,如下图,可以看出这个函数主要是用于读取assets目录下的文件,并返回文件内容。

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...


随后调用了函数“private byte[] a(byte[] arg7)”,如下图,这个函数看起来就比较复杂了,它其中调用了d类的“byte[] a(byte[] arg15, int arg16, int[]arg17, int arg18)“函数,我们先看一下d类的这个函数

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...


d类中的“byte[] a(byte[] arg15, int arg16, int[] arg17, int arg18)”函数如下图,看起来像某个加密算法,对其中的魔数0x9E3779B9进行搜索,搜索结果均指向tea加密,如下图

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...

通过比对加密算法,这部分算法是tea算法的解密算法。因此上面的整体流程就清晰了:首先读取assets目录下的nothere文件的内容,然后读取assets目录下flag文件的内容,随后对flag文件的内容进行tea算法解密,密钥在类初始化的时候写明,如下图,之后将nothere文件内容与flag文件解密后内容进行对比,如果相同则会进入下一步流程,不相同或文件不存在则显示“You need to press tha button harder”。

密钥:5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...

我们看一下下一步的流程,其将nothere的内容和apk中classes.dex的crc校验值传入类a的a函数,并且输出,推测该做法是为了防止修改dex文件,该函数并没有做多余的检查,推测该函数运行完成后会得到flag。

下面有两条道路,首先解密出nothere文件的内容,然后将nothere拷贝到apk的assets目录下,重打包运行即可;另一个思路是解密出nothere文件的内容,取出classes.dex文件的crc校验,复制出类a的a函数编译运行即可。

考虑到本题目名字为rererepack,因此采取第一种重打包的方法。

tea解密代码如下,参考http://www.blogjava.net/orangehf/archive/2007/10/12/152328.html

Tea.java

/**
  * Tea
算法
  * 每次操作可以处理8个字节数据
  * KEY为16字节,应为包含4个int型数的int[],一个int为4个字节
  * 加密解密轮数应为8的倍数,推荐加密轮数为64轮
  */
 
class Tea {
    
//加密
    
byte[] encrypt(byte[] content, int offset, int[] key, int times) {//times为加密轮数
        
int[] tempInt = byteToInt(content,  offset);
        
int y = tempInt[0], z = tempInt[1], sum = 0, i;
        
int delta = 0x9e3779b9; //这是算法标准给的值
        
int a = key[0], b = key[1], c = key[2], d = key[3];
 
        
for (i = 0; i < times; i++) {
 
             sum += delta;
             y += ((z <<
4) + a) ^ (z + sum) ^ ((z >> 5) + b);
             z += ((y <<
4) + c) ^ (y + sum) ^ ((y >> 5) + d);
         }
         tempInt[
0] = y;
         tempInt[
1] = z;
        
return intToByte(tempInt, 0);
     }
 
    
//解密
    
byte[] decrypt(byte[] encryptContent, int offset, int[] key, int times) {
        
int[] tempInt =  byteToInt(encryptContent, offset);
        
int y = tempInt[0], z = tempInt[1], sum = 0, i;
        
int delta = 0x9e3779b9; //这是算法标准给的值
        
int a = key[0], b = key[1], c = key[2], d = key[3];
        
if (times == 32)
             sum =
0xC6EF3720; /* delta << 5*/
        
else if (times == 16)
             sum =
0xE3779B90; /* delta << 4*/
        
else
            
sum = delta * times;
 
        
for (i = 0; i < times; i++) {
             z -= ((y <<
4) + c) ^ (y + sum) ^ ((y >> 5) + d);
             y -= ((z <<
4) + a) ^ (z + sum) ^ ((z >> 5) + b);
             sum -= delta;
         }
         tempInt[
0] = y;
         tempInt[
1] = z;
 
        
return intToByte(tempInt, 0);
     }
 
    
//byte[]型数据转成int[]型数据
    
private int[] byteToInt(byte[] content, int offset) {
 
        
int[] result = new int[content.length >> 2];//除以2的n次方 == 右移n位  即 content.length / 4 == content.length >> 2
        
for (int i = 0, j = offset; j < content.length; i++, j += 4) {
             result[i] = transform(content[j  +
3]) | transform(content[j + 2]) << 8 |
                     transform(content[j  +
1]) << 16 | (int) content[j] << 24;
         }
        
return result;
 
     }
 
    
//int[]型数据转成byte[]型数据
    
private byte[] intToByte(int[] content, int offset) {
        
byte[] result = new byte[content.length << 2];//乘以2的n次方 == 左移n位  即 content.length * 4 == content.length << 2
        
for (int i = 0, j = offset; j < result.length; i++, j += 4) {
             result[j +
3] = (byte) (content[i] & 0xff);
             result[j +
2] = (byte) ((content[i] >> 8) & 0xff);
             result[j +
1] = (byte) ((content[i] >> 16) & 0xff);
             result[j] = (
byte) ((content[i] >> 24) & 0xff);
         }
        
return result;
     }
 
      
//若某字节为负数则需将其转成无符号正数
    
private static int transform(byte temp) {
        
int tempInt = (int) temp;
        
if (tempInt < 0) {
             tempInt +=
256;
         }
        
return tempInt;
     }
 
 }

 

TeaMain.java

import java.io.*;
 
import java.nio.ByteBuffer;
 
import java.nio.channels.FileChannel;
 
 
public class TeaMain {
    
private Tea tea;
    
private int[] KEY = new int[]{//加密解密所用的KEY
            
0xdfabcbfa, 0xfdbaecba,
            
0xedbafced, 0xbfaecfda
    
};
 
    
public TeaMain() {
        
this.tea = new Tea();
     }
 
    
//通过TEA算法加密信息
    
public byte[] encryptByTea(byte[] temp) {
        
int n = 8 - temp.length % 8;//若temp的位数不足8的倍数,需要填充的位数
        
byte[] encryptStr = new byte[temp.length + n];
         encryptStr[
0] = (byte) n;
         System.arraycopy(temp,
0, encryptStr, n, temp.length);
        
byte[] result = new byte[encryptStr.length];
        
for (int offset = 0; offset < result.length; offset += 8) {
            
byte[] tempEncrpt = tea.encrypt(encryptStr, offset, KEY, 32);
             System.arraycopy(tempEncrpt,  
0, result, offset, 8);
         }
        
return result;
     }
 
    
//通过TEA算法解密信息
    
public byte[] decryptByTea(byte[] secretInfo) {
        
byte[] decryptStr = null;
        
byte[] tempDecrypt = new byte[secretInfo.length];
        
for (int offset = 0; offset < secretInfo.length; offset += 8) {
             decryptStr =
tea.decrypt(secretInfo, offset, KEY, 32);
             System.arraycopy(decryptStr,  
0, tempDecrypt, offset, 8);
         }
 
        
int n = tempDecrypt[0];
        
byte[] result = new byte[decryptStr.length - n];
         System.arraycopy(tempDecrypt,  n, result,
0, decryptStr.length - n);
 
//        return new String(tempDecrypt, n,  decryptStr.length - n);
        
return result;
     }
 
    
public byte[] readFile(String path) throws IOException {
         File file =
new File(path);
         FileInputStream in =
new FileInputStream(file);
         FileChannel channel =  in.getChannel();
         ByteBuffer byteBuffer =  ByteBuffer.allocate((
int) channel.size());
         
while ((channel.read(byteBuffer)) > 0) {
         }
         channel.close();
         in.close();
        
return byteBuffer.array();
     }
 
    
public void writeFile(String path, byte[] data) throws IOException {
         File file =
new File(path);
         FileOutputStream out =
new FileOutputStream(file);
         out.write(data);
         out.close();
     }
 
    
public static void main(String[] args) throws IOException {
         System.
out.println("Hello World!");
 
         TeaMain main =
new TeaMain();
 
        
byte[] encryptInfo = main.readFile("/Users/alset/Desktop/flag");
 
        
byte[] decryptInfo =  main.decryptByTea(encryptInfo);
         System.
out.print("解密后的数据:");
         System.
out.println(decryptInfo);
        
for (byte i : decryptInfo)
             System.
out.print(i + " ");
         System.
out.println();
         main.writeFile(
"/Users/a/Desktop/nothere", decryptInfo);
 

    
}
 
 
 }

 

用unzip将apk解压,拷贝assets目录下的flag文件至桌面,运行上面的TeaMain,即可得到解密后的nothere文件。

将生成的nothere文件拷贝到assets目录下,在apk解压目录下使用“zip -r app.apk .”命令重打包,再使用signapk.jar签名,即可安装到手机上,点击按钮将显示flag。

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...


提示:使用jeb1的快捷键b可以快速修改整数的显示方式。 

5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...


原文始发于微信公众号(补天漏洞响应平台):5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月1日11:19:59
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   5h没人做出来的脑洞大开折磨人CTF,思路原来是这样...http://cn-sec.com/archives/813165.html

发表评论

匿名网友 填写信息