XYCTF-WP-酱菜三剑客

misc

1、game

拿到图片,拖进谷歌搜图

点击链接

https://papersplease.fandom.com/wiki/EZIC

所以flag为

XYCTF{Papers Please}

2、熊博士

小纸条拖进随波逐流

得到flag xyctf{liu_ye_mei_you_xiao_jj}

再改成大写

得到最终flag

xyctf{LIU_YE_MEI_YOU_XIAO_JJ}(好狠毒-_-)

3、真>签到

拖进010直接看到flag

所以flag为

XYCTF{59bd0e77d13c_1406b23219e_f91cf3a_153e8ea4_77508ba}

4、签到

没啥好讲的,就记得好像关注一下公众号就出来了

5、Osint1

拖进百度识图,找到小红书博主的回复

评论区得到flag

xyctf{江苏省|南通市|滨海东路|黄海}

6、EZ_Base1024*2

上网找了Base2048的解码网站,解码出来就是flag

flag

XYCTF{84ca3a6e-3508-4e34-a5e0-7d0f03084181}

7、ez_隐写

先看hint,拖入010发现这个高度不对劲,逐步修改高度从1000-3000另存为,然后在3000的图片下面发现提示

所以压缩包的解压密码就是XYCTF的开赛日期20240401

解压得到图片WATERMARK

猜测可能是用WATERMARK进行的隐写

拖进WARTERMARK一看,得到flag

XYCTF{159-WSX-IJN-852}

8、美妙的歌声(未解出)

wave拖进Audacity看频谱图,得到一个像flag的东西

但是包装为XYCTF{XYCTF_1s_w3ll}或者是flag{XYCTF_1s_w3ll}

提交上去都是不对的

9、Osint2(未解出)

图片信息有:

出题人在2024年3月26日15:10分在洛阳龙门站坐了开往泸州的高铁

去查了符合时间的高铁

查了班次说是到洛阳龙门站的时候车号是G3293

于是构造flag为

xyctf{G3293|中国河南省|洛阳龙门古镇}

