没思路,直接找源码和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=64page_include_depth=0function include_page {# include_page <pathname>local pathname=$1local cmd=""[[ "${pathname:(-4)}" = '.wtf' ]];local can_execute=$?;page_include_depth=$(($page_include_depth+1))if [[ $page_include_depth -lt $max_page_include_depth ]]thenlocal 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 ]]thencmd+=$'\n'"${line#"$"}";elseif [[ -n $cmd ]]theneval "$cmd" || log "Error during execution of ${cmd}";cmd=""fiecho $linefidone < ${pathname}elseecho "<p>Max include depth exceeded!<p>"fi}
如果存在一个wtf文件,则文件内容会被当成命令执行,
#!/usr/bin/env bashsource 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 poststouch posts/.noread; # don't allow file reads on postlocal 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 homepageecho "<li><a href=\"/post.wtf?post=${post_id}\">$(htmlentities <<< ${title})</a> by $(htmlentities <<< ${username})</li>" >> .index_cache.html# add post to users' post cachelocal 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 cacheecho "${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。
