cl-secninja.lisp | ||
README.org |
Readme
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:
- Load in SBCL with
sbcl --load cl-secninja.lisp
. - Run
(four)
- Copy the base64 encrypted message into a file called
email
. - Run
(base64)
. - Done.
Code:
Quicklisp:
(ql:quickload '(:cl-ppcre
:cl-base64
:ironclad
:jsown))
Defpackage and in-package
(defpackage #:cl-secninja
(:use #:common-lisp #:cl #:uiop #:cl-ppcre #:ironclad #:cl-base64))
(in-package #:cl-secninja)
URL var
The trailing slash is important for consolidating the URL later.
(defvar *url* "redacted")
Run cURL
(defun run-curl (command)
(let* ((curl-command (concatenate 'string "curl -i " command))
(result (uiop:run-program curl-command
:output :string
:error-output T)))
result))
First page
Grab first page, print out the command and result, and return the parameter string.
(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))))
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.
(defun two ()
(let* ((command (concatenate 'string
"\"" *url* (one) "\""))
(result (run-curl command)))
result))
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.
(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))))
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…
(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)))
Sha256
Util function for the above code.
(defun sha-256 (str)
(ironclad:byte-array-to-hex-string
(ironclad:digest-sequence :sha256
(ironclad:ascii-string-to-byte-array str))))
Base64
Put the contents of the file into a variable.
(defvar *email* (alexandria:read-file-into-string "email"))
Then, run the base64 decoder over it until the string can't be decoded any more.
(defun base64 ()
(let ((email *email*))
(loop repeat 100 do
(if (ppcre:scan "@" email)
(print email)
(setf email (cl-base64:base64-string-to-string email))))))