Recently I presented a talk titled "Mostrame la Guita!" (regional Spanish for "show me the money") at Ekoparty in Buenos Aires, Argentina. The purpose of the talk was to provide transparency into the world of vulnerability markets and for the first time expose the inner workings, statistics and some anecdotal stories behind the TippingPoint Zero Day Initiative (ZDI) to the public. As far as I know, the only other available resource on the matter of markets and pricing is Charlie Miller's 2007 paper "The Legitimate Vulnerability Market". The information presented was garnered through public resources, numerous interviews and my own personal experiences in the marketplace from 2002 to present day. Market PricesFor the purposes of my talk I broke the market down into four categories:
![]() [full slide] The wide range of pricing has led many to claim that there is no fair market value for vulnerability research. I disagree and stated so in my talk. The reasoning behind my logic is that you get what you are willing to give. The individual markets offer different pricing but also demand different requirements. On the white market, and specifically at the ZDI for example we don't need nor do we ask for exploit code. If you can point out a bug we'll take it from there. In fact, frequently we receive very limited crash reports through the ZDI that require significant effort to dissect thoroughly. The white market also offers credit for your discovery and the guarantee that the reported issue will be forwarded to the affected vendor and disclosed in a responsible fashion. On the grey and black markets credit is completely out of the question. You are selling the rights to your information and expected to never talk about it again. The vendor will not be contacted. Crash reports will not be entertained and nor will buggy or basic proof-of-concept exploit code. Exploits must be polished and weaponized. This is a drastic departure from the white market and many researchers are not comfortable operating in it. Acquired Research StatisticsSince the launch of the Zero Day Initiative in August of 2005 we have signed up over 1,000 researchers and received almost 1,900 vulnerability reports between them. We only accept critical issues affecting top enterprise software and out of the the bugs we have seen we have purchased over 500 (about 30%) at an average of about 10 a month. That's a lot of bugs. The table presented in the following slide outlines purchase statistics for our most prominent vendors:![]() [full slide] The first numeric column shows how many vulnerability purchases we have made which affects the captioned vendor. Microsoft accounts for the largest number, 133, that's 33 Microsoft critical issues we are responsible for disclosing on average per year. The next column shows what our accept ratio is for that specific vendor, recall that our overall is roughly 30%. The final column shows how much of our total ZDI budget was spent securing that specific vendor. As Microsoft accounts for most of our purchases it is no surprise that they account for most of our expenditure as well, 30%. Apple comes in at a distance second accounting for 8% of our total budget. Vendor Patch Time StatisticsFor each of our most prominent vendors we queried and charted their average time to patch per reported year. Let me explain this more clearly with an example. Between all the bugs we reported to Apple in 2006 their averaged time to patch was 166 days. If we reported a bug to Apple in December of 2006 and it wasn't patched until December of 2008 that 2-year patch time was averaged into 2006. For each year the longest and shortest patch times are highlighted in red and green appropriately. Through this measurement Mozilla comes out on top pretty much across the board:![]() [ full slide ] We averaged the individual per year columns into the overall column which places Mozilla at the top with the quickest average patch time of 48 days and Symantec trailing the pack at 307 days. One thing to keep in mind however is that these numbers do not include the currently outstanding issues, which brings us to the next and final column. The upcoming column displays the average number of days that all issues reported to the vendor have been outstanding for. Up to date listings of all our outstanding TippingPoint and ZDI discoveries are available at:
Longest Patch TimesWe have sliced our vendor data subjectively, by patch time and by days outstanding. The final view I offered the audience is a listing of the top 10 most outstanding bugs:![]() [ full slide ] Entries with a + (plus) sign indicate that the issue is still outstanding, otherwise it has been patched. Note that although our recent disclosures to Hewlett-Packard improved their average in the previous slides upcoming column, they hold the title for the top two longest patch times... and counting. Holding the 3rd and 4th place positions is Microsoft with two issues that affect their Office Web Components (OWC) and along with another OWC issue have all been recently patched in MS09-043 (ZDI-09-054, ZDI-09-055, ZDI-09-056). The two and a half year time to patch triggered some media attention but as I mentioned earlier, patching is not a trivial process in some cases. I verbally covered this matter in my "Behind the Hype" slide stating that the cause for delay on Microsoft's behalf was legitimate and not due to any underlying disorganization. The presented data was our first unveiling of a vendor "report card". Within the next month or so we intend on creating a permanent home on the ZDI website with all these statistics and more. So check back at www.zerodayinitiative.com and let us know if there is anything in specific that you would like to see. -pedram Originally published 2009-10-29 on the TippingPoint DVLabs blog.
I watch Woot on pretty much a daily basis and as a result I frequently end up purchasing toys that I really don't need. Most recently I picked up this silly pair of USB Missile Launchers for just under 40$ shipped: .data:0048790C LibFileName db 'USBHID.dll',0Hit 'x' to pull up the cross-references to the string and you come across the following snippet of code: .text:00403830 030 xor edx, edx .text:00403832 030 mov dword_48E510, edx .text:00403838 030 mov ecx, [ebp+table] .text:0040383B 030 mov dword ptr [ecx+74h], 0FFFFFFFFh .text:00403842 030 push offset LibFileName ; "USBHID.dll" .text:00403847 034 call LoadLibraryA .text:0040384C 030 mov dword_48E514, eax .text:00403851 030 cmp dword_48E514, 0 .text:00403858 030 jz short loc_40386F .text:0040385A 030 push offset ProcName ; "OpenHID" .text:0040385F 034 push dword_48E514 ; hModule .text:00403865 038 call GetProcAddress .text:0040386A 030 mov dword_48E510, eaxSo we have EDX=0 at the top, moved into a global DWORD variable @48E510. Next we see the address of our string being pushed to LoadLibrary() and subsequently the return value transferred from EAX into a global DWORD variable @48E514, this is the handle to the loaded DLL. An error check is made to ensure the library exists then the resolved handle and the string "OpenHID", the relevant function to resolve, is pushed to GetProcAddress(). The resolved function pointer is transferred from EAX into a global DWORD variable @48E510. We can press 'n' on both of these global variables to rename them to say 'h_USBHID' and 'OpenHID' respectively. Now let's find out where OpenHID is called from, again examine cross-references by hitting 'x' when the cursor is on top of 'OpenHID'. There are 4 references. Two references from the above snippet which are assigning the variable and two CALL references from the function @00403B64. Here is the first: .text:00403B64 000 push ebp .text:00403B65 004 mov ebp, esp .text:00403B67 004 mov eax, [ebp+arg_0] .text:00403B6A 004 cmp dword ptr [eax+74h], 0FFFFFFFFh .text:00403B6E 004 jnz short loc_403B88 .text:00403B70 004 push 1 .text:00403B72 008 push 701h .text:00403B77 00C push 0A81h .text:00403B7C 010 call OpenHID .text:00403B82 004 mov edx, [ebp+arg_0] .text:00403B85 004 mov [edx+74h], eax .text:00403B88 .text:00403B88 loc_403B88:OpenHID() takes three arguments and is being called here like so: if (eax74 == 0x0FFFFFFFF)
{
eax74 = OpenHID(0x0A81, 0x0701, 1);
}The first argument is the HID Vendor ID (VID), 0x0A81. The second argument is the HID Product ID (PID), 0x0701. The code here opens a handle to the USB Missile Launcher HID if it hasn't already been done. Just a few lines later in the deadlisting we see a WriteFile() operation on the opened handle:.text:00403B88 004 mov ecx, [ebp+arg_0] .text:00403B8B 004 mov byte ptr [ecx+46h], 0 .text:00403B8F 004 mov eax, [ebp+arg_0] .text:00403B92 004 mov edx, [eax+68h] .text:00403B95 004 or byte ptr [edx], 40h .text:00403B98 004 push 0 ; lpOverlapped .text:00403B9A 008 mov ecx, [ebp+arg_0] .text:00403B9D 008 add ecx, 6Ch .text:00403BA0 008 push ecx ; lpNumberOfBytesWritten .text:00403BA1 00C push 2 ; nNumberOfBytesToWrite .text:00403BA3 010 mov eax, [ebp+arg_0] .text:00403BA6 010 add eax, 45h .text:00403BA9 010 push eax ; lpBuffer .text:00403BAA 014 mov edx, [ebp+arg_0] .text:00403BAD 014 push dword ptr [edx+74h] ; hFile .text:00403BB0 018 call WriteFileThe function we are examining takes a single argument, which is a pointer to a structure which contains the data to be written at offset 0x45. The length of the data is 2, the value of the nNumberOfBytesToWrite argument to WriteFile(). At this point we know that we can easily open a handle to the USB Missile Launcher via USBHID.OpenHID() and then we can use WriteFile() to write data to it and likely ReadFile() to read data back. The next step is to figure out the protocol and there are a number of ways we can go about doing this. One method is to use USB sniffing software such as SniffUSB. Here is a screenshot of SniffUSB in action: ![]() Note that in this screenshot I've already installed the filter control on the HID specific to the USB Missile Launcher. The red arrows highlight the VID/PID values we found earlier within MissileLauncher.exe from the call to OpenHID(). Another way to determine which VID/PID combination maps to the specific HID you are looking for is to unplug the device, hit refresh, then plug it back, hit refresh and see what new row pops up. Once the filter control is installed on your target HID, you must unplug/replug to activate it then you can start interacting with the MissileLauncher.exe GUI and examine the data going across from the UsbSnoop.log file in your Windows directory. I took a different approach. I decided to 'sniff' the protocol via debugger hooks on ReadFile() / WriteFile(). Load MissileLauncher.exe into OllyDbg and hit F9 to let the program run and the GUI start. Once the GUI is running pull up the modules list with 'Alt-e', select kernel32 and hit 'Ctrl-n' to pull up its list of contained names. Start typing out "writefile" to pull up the appropriate entry then hit 'Shift-F4' to set a condition log breakpoint. A dialogue box appears, for the row labeled "Log function arguments", select the "Always" column radio control. This is a pretty neat feature of OllyDbg by the way, it won't work for any API, just the ones OllyDbg knows about. Repeat the process for "readfile". Hit 'Alt+l' to bring up the log window then hit a button in the MissileLauncher.exe GUI and you should receive log output resembling the following: 7C810E17 CALL to WriteFile from MissileL.00403BB0 hFile = 000001A8 (window) Buffer = 00B6395D nBytesToWrite = 2 pBytesWritten = 00B63984 pOverlapped = NULL 7C810E17 CALL to WriteFile from MissileL.00404248 hFile = 000001A8 (window) Buffer = 00B6395D nBytesToWrite = 2 pBytesWritten = 00B63984 pOverlapped = NULL 7C801812 CALL to ReadFile from MissileL.00404265 hFile = 000001A8 (window) Buffer = 00B63954 BytesToRead = 2 pBytesRead = 00B63984 pOverlapped = NULL 7C810E17 CALL to WriteFile from MissileL.004042D0 hFile = 000001A8 (window) Buffer = 00B6395D nBytesToWrite = 2 pBytesWritten = 00B63984 pOverlapped = NULL 7C810E17 CALL to WriteFile from MissileL.00404316 hFile = 000001A8 (window) Buffer = 00B6395D nBytesToWrite = 2 pBytesWritten = 00B63984 pOverlapped = NULL 7C801812 CALL to ReadFile from MissileL.0040433A hFile = 000001A8 (window) Buffer = 00B63954 BytesToRead = 2 pBytesRead = 00B63984 pOverlapped = NULL 7C810E17 CALL to WriteFile from MissileL.00404357 hFile = 000001A8 (window) Buffer = 00B6395D nBytesToWrite = 2 pBytesWritten = 00B63984 pOverlapped = NULL 7C801812 CALL to ReadFile from MissileL.00404374 hFile = 000001A8 (window) Buffer = 00B63954 BytesToRead = 2 pBytesRead = 00B63984 pOverlapped = NULL 7C810E17 CALL to WriteFile from MissileL.0040453F hFile = 000001A8 (window) Buffer = 00B6395D nBytesToWrite = 2 pBytesWritten = 00B63984 pOverlapped = NULLLet's take this one step further and extract the 2-byte data value being written to the HID handle by extending the conditional log breakpoint on WriteFile. Press 'Alt+b' to bring up the breakpoints window, next drag out the 'Address' column with the mouse to reveal the DLL and API names in addition to the address (there is a lot of "hidden" data like this in various OllyDbg columns). Right click the breakpoint for WriteFile and select "Edit condition", the dialogue from earlier should pop back up. In the second row, for explanation put something like "buffer" and for expression enter "word ptr [[esp+8]]", this will dereference and display the 2-bytes being written from the second argument of WriteFile(). Next for the row that reads "Log value of expression", select the "Always" column radio control. Now when we interact with the MissileLauncher.exe GUI we should get log entries like this in addition to the ReadFile() / WriteFile() call / argument logs: 7C810E17 COND: buffer = 4000 7C810E17 COND: buffer = 4000 7C810E17 COND: buffer = 0800 7C810E17 COND: buffer = 4000 7C810E17 COND: buffer = 4000 7C810E17 COND: buffer = 2000There are two steps left for decoding the protocol now. One we have to hit each button in the MissileLauncher.exe GUI and take note of the data being written to the HID handle and two, we have to pay attention to the order of reads and writes on the handle and mimick it in our own program. Running through these steps we can conclude the following protocol (from my finished Python script, to be linked later): self.cmd_map = \
{
"down" : 0x01,
"up" : 0x02,
"left" : 0x04,
"right" : 0x08,
"fire" : 0x10,
"stop" : 0x20,
"start" : 0x40,
}The 'start' and 'stop' commands are control codes for putting the device into and out of action. Taking note of the read / write order we conclude the following general pattern:
self.hid = ctypes.WinDLL(r"C:\Program Files\USB Missile Launcher\usbhid.dll") self.launcher = self.hid.OpenHID(0x0a81, 0x701, 1)The various commands being written to the HID handle must be in the appropriate endianess (thanks ) and stored in a ctypes string buffer, like so: start = ctypes.create_string_buffer(struct.pack(">h", self.cmd_map["start"]))
command = ctypes.create_string_buffer(struct.pack(">h", self.cmd_map[cmd.lower()]))
stop = ctypes.create_string_buffer(struct.pack(">h", self.cmd_map["stop"]))The rest is up to your imagination. You can download my code, ped_missile.py, which implements a class for controlling the USB Missile Launcher as well as exposes three interfaces on top of that class. A command line interface, a socket server (protected by a password hardcoded on line 264) and a simple web interface. The web interface is fun for usage through the iPhone in conjunction with the 8$ application AirCam from Senstic.One interesting thing is that we have a lot more granular control of the turret movement now then we did with the original GUI. I wrote two simple loops to count the number of possible horizontal and vertical ticks and the results were 947 horizontal and 91 vertical versus 54 and 10 from the original GUI respectively. Granular control allows you to slowly and quietly reposition the turret for stealthy attacks. I also started writing a class function missile.reset() to center the device automatically but haven't finished it quite yet. Take a look at the source for further information. Everything should be fairly self explanatory. I'll conclude with an action video put together with the help of my teammates Ali, Cameron and Cody: Originally published 2009-02-12 on the TippingPoint DVLabs blog.
Like many others in the world, I've always been a skeptic of the need to disable cell phone antenna's on takeoff and landing. What kind of interference could possibly be caused to an airplane? We've all dealt with the minor nuisance of clicks and beeps when someone on a land line keeps their cell phone too close to the base, but serious interference to a plane? I always figured it was a better safe then sorry measure... Until a couple of weeks ago when I was doing some work in Photoshop on my old Dell laptop and randomly made an interesting discovery. A few seconds into the video, Cody starts to make a call to my cell from his. At about the 10 second marker you'll see a flurry of characters spew across the screen. You may notice what appears to be a pencil tracing around the iPhone. We quickly realized that moving the phone too far off a specific spot failed to produce any key strokes. At this point we had three primary questions in mind:
To answer the second question I coded up a little test harness. I created a small PHP script to generate random data of a suitable length to create interference. I loaded the URL on the iPhone and refreshed the page until a single character was sent. I marked the random data that resulted in that character and continued to map a few more characters. Here is the DB schema and simple script used to accomplish these tasks: <?
/*
MySQL Table structure:
CREATE TABLE 'tempest' (
'id' int(11) NOT NULL auto_increment,
'char' char(1) NOT NULL,
'data' longtext NOT NULL,
PRIMARY KEY ('id')
) ENGINE=MyISAM;
*/
mysql_connect("", "", "");
mysql_select_db("");
$id = 0;
if ($id)
{
$sql = "SELECT * FROM tempest WHERE id = $id";
$row = mysql_fetch_object(mysql_query($sql));
print base64_decode($row->data);
}
else
{
$x = "";
for ($i = 0; $i < 500; $i++)
$x .= chr(rand(0, 255));
print $x;
$sql = "INSERT INTO tempest SET data = '";
mysql_query($sql . base64_encode($x) . "'");
print mysql_error();
}
?>The table name TEMPEST by the way is the US government acronym for Transient ElectroMagnetic Pulse Emanation STandard. Running through this experiment provided no usable results. We postulated that the reason behind this is that the actual radio signals transmitting between the phone and the tower are not the same even for the same data. Despite the fact that we had already wasted half a day on this utterly useless (in terms of business) project, we were excited about the possibility of having a keystroke sending radio gun so we pressed on.Two hours of research time later we collectively decided that the next step would be to purchase a Universal Software Radio Peripheral (USRP) radio transceiver which we could write custom code for via the GNU Radio project. We speced out the cost of our ideal platform to be somewhere in the $2,000 to $3,000 range. Time for a budget approval, time to talk to the boss: ![]() Dave Endler Though amused, Dave was less then eager to drop precious budget dollars on a silly research project that would result in little to no re-usable security research. Our hopes and dreams shattered, we scrapped the project for a few weeks and have now resurrected it for public consumption. I recall being a CS student at Tulane university with lots of time and motivation and few solid project ideas. Today I have zillions of ideas and not enough hours in the day to accomplish them. Perhaps someone out there looking for a project idea can push this along... Write us if you do, we'd love to hear about it. Originally published 2008-06-30 on the TippingPoint DVLabs blog.
My DVLabs teammates recently presented on Reverse Engineering Dynamic Languages, specifically Python, at RECON 2008. As a case study, we demonstrated hacking cheats into Disney's Pirates of the Caribbean Online game. The game has received some pretty solid reviews and boasts over 10,000 paid online players. They also have a free download available for those of you who would like to follow along. Let's take a look at some of the hilarious effects of our case study and then cover some simple technical details so that readers can reproduce to join in on the fun. This next video demonstrates a similar cheat, modifying our character's speed. Towards the end of the video you'll see us stop and wait for another user to run by to give you a sense of what is "normal": Here is what the ridiculous jump height looks like from the perspective of another user: Some side effects of the jump hack include the ability to... fly? Apparently if a character is able to leave the "sphere" of influence of their ship, the physics engine sets the avatar's Z coordinate to the height at which they moved away from the ship: Some more flying... we can actually fly along the water faster than any ship in the game can move: Except of course, our ship, which can travel faster than enemy's cannonballs. This is the default ship given to you in the game. We have modified the ship's speed, acceleration, ramming power, maximum crew, maximum cannons, and other fun traits: If you're interested in the technical aspects behind these modifications, read on... There are three files we need to worry about modifying, all located within the install directory:
phase_1.mf is an archive containing DLLs needed during runtime, as well as a copy of Phase1.pyd. When the game is run, this file's contents are extracted and the internal Phase1.pyd overwrites the external one. Launcher1.exe is a UPX-packed executable that contains MD5 checks to ensure integrity of the .mf and .pyd files. UPX is an executable packer commonly used by malware. However, it is quite efficient and is also used for non-malicious executables like this one. Here's an example unpacking session: $ upx -d Launcher1.exe File size Ratio Format Name -------------------- ------ ----------- ----------- 1925120 <- 1512960 78.59% win32/pe Launcher1.exe Unpacked 1 file. Once we've unpacked the file, we can make the 2-byte NOP patch to bypass the MD5 check, allowing us to arbitrarily modify the Python code within the other files. Here's the before: Launcher1.exe (base address 0x400000) 00422188 E8 88 8F FE+ call std::basic_string.... 0042218D 85 C0 test eax, eax 0042218F 75 49 jnz short loc_4221DA 00422191 8D 75 94 lea esi, [ebp+var_6C] And here's the after: Launcher1.exe (base address 0x400000) 00422188 E8 88 8F FE+ call std::basic_string.... 0042218D 85 C0 test eax, eax 0042218F 90 nop 00422190 90 nop 00422191 8D 75 94 lea esi, [ebp+var_6C]The above code is where the Launcher does a string comparison between the calculated MD5 on disk and the MD5 value sent from the server. By overwriting the conditional jump that was there with NOP instructions, we force the check to always pass. Now that the Launcher process ignores the integrity of the frozen Python code, we can go ahead and begin to modify the Python objects in Phase1.pyd. The code objects within the PYD file are serialized, and while we could modify values in a hex editor for a quick and dirty cheat, there is a much more surgically precise and flexible method that we demonstrated in our presentation at REcon. The process involves deserializing and disassembling the Python code objects. You can then modify constant values, change byte code instruction logic, and re-inject the code back into the PYD. If you're interested in this method, you can peruse our slides. For the rest of you, we will continue and walk through some simple byte swap cheats that require nothing more than a hex editor. If we load Phase1.pyd into a hex editor, or IDA, we can see string references to Python namespaces. Most of the interesting ones are suffixed with Globals. For example, pirates.reputation.ReputationGlobals, which governs experience needed to level up, pirates.economy.EconomyGlobals defines the cost of any item in the game, pirates.piratebase.PirateGlobals controls the physics of your character, including speed, acceleration, and jump height. The format of the object table within the PYD is: name, pointer, length. So, if you find a reference to a possibly interesting namespace, you can follow the pointer value to locate the serialized object. That would be where the values to change would reside. I will cover one simple jump hack and leave the rest as an exercise for the reader. The default value for a character's speed and jump force in Pirates is 24.0. I discovered this using our AntiFreeze tool (see slides mentioned previously). For the purposes of continuing this walkthrough, here is what the disassembly of the Python code looks like that initializes the in-game character's physics attributes: load_const 161 # '24.0' store_name 196 # "'ToonForwardFastSpeed'" load_const 161 # '24.0' store_name 197 # "'ToonJumpFastForce'" load_const 162 # '8.0' store_name 198 # "'ToonReverseFastSpeed'" load_const 163 # '120.0' store_name 199 # "'ToonRotateFastSpeed'" load_const 161 # '24.0' store_name 200 # "'ToonForwardSpeed'" load_const 161 # '24.0' store_name 201 # "'ToonJumpForce'"The code above is from a disassembled python code object stored within the PYD. The float values themselves can be found with a simple byte search. However, because of how Python code objects are structured, these data values are not necessarily next to each other within the file. Also, if you are not rebuilding the PYD's object table, any changes must not affect the size of the file. Otherwise, the game will bail while trying to deserialize the code objects. When a float value is serialized in Python, it is stored in a string representation. So, 24.0 is stored in file as "24.0f", the "f" indicating it's a float. For the purposes of a demonstration everyone can easily follow along with, simply search Phase1.pyd for all occurrences of "24.0f". Now, if we simply replace the correct 24.0 value to, say, a 99.0 value, our character will be able to leap buildings. After we do that, however, we also have to consider the phase_1.mf archive file. This file contains a copy of Phase1.pyd, and it overwrites our changes upon game execution. Here is a quick Python script that will copy the contents of your Phase1.pyd file into the phase_1.mf so that the changes are persistent: #!c:\python\python.exe # # injects Phase1.pyd into phase_1.mf in parts because # the file size is too large for python to manipulate # in memory as a string.Once this has been completed, we are able to run the game and test out the injected cheat. If all went well, our character should be able to jump as depicted in the videos above. The cheats involving modification of the game economy and ship characteristics become more complicated--too complicated to attempt using simply a hex editor--but our slides give the information needed to start down that path. Hopefully you've got a basic understanding of how to mess around with this game. The demonstrated method above is a quick solution, so check out the slides if you'd like to see the "correct" way to do it. Also, keep in mind these techniques can be applied to any game written in a dynamic language due to the inherent type information requirements. For those who would like to extend the research, take a look at EVE Online, which reportedly also utilizes Python. Originally published 2008-06-23 on the TippingPoint DVLabs blog.
I've been too lazy and too busy to maintain this site. If you are interested in tracking anything recent of mine check out my home at TippingPoint: Where I started the Zero Day Initiative: Or follow me on Twitter: |