勇攀高峰flag夺旗赛相关题目writeup

admin 2022年10月19日08:03:29勇攀高峰flag夺旗赛相关题目writeup已关闭评论47 views字数 10159阅读33分51秒阅读模式
图片

雷迪森and乡亲们

经过了8月的洗礼

我们迎来了9月的奋斗季

在8月

我们举办了勇攀高峰flag夺旗赛

大家在云演

留下了自己的脚步

有人在中途倒下

也有人成功登顶

拿到了属于自己的奖励

今天

为大家揭秘

在攀登途中

我们遇到的那些难题

图片

可爱的表情emoji

【题目难度】

★★

【考察知识点】

Base解密 zlib

【解题步骤】

第一步

下载题目,打开发现是一堆表情。

图片

第二步

根据题目描述判断是base100加密,使用如下在线网站base100解密:http://www.atoolbox.net/Tool.php?Id=936

图片

得到:

GY2TIQJXG42EKNZZGRBTOMZUIU3DONBRGQYTIOJUGI2EKNBTGU3DIQJUHE3DIMZYG44DONJUGU3DOMZSG4ZDMOJUGI3DGNJYGY2DOMZTGMZEMNJYGU2TERRVGU3TCNZZGZATMNBUGI3EMNZZGQ4TINBWIY2DSMZTGRCTEQRUIYZTSNJVGVATGNJXGY2TKNZUGM2TIRRTGQ2EGNJXG43TGRBTIQ======

第三步

解码后发现有=符号,尝试base64后是乱码,尝试使用在线网站解密base32:https://www.qqxiuzi.cn/bianma/base.php

图片

得到:

654A774E794C734E67414149424E43564A496438787545673272694263586473332F58552F5571796A64426F7949446F49334E2B4F39555A35765574354F344C57773D3D

第四步
解出的全为大写字母,为base16解密,使用在线网站进行base16解密:

https://www.qqxiuzi.cn/bianma/base.php?type=16

图片

得到:

eJwNyLsNgAAIBNCVJId8xuEg2riBcXds3/XU/UqyjdBoyIDoI3N+O9UZ5vUt5O4LWw==

