Last active
September 29, 2022 19:51
-
-
Save stephancasas/692949c76fcd425514fd4b3e20694016 to your computer and use it in GitHub Desktop.
straycat.sh — a minimal HTTP server script for netcat.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# ------------------------------------------------------------------------------ | |
# Straycat.sh HTTP Server (straycat) | |
# ------------------------------------------------------------------------------ | |
# | |
# Author: | |
# Stephan Casas <[email protected]> | |
# | |
# Description: | |
# Straycat.sh (straycat) is designed to receive HTTP payloads over netcat TCP | |
# sessions — removing the need to install a dedicated HTTP server like Apache | |
# or Lighttpd, where such an installation would be unfeasible, or excessive | |
# to the objective or available resources. | |
# | |
# Received payloads are digested into their respective fundamental units and | |
# exposed as script variables which may then be piped or handled according to | |
# user-defined server logic. | |
# | |
# The set of commands used by straycat is minimal, and should be supported by | |
# most, if not all, Docker images — including base Alpine Linux. | |
# | |
# Use with netcat: | |
# nc -lk -p 4444 -e /opt/straycat.sh # listen for HTTP on port 4444 | |
# | |
# Server Logic Example: | |
# echo $__METHOD >&2 # echo the HTTP method to the netcat console | |
# echo $__PATH >&2 # echo the request path to the netcat console | |
# echo $__BODY >&2 # echo the request body to the netcat console | |
# | |
# Tips: | |
# * Define your server logic on line 144 -- after all variables are hydrated. | |
# * Variables are prefixed with a double underscore (e.g. $__BODY, $__PATH). | |
# * Use stderr (see examples above) to log to console. Printing to stdout will | |
# send a response to the HTTP client. | |
# * Your response is sent and the HTTP connection ends once you `echo` or | |
# `printf` on stdout to the client. | |
# * Always send a valid HTTP response (with headers) to avoid client errors. | |
# ---------------------------------------------------------------------------- # | |
# --- Request Timeout Config ------------------------------------------------- # | |
_TIMEOUT=1 # how long should the server wait for new bytes to buffer? | |
# --- Request Header Digest -------------------------------------------------- # | |
_BUF="" | |
_LAST="" | |
_SET="METHOD" | |
_SUBSET="" | |
while read -r -t $_TIMEOUT -n1 CHAR; do | |
_CLEAR=false | |
STASH="" | |
_CODE=$(printf %d \'$CHAR) | |
if [ "$_CODE" = "0" ]; then | |
if [ "$_LAST" != "13" ]; then | |
if [ "$_SET" = "METHOD" ]; then | |
__METHOD="$_BUF" | |
_SET="PATH" | |
_CLEAR=true | |
fi | |
if [ "$_SET" = "PATH" ] && ! $_CLEAR; then | |
__PATH="$_BUF" | |
_SET="PROTOCOL" | |
_CLEAR=true | |
fi | |
if [ "$_SET" = "HEADER" ] && [ -z "$_BUF" ] && ! $_CLEAR; then | |
# ignore the first space after a header definition | |
STASH=" " | |
else | |
_BUF="$_BUF " | |
fi | |
fi | |
else | |
if [ "$_CODE" = "13" ]; then | |
if [ "$_SET" = "PROTOCOL" ] && ! $_CLEAR; then | |
__PROTOCOL="$_BUF" | |
_SET="HEADER" | |
_CLEAR=true | |
fi | |
if [ "$_SET" = "HEADER" ] && ! $_CLEAR; then | |
if [ -z "$_SUBSET" ]; then | |
_SET="BODY" | |
_CLEAR=true | |
# skip next empty line | |
read | |
else | |
_SUBSET=$(echo "$_SUBSET" | | |
awk '{gsub(/-/, "_")}{print toupper($0)}') | |
eval "__HEADER_$_SUBSET"="\"$_BUF\"" | |
_SET="HEADER" | |
_SUBSET="" | |
_CLEAR=true | |
fi | |
fi | |
_BUF="$_BUF$(printf '\n\r')" | |
else | |
if [ "$_CODE" = "58" ]; then | |
if [ "$_SET" = "HEADER" ]; then | |
if [ -z "$_SUBSET" ]; then | |
_SUBSET=$_BUF | |
_CLEAR=true | |
fi | |
fi | |
fi | |
_BUF="$_BUF$CHAR" | |
fi | |
fi | |
__REQUEST="$__REQUEST$STASH" | |
$_CLEAR && __REQUEST="$__REQUEST$_BUF" | |
$_CLEAR && _BUF="" | |
_LAST="$_CODE" | |
[ "$_SET" = "BODY" ] && break | |
done | |
# --- Request Body Digest ---------------------------------------------------- # | |
__BODY="" | |
_OFFSET=0 | |
while [ ${#__BODY} -lt $__HEADER_CONTENT_LENGTH ]; do | |
[ ! -z "$__BODY" ] && __BODY="$__BODY$(printf '\n\r')" | |
eval "read -r -n$((($__HEADER_CONTENT_LENGTH + $_OFFSET) - ${#__BODY})) \ | |
-t $_TIMEOUT _CHARS" | |
_OFFSET=$(($_OFFSET + 1)) | |
__BODY="$__BODY$_CHARS" | |
done | |
__REQUEST="$__REQUEST$__BODY" | |
# --- Server Logic ----------------------------------------------------------- # | |
echo "$__BODY" >&2 # example -- echo request body to console | |
# --- Response --------------------------------------------------------------- # | |
echo "HTTP/1.1 202 Accepted | |
Content-Length: 2 | |
Content-Type: text/plain | |
Server: straycat | |
OK" | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment