· 10 years ago · Sep 17, 2016, 04:32 PM
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
28function contains {
29 local e;
30 for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done;
31 return 1;
32}
33
34function file_exists {
35 local file=$1;
36 stat ${file} > /dev/null;
37}
38
39function 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
51function 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
58YESTERDAY_CMD="";
59TOMORROW_CMD="";
60if date --help | grep "GNU" > /dev/null
61then
62 # Using GNU date
63 TOMORROW_CMD="date -d tomorrow";
64 YESTERDAY_CMD="date -d yesterday";
65else
66 # Using BSD date
67 TOMORROW_CMD="date -v +1d";
68 YESTERDAY_CMD="date -v -1d";
69fi
70
71function 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
79function get_cookie {
80 echo "${COOKIES[$1]}";
81}
82
83function 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
91function htmlentities {
92 sed "s/\&/\&/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
194source user_functions.sh
195
196# Create a new post. Returns the post id.
197function 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
230function 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
296cp -R /opt/wtf.sh /tmp/wtf_runtime;
297
298# protect our stuff
299chmod -R 555 /tmp/wtf_runtime/wtf.sh/*.wtf;
300chmod -R 555 /tmp/wtf_runtime/wtf.sh/*.sh;
301chmod 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)
306chmod -R 777 /tmp/wtf_runtime/wtf.sh/posts/;
307chown -R www:www /tmp/wtf_runtime/wtf.sh/posts/;
308
309chmod -R 777 /tmp/wtf_runtime/wtf.sh/users/;
310chown -R www:www /tmp/wtf_runtime/wtf.sh/users/;
311
312chmod -R 777 /tmp/wtf_runtime/wtf.sh/users_lookup/;
313chown -R www:www /tmp/wtf_runtime/wtf.sh/users_lookup/;
314
315# let's get this party started!
316su www -c "/tmp/wtf_runtime/wtf.sh/wtf.sh 8000";</span>
317</div>
318
319#!/usr/local/bin/bash
320function hash_password {
321 local password=$1;
322 (shasum <<< ${password}) | cut -d\ -f1;
323}
324
325#!/usr/local/bin/bash
326function get_flag1 {
327 echo "Congratz! The flag is : CSAW2016{c0n6r47zbu7d0n7b350n41v3}"
328}
329
330# hash usernames for lookup in the users_lookup table
331function 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
338function generate_token {
339 (head -c 64 | (base64 --wrap=0 || base64)) < /dev/urandom 2> /dev/null;
340}
341
342function 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
356function 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
391function 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
407function 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
419function 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
431PROCESS_LIMIT=512 # per connection
432PROFILE=false
433
434# shell options
435shopt -s extglob;
436
437# ~~ PROFILING ~~
438if [[ $PROFILE = true ]]
439then
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
443fi
444
445# sick facts about bash
446declare -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
457source lib.sh # import stdlib
458
459VERSION="0.0.0.0.1 \"alphaest of bets\""
460declare -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
465declare -A URL_PARAMS # hashtable of url parameters
466declare -A POST_PARAMS # hashtable of post parameters
467declare -A HTTP_HEADERS # hashtable of http headers
468declare -A COOKIES # hashtable of cookies
469
470
471
472function log {
473 echo "[`date`] $@" 1>&9
474}
475
476urldecode() {
477 # urldecode <string>
478
479 local url_encoded="${1//+/ }"
480 printf '%b' "${url_encoded//%/\\x}"
481}
482
483max_page_include_depth=64
484page_include_depth=0
485function 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
519function 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
533function 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
541function 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
720function 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
726if [[ $# != 1 ]]
727then
728 echo "Usage: $0 port"
729 exit
730fi
731
732if [[ $1 == '-r' ]]
733then
734 handle_connection
735else
736 start_server $0 $1 # start server on specified port
737fi</span>
738</div>