03月05, 2019

CTF-神仙系列 1 CSAW CTF Qualification Round 2016 wtf.sh

开始做一些世界级比赛的真题,这个系列的题目全部超出我的能力范围,故叫做神仙系列。

吃透了 writeup 后,写此篇博客记录下自己的理解,为以后的比赛做储备。

个人觉得光做做之前那样的“糖果题”收获不了多少东西,所以来找找虐,被虐得多了自然就强了。

就像某传火游戏一样,一开始玩的时候死十次才打过古达老师,现在我就算不翻滚也能无伤虐他~


前言

这题是 CSAW CTF Qualification Round 2016 的 web 题,有两个 flag,第一个 flag 值 150 分,第二个 flag 值 400 分,是该场比赛难度最高的 web 题。

xctf 平台上重现了此题,但是必须获得两个 flag 才能得分,flag1 我是独自获得的,flag2 部分参照了某神仙老外的writeup


flag1

题目环境是一个论坛,有注册、登录、发文章、评论的功能。

在展示文章的页面 post.wtf 下发现路径穿越漏洞,可以获得网站源码:

traversal

下意识找 flag 字眼:

flag

代码格式化:

<html>
<head>
    <link rel="stylesheet" type="text/css" href="/css/std.css" >
</head>
$ if contains 'user' ${!URL_PARAMS[@]} && file_exists "users/${URL_PARAMS['user']}"
$ then
$   local username=$(head -n 1 users/${URL_PARAMS['user']});
$   echo "<h3>${username}'s posts:</h3>";
$   echo "<ol>";
$   get_users_posts "${username}" | while read -r post; do
$       post_slug=$(awk -F/ '{print $2 "#" $3}' <<< "${post}");
$       echo "<li><a href=\"/post.wtf?post=${post_slug}\">$(nth_line 2 "${post}" | htmlentities)</a></li>";
$   done 
$   echo "</ol>";
$   if is_logged_in && [[ "${COOKIES['USERNAME']}" = 'admin' ]] && [[ ${username} = 'admin' ]]
$   then
$       get_flag1
$   fi
$ fi
</html>

发现如下部分:

$   if is_logged_in && [[ "${COOKIES['USERNAME']}" = 'admin' ]] && [[ ${username} = 'admin' ]]
$   then
$       get_flag1
$   fi

只要登录 admin 账号就可以获得 flag1。

先尝试注册一个账号并登录,发现如下 cookie:

cookie

在泄露的源码里得知有 users 目录,继续尝试路径穿越:

traversal

发现自己注册的账号以及 admin 都被显示了出来,对比之前的 cookie,发现这个就是TOKEN的值。

admin 的TOKEN也知道了,可以进行 cookie 欺骗。

另外一串东西发现是密码的 sha1,不过对做题没有什么帮助:

sha1

故登录 admin 所需的东西如下:

cookies

登陆成功,获得 flag1:

flag1


flag2

因为 wtf 不是常规的网页文件,故寻找解析 wtf 文件的代码:

wtf

格式化:

max_page_include_depth=64
page_include_depth=0
function include_page {
    # include_page pathname
    local pathname=$1
    local cmd=
    [[ ${pathname(-4)} = '.wtf' ]];
    local can_execute=$;
    page_include_depth=$(($page_include_depth+1))
    if [[ $page_include_depth -lt $max_page_include_depth ]]
    then
        local line;
        while read -r line; do
            # check if we're in a script line or not ($ at the beginning implies script line)
            # also, our extension needs to be .wtf
            [[ $ = ${line01} && ${can_execute} = 0 ]];
            is_script=$;

            # execute the line.
            if [[ $is_script = 0 ]]
            then
                cmd+=$'n'${line#$};
            else
                if [[ -n $cmd ]]
                then
                    eval $cmd  log Error during execution of ${cmd};
                    cmd=
                fi
                echo $line
            fi
        done  ${pathname}
    else
        echo pMax include depth exceeded!p
    fi
}

能够解析并执行 wtf 文件,如果还能够上传 wtf 文件并执行的话,就可以达到控制服务器的目的。

于是继续审计代码,发现如下代码给了这个机会:

function reply {
    local post_id=$1;
    local username=$2;
    local text=$3;
    local hashed=$(hash_username "${username}");

    curr_id=$(for d in posts/${post_id}/*; do basename $d; done | sort -n | tail -n 1);
    next_reply_id=$(awk '{print $1+1}' <<< "${curr_id}");
    next_file=(posts/${post_id}/${next_reply_id});
    echo "${username}" > "${next_file}";
    echo "RE: $(nth_line 2 < "posts/${post_id}/1")" >> "${next_file}";
    echo "${text}" >> "${next_file}";

    # add post this is in reply to to posts cache
    echo "${post_id}/${next_reply_id}" >> "users_lookup/${hashed}/posts";
}

这是评论功能的后台代码,这部分也是存在路径穿越的。

这行代码把用户名写在了评论文件的内容中:

echo "${username}" > "${next_file}";

如果用户名是一段可执行代码,而且写入的文件是 wtf 格式的,那么这个文件就能够执行我们想要的代码。

先普通地评论一下,知晓评论发送的数据包的结构:

reply

在普通评论的基础上,进行路径穿越,上传后门sh.wtf

traversal

%09是水平制表符,必须添加,不然后台会把我们的后门当做目录去解析。

访问后门,发现成功写入:

back

为了写入恶意代码,我们得让用户名里携带代码,故注册这样一个用户:

register

写入后门:

back

访问后门,执行代码,寻找get_flag2(因为之前获得 flag1 的时候是 get_flag1):

res

获得 flag2 所在的路径。

继续注册新用户:

register

写入后门:

back

访问后门,获得 flag2:

flag2

本文链接:https://blog.cindemor.com/post/ctf-fairy-1.html

-- EOF --