Guest User

Untitled

a guest
Sep 17th, 2016
178
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.65 KB | None | 0 0
  1.  
  2. # vim: ft=wtf
  3. $ source user_functions.sh
  4. $ if is_logged_in
  5. $ then
  6. $ echo "<p>Hi, ${COOKIES['USERNAME']}. <a href='/logout.wtf'>Logout</a> <a href='/profile.wtf?user=$(basename $(find_user_file ${COOKIES['USERNAME']}))'>Profile</a></p>"
  7. $ echo "<a href=/new_post.wtf>New Post</a>";
  8. $ else
  9. $ echo "<p>You're not logged in. <a href='/login.wtf'>Login</a> <a href='/new_user.wtf'>Register</a></p>"
  10. $ fi
  11. $ if [[ -e .index_cache.html ]]
  12. $ then
  13. $ cat .index_cache.html;
  14. $ else
  15. $ for post_file in posts/*; do
  16. $ post_file=$(basename $post_file);
  17. $ post_title=$(nth_line 2 < posts/$post_file | htmlentities);
  18. $ post_user=$(nth_line 1 < posts/$post_file | htmlentities);
  19. $ echo "<li><a href=\"/post.wtf?post=$post_file\">$post_title</a> by ${post_user}</li>";
  20. $ done;
  21. $ fi
  22.  
  23. #!/usr/bin/env bash
  24. # Some useful standard functions to have around :)
  25.  
  26. # check if an array contains a given value
  27. # contains "asdf" "asdf an array of values" => has exit code 0
  28. function contains {
  29. local e;
  30. for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done;
  31. return 1;
  32. }
  33.  
  34. function file_exists {
  35. local file=$1;
  36. stat ${file} > /dev/null;
  37. }
  38.  
  39. function nth_line {
  40. local n=$1;
  41. local filename;
  42. if [[ $# != 1 ]]
  43. then
  44. filename=$2;
  45. sed "${n}q;d" < $filename;
  46. else
  47. sed "${n}q;d"
  48. fi 2> /dev/null
  49. }
  50.  
  51. function redirect {
  52. local target="$1";
  53. echo "<script>window.location.href='${target}';</script>";
  54. }
  55.  
  56. # Hacky way of figuring out which date command is appropriate,
  57. # depending if we're on BSD or GNU coreutils
  58. YESTERDAY_CMD="";
  59. TOMORROW_CMD="";
  60. if date --help | grep "GNU" > /dev/null
  61. then
  62. # Using GNU date
  63. TOMORROW_CMD="date -d tomorrow";
  64. YESTERDAY_CMD="date -d yesterday";
  65. else
  66. # Using BSD date
  67. TOMORROW_CMD="date -v +1d";
  68. YESTERDAY_CMD="date -v -1d";
  69. fi
  70.  
  71. function set_cookie {
  72. local key="$1";
  73. local value="$2";
  74. local expiry=$(${TOMORROW_CMD});
  75. echo "<script>document.cookie = '${key}=${value}; expires=${expiry}; path=/';</script>";
  76. COOKIES[$key]="${value}";
  77. }
  78.  
  79. function get_cookie {
  80. echo "${COOKIES[$1]}";
  81. }
  82.  
  83. function remove_cookie {
  84. local key="$1";
  85. local expiry=$(${YESTERDAY_CMD}); # expiration dates in the past delete cookies
  86. echo "<script>document.cookie = '${key}=riperino; expires=${expiry}; path=/';</script>";
  87. unset COOKIES[$key];
  88. }
  89.  
  90. # take text on input, transform any html special chars to the corresponding entities
  91. function htmlentities {
  92. sed "s/\&/\&amp;/g" | sed "s/</\</g" | sed "s/>/\>/g";
  93. }
  94.  
  95. # vim: ft=wtf
  96.  
  97. $ source user_functions.sh
  98. $ if [[ $method = 'POST' ]]
  99. $ then
  100. $ local username=${POST_PARAMS['username']};
  101. $ local password=${POST_PARAMS['password']};
  102. $ local userfile=$(find_user_file ${username});
  103. $ if [[ ${userfile} != 'NONE' ]]
  104. $ then
  105. $ # User exists, try to login
  106. $ if $(check_password ${username} ${password})
  107. $ then
  108. $ # correct pass
  109. $ set_cookie "USERNAME" ${username};
  110. $ set_cookie "TOKEN" $(nth_line 3 ${userfile});
  111. $ redirect "/";
  112. $ else
  113. $ # incorrect pass
  114. $ echo "<h3>Sorry, wrong password for user ${username}:(<br>Try again?</h3>";
  115. $ fi
  116. $ else
  117. $ # user doesn't exist
  118. $ echo "<h3>Sorry, user ${username} doesn't exist :(<br>Try again?</h3>"
  119. $ fi
  120. $ fi
  121.  
  122. # vim: ft=wtf
  123. $ source user_functions.sh
  124. $ if is_logged_in
  125. $ then
  126. $ remove_cookie 'USERNAME';
  127. $ remove_cookie 'TOKEN';
  128. $ redirect "/";
  129. $ else
  130. $ echo "<h3>You need to be logged in to log out, bud.</h3>";
  131. $ fi
  132.  
  133. # vim: ft=wtf
  134. $ source user_functions.sh
  135. $ source post_functions.sh
  136. $ if [[ $method = 'POST' ]]
  137. $ then
  138. $ if is_logged_in
  139. $ then
  140. $ post_id=$(create_post "${COOKIES['USERNAME']}" "${POST_PARAMS['title']}" "${POST_PARAMS['text']}");
  141. $ redirect "/post.wtf?post=${post_id}";
  142. $ else
  143. $ echo "<h3>pls login 2 be posting</h3>";
  144. $ fi
  145. $ fi
  146.  
  147. # vim: ft=wtf
  148. $ source user_functions.sh
  149. $ if [[ $method = 'POST' ]]
  150. $ then
  151. $ local username=${POST_PARAMS['username']};
  152. $ local password=${POST_PARAMS['password']};
  153. $ if [[ $(find_user_file "${username}") = 'NONE' ]]
  154. $ then
  155. $ # create user
  156. $ echo "ok, gonna go make that user... ${username}";
  157. $ local user_id=$(create_user "${username}" "${password}");
  158. $ redirect "/login.wtf";
  159. $ else
  160. $ # user already exists
  161. $ echo "<h3>Sorry, user ${username} already exists :(<br>Try again?</h3>"
  162. $ fi
  163. $ fi
  164.  
  165. # vim: ft=wtf
  166. $ source user_functions.sh
  167. $ if contains 'post' ${!URL_PARAMS[@]} && file_exists "posts/${URL_PARAMS['post']}"
  168. $ then
  169. $ post_id=${URL_PARAMS['post']};
  170. $ for post_file in $(ls posts/${post_id}/* | sort --field-separator='/' --key=3 -n); do
  171. $ echo "<div class=\"post\">";
  172. $ poster=$(nth_line 1 ${post_file} | htmlentities);
  173. $ title=$(nth_line 2 ${post_file} | htmlentities);
  174. $ body=$(tail -n +3 ${post_file} | htmlentities 2> /dev/null);
  175. $ echo "<span class=\"post-poster\">Posted by <a href=\"/profile.wtf?user=$(basename $(find_user_file "${poster}"))\">${poster}</a></span>";
  176. $ echo "<span class=\"post-title\">$title</span>";
  177. $ echo "<span class=\"post-body\">$body</span>";
  178. $ echo "</div>";
  179. $ done
  180. $ else
  181. $ echo "Pls give a (valid) post id";
  182. $ fi;
  183.  
  184. <div class="action-btns">
  185. $ echo "<a href=\"/reply.wtf?post=${post_id}\">Reply</a>"
  186. <a href="/">Back</a>
  187. </div>
  188.  
  189. </body>
  190.  
  191. </div>
  192.  
  193. #!/usr/bin/env bash
  194. source user_functions.sh
  195.  
  196. # Create a new post. Returns the post id.
  197. function create_post {
  198. local username=$1;
  199. local title=$2;
  200. local text=$3;
  201. local hashed=$(hash_username "${username}");
  202.  
  203. # ensure posts dir exists and isn't listable.
  204. mkdir posts 2> /dev/null;
  205. touch posts/.nolist; # don't allow directory listing on posts
  206. touch posts/.noread; # don't allow file reads on post
  207.  
  208. local post_id=$(basename $(mktemp --directory posts/XXXXX));
  209.  
  210.  
  211. echo ${username} > "posts/${post_id}/1";
  212. echo ${title} >> "posts/${post_id}/1";
  213. echo ${text} >> "posts/${post_id}/1";
  214.  
  215. touch "posts/${post_id}/.nolist";
  216. touch "posts/${post_id}/.noread";
  217.  
  218.  
  219. # add to our cache for the homepage
  220. echo "<li><a href=\"/post.wtf?post=${post_id}\">$(htmlentities <<< ${title})</a> by $(htmlentities <<< ${username})</li>" >> .index_cache.html
  221.  
  222. # add post to users' post cache
  223. local hashed=$(hash_username "${username}");
  224. echo "${post_id}/1" >> "users_lookup/${hashed}/posts";
  225.  
  226. echo ${post_id};
  227.  
  228. }
  229.  
  230. function reply {
  231. local post_id=$1;
  232. local username=$2;
  233. local text=$3;
  234. local hashed=$(hash_username "${username}");
  235.  
  236. curr_id=$(for d in posts/${post_id}/*; do basename $d; done | sort -n | tail -n 1);
  237. next_reply_id=$(awk '{print $1+1}' <<< "${curr_id}");
  238. next_file=(posts/${post_id}/${next_reply_id});
  239. echo "${username}" > "${next_file}";
  240. echo "RE: $(nth_line 2 < "posts/${post_id}/1")" >> "${next_file}";
  241. echo "${text}" >> "${next_file}";
  242.  
  243. # add post this is in reply to to posts cache
  244. echo "${post_id}/${next_reply_id}" >> "users_lookup/${hashed}/posts";
  245. }
  246.  
  247. # vim: ft=wtf
  248. $ if contains 'user' ${!URL_PARAMS[@]} && file_exists "users/${URL_PARAMS['user']}"
  249. $ then
  250. $ local username=$(head -n 1 users/${URL_PARAMS['user']});
  251. $ echo "<h3>${username}'s posts:</h3>";
  252. $ echo "<ol>";
  253. $ get_users_posts "${username}" | while read -r post; do
  254. $ post_slug=$(awk -F/ '{print $2 "#" $3}' <<< "${post}");
  255. $ echo "<li><a href=\"/post.wtf?post=${post_slug}\">$(nth_line 2 "${post}" | htmlentities)</a></li>";
  256. $ done
  257. $ echo "</ol>";
  258. $ if is_logged_in && [[ "${COOKIES['USERNAME']}" = 'admin' ]] && [[ ${username} = 'admin' ]]
  259. $ then
  260. $ get_flag1
  261. $ fi
  262. $ fi
  263.  
  264. # vim: ft=wtf
  265. $ source user_functions.sh
  266. $ source post_functions.sh
  267. $ if [[ $method = 'POST' ]]
  268. $ then
  269. $ if is_logged_in
  270. $ then
  271. $ if [[ ${POST_PARAMS['text']} != '' && ${URL_PARAMS['post']} != '' ]]
  272. $ then
  273. $ reply "${URL_PARAMS['post']}" "${COOKIES['USERNAME']}" "${POST_PARAMS['text']}"
  274. $ redirect "/post.wtf?post=${URL_PARAMS['post']}#${next_post_id}";
  275. $ else
  276. $ redirect "/reply.wtf?post=${URL_PARAMS['post']}";
  277. $ fi
  278. $ else
  279. $ redirect "/login.wtf"
  280. $ fi
  281. $ fi
  282.  
  283. $ if [[ ${URL_PARAMS['post']} = '' ]]
  284. $ then
  285. $ echo "u need to be replying to a post, you dumdum";
  286. $ fi
  287.  
  288. $ if is_logged_in
  289. $ then
  290. $ echo "u r logged in, s0 gib reply"
  291. $ else
  292. $ redirect "/login.wtf"
  293. $ fi
  294.  
  295. #!/usr/bin/env bash
  296. cp -R /opt/wtf.sh /tmp/wtf_runtime;
  297.  
  298. # protect our stuff
  299. chmod -R 555 /tmp/wtf_runtime/wtf.sh/*.wtf;
  300. chmod -R 555 /tmp/wtf_runtime/wtf.sh/*.sh;
  301. chmod 777 /tmp/wtf_runtime/wtf.sh/;
  302.  
  303. # set all dirs we could want to write into to be owned by www
  304. # (We don't do whole webroot since we want the people to be able to create
  305. # files in webroot, but not overwrite existing files)
  306. chmod -R 777 /tmp/wtf_runtime/wtf.sh/posts/;
  307. chown -R www:www /tmp/wtf_runtime/wtf.sh/posts/;
  308.  
  309. chmod -R 777 /tmp/wtf_runtime/wtf.sh/users/;
  310. chown -R www:www /tmp/wtf_runtime/wtf.sh/users/;
  311.  
  312. chmod -R 777 /tmp/wtf_runtime/wtf.sh/users_lookup/;
  313. chown -R www:www /tmp/wtf_runtime/wtf.sh/users_lookup/;
  314.  
  315. # let's get this party started!
  316. su www -c "/tmp/wtf_runtime/wtf.sh/wtf.sh 8000";</span>
  317. </div>
  318.  
  319. #!/usr/local/bin/bash
  320. function hash_password {
  321. local password=$1;
  322. (shasum <<< ${password}) | cut -d\ -f1;
  323. }
  324.  
  325. #!/usr/local/bin/bash
  326. function get_flag1 {
  327. echo "Congratz! The flag is : CSAW2016{c0n6r47zbu7d0n7b350n41v3}"
  328. }
  329.  
  330. # hash usernames for lookup in the users_lookup table
  331. function hash_username {
  332. local username=$1;
  333. (shasum <<< ${username}) | cut -d\ -f1;
  334. }
  335.  
  336. # generate a random token, base64 encoded
  337. # on GNU base64 wraps at 76 characters, so we need to pass --wrap=0
  338. function generate_token {
  339. (head -c 64 | (base64 --wrap=0 || base64)) < /dev/urandom 2> /dev/null;
  340. }
  341.  
  342. function find_user_file {
  343. local username=$1;
  344. local hashed=$(hash_username "${username}");
  345. local f;
  346. if [[ -n "${username}" && -e "users_lookup/${hashed}" ]]
  347. then
  348. echo "users/$(cat "users_lookup/${hashed}/userid")";
  349. else
  350. echo "NONE"; # our failure case -- ugly but w/e...
  351. fi;
  352. return;
  353. }
  354.  
  355. # The caller is responsible for checking that the user doesn't exist already calling this
  356. function create_user {
  357. local username=$1;
  358. local password=$2;
  359. local hashed_pass=$(hash_password ${password});
  360. local hashed_username=$(hash_username "${username}");
  361. local token=$(generate_token);
  362.  
  363. mkdir users 2> /dev/null; # make sure users directory exists
  364. touch users/.nolist; # make sure that the users dir can't be listed
  365. touch users/.noread; # don't allow reading of user files directly
  366.  
  367. mkdir users_lookup 2> /dev/null; # make sure the username -> userid lookup directory exists
  368. touch users_lookup/.nolist; # don't let it be listed
  369.  
  370. local user_id=$(basename $(mktemp users/XXXXX));
  371.  
  372.  
  373. # user files look like:
  374. # username
  375. # hashed_pass
  376. # token
  377. echo "${username}" > "users/${user_id}";
  378. echo "${hashed_pass}" >> "users/${user_id}";
  379. echo "${token}" >> "users/${user_id}";
  380.  
  381.  
  382. mkdir "users_lookup/${hashed_username}" 2> /dev/null;
  383. touch "users_lookup/${hashed_username}/.nolist"; # lookup dir for this user can't be readable
  384. touch "users_lookup/${hashed_username}/.noread"; # don't allow reading the lookup dir
  385. touch "users_lookup/${hashed_username}/posts"; # lookup for posts this user has participated in
  386. echo "${user_id}" > "users_lookup/${hashed_username}/userid"; # create reverse lookup
  387.  
  388. echo ${user_id};
  389. }
  390.  
  391. function check_password {
  392. local username=$1;
  393. local password=$2;
  394. local userfile=$(find_user_file ${username});
  395.  
  396. if [[ ${userfile} = 'NONE' ]]
  397. then
  398. return 1;
  399. fi
  400.  
  401. local hashed_pass=$(hash_password ${password});
  402. local correct_hash=$(head -n2 ${userfile} | tail -n1);
  403. [[ ${hashed_pass} = ${correct_hash} ]];
  404. return $?;
  405. }
  406.  
  407. function is_logged_in {
  408. contains 'TOKEN' ${!COOKIES[@]} && contains 'USERNAME' ${!COOKIES[@]};
  409. local has_cookies=$?
  410. local userfile=$(find_user_file ${COOKIES['USERNAME']});
  411. [[ ${has_cookies} \
  412. && ${userfile} != 'NONE' \
  413. && $(tail -n1 ${userfile} 2>/dev/null) = ${COOKIES['TOKEN']} \
  414. && $(head -n1 ${userfile} 2>/dev/null) = ${COOKIES['USERNAME']} \
  415. ]];
  416. return $?;
  417. }
  418.  
  419. function get_users_posts {
  420. local username=$1;
  421. local hashed=$(hash_username "${username}");
  422. # we only have to iterate over posts a user has replied to
  423. while read -r post_id; do
  424. echo "posts/${post_id}";
  425. done < "users_lookup/${hashed}/posts";
  426. }
  427.  
  428.  
  429. #!/usr/bin/env bash
  430. # config stuff
  431. PROCESS_LIMIT=512 # per connection
  432. PROFILE=false
  433.  
  434. # shell options
  435. shopt -s extglob;
  436.  
  437. # ~~ PROFILING ~~
  438. if [[ $PROFILE = true ]]
  439. then
  440. PS4='+ $(date "+%s.%N") $(if [[ ${FUNCNAME} = "" ]]; then echo NONE; else echo "${FUNCNAME}"; fi ) ${LINENO}\011 '
  441. exec 3>&2 2>/tmp/bashprof.$$.log
  442. set -x
  443. fi
  444.  
  445. # sick facts about bash
  446. declare -a BASH_FACTS=(
  447. $'Bash has an `until` keyword, which is equivalent to `while not`.'
  448. $'Single and Double quotes do different things in bash -- single quotes do not interpolate variables, while double quotes do.'
  449. $'When globbing on arrays in bash, you have the option to use [*] and [@], which appear to both return all the elements of the array. However, [*] acts like a "splat operator", while [@] keeps all everything constrained to the same argument.'
  450. $'The bash array access syntax looks like ${array[$idx]}.'
  451. $'If you forget the brackets in an array access, bash will just return the first element of the array.'
  452. $'Bash didn\'t have Associative Arrays until Bash 4'
  453. $'The idomatic way of iterating over all the lines in a file in bash is `while read -r line; do <something with line>; done < <filename>`'
  454. $'Loops are just commands. So, you can pipe things into and out of them!'
  455. );
  456.  
  457. source lib.sh # import stdlib
  458.  
  459. VERSION="0.0.0.0.1 \"alphaest of bets\""
  460. declare -a REPLY_HEADERS=(
  461. "X-Powered-By: wtf.sh ${VERSION}" # Fly the banner of wtf.sh proudly!
  462. "X-Bash-Fact: $(shuf --head-count=1 -e "${BASH_FACTS[@]}")" # select a random BASH FACT to include
  463. );
  464.  
  465. declare -A URL_PARAMS # hashtable of url parameters
  466. declare -A POST_PARAMS # hashtable of post parameters
  467. declare -A HTTP_HEADERS # hashtable of http headers
  468. declare -A COOKIES # hashtable of cookies
  469.  
  470.  
  471.  
  472. function log {
  473. echo "[`date`] $@" 1>&9
  474. }
  475.  
  476. urldecode() {
  477. # urldecode <string>
  478.  
  479. local url_encoded="${1//+/ }"
  480. printf '%b' "${url_encoded//%/\\x}"
  481. }
  482.  
  483. max_page_include_depth=64
  484. page_include_depth=0
  485. function include_page {
  486. # include_page <pathname>
  487. local pathname=$1
  488. local cmd=""
  489. [[ "${pathname:(-4)}" = '.wtf' ]];
  490. local can_execute=$?;
  491. page_include_depth=$(($page_include_depth+1))
  492. if [[ $page_include_depth -lt $max_page_include_depth ]]
  493. then
  494. local line;
  495. while read -r line; do
  496. # check if we're in a script line or not ($ at the beginning implies script line)
  497. # also, our extension needs to be .wtf
  498. [[ "$" = "${line:0:1}" && ${can_execute} = 0 ]];
  499. is_script=$?;
  500.  
  501. # execute the line.
  502. if [[ $is_script = 0 ]]
  503. then
  504. cmd+=$'\n'"${line#"$"}";
  505. else
  506. if [[ -n $cmd ]]
  507. then
  508. eval "$cmd" || log "Error during execution of ${cmd}";
  509. cmd=""
  510. fi
  511. echo $line
  512. fi
  513. done < ${pathname}
  514. else
  515. echo "<p>Max include depth exceeded!<p>"
  516. fi
  517. }
  518.  
  519. function parse_headers {
  520. while read -r line; do
  521. if [[ $line = $'\r' || $line == $'\n' ]]
  522. then
  523. break
  524. else
  525. a=($line)
  526. key=${a[0]%?}
  527. value=${a[@]:1}
  528. HTTP_HEADERS[$key]=${value:0:-1}; # remove \r from end
  529. fi
  530. done
  531. }
  532.  
  533. function parse_cookies {
  534. while read -d ';' -r cookie; do
  535. local key=$(cut -d\= -f1 <<< "${cookie}");
  536. local value=${cookie#*=};
  537. COOKIES[${key}]=${value};
  538. done <<< "${HTTP_HEADERS['Cookie']};" # append a ; so we still get the last field -- read drops the last thing >_<
  539. }
  540.  
  541. function handle_connection {
  542. ulimit -u "${PROCESS_LIMIT}"; # limit num processes per connection
  543.  
  544. # Parse query and any url parameters that may be in the path
  545. IFS=' ' read -r method path version
  546.  
  547. # fast fail on empty request
  548. if [[ ${method} = '' ]]
  549. then
  550. return
  551. fi
  552.  
  553. query=${path##*\?};
  554. if [[ $query != $path ]]
  555. then
  556. while read -d '&' -r param; do
  557. IFS='=' read key value <<< ${param};
  558. URL_PARAMS[$key]=$(urldecode $value)
  559. done <<< "${query}&" # add & so last argument is seen
  560. fi
  561.  
  562. request=("$method" "$path" "$version")
  563. path=$(urldecode $(cut -d\? -f1 <<< "${path}")) # strip url parameters, urldecode
  564.  
  565. # parse headers
  566. parse_headers;
  567.  
  568. # parse out cookie values, if they exist
  569. if contains "Cookie" "${!HTTP_HEADERS[@]}"
  570. then
  571. parse_cookies;
  572. fi
  573.  
  574. if [[ $method == "POST" ]]
  575. then
  576. # TODO: handle multipart bodies
  577. local line;
  578. local n;
  579. n=${HTTP_HEADERS['Content-Length']};
  580. read -n$n -r line;
  581. params=($(sed "s/\&/ /g" <<< "${line}"))
  582. for param in ${params[@]}; do
  583. IFS='=' read key value <<< ${param};
  584. POST_PARAMS[$key]=$(urldecode $value)
  585. done
  586. fi
  587.  
  588. # if we know the IP (via an X-Forwarded-For header), stick the user in a sandbox
  589. # (Cloudflare will fill in this IP in prod, we can also have nginx fill it in in dev if we want)
  590. if contains "X-Forwarded-For" "${!HTTP_HEADERS[@]}"
  591. then
  592. sandbox_dir="$((cksum <<< ${HTTP_HEADERS["X-Forwarded-For"]}) | cut -d\ -f1).sandbox";
  593. # create sandbox if it doesn't exist
  594. if [[ ! -e "${sandbox_dir}" ]]
  595. then
  596. mkdir "${sandbox_dir}";
  597. # copy anything that isn't itself a sandbox to the dir
  598. cp -R !(*.sandbox) "${sandbox_dir}";
  599. fi
  600. cd "${sandbox_dir}";
  601. else
  602. log "WARNING: Not sandboxing: no X-Forwarded-For header found!"
  603. fi
  604.  
  605. requested_path=$(pwd)/${path}
  606.  
  607. # if a directory is requested, try each of the following, in order
  608. # index.wtf, index.html
  609. local index_fills=("index.wtf" "index.html");
  610. if [[ -d ${requested_path} ]]
  611. then
  612. for i in ${index_fills}; do
  613. if [[ -e "${requested_path}/${i}" ]]
  614. then
  615. requested_path="${requested_path}/${i}";
  616. break;
  617. fi
  618. done
  619. fi
  620.  
  621. # check for possible directory traversals / other undesirable path elements by
  622. # removing them and 503-ing if the string was changed
  623. test_path=$(sed "s/\.\.//g" <<< "${requested_path}")
  624. if [[ ${test_path} != ${requested_path} ]]
  625. then
  626. echo "HTTP/1.1 503 Forbidden"
  627. echo "Content-Type: text/html"
  628. for reply_header in "${REPLY_HEADERS[@]}"; do
  629. echo "${reply_header}"
  630. done
  631. printf "\r\n\r\n"
  632. echo "<html><title>503</title><body>503 Forbidden</body></html>"
  633. echo "<p>Sorry, directory traversal is strongly frowned upon here at wtf.sh enterprises</p>";
  634. log "503: ${request[@]}"
  635. exit 0; # terminate early for 503
  636. fi
  637.  
  638. [[ ! -e "${requested_path}/.nolist" ]];
  639. local can_list=$?;
  640. [[ ! -e "$(dirname "${requested_path}")/.noread" ]];
  641. local can_read=$?;
  642.  
  643. if [[ -e ${requested_path} ]]
  644. then
  645. if [[ -f ${requested_path} \
  646. && ${requested_path:(-4)} != ".log"\
  647. && ${can_read} = 0 ]] # can't end in .log, can't have .noread in the parent directory
  648. then
  649. echo "HTTP/1.1 200 OK"
  650. echo "Content-Type: text/html"
  651. for reply_header in "${REPLY_HEADERS[@]}"; do
  652. echo "${reply_header}"
  653. done
  654. printf "\r\n\r\n"
  655. include_page ${requested_path};
  656. elif [[ -d ${requested_path} \
  657. && ${can_list} = 0 ]] # handle directory listing if it isn't a file and no `.nolist` file in the directory
  658. then
  659. log "$(dirname "${requested_path}")/.noread"
  660. echo "HTTP/1.1 200 OK"
  661. echo "Content-Type: text/html"
  662. for reply_header in "${REPLY_HEADERS[@]}"; do
  663. echo "${reply_header}"
  664. done
  665. printf "\r\n\r\n"
  666. echo "<h3>Index of ${request[1]}</h3>"
  667. echo "<ul>"
  668. for d in ${requested_path}/*; do
  669. size_info=($(du -h ${requested_path} | tail -n 1))
  670. echo "<li><a href="/${request[1]#"/"}${d}">${d}</a>: ${size_info[0]}</li>"
  671. done
  672. echo "</ul>"
  673. echo "<font size=2>generated by wtf.sh ${VERSION} on $(date)</font>"
  674. else
  675. echo "HTTP/1.1 503 Forbidden"
  676. echo "Content-Type: text/html"
  677. for reply_header in "${REPLY_HEADERS[@]}"; do
  678. echo "${reply_header}"
  679. done
  680. printf "\r\n\r\n"
  681. echo "<title>503 Forbidden</title>";
  682. echo "<h3>I'm sorry, I'm afraid I can't let you see that</h3>";
  683. echo "<p>It seems that you tried to list a directory with a <code>.nolist</code> file in it, or a <code>.noread</code> file in it's parent, or a forbidden file type.</p>";
  684. echo "<p>If you think this was a mistake, I feel bad for you, son. I got 99 problems, but a 503 ain't one.</p>";
  685. log "503: ${request[@]}"
  686. exit 0;
  687. fi
  688. log "200: ${request[@]}"
  689. exit 0
  690. else
  691. # If we were noread or nolist, send a 503, even though the resource doesn't even exist -- we don't want to leak what forbidden resources do and do not exist
  692. if [[ ${can_read} = 1 || ${can_list} = 1 ]];
  693. then
  694. echo "HTTP/1.1 503 Not Found";
  695. echo "Content-Type: text/html"
  696. for reply_header in "${REPLY_HEADERS[@]}"; do
  697. echo "${reply_header}"
  698. done
  699. printf "\r\n\r\n"
  700. echo "<title>503 Forbidden</title>";
  701. echo "<h3>I'm sorry, I'm afraid I can't let you see that</h3>";
  702. echo "<p>It seems that you tried to list a directory with a <code>.nolist</code> file in it, or a <code>.noread</code> file in it's parent, or a forbidden file type.</p>";
  703. echo "<p>If you think this was a mistake, I feel bad for you, son. I got 99 problems, but a 503 ain't one.</p>";
  704. log "503: ${request[@]}"
  705. else
  706. echo "HTTP/1.1 404 Not Found"
  707. echo "Content-Type: text/html"
  708. for reply_header in "${REPLY_HEADERS[@]}"; do
  709. echo "${reply_header}"
  710. done
  711. printf "\r\n\r\n"
  712. echo "<html><title>404</title><body>404, not found:<code>${request[1]}</code></body></html>"
  713. log "404: ${request[@]}"
  714. fi
  715. exit 0
  716. fi
  717. }
  718.  
  719. # start socat on specified port
  720. function start_server {
  721. echo "wtf.sh ${VERSION}, starting!";
  722. socat -T10 TCP-LISTEN:$2,fork,readbytes=4096,backlog=256,reuseaddr EXEC:"$1 -r" 9>&1
  723. echo "Socket was occupied... try again later...";
  724. }
  725.  
  726. if [[ $# != 1 ]]
  727. then
  728. echo "Usage: $0 port"
  729. exit
  730. fi
  731.  
  732. if [[ $1 == '-r' ]]
  733. then
  734. handle_connection
  735. else
  736. start_server $0 $1 # start server on specified port
  737. fi</span>
  738. </div>
Add Comment
Please, Sign In to add comment