第五步
解出的字符有=符号,但是尝试base解密均失败,使用CyberChef工具base64和zlib组合解出flag(在线网站:https://gchq.github.io/CyberChef/)。

图片

然后,flag到手!
图片

animals

【题目难度】

      ★★★

【考察知识点】

反编译, java,smali语法,AES,DES,Base64加密

【解题步骤】

第一步

题目信息:安装apk软件到模拟器,页面只有一段提示语:请开始你的寻找,如图:
图片
使用JEB打开程序进行反编译,有一个HelloJni派生自主Activity:
图片
public class HelloJni extends Activity {
    public static int o;
    public static int p;
    public static Bitmap t;
    public void onCreate(Bundle arg4) {
        super.onCreate(arg4);
        TextView v0 = new TextView(((Context)this));
        v0.setText("请开始你的寻找");
        this.setContentView(((View)v0));
        HelloJni.t = BitmapFactory.decodeResource(this.getResources(), 0x7F020001);
    }  
在oncreate函数中,定义了一个Textview控件,并显示出来,同时还赋值给Bitmap静态变量。类中还定义了一个a函数,用来解析读取的资源文件,代码如下:
public void a() {
        int v12 = 25;
        int v4 = HelloJni.t.getHeight() / 25;
        int v5 = HelloJni.t.getWidth() / 25;
        byte[] v3 = new byte[v12];
        HelloJni.o = v4;
        HelloJni.p = v5;
        int v7 = 10;
        int v8 = 20;
        Boolean.valueOf(String.valueOf(v4).equals(String.valueOf(v5)));
        int v9;
        for(v9 = 0; v9 < v12; ++v9) {
            int v6 = HelloJni.t.getPixel(v8, v7);
            v8 += v5;
            v7 += v4;
            if(v9 % 3 == 0) {
                v3[v9] = ((byte)Color.red(v6));
            }
            else if(v9 % 3 == 1) {
                v3[v9] = ((byte)Color.green(v6));
            }
            else {
                v3[v9] = ((byte)Color.blue(v6));
            }
        }
    }
最后函数还载入了一个so库的hello-jni,同时还有两个native层的原生方法:
public native String stringFromJNI(String arg1, int arg2, int arg3) {
}
public native String unimplementedStringFromJNI() {
}
由于该activity再无其他任何可用的信息;且该APK中只有两个activity,于是有一个真正的activity被藏在另一个activity中。

第二步

在JEB中,能够看到另一个的activity代码如下:
public class animals extends Activity {
    public static byte[] a;
    public static byte[] g1;
    private String h;
    public static mainctivity kk;
    private int m;
    private int n;

      static {
        animals.a = new byte[20];
        animals.kk = new mainctivity();
        animals.g1 = new byte[]{0x76, 89, 71, 81, 0x76, 103, 107, 49, 55, 43, 0x30, 69, 78, 57, 80, 0x73, 67, 120, 83, 75, 71, 81, 61, 61};
    }

    public animals() {
        super();
    }

protected void onCreate(Bundle arg3) {
        super.onCreate(arg3);
        this.setContentView(0x7F030000);
        this.findViewById(0x7F080002);
        animals.textview = this.findViewById(0x7F080000);
    }
public boolean onCreateOptionsMenu(Menu arg3) {
        this.getMenuInflater().inflate(0x7F070000, arg3);
        return 1;
    }

protected void onResume() {
        super.onResume();
        this.m = HelloJni.o;
        this.n = HelloJni.p;
        String.valueOf(this.m).equals(String.valueOf(this.n));
    }
protected void onStart() {
    super.onStart();
    animals.text = new HelloJni();
    new animals().l();
}

在OnClick函数中,定义了一个按钮,以及一个可输入的文本框,即此处可能是输入flag并提交的地方,通过使用am指令调用此activity进行验证:
图片
验证成功,此activity才是题目的入口点,随意输入内容进行提交测试,如下:
图片
按钮中设置了监听函数,但在oncreat函数未得到体现,经过寻找,即按钮的响应函数定义在控件当中
图片
因此,按钮的响应函数为a函数,跟进a函数查看,代码结构如下:
public void a(View arg9) {
        String v4 = animals.textview.getText().toString();
        if(animals.kk.Checkable(v4)) {
            animals.a = new HelloJni().stringFromJNI(v4, this.m, this.n).getBytes();
            animals.t = new mainctivity();
            mainctivity.a = animals.a;
            try {
                if(animals.t.off().booleanValue()) {
                    animals.t.ss(mainctivity.a);
                    if(Boolean.valueOf(new String(mainctivity.a).trim().equals(new String(animals.g1))).booleanValue()) {
                        Toast.makeText(((Context)this), "congratulations", 0).show();
                        return;
                    }

                    Toast.makeText(((Context)this), "加油~~~~加油", 0).show();
                    return;
                }

                Toast.makeText(((Context)this), "努力~~~~努力", 0).show();
                return;
            }
            catch(BadPaddingException v0) {
                goto label_65;
            }
            catch(NoSuchAlgorithmException v0_1) {
                goto label_56;
            }
            catch(NoSuchPaddingException v0_2) {
                goto label_59;
            }
            catch(IllegalBlockSizeException v0_3) {
                goto label_62;
            }
            catch(InvalidKeyException v0_4) {
                goto label_48;
            }
        }

        Toast.makeText(((Context)this), "好像不对哦~~~~", 0).show();
        return;

输入的内容能够存入到字符串v4当中,之后进行if条件判断,当判断全部通过时,flag为正确。分析第一个if条件判断,如下:
图片
字符串v4被传入到的Checkable函数,返回真时,条件判断才能够通过;跟进Checkable函数进行查看:
图片
即函数会限定输入的字符长度为20,因此flag为20位。当第一个if判断通过后,会调用原生方法stringFromJNI,该函数的参数有3个,第1个参数为我们的输入字符串v4,其余2个参数分别为静态变量m,n;查找m与n的调用处发现,m,n是在animals类的回调方法中赋值的,如下:
public boolean onOptionsItemSelected(MenuItem arg3) {
    boolean v1 = arg3.getItemId() == 0x7F080003 ? true : super.onOptionsItemSelected(arg3);
    return v1;
}

protected void onResume() {
    super.onResume();
    this.m = HelloJni.o;
    this.n = HelloJni.p;
    String.valueOf(this.m).equals(String.valueOf(this.n));
}

protected void onStart() {
    super.onStart();
    animals.text = new HelloJni();
    new animals().l();

由于此赋值结果与我们的输入无关,因此我们可以采用smali Inject的方式,得到m,n的值;以日志方式输出参数m与n的值,如下图所示:
图片
通过am命令启动activity-animals,得到结果:
图片
即输入原生方法3个参数中的2个参数的值:m=12 , n=21; 继续分析,原生方法的返回值将会赋值给a, a再赋值给类mainactivity的静态变量a;然后调用mainactivity类的off方法,返回值作为下一个if判断的条件,跟进off进行方法的查看:
public Boolean off() {
        Boolean v0 = mainctivity.a[16] != 8 || mainctivity.a[17] != 72 || mainctivity.a[18] != 13 || mainctivity.a[19] != 26 || mainctivity.a.length != 20 ? Boolean.valueOf(false) : Boolean.valueOf(true);
        return v0;
    }
原生方法stringFromJNI返回值得后四位为8,72,13,26,且长度为20时,返回真;因此,得知stringFromJNI的返回值为20位及其后四位的值。
继续分析,下一步会调用ss函数:
if(animals.t.off().booleanValue()) {
                    animals.t.ss(mainctivity.a);
                    if(Boolean.valueOf(new String(mainctivity.a).trim().equals(new String(animals.g1))).booleanValue()) {
                        Toast.makeText(((Context)this), "congratulations", 0).show();
                        return;
                    }
            Toast.makeText(((Context)this), "加油~~~~加油", 0).show();
                return;
            }
跟进ss函数,查看代码:
图片
该函数中包含多个for循环,noc函数,和DES加密算法,跟进noc函数进行查看:
图片
noc函数定义了一个DES加密算法的key,同时调用了noe函数,我们再跟进noe函数进行查看:
@TargetApi(value=8) public void noe() {
        int v8 = 16;
        int v6 = 16;
        try {
            byte[] v5 = new byte[v6];
            int v2 = 0;
            int v3;
            for(v3 = 0; v2 < v8; ++v3) {
                v5[v2] = mainctivity.c[v3];
                v5[v2 + 1] = mainctivity.c[v3];
                v2 += 2;
            }
       SecretKeySpec v4 = new SecretKeySpec(v5, "AES");
        Cipher v0 = Cipher.getInstance("AES/ECB/NoPadding");
        try {
            v0.init(1, ((Key)v4));
            try {
                this.no();
                mainctivity.a = Base64.encode(v0.doFinal(mainctivity.x), 0);
            }
            catch(BadPaddingException v1_3) {
                try {
                    v1_3.printStackTrace();
                }
                catch(NoSuchPaddingException v1_1) {
                }
                catch(NoSuchAlgorithmException v1) {
                }
                catch(InvalidKeyException v1_2) {
                    try {
                    label_34:
                        v1_2.printStackTrace();
                    }
                    catch(NoSuchPaddingException v1_1) {
                    label_43:
                        v1_1.printStackTrace();
                    }
                    catch(NoSuchAlgorithmException v1) {
                    label_37:
                        v1.printStackTrace();
                    }
                }
            }
在noe函数中,定义了一个AES加密算法,key为for循环得到,同时调用了no函数,跟进no函数进行查看:
public void no() {
        int v0;
        for(v0 = 0; v0 < 16; ++v0) {
            mainctivity.x[v0] = mainctivity.a[v0];
        }
    }
发现no函数将字节数组a的值赋给了x数组。经过分析,整个ss函数引发的调用过程,相当于进行了两个加密算法DES与AES,但只有AES加密算法的返回值对后面的判断起作用,赋值给了mainactivity类的静态变量a,DES算法的返回值将会在返回中无变量接受而丢失。继续看最后一个if条件判断,如下代码:
  if(Boolean.valueOf(new String(mainctivity.a).trim().equals(new String(animals.g1))).booleanValue()) {
                        Toast.makeText(((Context)this), "congratulations", 0).show();
                        return;
                    }
         Toast.makeText(((Context)this), "加油~~~~加油", 0).show();
                return;
            }
当AES算法的加密结果 等于静态变量g1时,返回真,而g1的值已知:
animals.g1 =newbyte[]{0x76,89,71,81,0x76,103,107,49,55,43,0x30,69,78,57,80,0x73,67,120,83,75,71,81,61,61};
此时,整个程序的执行流程分析完毕。目的是得到传入stringFromJNI方法的正确参数1,stringFromJNI方法的返回值会截取前16位进行AES加密,加密的key与结果已知。因此,需要逆向AES加密过程,得到stringFromJNI函数的返回值,再通过分析so库中的原生方法stringFromJNI得到需要传入的参数1的值。

第三步

获得flag 编写代码,逆向推出stringFromJNI的返回值的前16位,如下图:
图片
运行完成后,得到的结果如下:
图片
结合前面分析出的函数返回值的后 4 位,我们得知 stringFromJNI 的返回值:
图片
此时,我们已经得到了stringFromJNI函数的返回值,与3个输入参数的2个,使用IDA工具,分析so库。
使用IDA软件分析完成后,得到以下结果:
图片
可以看到,输入的参数1的前10位与m异或,后10位与n进行异或,前17位再进行循环自身异或,最后的5位,与定义so中定义的ID48数组异或,最后得到返回结果。
编写代码,逆向完成so中的过程,推出参数1的值:
图片
运行结果如下:
图片
flag到手,又是收获的一天。
图片

中国象棋博大精深

【题目难度】

★★

【考察知识点】

Python 游戏

【解题步骤】

第一步

题目信息:进入房间,电脑玩家会作弊买车,我们不可能在正常的情况下赢得游戏。
图片
图片

第二步

  1. 服务端检测从127.0.0.1连接(机器人)默认有99999金币,否则为0金币。
  2. 玩家需要通过负数购买漏洞,增加自己的金币。在chrome的F12中输入:
gamesocket.send(JSON.stringify({
  type'on_buy',
  count: -999999
}));
就可以购买车
图片
  1. 玩家购买机器人的车,从而保证一步可以获胜:
gamesocket.send(JSON.stringify({
  type'on_buyother',
  count: 45
}));

图片

第三步

获得flag,奖品到手!

图片

图片

我们的题目及解题过程已经上传到云演啦!快来云演网安实验 —— CTF实验中查漏补缺吧!

图片

点击下方“阅读原文”直接进入CTF习题中心,复制到电脑端练习哦☺

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年10月19日08:03:29
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   勇攀高峰flag夺旗赛相关题目writeuphttps://cn-sec.com/archives/543175.html