ISCC校赛haN的wp
Misc
书法大师
是一张书法作品,题目很明显在强调笔画。
右键查看图片属性得到压缩包的密码:L9k8JhGfDsA
用binwalk分离图片文件,有message*的txt文件若干。
我随便找了一个message25,内容是“ 边个 乐成 少旗 太不 兄一 土上 时个 卫厂 中旗 卫丁 没牛 没乙 乐羊 尘群 比慢 边那 生亮 自船 巧挂 下上 衣西 石一 马罪 上睡”。
将笔画数写出来,是5 3 5 6 4 14 4 4 5 1 3 3 7 3 3 2 4 14 3 2 7 4 7 1 5 6 6 13 4 14 5 6 5 9 6 11 5 9 3 3 6 6 5 1 3 13 3 13
解码无果,将大于10的笔画数转成16进制的表示:53564e44513373324e327471566d4e56596b593366513d3d
16进制转字符串:SVNDQ3s2N2tqVmNVYkY3fQ==
解码,ISCC{67kjVcUbF7}
反方向的钟
打开附件是一个txt文件,图片,音频。
看题目,在深度声音找,将wav文件放进去,有一段加密的文件,需要密钥。
分析txt文件,一眼其实能看出是零宽字符。解的是
得到iscc2025Icf4
放到deepsound尝试解密,发现失败了。
后面想到分析图片,搜FF D9 jpg文件尾部,将图片部分删掉
发现这里应该是一个usbpcap流量。但是我尝试对流量包分析但是没有用,不知道是不是没找到方向。
最后没招了,看Dx8CBEldRG85JSoGLCIlNABN格式猜测是base的一种,然后iscc2025Icf4作为密钥解前面的字符串?
最后试到是将二者xor。
得到ISCC{mvZUFL2EQFW2}
Reverse
SP
首先用die打开,发现是upx壳,脱壳ida分析。
设断点调试,看程序怎么运行。
在右边隐藏fpu看到了flag
000000000079FD20 0000000000C21CA0 "ISCC{V!9#Lp@qX^eT}"
我爱看小品
一样,用die打开分析。
看到打包工具是pyinstaller,就知道要反编译pyc文件。
使用pyinstxtractor的在线网页(https://pyinstxtractor-web.netlify.app/)上传exe文件,点击Prcocess执行反编译。
反编译一下something.pyc
# Visit https://www.lddgo.net/string/pyc-compile-decompile for more information
# Version : Python 3.8
import mypy
import yourpy
def something():
print(' 打工奇遇')
print('宫室长悬陇水声')
print('廷陵刻此侈宠光')
print('玉池生肥咽不彻')
print('液枯自断仙无分')
print('酒醒玉山来映人')
def check():
your_input = input()
if your_input[:5] == 'ISCC{' and your_input[-1] == '}':
print("Come along, you'll find the answer!")
else:
print('Flag is wrong!')
if __name__ == '__main__':
mypy.myfun()
something()
print('Please enter flag:')
check()
这说明我们还要找到mypy和yourpy文件
但是我发现他们加密了。
在网上搜索了一下,找到了这篇博客:python逆向之pyc反编译-CSDN博客
我的是55 0d 0d 0a,是python3.8版本,下载一下。然后反编译密码得到密钥:
# Visit https://www.lddgo.net/string/pyc-compile-decompile for more information
# Version : Python 3.8
key = 'yibaibayibei1801'
所以将文件夹的东西解密
# For pyinstaller >=4.0
import glob
import zlib
import tinyaes
from pathlib import Path
CRYPT_BLOCK_SIZE = 16
# key obtained from pyimod00_crypto_key
key = bytes('yibaibayibei1801', 'utf-8')
for p in Path("PYZ-00.pyz_extracted").glob("**/*.pyc.encrypted"):
inf = open(p, 'rb') # encrypted file input
outf = open(p.with_name(p.stem), 'wb') # output file
# Initialization vector
iv = inf.read(CRYPT_BLOCK_SIZE)
cipher = tinyaes.AES(key, iv)
# Decrypt and decompress
plaintext = zlib.decompress(cipher.CTR_xcrypt_buffer(inf.read()))
# Write pyc header
# The header below is for Python 3.8
outf.write(b'\x55\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0')
# Write decrypted data
outf.write(plaintext)
inf.close()
outf.close()
# Delete .pyc.encrypted file
p.unlink()
就解密了这些文件。找到mypy和yourpy解密
- flag格式为
ISCC{xxxxxxxxxxxxxxxxxxxxxxxxxxxx}
- part2被分成4部分处理:
- 前11个字符:每个字符ASCII值+1
- 接下来的4个字符:字母则异或32(大小写转换),其他不变
- 接下来的5个字符:每个字符ASCII值-1
- 最后10个字符:每个字符ASCII值+i%2(即奇数位+1,偶数位+0)
- 处理后得到密文:
qzjotubmmfs_IS_udqx^iotfrfsuiog
cipher = "qzjotubmmfs_IS_udqx^iotfrfsuiog"
# Split the cipher into parts based on how it was encrypted
part1 = cipher[:11] # These characters were +1
part2 = cipher[11:15] # These were XOR 32 if alphabetic
part3 = cipher[15:20] # These were -1
part4 = cipher[20:] # These were +i%2
# Decrypt part1 (reverse the +1 operation)
dec_part1 = "".join([chr(ord(c) - 1) for c in part1])
# Decrypt part2 (reverse the XOR 32 operation)
dec_part2 = "".join([chr(ord(c) ^ 32) if c.isalpha() else c for c in part2])
# Decrypt part3 (reverse the -1 operation)
dec_part3 = "".join([chr(ord(c) + 1) for c in part3])
# Decrypt part4 (reverse the +i%2 operation)
dec_part4 = "".join([chr(ord(c) - (i % 2)) for i, c in enumerate(part4)])
# Combine all parts to get the original part2
original_part2 = dec_part1 + dec_part2 + dec_part3 + dec_part4
flag = f"ISCC{{{original_part2}}}"
print(flag)
#ISCC{pyinstaller_is_very_interesting}
Web
战胜变相一
看robots.txt,得到 f10g.txt,提示细节决定成败。
f12给了一个棋谱,我和ai下了半天的棋,没结果。
提示下棋要两个人下,访问f12g,拿到flag。
提交发现不对,想到f10g.txt-->f12g,故将答案里的0改成2提交成功。
纸家衣外传
用dirsearch.py看了一下目录,有upload和include,应该是文件包含+文件上传。
访问/includes,提示提示flag is here,尝试访问/flag.php
web环境现在没了,我记得是get一把锤子,那就是?chuizi=XXXXXXX格式的
文件上传部分尝试传shell,但是后缀名过滤了,试了几种绕过不成功,只能上传txt文件。
txt文件:<php highlight_file("/var/www/html/includes/flag. php");eval($_POST[' cmd']);phpinfo();?>
然后?chuizi=upload/114514.txt,访问,代码自动执行,答案再base64解码即可。
(由于是公共靶机,访问1.txt可以偷看别人的🐎
Mobile
encode
用jadx分析发现打不开。
而且雷电模拟器安装不了。
在iscc群里看见顾师兄说了一句提dex分析(bushi
反编译classes.dex
因为apk的本质是zip,我们改一下后缀zip,解压。
package com.example.encode;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
/* loaded from: C:\Users\28421\Desktop\classes.dex */
public class MainActivity extends AppCompatActivity {
Button but_1;
EditText edt_1;
TextView tv_1;
private native boolean nativeCheckFormat(String str);
private native boolean nativeCheckLast(String str);
static {
System.loadLibrary("encode");
}
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
this.but_1 = (Button) findViewById(R.id.Push_Yes);
this.edt_1 = (EditText) findViewById(R.id.Flag_Edit);
this.tv_1 = (TextView) findViewById(R.id.Tip);
this.but_1.setOnClickListener(new View.OnClickListener() { // from class: com.example.encode.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View view) {
if (MainActivity.this.Jformat(MainActivity.this.edt_1.getText().toString())) {
Toast.makeText(MainActivity.this, "闯关成功!!!", 0).show();
} else {
Toast.makeText(MainActivity.this, "输入错误,继续加油!", 0).show();
}
}
});
}
/* JADX INFO: Access modifiers changed from: private */
public boolean Jformat(String str) {
int lastIndexOf = str.lastIndexOf("_");
if (lastIndexOf == -1 || str.length() < 10 || !str.startsWith("ISCC{") || !str.endsWith("}")) {
return false;
}
return nativeCheckLast(str.substring(lastIndexOf + 1, str.length() - 1)) && nativeCheckFormat(str);
}
}
Java_com_example_encode_MainActivity_nativeCheckLast .text 00000000000224C4 000001C8 00000090 R . . . . B . .
Java_com_example_encode_MainActivity_nativeCheckFormat .text 000000000002268C 00000318 000000A0 R . . . . B . .
搜索main有这两个主函数。
还有一个encode_last_part加密函数。
LOBYTE(v3) = (*(_QWORD *)v9 ^ 0x43655C756C71776BLL | (unsigned __int8)v9[8] ^ 0x4FLL) == 0;
从encode_last_part函数可以看出加密步骤,对输入字符串的每个字符的ASCII值加3
ds说是小端序,所以应该是0x4f43655C756C71776B
-
0x4F-3 = 0x4C
→ 'L' -
0x43-3 = 0x40
→ '@' -
0x65-3 = 0x62
→ 'b' -
0x5C-3 = 0x59
→ 'Y' -
0x75-3 = 0x72
→ 'r' -
0x6C-3 = 0x69
→ 'i' -
0x71-3 = 0x6E
→ 'n' -
0x77-3 = 0x74
→ 't' -
0x6B-3 = 0x68
→ 'h'得到L@bYrinth
第二个函数的关键点在
v18 = _mm_xor_si128(_mm_loadu_si128(v17), *(__m128i *)aFenww0ccxq5bca);
LOBYTE(v11) = _mm_testz_si128(v18, v18);
aFenww0ccxq5bca
的16字节值为:fENWW0ccXQ5BcAAA
encode_front_part中对每个字符执行了^ 0x2F
,所以我们需要再次对解码结果执行^ 0x2F
后面还有一个simplebase64encode函数,解密的时候加上。
得到Slyth3r!n_//
答案是ISCC{Slyth3r!n_L@bYrinth}
三进制战争
jadx反编译,看主函数
public final class CHECK0 implements View.OnClickListener {
public CHECK0() {
}
@Override // android.view.View.OnClickListener
public void onClick(View view) {
Intrinsics.checkNotNullParameter(view, "view");
EditText edit1 = MainActivity.this.getEdit1();
Intrinsics.checkNotNull(edit1);
if (MainActivity.this.CHECK1(edit1.getText().toString())) {
TextView tv1 = MainActivity.this.getTv1();
Intrinsics.checkNotNull(tv1);
tv1.setText("Right");
} else {
TextView tv12 = MainActivity.this.getTv1();
Intrinsics.checkNotNull(tv12);
tv12.setText("Wrong");
}
}
}
/* JADX INFO: Access modifiers changed from: private */
public final boolean CHECK1(String str1) {
if (str1.length() <= 13) {
return false;
}
String substring = str1.substring(0, 5);
Intrinsics.checkNotNullExpressionValue(substring, "substring(...)");
if (Intrinsics.areEqual(substring, "ISCC{") && str1.charAt(str1.length() - 1) == '}') {
String stringFromJN1 = stringFromJN1();
String substring2 = str1.substring(11, str1.length() - 1);
Intrinsics.checkNotNullExpressionValue(substring2, "substring(...)");
String stringFromJNl = stringFromJNl(stringFromJN1, substring2);
String substring3 = str1.substring(5, 11);
Intrinsics.checkNotNullExpressionValue(substring3, "substring(...)");
if (Intrinsics.areEqual(substring3, stringFromJNI("4zA(19")) && CHECK2(stringFromJNl)) {
return true;
}
}
return false;
}
private final boolean CHECK2(String str1) {
return Intrinsics.areEqual(str1, "010201010222020102010002001220021221");
}
static {
System.loadLibrary("mobile02");
}
}
确定 Flag 结构
从 MainActivity
代码可知,Flag 格式为:
ISCC{XXXXXXYYYYYYYYYY}
XXXXXX
=stringFromJNI("4zA(19")
的返回值(6 个字符)YYYYYYYYYY
= 让stringFromJNl(key, YYYYYYYYYY)
返回"010201010222020102010002001220021221"
的字符串
本来是尝试逆向stringFromJN1(),stringFromJNI(String),stringFromJNl(String, String)三个函数的,但是发现太难了,函数太多。
if (Intrinsics.areEqual(substring3, stringFromJNI("4zA(19")) && CHECK2(stringFromJNl)) {
return true;
用frida来hook函数,输入4zA(19,获取stringFromJNI("4zA(19")的值
p1.txt
// 步骤1: 环境初始化
Java.perform(function() {
console.log("[*] 开始Hook第一部分: 获取XXXXXX和key");
const MainActivity = Java.use("com.example.mobile02.MainActivity");
const JavaString = Java.use("java.lang.String");
// Hook stringFromJNI("4zA(19")
MainActivity.stringFromJNI.implementation = function(input) {
console.log("[*] stringFromJNI 输入:", input);
const result = this.stringFromJNI(input);
console.log("[√] XXXXXX =", result); // 输出6字符的XXXXXX
return result;
};
// Hook stringFromJN1()
MainActivity.stringFromJN1.implementation = function() {
const result = this.stringFromJN1();
console.log("[√] key =", result); // 输出key
return result;
};
// 触发调用(自动点击按钮)
Java.choose("com.example.mobile02.MainActivity", {
onMatch: function(instance) {
instance.stringFromJNI(JavaString.$new("4zA(19")); // 触发XXXXXX计算
instance.stringFromJN1(); // 触发key计算
},
onComplete: function() {}
});
});
[*] 开始Hook第一部分: 获取XXXXXX和key
[*] stringFromJNI 输入: 4zA(19
[√] XXXXXX = 'cg&Bf'bU
[√] key = ac35b0fc1a63e0657ed4bc5da3cabef6
[Android Emulator 5554::PID::2783 ]->
p2.txt
Java.perform(function() {
// 1. 配置参数
const TARGET_ENCODING = "010201010222020102010002001220021221";
const MAX_LENGTH = TARGET_ENCODING.length / 6; // 6位三进制对应1字符
const CHAR_SET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+-=[]{}|;':\",./<>?`~ ";
// 2. 破解主函数
function fullBruteForce(instance, key) {
console.log("[*] 开始全量暴力破解...");
console.log("[*] 目标编码:", TARGET_ENCODING);
console.log("[*] 最大长度:", MAX_LENGTH);
console.log("[*] 字符集长度:", CHAR_SET.length);
// 使用队列进行广度优先搜索
let queue = [""];
let found = null;
while (queue.length > 0 && !found) {
const current = queue.shift();
// 遍历字符集
for (const char of CHAR_SET) {
const nextStr = current + char;
try {
const result = instance.stringFromJNl(key, nextStr);
const expected = TARGET_ENCODING.substring(0, nextStr.length * 6);
// 打印进度(每100次尝试)
if (Math.random() < 0.01) {
console.log(`[.] 测试: ${nextStr} -> ${result.substr(0, 20)}...`);
}
if (result === TARGET_ENCODING) {
console.log("\n[√] 完全匹配找到!");
console.log(`[√] 原始字符串: ${nextStr}`);
console.log(`[√] 编码结果: ${result}`);
found = nextStr;
break;
}
if (result === expected) {
if (nextStr.length < MAX_LENGTH) {
queue.push(nextStr);
}
}
} catch (e) {
console.log(`[!] 测试 ${nextStr} 时出错: ${e}`);
}
}
}
if (!found) {
console.log("[X] 未找到匹配的字符串");
}
return found;
}
// 3. 主执行流程
Java.choose("com.example.mobile02.MainActivity", {
onMatch: function(instance) {
try {
const key = instance.stringFromJN1();
console.log("[*] 获取到的key:", key);
const startTime = Date.now();
const result = fullBruteForce(instance, key);
const elapsed = (Date.now() - startTime) / 1000;
if (result) {
console.log(`\n[√] 破解成功! 耗时: ${elapsed.toFixed(2)}秒`);
console.log(`[√] 最终结果: ${result}`);
// 自动填写结果
Java.scheduleOnMainThread(function() {
try {
const EditText = Java.use("android.widget.EditText");
const edit = Java.cast(instance.getEdit1(), EditText);
edit.setText(result);
console.log("[√] 已自动填写结果");
} catch (e) {
console.log("[!] 自动填写失败:", e);
}
});
}
} catch (e) {
console.log("[!] 主流程出错:", e);
}
},
onComplete: function() {
console.log("[*] 扫描完成");
}
});
});
[*] 获取到的key: ac35b0fc1a63e0657ed4bc5da3cabef6
[*] 开始全量暴力破解...
[*] 目标编码: 010201010222020102010002001220021221
[*] 最大长度: 6
[*] 字符集长度: 94
[.] 测试: o -> 011202...
[.] 测试: V6J -> 010201010222011202...
[.] 测试: V6w)$ -> 01020101022202010201...
[.] 测试: V6w)7z -> 01020101022202010201...
[√] 完全匹配找到!
[√] 原始字符串: V6w)7X
[√] 编码结果: 010201010222020102010002001220021221
[√] 破解成功! 耗时: 0.02秒
[√] 最终结果: V6w)7X
[*] 扫描完成
[!] 自动填写失败: Error: setText(): argument types do not match any of:
.overload('java.lang.CharSequence', 'android.widget.TextView$BufferType')
[Android Emulator 5554::PID::2783 ]->
最后我答案是ISCC{'cg&Bf'bUV6w)7X}