sctf2016_sycshell出题小结

0x01

tip:

<!-- 内部系统资料:http://sycshell.sycsec.com:61180/ -->
http://58.213.63.27:61180/phpinfo.php

对于企业那种,可能会在源码中看到一些虚拟主机配置后,在内网里面是使用hosts,由于没对其进行限制,可导致外网访问其内容

即修改一下本地的hosts:

58.213.63.27 sycshell.sycsec.com

0x02

tip:

一个图片地址
./image/
另外就是一个jsfuck编码

jsfuck下载

直接运行时弹框:
/NO TIP/

他的函数运行规则是,所以重在这个函数体

[]['sort']['constructor']('函数体')();
解法一:
<script>
alert(/\n(.+)/.exec(eval(prompt().slice(0,-2)))[1]);
</script>

解法二:
将最后两个()换成.toString()输出

解法三:
将函数体复制出来,像解正常的jsfuck一样

jsfuck详解:
http://drops.wooyun.org/web/4410

0x03

http://sycshell.sycsec.com:61180/W0Ca1N1CaiBuDa0/read.php
测试:php5.3.9 解题受版本影响

<?php
show_source(__FILE__);
$pass = @$_GET['pass'];
$a = "syclover";

strlen($pass) > 15 ? die("Don't Hack me!") : "";

if(!is_numeric($pass) || preg_match('/0(x)?|-|\+|\s|^(\.|\d).*$/i',$pass)){
    die('error');
}

if($pass == 1 &&  $a[$pass] === "s"){
    $file = isset($_GET['f']) ? $_GET['f'].'.php' : 'index.php';
    @include $file;
}

大概思路就是要绕过is_numeric,并且让$pass == 1 && $a[$pass] === "s",也就是要为1的时候又要在另外一种情况为0

1、is_numeric绕过
is_numeric实现代码,is_numeric对输入的参数,先做了样式判断如果是整型、浮点型就直接返回true,如果是字符串则进入is_numeric_string函数进行判断,然后这个函数是这样判断的。

关键代码:

END_API zend_uchar ZEND_FASTCALL _is_numeric_string_ex(......) /* {{{ */ 
{   
    ...... 
    /* Skip any whitespace 
     * This is much faster than the isspace() function */ 
    while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' || *str == '\f') { 
        str++; 
        length--; 
    } 
    ptr = str; 
    if (*ptr == '-') { 
        neg = 1; 
        ptr++; 
    } else if (*ptr == '+') { 
        ptr++; 
    } 
    if (ZEND_IS_DIGIT(*ptr)) { 
        /* Skip any leading 0s */ 
        while (*ptr == '0') { 
            ptr++; 
        } 
.... 
        for (type = IS_LONG; !(digits >= MAX_LENGTH_OF_LONG && (dval || allow_errors == 1)); digits++, ptr++) { 
check_digits: 
            if (ZEND_IS_DIGIT(*ptr)) { 
                tmp_lval = tmp_lval * 10 + (*ptr) - '0'; 
                continue; 
            } else if (*ptr == '.' && dp_or_e < 1) { 
                goto process_double; 
            } else if ((*ptr == 'e' || *ptr == 'E') && dp_or_e < 2) { 
                const char *e = ptr + 1; 
                if (*e == '-' || *e == '+') { 
                    ptr = e++; 
                } 
                if (ZEND_IS_DIGIT(*e)) { 
                    goto process_double; 
                } 
            } 
            break; 
        } 
...... 
    } 
}

空格、\t、\n、\r、\v、\f、+、-能够出现在参数开头,“点”能够在参数任何位置,E、e只能出现在参数中间。

所以主要来看看这个正则:

preg_match('/0(x)?|-|\+|\s|^(\.|\d).*$/i',$pass)

对0、0x、0X、-、+、空白字符(也就是\v\t那些)、以.或者以数字开头的,都会被拦截
php5,3,29,这里可以直接用%0b绕过\s的匹配

更多参考:
http://zone.wooyun.org/content/24075
http://zone.wooyun.org/content/23961

2、$pass == 1 && $a[$pass] === "s"
感觉是不可能,因为要$pass=1又要=0
但是有个这样的小trick

$a="syclover"
$a['a']
如果是字符串,也就是a的时候,offset取值的时候会转化为整数,也就是0

https://github.com/80vul/phpcodz/blob/master/research/pch-009.md


所以综上可以有两种做法:

1、很多9的时候,php精度问题,最后会转化为1
%0b.99999999999999999999999999999999

2、这个就是利用e科学计数法来使得为1
%0b.1e1

然而.又是属于字符串,前面的%0b会忽略掉,所以\(a['.1e1']就是\)a[0]
最后我是限制了pass的长度,所以第一种解法是不行的

0x04

    $file = isset($_GET['f']) ? $_GET['f'].'.php' : 'index.php';
    @include $file;

一个包含,可控名,但是后面有一个.php,最后我也是给出一个phpinfo地址,所以就很明确了。

通过phpinfo上传文件,得到临时文件,然后利用竞争去包含(我做了处理,1s删一次文件)

但是在phpinfo可以看到

auto_prepend_file	/home/wwwroot/waf.php

也就是任何php运行前都会加载这个

http://sycshell.sycsec.com:61180/W0Ca1N1CaiBuDa0/read.php?pass=%0b.1e1&f=php://filter/convert.base64-encode/resource%3D/home/wwwroot/waf

读取一下waf内容

<?php
if(isset($_GET['f']) && preg_match("/zip|phar/",$_GET['f'],$array)){
	die("SycWaf: Don't Hack me!");
}

其中preg_match并没有进行i模式,也就是对大小写敏感,所以直接ZIP可绕过

所以最后也就是通过phpinfo竞争上传一个zip临时文件,然后再通过zip协议包含(phar协议好像也行),生成一个shell

shell地址可以是/tmp或者一开始的./image下面,其他地方并没有权限

0x05

记录一下服务器对tmp文件缓删过程
禁删

./inotifywait -mq -e create --timefmt '%y-%m-%d %H:%M' --format '%w%f' --exclude "^/tmp/[^(php)]" /tmp/ | while read file
do
        chattr +i  ${file}
        echo 1
done

删除

chattr -i  /tmp/php*;rm -f `ls /tmp/php* | egrep -v php-cgi.sock`

后台运行

nohup watch -n 1 ./rm.sh >/dev/null &
nohup ./inotifywait.sh >/dev/null &
posted @ 2016-05-09 19:51  l3m0n  阅读(1260)  评论(0编辑  收藏  举报