commit 9212febae27085d39f0090b51030dc9697d5a2f0
Author: Phil Bajsicki <phil@bajsicki.com>
Date:   Sat May 18 11:58:13 2024 +0200

    Redacting 2024.05.18

diff --git a/README.org b/README.org
new file mode 100644
index 0000000..3d550b0
--- /dev/null
+++ b/README.org
@@ -0,0 +1,183 @@
+#+title: Readme
+#+author: Phil Bajsicki
+#+options: finline toc:nil
+
+* Intro
+This is a coding task from [redacted], which I found
+rather fun to tinker with.
+
+This is an org-mode file. The code blocks are tangled using =org-babel= into =cl-secninja.lisp=.
+
+* Dependencies:
+- curl
+- sbcl with quicklisp
+* Usage:
+1. Load in SBCL with ~sbcl --load cl-secninja.lisp~.
+2. Run ~(four)~
+3. Copy the base64 encrypted message into a file called =email=.
+4. Run ~(base64)~.
+5. Done.
+* Code:
+** Quicklisp:
+#+begin_src lisp :tangle cl-secninja.lisp
+(ql:quickload '(:cl-ppcre
+				:cl-base64
+				:ironclad
+				:jsown))
+#+end_src
+
+** Defpackage and in-package
+#+begin_src lisp :tangle cl-secninja.lisp
+(defpackage #:cl-secninja
+  (:use #:common-lisp #:cl #:uiop #:cl-ppcre  #:ironclad #:cl-base64))
+
+(in-package #:cl-secninja)
+#+end_src
+
+** URL var
+The trailing slash is important for consolidating the URL later.
+#+begin_src lisp :tangle cl-secninja.lisp
+(defvar *url* "redacted")
+#+end_src
+** Run cURL
+#+begin_src lisp :tangle cl-secninja.lisp
+(defun run-curl (command)
+  (let* ((curl-command (concatenate 'string "curl -i " command))
+    (result (uiop:run-program curl-command
+                              :output :string
+                              :error-output T)))
+         result))
+
+#+end_src
+
+** First page
+Grab first page, print out the command and result, and return the parameter string.
+#+begin_src lisp :tangle cl-secninja.lisp
+(defun one ()
+  (let* ((command *url*)
+		 (result (run-curl *url*)))
+	(format t "~%COMMAND: curl ~a" command)
+	(format t "~%RESULT: curl ~a" result)
+	(car (ppcre:all-matches-as-strings "\\?.*" result))))
+#+end_src
+** Second page
+This function calls ~(one)~, so calling it directly for seeing the second page
+works.
+
+Concatenate the URL with the parameter string from the first function, and grab
+and return the second page.
+
+#+begin_src lisp :tangle cl-secninja.lisp
+(defun two ()
+  (let* ((command (concatenate 'string
+							   "\"" *url* (one) "\""))
+		 (result (run-curl command)))
+	result))
+#+end_src
+** Third
+This is where we get interesting. This function calls ~(two)~, which returns the
+second page for processing, and then calls ~(run-curl)~ on the command it
+constructs, and returns the output.
+
+
+I wanted to see what the values of the variables are while testing, thus the
+print statements and triple let. I know I could consolidate that, but I feel
+like this way is more transparent than having a ~(let* (...))~ that does
+everything in the background.
+
+The thing that was most tricky here was getting the double quotes to work
+properly between this function and ~(run-curl)~, because the =command= string is
+being passed around, and it itself includes quotes.
+
+
+
+#+begin_src lisp :tangle cl-secninja.lisp
+(defun three ()
+  (let ((headers (ppcre:all-matches-as-strings "X-.*" (two))))
+	(print headers)
+	(print (car headers))
+	(print (cadr headers))
+	(let ((command (concatenate 'string
+								"\"" *url* "\?step=2" "\""
+								" -H \"" (car headers) "\""
+								" -H \"" (cadr headers) "\"")))
+	  (let ((result (run-curl command)))
+		(format t "~%RESULT: curl ~a" result)
+		result))))
+
+#+end_src
+** Four
+This was the most fun one, given all the processing. I can't say that the loop
+worked the first time around.
+
+After running ~(four)~, your terminal will print out the base64 encoded string.
+I can't say I want to mess with extracting the base64 string with another
+regexp, so I just manually copied the string into a file called =email=.
+
+Then...
+
+#+begin_src lisp :tangle cl-secninja.lisp
+
+(defun four ()
+  (let*
+	  ((data (three))
+	   (input
+		 (car (ppcre:all-matches-as-strings "(?sm)\{.*}$" data)))
+	   (challenge
+		 (string-left-trim "challenge: " (car (ppcre:all-matches-as-strings "challenge:(.*)" data))))
+	   (timestamp
+		 (string-left-trim "timestamp: " (car (ppcre:all-matches-as-strings "timestamp:(.*)" data))))
+	   (sorted (sort (cdr (jsown:parse input)) #'string<= :key #'first))
+	   (connected
+		 (string-right-trim "\&"
+							(format nil "~{~A~}"
+									(loop
+									  for item in sorted
+									  collect
+									  (concatenate 'string  (car item) "\=" (cdr item) "\&")))))
+	   (hash (sha-256 connected))
+	   (command
+		 (string-right-trim "\-"
+							(concatenate 'string
+										 "-X POST \"" *url* "\?step=3" "\""
+										 " -H \"Content-Type: application/x-www-form-urlencoded\" "
+										 " -d \"challenge=" challenge "\""
+										 " -d \"timestamp=" timestamp "\""
+										 " -d \"hash=" hash "\""
+										 ))))
+	(terpri)
+	(print challenge)
+	(print timestamp)
+	(print hash)
+	(terpri)
+	(format t "~%COMMAND: curl ~a" command)
+	(terpri)
+	(let ((result (run-curl command)))
+	  (format t "~%RESULT: curl ~a" result))
+	(terpri)))
+
+#+end_src
+
+*** Sha256
+Util function for the above code.
+#+begin_src lisp :tangle cl-secninja.lisp
+(defun sha-256 (str)
+  (ironclad:byte-array-to-hex-string
+   (ironclad:digest-sequence :sha256
+							 (ironclad:ascii-string-to-byte-array str))))
+
+#+end_src
+** Base64
+Put the contents of the file into a variable.
+#+begin_src lisp :tangle cl-secninja.lisp
+(defvar *email* (alexandria:read-file-into-string "email"))
+#+end_src
+Then, run the base64 decoder over it until the string can't be decoded any more.
+#+begin_src lisp :tangle cl-secninja.lisp
+(defun base64 ()
+  (let ((email *email*))
+	(loop repeat 100 do
+	  (if (ppcre:scan "@" email)
+		  (print email)
+		  (setf email (cl-base64:base64-string-to-string email))))))
+#+end_src
diff --git a/cl-secninja.lisp b/cl-secninja.lisp
new file mode 100644
index 0000000..31bbd8b
--- /dev/null
+++ b/cl-secninja.lisp
@@ -0,0 +1,96 @@
+(ql:quickload '(:cl-ppcre
+				:cl-base64
+				:ironclad
+				:jsown))
+
+(defpackage #:cl-secninja
+  (:use #:common-lisp #:cl #:uiop #:cl-ppcre  #:ironclad #:cl-base64))
+
+(in-package #:cl-secninja)
+
+(defvar *url* "redacted")
+
+(defun run-curl (command)
+  (let* ((curl-command (concatenate 'string "curl -i " command))
+    (result (uiop:run-program curl-command
+                              :output :string
+                              :error-output T)))
+         result))
+
+(defun one ()
+  (let* ((command *url*)
+		 (result (run-curl *url*)))
+	(format t "~%COMMAND: curl ~a" command)
+	(format t "~%RESULT: curl ~a" result)
+	(car (ppcre:all-matches-as-strings "\\?.*" result))))
+
+(defun two ()
+  (let* ((command (concatenate 'string
+							   "\"" *url* (one) "\""))
+		 (result (run-curl command)))
+	result))
+
+(defun three ()
+  (let ((headers (ppcre:all-matches-as-strings "X-.*" (two))))
+	(print headers)
+	(print (car headers))
+	(print (cadr headers))
+	(let ((command (concatenate 'string
+								"\"" *url* "\?step=2" "\""
+								" -H \"" (car headers) "\""
+								" -H \"" (cadr headers) "\"")))
+	  (let ((result (run-curl command)))
+		(format t "~%RESULT: curl ~a" result)
+		result))))
+
+(defun four ()
+  (let*
+	  ((data (three))
+	   (input
+		 (car (ppcre:all-matches-as-strings "(?sm)\{.*}$" data)))
+	   (challenge
+		 (string-left-trim "challenge: " (car (ppcre:all-matches-as-strings "challenge:(.*)" data))))
+	   (timestamp
+		 (string-left-trim "timestamp: " (car (ppcre:all-matches-as-strings "timestamp:(.*)" data))))
+	   (sorted (sort (cdr (jsown:parse input)) #'string<= :key #'first))
+	   (connected
+		 (string-right-trim "\&"
+							(format nil "~{~A~}"
+									(loop
+									  for item in sorted
+									  collect
+									  (concatenate 'string  (car item) "\=" (cdr item) "\&")))))
+	   (hash (sha-256 connected))
+	   (command
+		 (string-right-trim "\-"
+							(concatenate 'string
+										 "-X POST \"" *url* "\?step=3" "\""
+										 " -H \"Content-Type: application/x-www-form-urlencoded\" "
+										 " -d \"challenge=" challenge "\""
+										 " -d \"timestamp=" timestamp "\""
+										 " -d \"hash=" hash "\""
+										 ))))
+	(terpri)
+	(print challenge)
+	(print timestamp)
+	(print hash)
+	(terpri)
+	(format t "~%COMMAND: curl ~a" command)
+	(terpri)
+	(let ((result (run-curl command)))
+	  (format t "~%RESULT: curl ~a" result))
+	(terpri)))
+
+(defun sha-256 (str)
+  (ironclad:byte-array-to-hex-string
+   (ironclad:digest-sequence :sha256
+							 (ironclad:ascii-string-to-byte-array str))))
+
+(defvar *email* (alexandria:read-file-into-string "email"))
+
+(defun base64 ()
+  (let ((email *email*))
+	(loop repeat 100 do
+	  (if (ppcre:scan "@" email)
+		  (print email)
+		  (setf email (cl-base64:base64-string-to-string email))))))