但提交上去错误(为什么第一个空里面要填6个字符呢_

10、Ez_osint(未解出)

上网查了这段话发现只有知乎的一个专栏有,不知道这个邮件是藏了个啥东西

(怎么还有在ctf题目里面杀单身狗的啊-_-)

11、彩蛋?(未解出)

在比赛须知里面找到一段

**and I think you know : **130131103124106173164150151163137141137

丢进随波逐流里面得到第一部分

8进制转字符: XYCTF{this_a_

后面的几部分就没找出来了-_-

12、疯狂大杂烩!九转功成(未解出)

解压密码是xyctf开始日期20240401

第一层

hint放进随波逐流得到

天书(曰)解码: First_layer_simple

解压得到图片,拖进010修改高度,在图片尾部得到第一部分的flag

XYCTF{T3e_c0mb1nation_

第二层

hint拖进随波逐流得到解压密码
BubbleBabble解码: The_second_layer_is_also_simple

解压得到的图片LSB隐写了Base64编码:

解码得到第二部分的flag:flag2:0f_crypt0_and_

第三层

随波逐流224查到hint对应的是circle编码

**解码得到 **

U K I J H G M

I N H G V T J

W K X B H J I

H D I G C J U

H K Y I I

Crypto

1、Sign1n[签到]和Sign1n_Revenge

先用gpt对代码注释了一下:

这段代码似乎在以一种有些复杂的方式操纵一个标志值。让我们逐步解释:

  1. **导入库**:

    1
    2
    3
    from Crypto.Util.number import *
    from tqdm import *
    import gmpy2

    这里导入了来自Crypto.Util.number、tqdm和gmpy2库的必要函数。

  2. **标志初始化**:

    1
    2
    flag = b'XYCTF{uuid}'
    flag = bytes_to_long(flag)

    将标志初始化为字节串b'XYCTF{uuid}',然后使用Crypto库中的bytes_to_long()函数将其转换为长整数。

  3. **位泄漏**:

    1
    2
    3
    4
    5
    leak = bin(int(flag))
    while 1:
       leak += "0"
       if len(leak) == 514:
           break

    该代码将标志整数转换为二进制字符串,并通过附加"0"扩展它,直到其达到514位的长度为止。

  4. **位交换函数**:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def swap_bits(input_str):
       input_list = list(input_str[2:])
       length = len(input_list)

       for i in range(length // 2):
           temp = input_list[i]
           input_list[i] = input_list[length - 1 - i]
           input_list[length - 1 - i] = temp

       return ''.join(input_list)

    此函数接受一个二进制字符串作为输入,交换其中的位,并返回结果。

    这个函数的目的是交换输入的二进制字符串中的位。现在让我逐行解释它:

    • input_list = list(input_str[2:]):将输入字符串转换为列表,起始索引从第二位开始,这是因为在输入字符串中,前两个字符是0b表示二进制的前缀。
    • length = len(input_list):获取输入列表的长度,以便后续迭代使用。
    • for i in range(length // 2)::迭代处理列表的前一半元素。这是因为在交换位的过程中,我们只需要交换前一半与后一半的位。
    • temp = input_list[i]:将当前位置的元素保存在临时变量temp中,以便稍后使用。
    • input_list[i] = input_list[length - 1 - i]:将当前位置的元素替换为与之对称的对应元素。例如,如果当前位置为i,则与之对称的位置为length - 1 - i。
    • input_list[length - 1 - i] = temp:将之前保存的临时变量temp的值赋给对称位置上的元素,完成交换。
    • return ''.join(input_list):将交换后的列表转换回字符串形式,并返回交换后的结果。

    这样,swap_bits函数会返回输入字符串中位的顺序被颠倒的结果。

  5. **位操作**:

    1
    2
    3
    input_str = leak
    result = swap_bits(input_str)
    a = result

    在这里,将leak(标志的扩展二进制表示)通过swap_bits()函数,并将结果存储在a中。

  6. **自定义加法函数**:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def custom_add(input_str):
       input_list = list(input_str)
       length = len(input_list)
       
       for i in range(length):
           input_list[i] = str((int(input_list[i]) + i + 1) % 10)

       result = ''.join(input_list)
       return result

    此函数接受一个数字字符串作为输入,对每个数字加上其索引位置(加一),取结果模10,并返回新的字符串。

    这个函数的目的是对输入的数字字符串进行加法操作。现在让我逐行解释它:

    • input_list = list(input_str):将输入的字符串转换为列表,这样我们可以逐个访问其中的每个数字。
    • length = len(input_list):获取输入列表的长度,以便后续迭代使用。
    • for i in range(length)::对列表中的每个元素进行迭代,即对输入的每个数字进行操作。
    • input_list[i] = str((int(input_list[i]) + i + 1) % 10):将当前数字字符串转换为整数,并加上当前索引i以及额外的1。然后取结果对10的模,并将其转换回字符串形式,更新到输入列表中的当前位置。
    • result = ''.join(input_list):将操作后的列表重新组合成一个字符串。
    • return result:返回加法操作后的结果字符串。

    这样,custom_add函数会对输入的数字字符串进行自定义的加法操作,其中每个数字都会加上它的索引值再加1,并且结果会取模10以确保结果在0到9之间。

  7. **位操作(第二部分)**:

    1
    2
    3
    input_str = a
    result = custom_add(input_str)
    b = result

    将修改后的二进制字符串a通过custom_add()函数,并将结果存储在b中。

  8. **打印结果**:

    1
    print(b)

    打印最终结果(b)。

最终输出是一长串数字,看起来经历了某种操作,可能是加密或混淆。这段代码主要展示了对标志的位操作技术,可能作为密码挑战的一部分。****

然后用gpt写了一个解密函数:

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
from Crypto.Util.number import *
from tqdm import *
import gmpy2
def custom_sub(input_str):
   input_list = list(input_str)
   length = len(input_list)

   for i in range(length):
       input_list[i] = str((int(input_list[i]) - i - 1) % 10)

   result = ''.join(input_list)
   return result


def reverse_bits(input_str):
   input_list = list(input_str)
   length = len(input_list)

   for i in range(length // 2):
       temp = input_list[i]
       input_list[i] = input_list[length - 1 - i]
       input_list[length - 1 - i] = temp

   return ''.join(input_list)




b = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567891134567780113445779912234667801123467889113346778901344567990233467780113355689902235667991124556899122355788001245578890233467790012455678012235578800133456899112346779911344567991223557880012346788911245667900134456789122355788001335568991123566799112455779902334577900133457889113356679901344578801233467789112355779912234577990233556780113"  # Your encrypted string here

input_str = b
result = custom_sub(input_str)
a = result

input_str = a
result = reverse_bits(input_str)
leak = result[:-1]

flag = int(leak, 2)
flag = long_to_bytes(flag)
print(flag.decode())

就可以得到两道题的flag

flag{8bb42495-c087-40d6-b854-0b1ae37e29e8}

XYCTF{1514ee1e-0398-45aa-8cf6-6e2efd0e8b56}

2、happy_to_solve1

抄网上流出的wp

题目给了n,c,e的值,要得到flag就得先解m的值。又因为m = pow(c, d, n), 且给了e值,所以利用p,q的生成规则求phi_n的值成为了问题的关键!
观察p, q生成的规则,发现p是随机取的512位的素数;
sympy.nextprime() 函数接受一个整数作为参数,并返回大于该整数的下一个素数;
1 << 512 是一个位运算,表示将1左移512位,即得到一个只有第513位为1的数,其他位都为0的数;
(1 << 512) - 1 是将上述结果减去1,得到一个二进制表示全为1的512位数;
所以q的结果就是p与512位的1进行异或的结果,再进行nextprime。
因为phi_n = n - (p + q) + 1 ,且n位数与全1按位异或等于取反,原码 + 反码 = 2 ** n - 1
我们先不计q在nextprime函数后的差值,所以p + q = 2 ** 512 - 1
又因为素数性质(除2之外都是奇数),因此,p + q = 2 ** 512 - 1 + t
所以,爆破!这里的t从1开始,每次爆完t + 2,满足奇数条件,直到有符合的怕p + q的值便可得到flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import gmpy2
from Crypto.Util.number import *

n =  24852206647750545040640868093921252282805229864862413863025873203291042799096787789288461426555716785288286492530194901130042940279109598071958012303179823645151637759103558737126271435636657767272703908384802528366090871653024192321398785017073393201385586868836278447340624427705360349350604325533927890879
c =  14767985399473111932544176852718061186100743117407141435994374261886396781040934632110608219482140465671269958180849886097491653105939368395716596413352563005027867546585191103214650790884720729601171517615620202183534021987618146862260558624458833387692782722514796407503120297235224234298891794056695442287
e = 65537
t = 1
# 因为q是p的取反后取比其值大的最近的一个素数,所以p + q = 2**512 - 1 + t
for i in range(400):
   phi = n - (2**512 - 1 + t) + 1
   d = gmpy2.invert(e, phi)
   m = pow(c, d, n)
   print(long_to_bytes(m))
   t += 2

最终flag

XYCTF{3f22f4efe3bbbc71bbcc999a0a622a1a23303cdc}

WEB

1、warm up

level1主要考phpmd5绕过和动态变量的变量覆盖

构造的payload为

http://localhost:9733/?val1=QNKCDZO&val2=s1091221200a&md5=0e215962017&XYCTF=QNKCDZO&XY=QNKCDZO

得到level2的地址

LLeeevvveeelll222.php

level2第一关先是post一个数组实现数组绕过

post a[]=1

第二关是preg_match命令执行漏洞

构造payload为

/LLeeevvveeelll222.php?a=/(.*)/e&b=system('cat /flag');

获得flag

XYCTF{e552eaa1-8e7f-4d76-9e22-1505d2a45295}

2、ezhttp

查看源代码可知密码藏起来了

于是猜是不是会在robots.txt里面

访问得到

再访问得到

username: XYCTF
password: @JOILha!wuigqi123$

拿着这两个去登录

发现出来这个

burp抓包,修改Referer为

yuanshen.com

发送得到提示

你用的不是XYCTF的浏览器

修改UA为XYCTF得到

非本地用户禁止访问!

添加XFF:127.0.0.1,结果

xff 打咩!!!

换成 Client-IP:127.0.0.1

得到

不是从 ymzx.qq.com 代理来的我不玩

构造代理

Via: ymzx.qq.com,得到

有点饿,想吃点XYCTF的小饼干

于是修改cookie,Cookie: XYCTF

最终得到flag

XYCTF{093907da-19e3-4a15-aa56-62f77cd92f66}

3、ezmd5

查看源码可知这题就是进行一个图片的md5值的比较,并且要求是两个图片不一样但是md5的值要一样

于是上网寻找,还真在csdn的一个博客里面找到了两个图

https://blog.csdn.net/rclijia/article/details/114400242

把原文地址的两个图片上传上去就可以得到flag

XYCTF{1b3eb8cc-336a-45b9-a376-e98c9cfb9adc}

Reverse

1、聪明的信使

拖进die一看是32位的程序,于是用ida打开

按F5看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str1[100]; // [esp+18h] [ebp-68h] BYREF
int v5; // [esp+7Ch] [ebp-4h]

__main();
v5 = 9;
printf("Input your flag:");
scanf("%s", Str1);
encrypt(Str1, v5);
if ( !strcmp(Str1, "oujp{H0d_TwXf_Lahyc0_14_e3ah_Rvy0ac@wc!}") )
printf("Good job!");
else`
printf("Try again!");
return 0;
}

发现一个很像flag{}的东西

oujp{H0d_TwXf_Lahyc0_14_e3ah_Rvy0ac@wc!}"

查看encrypt函数的源码

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
int __cdecl encrypt(char *a1, int a2)
{
int result; // eax
char v3; // [esp+Bh] [ebp-5h]
int i; // [esp+Ch] [ebp-4h]

for ( i = 0; ; ++i )
{
result = (unsigned __int8)a1[i];
if ( !(_BYTE)result )
break;
v3 = a1[i];
if ( v3 <= 96 || v3 > 122 )
{
if ( v3 > 64 && v3 <= 90 )
v3 = (v3 + a2 - 65) % 26 + 65;
}
else
{
v3 = (v3 + a2 - 97) % 26 + 97;
}
a1[i] = v3;
}
return result;
}

跑一个gpt,告诉我如下:

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
这段代码是一个简单的加密函数,接受一个字符串和一个整数作为参数。它通过将字符串中的每个字母按照指定的偏移量进行加密。

让我逐行解释:

1. `int __cdecl encrypt(char *a1, int a2)`:这是一个名为`encrypt`的函数,接受一个字符指针 `a1` 和一个整数 `a2` 作为参数。函数返回一个整数。

2. `int result;`:定义一个整数变量 `result`,用于存储函数的返回值。

3. `char v3;`:定义一个字符变量 `v3`,用于存储字符串中的当前字符。

4. `int i;`:定义一个整数变量 `i`,用于循环计数。

5. `for (i = 0; ; ++i)`:开始一个无限循环,从字符串的第一个字符开始遍历。

6. `result = (unsigned __int8)a1[i];`:将字符串中当前位置的字符转换为无符号字节并赋给 `result`。

7. `if (!(_BYTE)result)`:如果 `result` 是空字符(即字符串结束符 `\0`),则退出循环。

8. `v3 = a1[i];`:将当前字符赋给 `v3`。

9. `if (v3 <= 96 || v3 > 122)`:如果当前字符是小写字母(ASCII码为97到122之间),则执行下面的语句块。

10. `if (v3 > 64 && v3 <= 90)`:如果当前字符是大写字母(ASCII码为65到90之间),则执行下面的语句块。

11. `v3 = (v3 + a2 - 65) % 26 + 65;`:对大写字母进行加密,加上偏移量 `a2` 后取余数,确保加密后的字符仍在大写字母的范围内。

12. `else`:如果当前字符既不是大写字母也不是小写字母,则不对其进行加密。

13. `v3 = (v3 + a2 - 97) % 26 + 97;`:对小写字母进行加密,加上偏移量 `a2` 后取余数,确保加密后的字符仍在小写字母的范围内。

14. `a1[i] = v3;`:将加密后的字符写回原字符串中。

15. `return result;`:返回函数的结果(最后一个字符的 ASCII 值)。

这段代码实现了一个简单的 Caesar 加密算法,通过将字母按照指定的偏移量进行移位来加密字符串。

发现这就是一个简单的凯撒密码

那么这个函数的逻辑就不言而喻了,所以encrypt函数的逻辑就是输入一个字符串和一个偏移量,然后进行凯撒密码移位处理

从源码得知移位量是9,并且后续是和 oujp{H0d_TwXf_Lahyc0_14_e3ah_Rvy0ac@wc!}"比较要相等,所以可以把这个字符串逆向移位,得到flag

flag{Y0u_KnOw_Crypt0_14_v3ry_Imp0rt@nt!}

2、喵喵喵的flag碎了一地

拖进die一看发现是64位程序,用ida64打开

F5看源码得到提示

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
_main(argc, argv, envp);
puts("Hint:");
puts("1. Open in IDA and Learn about `Strings` to find the first part of the flag");
puts("2. Learn about `Functions` to find the second part of the flag which is the name of a function");
puts("3. The hint for the last part is in the function you found in the second part");
return 0;
}

第一个hint要我们看strings,那就看,shift+F12得到

flag{My_fl@g_h4s_

第二个hint让我们看fuctions,那就看

得到

br0ken_4parT_

给了提示

1
2
3
4
int br0ken_4parT_()
{
return puts("Learn about `Xref` and Find out which function refers me to get the last part of the flag!");
}

提示要让我们找引用了这个函数的函数,那就找

按x得到

于是跳转到fuction718,得到最后一部分flag

Bu7_Y0u_c@n_f1x_1t!}

所以完整的flag为

flag{My_fl@g_h4s_br0ken_4parT_Bu7_Y0u_c@n_f1x_1t!}


赛后复盘

1
https://blog.csdn.net/qq_42557115/article/details/138267583