Skip to content

TSCTF-J WP

Reverse - baby_xor

直接丢 IDA ,写个程序运行一下

a=[18,20,7,17,4,110,10,58,25,124,32,14,122,6,123,22,100,8,6,48,4,22,34,117,27,0,36,18,40,4,105,42,57,67,43,85,13,60,5,83,19]

ans=str()

for i in range(0,41):
    ans+=chr(a[i]^0x46^i)

print(ans)

Reverse - byte_code

这题是真的离谱,虽然很容易识别出来这个是 python 字节码,但是我是手动看着代码复原的,贼麻烦(有部分输出进行了修改)。

这个比汇编看起来简单太多了,复原难度不大,就提几个重点:

LOAD_NAME                6 (c)
LOAD_NAME                8 (i)
BINARY_SUBSCR

比如以上这一段就是把 c[i] 压入栈中

LOAD_NAME                9 (print)
LOAD_NAME               10 (chr)
LOAD_NAME                6 (c)
LOAD_NAME                8 (i)
BINARY_SUBSCR
CALL_FUNCTION            1

上面这个就是 执行 print(chr(c[i]))

完整代码在下面:

a=[114,101,118,101,114,115,101,95,116,104,101,95,98,121,116,101]

b=[99,111,100,101,95,116,111,95,103,101,116,95,102,108,97,103]

e=[80,115,193,24,226,237,202,212,126,46,205,208,215,135,228,199,63,159,117,52,254,247,0,133,163,248,47,115,109,248,236,68]

pos=[9,6,15,10,1,0,11,7,4,12,5,3,8,2,14,13]

d=[335833164,1155265242,627920619,1951749419,1931742276,856821608,489891514,366025591,1256805508,1106091325,128288025,234430359,314915121,249627427,207058976,1573143998,1443233295,245654538,1628003955,220633541,1412601456,1029130440,1556565611,1644777223,853364248,58316711,734735924,1745226113,1441619500,1426836945,500084794,1534413607]

c=a+b

s=str()

for i in range(32):
    s+=chr(c[i])

print(s)

for i in range(16):
    a[i]=(a[i]+d[i])^b[pos[i]]

for i in range(16):
    b[i]=b[i]^a[pos[i]]

c=a+b

s=str()

for i in range(32):
    c[i]=(c[i]*d[i]%256)
    c[i]=c[i]^e[i]
    s+=chr(c[i])
    print(c[i])

print(s)

Web - 词超人

由于我个人 python 使用并不是很熟练,这道题我是分了以下几步进行:

首先先获取 id 和 单词

(本人菜鸡代码写的很丑很乱,大佬轻喷)

import requests
import json

response = requests.get(url="http://49.232.201.163:46128/",headers={"Connection": "close"})

recv = response.text

temp=recv

#for i in range(1,1024):
#    temp=temp.split("<p class=\"en\" style=\"visibility:hidden;display: inline\">", 1)[1]
#    print(temp.split("</p>", 1)[0])
#    temp=temp.split("</p>", 1)[1]

temp=recv

for i in range(1,1024):
    temp=temp.split("<div id=\"", 1)[1]
    print(temp.split("\"", 1)[0])
    temp=temp.split("\"", 1)[1]

# print(response.text)

接着我是用 C++ 进行拼接的:

//[{"id":"11","answer":""},
#include <cstdio>
#include <cstring>

int main()
{
    FILE *f1,*f2;
    char a[100];
    char b[100];
    f1=fopen("1.txt","r");
    f2=fopen("2.txt","r");
    for(int i=1;i<=1000;i++)
    {
        fgets(a,100,f1);
        fgets(b,100,f2);
        if(a[strlen(a)-1]=='\n')
            a[strlen(a)-1]='\0';
        if(b[strlen(b)-1]=='\n')
            b[strlen(b)-1]='\0';
        printf("{\"id\":\"%s\",\"answer\":\"%s\"},",b,a);
    }
    return 0;
}

然后很容易就能写出完整 payload ,用 burpsuite 提交一下就行。

Web - 真真历险记

我们关注到 CyberSpace Securiety 这个单词是蓝色的,很特殊啊。最后面也有提示说看单词首字母缩写,很明显得出 CSS

我们翻翻 CSS ,可以看到有四个被注释掉的部分,拼接起来可以看到:

(+[![]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+
后面略

很容易想到 JSFuck 。

我们扔到浏览器控制台运行一下,直接得到 flag 。

Pwn - checkin

简单的签到题。扔进 IDA ,可以发现下面这部分

    gets(buf);
    if ( !strncmp(ch_0, "BUPTBUPT", 8uLL) )
      break;
    puts("\x1B[40;31m ### Try again ### \x1B[0m ");
    printf("[buf] ==> %s\n", buf);
    printf("[ch] ==> %s\n", ch_0);

所以我们只要构造一个字符串,填充完 buf 数组,再溢出到 ch 数组即可。

构造的字符串如下 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUPTBUPT

直接 cat flag

Misc - 北邮人之声

很明显这段音频是倒放过的。

再次倒放,然后辨认每个单词的首字母即可(注意大括号的英文)。

Misc - Just_Play

根据剧情,我们需要搜集三种颜色的宝石。

我们拿出 rgss3a 解包器 ,直接解包游戏根目录下的 Game.rgss3a 文件。

我们直接使用文本编辑器打开解压得到的一堆 *.rvdata2 (有乱码没关系,大部分对话都是明文的),搜索 flag 字符串,可以看到有一个名为 FLAG PART1 的物品,物品描述是 flag 的第一部分,还有出题人留下的一句话。

仿照上一步继续搜索,可以得到 NPC 拿到三颗宝石后的对话,得到 flag 第三部分。

我们接着拿出 RMModify 游戏存档编辑器,打开游戏存档(肯定要先进入游戏存个档),在浏览角色技能时可以看到名为 鼠鼠的呻吟鼠鼠的呻吟? 的两个技能。我们先将这两个技能打开(顺便可以开一下 不遇敌(暗雷) 方便跑图。

随后进入游戏开始走剧情。

我们会在迷宫部分发现地图上有 1s_S0 字样,这个很明显也是 flag 的一部分。

在遇到 A.R. 的时候,我使用了 鼠鼠的呻吟 技能,发现背景音乐中掺杂着说话声,于是寻找游戏资源中这个技能的音频。

我们打开 \Audio\SE 文件夹,在一堆 .ogg 的文件中找到一个 FF.mp3 ,听一下音频即可得到 flag 的第四部分。

Misc - Black Tea

这道题一眼就可以看出这是 不可能的棋盘谜题

下面是网络上的解法:

来源:https://www.bilibili.com/video/av711666649/

64格棋盘问题的解法,精简版:

首先定义非负整数a、b的异或运算a⊕b为不带任何进位的二进制加法。
例如:6⊕13,也即二进制里的110⊕1101,二进制个位是0+1=1,二位是1+0=1,四位是1+1=(1)0,不进位只保留一个0,八位是0+1=1,所以结果是二进制的1011,也即6⊕13=11。
异或的性质有:
1. 交换律:a⊕b=b⊕a
2. 结合律:a⊕(b⊕c)=(a⊕b)⊕c
3. 单位元:a⊕0=0⊕a=a
4. 加即为减:a⊕a=0
5. 对2的幂封闭:假设存在非负整数n使得a,b<2^n,那么有a⊕b<2^n。
证明及其他具体细节请参考百度等。

给64个棋盘格标号为0、1、……、63。假设在某一时刻棋盘上任意摆满了硬币,那么从这个硬币的排列里挑出所有正面朝上的硬币,把所有这些硬币的序号取异或运算,然后可以把棋盘上的这一种硬币排列对应到序号为这个运算结果的棋盘格上,如果没有硬币朝上则取0号棋盘格。当1号囚犯进入时,会见到对应某a号方格的硬币排列,和藏在某x号方格下的钥匙。此时,1号囚犯翻转a⊕x号方格上的硬币。
例如:1号囚犯见到3、14、15号硬币正面朝上,此时对应a=3⊕14⊕15=2。如果钥匙藏在x=9号格下,那么1号囚犯选择翻转a⊕x=2⊕9=11号硬币。2号囚犯进入,见到3、11、14、15号硬币正面朝上,会计算x=3⊕11⊕14⊕15=9。

接下来分析这个策略为什么会成功:由以上性质5,1号囚犯永远有硬币可以翻。
1. 如果这个硬币a⊕x本来正面朝下,那么翻转之后的硬币排列对应的结果是a⊕(a⊕x),这其中a是本来所有硬币的运算结果,而(a⊕x)是新多出来的一项。由于上述性质2到4,a⊕(a⊕x)=x,那么2号囚犯顺利得到钥匙x。
2. 如果这个硬币a⊕x本来正面朝上,那么翻转之后本来的运算结果仍然是a,但是少了(a⊕x)这一项。由于性质4,从a中“减去”(a⊕x)等同于向a中“加上”(a⊕x),因此得到的结果仍然是a⊕(a⊕x)=x,2号囚犯顺利得到钥匙x。

我们可以根据此解法写一个程序来进行计算。

#include <iostream>
using namespace std;

int main()
{
    int a,ans=0;
    for(int i=0;i<=63;i++)
    {
        cin>>a;
        if(a)
        {
            if(ans==0)
                ans=i;
            else
                ans^=i;
        }
    }
    cout<<ans;
    return 0;
}

然后 nc 上去回答 10 次即可得到 flag 。


Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.