#+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