Guest User

Untitled

a guest
Sep 16th, 2016
357
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 24.69 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/\&/\&/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&gt; /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} &gt; "posts/${post_id}/1";
  212. echo ${title} &gt;&gt; "posts/${post_id}/1";
  213. echo ${text} &gt;&gt; "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 "&lt;li&gt;&lt;a href=\"/post.wtf?post=${post_id}\"&gt;$(htmlentities &lt;&lt;&lt; ${title})&lt;/a&gt; by $(htmlentities &lt;&lt;&lt; ${username})&lt;/li&gt;" &gt;&gt; .index_cache.html
  221.  
  222. # add post to users' post cache
  223. local hashed=$(hash_username "${username}");
  224. echo "${post_id}/1" &gt;&gt; "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}' &lt;&lt;&lt; "${curr_id}");
  238. next_file=(posts/${post_id}/${next_reply_id});
  239. echo "${username}" &gt; "${next_file}";
  240. echo "RE: $(nth_line 2 &lt; "posts/${post_id}/1")" &gt;&gt; "${next_file}";
  241. echo "${text}" &gt;&gt; "${next_file}";
  242.  
  243. # add post this is in reply to to posts cache
  244. echo "${post_id}/${next_reply_id}" &gt;&gt; "users_lookup/${hashed}/posts";
  245. }
  246.  
  247. # vim: ft=wtf
  248. $ if contains 'user' ${!URL_PARAMS[@]} &amp;&amp; file_exists "users/${URL_PARAMS['user']}"
  249. $ then
  250. $ local username=$(head -n 1 users/${URL_PARAMS['user']});
  251. $ echo "&lt;h3&gt;${username}'s posts:&lt;/h3&gt;";
  252. $ echo "&lt;ol&gt;";
  253. $ get_users_posts "${username}" | while read -r post; do
  254. $ post_slug=$(awk -F/ '{print $2 "#" $3}' &lt;&lt;&lt; "${post}");
  255. $ echo "&lt;li&gt;&lt;a href=\"/post.wtf?post=${post_slug}\"&gt;$(nth_line 2 "${post}" | htmlentities)&lt;/a&gt;&lt;/li&gt;";
  256. $ done
  257. $ echo "&lt;/ol&gt;";
  258. $ if is_logged_in &amp;&amp; [[ "${COOKIES['USERNAME']}" = 'admin' ]] &amp;&amp; [[ ${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']} != '' &amp;&amp; ${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 &lt;&lt;&lt; ${password}) | cut -d\ -f1;
  323. }
  324.  
  325. # hash usernames for lookup in the users_lookup table
  326. function hash_username {
  327. local username=$1;
  328. (shasum &lt;&lt;&lt; ${username}) | cut -d\ -f1;
  329. }
  330.  
  331. # generate a random token, base64 encoded
  332. # on GNU base64 wraps at 76 characters, so we need to pass --wrap=0
  333. function generate_token {
  334. (head -c 64 | (base64 --wrap=0 || base64)) &lt; /dev/urandom 2&gt; /dev/null;
  335. }
  336.  
  337. function find_user_file {
  338. local username=$1;
  339. local hashed=$(hash_username "${username}");
  340. local f;
  341. if [[ -n "${username}" &amp;&amp; -e "users_lookup/${hashed}" ]]
  342. then
  343. echo "users/$(cat "users_lookup/${hashed}/userid")";
  344. else
  345. echo "NONE"; # our failure case -- ugly but w/e...
  346. fi;
  347. return;
  348. }
  349.  
  350. # The caller is responsible for checking that the user doesn't exist already calling this
  351. function create_user {
  352. local username=$1;
  353. local password=$2;
  354. local hashed_pass=$(hash_password ${password});
  355. local hashed_username=$(hash_username "${username}");
  356. local token=$(generate_token);
  357.  
  358. mkdir users 2&gt; /dev/null; # make sure users directory exists
  359. touch users/.nolist; # make sure that the users dir can't be listed
  360. touch users/.noread; # don't allow reading of user files directly
  361.  
  362. mkdir users_lookup 2&gt; /dev/null; # make sure the username -&gt; userid lookup directory exists
  363. touch users_lookup/.nolist; # don't let it be listed
  364.  
  365. local user_id=$(basename $(mktemp users/XXXXX));
  366.  
  367.  
  368. # user files look like:
  369. # username
  370. # hashed_pass
  371. # token
  372. echo "${username}" &gt; "users/${user_id}";
  373. echo "${hashed_pass}" &gt;&gt; "users/${user_id}";
  374. echo "${token}" &gt;&gt; "users/${user_id}";
  375.  
  376.  
  377. mkdir "users_lookup/${hashed_username}" 2&gt; /dev/null;
  378. touch "users_lookup/${hashed_username}/.nolist"; # lookup dir for this user can't be readable
  379. touch "users_lookup/${hashed_username}/.noread"; # don't allow reading the lookup dir
  380. touch "users_lookup/${hashed_username}/posts"; # lookup for posts this user has participated in
  381. echo "${user_id}" &gt; "users_lookup/${hashed_username}/userid"; # create reverse lookup
  382.  
  383. echo ${user_id};
  384. }
  385.  
  386. function check_password {
  387. local username=$1;
  388. local password=$2;
  389. local userfile=$(find_user_file ${username});
  390.  
  391. if [[ ${userfile} = 'NONE' ]]
  392. then
  393. return 1;
  394. fi
  395.  
  396. local hashed_pass=$(hash_password ${password});
  397. local correct_hash=$(head -n2 ${userfile} | tail -n1);
  398. [[ ${hashed_pass} = ${correct_hash} ]];
  399. return $?;
  400. }
  401.  
  402. function is_logged_in {
  403. contains 'TOKEN' ${!COOKIES[@]} &amp;&amp; contains 'USERNAME' ${!COOKIES[@]};
  404. local has_cookies=$?
  405. local userfile=$(find_user_file ${COOKIES['USERNAME']});
  406. [[ ${has_cookies} \
  407. &amp;&amp; ${userfile} != 'NONE' \
  408. &amp;&amp; $(tail -n1 ${userfile} 2&gt;/dev/null) = ${COOKIES['TOKEN']} \
  409. &amp;&amp; $(head -n1 ${userfile} 2&gt;/dev/null) = ${COOKIES['USERNAME']} \
  410. ]];
  411. return $?;
  412. }
  413.  
  414. function get_users_posts {
  415. local username=$1;
  416. local hashed=$(hash_username "${username}");
  417. # we only have to iterate over posts a user has replied to
  418. while read -r post_id; do
  419. echo "posts/${post_id}";
  420. done &lt; "users_lookup/${hashed}/posts";
  421. }
  422.  
  423.  
  424. #!/usr/bin/env bash
  425. # config stuff
  426. PROCESS_LIMIT=512 # per connection
  427. PROFILE=false
  428.  
  429. # shell options
  430. shopt -s extglob;
  431.  
  432. # ~~ PROFILING ~~
  433. if [[ $PROFILE = true ]]
  434. then
  435. PS4='+ $(date "+%s.%N") $(if [[ ${FUNCNAME} = "" ]]; then echo NONE; else echo "${FUNCNAME}"; fi ) ${LINENO}\011 '
  436. exec 3&gt;&amp;2 2&gt;/tmp/bashprof.$$.log
  437. set -x
  438. fi
  439.  
  440. # sick facts about bash
  441. declare -a BASH_FACTS=(
  442. $'Bash has an `until` keyword, which is equivalent to `while not`.'
  443. $'Single and Double quotes do different things in bash -- single quotes do not interpolate variables, while double quotes do.'
  444. $'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.'
  445. $'The bash array access syntax looks like ${array[$idx]}.'
  446. $'If you forget the brackets in an array access, bash will just return the first element of the array.'
  447. $'Bash didn\'t have Associative Arrays until Bash 4'
  448. $'The idomatic way of iterating over all the lines in a file in bash is `while read -r line; do &lt;something with line&gt;; done &lt; &lt;filename&gt;`'
  449. $'Loops are just commands. So, you can pipe things into and out of them!'
  450. );
  451.  
  452. source lib.sh # import stdlib
  453.  
  454. VERSION="0.0.0.0.1 \"alphaest of bets\""
  455. declare -a REPLY_HEADERS=(
  456. "X-Powered-By: wtf.sh ${VERSION}" # Fly the banner of wtf.sh proudly!
  457. "X-Bash-Fact: $(shuf --head-count=1 -e "${BASH_FACTS[@]}")" # select a random BASH FACT to include
  458. );
  459.  
  460. declare -A URL_PARAMS # hashtable of url parameters
  461. declare -A POST_PARAMS # hashtable of post parameters
  462. declare -A HTTP_HEADERS # hashtable of http headers
  463. declare -A COOKIES # hashtable of cookies
  464.  
  465.  
  466.  
  467. function log {
  468. echo "[`date`] $@" 1&gt;&amp;9
  469. }
  470.  
  471. urldecode() {
  472. # urldecode &lt;string&gt;
  473.  
  474. local url_encoded="${1//+/ }"
  475. printf '%b' "${url_encoded//%/\\x}"
  476. }
  477.  
  478. max_page_include_depth=64
  479. page_include_depth=0
  480. function include_page {
  481. # include_page &lt;pathname&gt;
  482. local pathname=$1
  483. local cmd=""
  484. [[ "${pathname:(-4)}" = '.wtf' ]];
  485. local can_execute=$?;
  486. page_include_depth=$(($page_include_depth+1))
  487. if [[ $page_include_depth -lt $max_page_include_depth ]]
  488. then
  489. local line;
  490. while read -r line; do
  491. # check if we're in a script line or not ($ at the beginning implies script line)
  492. # also, our extension needs to be .wtf
  493. [[ "$" = "${line:0:1}" &amp;&amp; ${can_execute} = 0 ]];
  494. is_script=$?;
  495.  
  496. # execute the line.
  497. if [[ $is_script = 0 ]]
  498. then
  499. cmd+=$'\n'"${line#"$"}";
  500. else
  501. if [[ -n $cmd ]]
  502. then
  503. eval "$cmd" || log "Error during execution of ${cmd}";
  504. cmd=""
  505. fi
  506. echo $line
  507. fi
  508. done &lt; ${pathname}
  509. else
  510. echo "&lt;p&gt;Max include depth exceeded!&lt;p&gt;"
  511. fi
  512. }
  513.  
  514. function parse_headers {
  515. while read -r line; do
  516. if [[ $line = $'\r' || $line == $'\n' ]]
  517. then
  518. break
  519. else
  520. a=($line)
  521. key=${a[0]%?}
  522. value=${a[@]:1}
  523. HTTP_HEADERS[$key]=${value:0:-1}; # remove \r from end
  524. fi
  525. done
  526. }
  527.  
  528. function parse_cookies {
  529. while read -d ';' -r cookie; do
  530. local key=$(cut -d\= -f1 &lt;&lt;&lt; "${cookie}");
  531. local value=${cookie#*=};
  532. COOKIES[${key}]=${value};
  533. done &lt;&lt;&lt; "${HTTP_HEADERS['Cookie']};" # append a ; so we still get the last field -- read drops the last thing &gt;_&lt;
  534. }
  535.  
  536. function handle_connection {
  537. ulimit -u "${PROCESS_LIMIT}"; # limit num processes per connection
  538.  
  539. # Parse query and any url parameters that may be in the path
  540. IFS=' ' read -r method path version
  541.  
  542. # fast fail on empty request
  543. if [[ ${method} = '' ]]
  544. then
  545. return
  546. fi
  547.  
  548. query=${path##*\?};
  549. if [[ $query != $path ]]
  550. then
  551. while read -d '&amp;' -r param; do
  552. IFS='=' read key value &lt;&lt;&lt; ${param};
  553. URL_PARAMS[$key]=$(urldecode $value)
  554. done &lt;&lt;&lt; "${query}&amp;" # add &amp; so last argument is seen
  555. fi
  556.  
  557. request=("$method" "$path" "$version")
  558. path=$(urldecode $(cut -d\? -f1 &lt;&lt;&lt; "${path}")) # strip url parameters, urldecode
  559.  
  560. # parse headers
  561. parse_headers;
  562.  
  563. # parse out cookie values, if they exist
  564. if contains "Cookie" "${!HTTP_HEADERS[@]}"
  565. then
  566. parse_cookies;
  567. fi
  568.  
  569. if [[ $method == "POST" ]]
  570. then
  571. # TODO: handle multipart bodies
  572. local line;
  573. local n;
  574. n=${HTTP_HEADERS['Content-Length']};
  575. read -n$n -r line;
  576. params=($(sed "s/\&amp;/ /g" &lt;&lt;&lt; "${line}"))
  577. for param in ${params[@]}; do
  578. IFS='=' read key value &lt;&lt;&lt; ${param};
  579. POST_PARAMS[$key]=$(urldecode $value)
  580. done
  581. fi
  582.  
  583. # if we know the IP (via an X-Forwarded-For header), stick the user in a sandbox
  584. # (Cloudflare will fill in this IP in prod, we can also have nginx fill it in in dev if we want)
  585. if contains "X-Forwarded-For" "${!HTTP_HEADERS[@]}"
  586. then
  587. sandbox_dir="$((cksum &lt;&lt;&lt; ${HTTP_HEADERS["X-Forwarded-For"]}) | cut -d\ -f1).sandbox";
  588. # create sandbox if it doesn't exist
  589. if [[ ! -e "${sandbox_dir}" ]]
  590. then
  591. mkdir "${sandbox_dir}";
  592. # copy anything that isn't itself a sandbox to the dir
  593. cp -R !(*.sandbox) "${sandbox_dir}";
  594. fi
  595. cd "${sandbox_dir}";
  596. else
  597. log "WARNING: Not sandboxing: no X-Forwarded-For header found!"
  598. fi
  599.  
  600. requested_path=$(pwd)/${path}
  601.  
  602. # if a directory is requested, try each of the following, in order
  603. # index.wtf, index.html
  604. local index_fills=("index.wtf" "index.html");
  605. if [[ -d ${requested_path} ]]
  606. then
  607. for i in ${index_fills}; do
  608. if [[ -e "${requested_path}/${i}" ]]
  609. then
  610. requested_path="${requested_path}/${i}";
  611. break;
  612. fi
  613. done
  614. fi
  615.  
  616. # check for possible directory traversals / other undesirable path elements by
  617. # removing them and 503-ing if the string was changed
  618. test_path=$(sed "s/\.\.//g" &lt;&lt;&lt; "${requested_path}")
  619. if [[ ${test_path} != ${requested_path} ]]
  620. then
  621. echo "HTTP/1.1 503 Forbidden"
  622. echo "Content-Type: text/html"
  623. for reply_header in "${REPLY_HEADERS[@]}"; do
  624. echo "${reply_header}"
  625. done
  626. printf "\r\n\r\n"
  627. echo "&lt;html&gt;&lt;title&gt;503&lt;/title&gt;&lt;body&gt;503 Forbidden&lt;/body&gt;&lt;/html&gt;"
  628. echo "&lt;p&gt;Sorry, directory traversal is strongly frowned upon here at wtf.sh enterprises&lt;/p&gt;";
  629. log "503: ${request[@]}"
  630. exit 0; # terminate early for 503
  631. fi
  632.  
  633. [[ ! -e "${requested_path}/.nolist" ]];
  634. local can_list=$?;
  635. [[ ! -e "$(dirname "${requested_path}")/.noread" ]];
  636. local can_read=$?;
  637.  
  638. if [[ -e ${requested_path} ]]
  639. then
  640. if [[ -f ${requested_path} \
  641. &amp;&amp; ${requested_path:(-4)} != ".log"\
  642. &amp;&amp; ${can_read} = 0 ]] # can't end in .log, can't have .noread in the parent directory
  643. then
  644. echo "HTTP/1.1 200 OK"
  645. echo "Content-Type: text/html"
  646. for reply_header in "${REPLY_HEADERS[@]}"; do
  647. echo "${reply_header}"
  648. done
  649. printf "\r\n\r\n"
  650. include_page ${requested_path};
  651. elif [[ -d ${requested_path} \
  652. &amp;&amp; ${can_list} = 0 ]] # handle directory listing if it isn't a file and no `.nolist` file in the directory
  653. then
  654. log "$(dirname "${requested_path}")/.noread"
  655. echo "HTTP/1.1 200 OK"
  656. echo "Content-Type: text/html"
  657. for reply_header in "${REPLY_HEADERS[@]}"; do
  658. echo "${reply_header}"
  659. done
  660. printf "\r\n\r\n"
  661. echo "&lt;h3&gt;Index of ${request[1]}&lt;/h3&gt;"
  662. echo "&lt;ul&gt;"
  663. for d in ${requested_path}/*; do
  664. size_info=($(du -h ${requested_path} | tail -n 1))
  665. echo "&lt;li&gt;&lt;a href="/${request[1]#"/"}${d}"&gt;${d}&lt;/a&gt;: ${size_info[0]}&lt;/li&gt;"
  666. done
  667. echo "&lt;/ul&gt;"
  668. echo "&lt;font size=2&gt;generated by wtf.sh ${VERSION} on $(date)&lt;/font&gt;"
  669. else
  670. echo "HTTP/1.1 503 Forbidden"
  671. echo "Content-Type: text/html"
  672. for reply_header in "${REPLY_HEADERS[@]}"; do
  673. echo "${reply_header}"
  674. done
  675. printf "\r\n\r\n"
  676. echo "&lt;title&gt;503 Forbidden&lt;/title&gt;";
  677. echo "&lt;h3&gt;I'm sorry, I'm afraid I can't let you see that&lt;/h3&gt;";
  678. echo "&lt;p&gt;It seems that you tried to list a directory with a &lt;code&gt;.nolist&lt;/code&gt; file in it, or a &lt;code&gt;.noread&lt;/code&gt; file in it's parent, or a forbidden file type.&lt;/p&gt;";
  679. echo "&lt;p&gt;If you think this was a mistake, I feel bad for you, son. I got 99 problems, but a 503 ain't one.&lt;/p&gt;";
  680. log "503: ${request[@]}"
  681. exit 0;
  682. fi
  683. log "200: ${request[@]}"
  684. exit 0
  685. else
  686. # 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
  687. if [[ ${can_read} = 1 || ${can_list} = 1 ]];
  688. then
  689. echo "HTTP/1.1 503 Not Found";
  690. echo "Content-Type: text/html"
  691. for reply_header in "${REPLY_HEADERS[@]}"; do
  692. echo "${reply_header}"
  693. done
  694. printf "\r\n\r\n"
  695. echo "&lt;title&gt;503 Forbidden&lt;/title&gt;";
  696. echo "&lt;h3&gt;I'm sorry, I'm afraid I can't let you see that&lt;/h3&gt;";
  697. echo "&lt;p&gt;It seems that you tried to list a directory with a &lt;code&gt;.nolist&lt;/code&gt; file in it, or a &lt;code&gt;.noread&lt;/code&gt; file in it's parent, or a forbidden file type.&lt;/p&gt;";
  698. echo "&lt;p&gt;If you think this was a mistake, I feel bad for you, son. I got 99 problems, but a 503 ain't one.&lt;/p&gt;";
  699. log "503: ${request[@]}"
  700. else
  701. echo "HTTP/1.1 404 Not Found"
  702. echo "Content-Type: text/html"
  703. for reply_header in "${REPLY_HEADERS[@]}"; do
  704. echo "${reply_header}"
  705. done
  706. printf "\r\n\r\n"
  707. echo "&lt;html&gt;&lt;title&gt;404&lt;/title&gt;&lt;body&gt;404, not found:&lt;code&gt;${request[1]}&lt;/code&gt;&lt;/body&gt;&lt;/html&gt;"
  708. log "404: ${request[@]}"
  709. fi
  710. exit 0
  711. fi
  712. }
  713.  
  714. # start socat on specified port
  715. function start_server {
  716. echo "wtf.sh ${VERSION}, starting!";
  717. socat -T10 TCP-LISTEN:$2,fork,readbytes=4096,backlog=256,reuseaddr EXEC:"$1 -r" 9&gt;&amp;1
  718. echo "Socket was occupied... try again later...";
  719. }
  720.  
  721. if [[ $# != 1 ]]
  722. then
  723. echo "Usage: $0 port"
  724. exit
  725. fi
  726.  
  727. if [[ $1 == '-r' ]]
  728. then
  729. handle_connection
  730. else
  731. start_server $0 $1 # start server on specified port
  732. fi</span>
  733. </div>
Add Comment
Please, Sign In to add comment