Wednesday, April 7, 2010

Online Competitions: Fail by design voting system

These days we are seeing more and more online competitions that involve some sort of e-voting for the participants. If you're planning to have one, there is only one advice I would like to give; don't do anything client side if you're planning to have some sort of protection against possible abuse.

Recently, there was this competition which a friend of mine took part. On the website, you can vote as many times as you would like as there is no registration needed, but you can only vote once every 1 hour.

Out of curiosity, I decided to figure out how their voting system works.

To my surprise, I found out that the mechanism the website is using to stop people from cheating is based on the information provided by the bwowser.



POST /entry/vote.asmx/VoteForMe HTTP/1.1
Host: example.example.com
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT) Version/4.0.2 Safari/530.19
Content-Type: application/json; charset=utf-8
Referer: http://example.example.com/entry/Users/Kitty_Girl/Default.aspx?UserId=315
Origin: http://example.example.com
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Cookie: __utma=156400292.896968250.1270549747.1270671468.1270678496.15; __utmb=156400292.2.10.1270678496; __utmc=156400292; __utmz=156400292.1270549747.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); ASP.NET_SessionId=kghh0yvgbd35m055wfm0cq55
Content-Length: 42
Connection: keep-alive

{"UserId":"315","UserIp":"122.19.21.47"}



HTTP/1.1 200 OK
Date: Wed, 07 Apr 2010 22:15:58 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Cache-Control: private, max-age=0
Content-Type: application/json; charset=utf-8
Content-Length: 33

{"d":"Thank you for your vote #1957"}




So it's getting the IP of the client to check if the user has voted before, this is very easy to bypass with something like this:


from random import randint

def randip():
return str(randint(1, 250)) + '.' + str(randint(1, 254)) + '.' + str(randint(1, 254)) + '.' + str(randint(1, 254))




Of course this code can be improved further.

In conclusion, while this will stop many people from abusing the system, it's still a bad anti-cheating mechanism. Web developers are not security experts excuse, anyone?

HITB Ezine

Sorry for not updating my blog. Been busy with the ezine lately. We are going to release the 2nd issue of the ezine on 21st of April. As we promised before, we always try to improve the quality of the ezine and try to get as many interesting articles as possible.

Sunday, February 21, 2010

Facebook Applications: Don't Get Social Engineered!

I found something rather interesting today when I logged into my Facebook account. Looking at the notification messages, I saw these comment notifications:



Clicking over the link"post" will then bring you to the page asking for access to your personal information rather than your so called post:


By now, some would already realize that they have been deceived by a smart social engineering trick (Some might consider this as an effective "Marketing strategy" ) while most will continue by allowing access to their personal information.

The reason why many had fall to this trick is probably because they didn't pay attention or simply never realized the difference between the icon of the application above and the actual one below:


(Notice the difference between the two icons)

It's just a matter of time before some Facebook worms do the same thing (Perhaps there is already one) considering how effective it is.

p/s: How easy it's to give out our personal info these days, worse if they fall to the wrong hands.

Friday, February 19, 2010

Olly Debugger / Immunity Debugger long file extension Buffer Overflow

So today out of boredom, I discovered a bug that somehow affected both Ollydbg and Immunity Debugger. I am not sure whether this has been discovered by anyone in the past as I couldn't find any on Google. The bug has to do with how both of them handle a long file extension and you can easily reproduce it by renaming any executable file with a very long extension. As example:

notepad.AAAAAAAAAAAAAAAAAAAAAAAAAAABBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA


Try to load this file into ollydbg, it will then crashes and from the post-mortem debugger, you can see that we have control over the EIP:

(758.268): Access violation - code c0000005 (!!! second chance !!!)
eax=00000000 ebx=00000000 ecx=6d5117d4 edx=41414141 esi=01fca1e0 edi=0202fee0
eip=42424242 esp=0012ac8c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
42424242 ?? ???


ESP will be pointing to the top of our buffer:

0:000> d esp
0012ac8c 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0012ac9c 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0012acac 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0012acbc 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0012accc 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0012acdc 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0012acec 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0012acfc 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA


Immunity Debugger will also crash to this bug. The culprit is the DbgHelp.dll which has been fixed when Vista was released. Both OllyDbg and Immnunity debugger are still using the old version but you can get the latest one from WinDbg Installation.

How I crashed Immunity Debugger by playing safe

Not having any plan when I woke up today, I decided to take a look at some samples which I downloaded from the internet. After renaming those files to ".exe.1", I decided to load one of the samples into Immunity Debugger instead of Ollydbg which I normally use. Guess what? It crashed.

At first I thought this must be one very cool anti-debugging trick, but it seems not to affect Ollydbg. Being curious about that, I loaded the Immunity Debugger into IDA Debugger and loaded the sample file again. This is what I got from the log window:

4E3410: The instruction at 0x4E3410 referenced memory at 0x0. The memory could not be read -> 00000000 (exc.code c0000005, tid 1700)

Great, Null pointer. FLIRT is really awesome for telling me this happened in the strlen function:

.text:004E3408 ; size_t __cdecl strlen(const char *s)
.text:004E3408 _strlen proc near ; CODE XREF: _Assemble+88E p
.text:004E3408 ; _Simpleassemble+88E p ...
.text:004E3408
.text:004E3408 s= dword ptr 4
.text:004E3408
.text:004E3408 mov eax, [esp+s]
.text:004E340C test al, 3
.text:004E340E jnz short loc_4E343D
.text:004E3410
.text:004E3410 loc_4E3410: ; CODE XREF: _strlen+1B j
.text:004E3410 ; _strlen+21 j ...
.text:004E3410 mov edx, [eax]
.text:004E3412 add eax, 4

I then take a look at the caller of the strlen:

.text:0048A856 lea eax, [esi+0EBh]
.text:0048A85C push eax ; s2
.text:0048A85D push edi ; s1
.text:0048A85E call j__strstr
.text:0048A863 add esp, 8
.text:0048A866 mov [ebp+src], eax
.text:0048A869 mov edx, [ebp+src]
.text:0048A86C push edx ; s
.text:0048A86D call _strlen

Before we go any further. Let's first take a look at the definition of strstr:

STRSTR

Locate substring

Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.

So what happened was, the routine tries to search for a ' ' (space) in the ".exe.1" . Obviously not finding it, strstr returns zero (this return value then will be passed as an argument to strlen and that's how we got our null pointer). My guess why the application is looking for a space in the string is because of the code before it:

.text:0048A846 push edi ; s
.text:0048A847 call _strlen
.text:0048A84C pop ecx
.text:0048A84D mov [ebp+n], eax
.text:0048A850 cmp [ebp+n], 5
.text:0048A854 jle short loc_

The comparison failed because the extension is ".exe.1" and not just ".exe" (It's quite common for malware analysts to rename the extension of an executable file to avoid from accidentally clicking them).

What more interesting is that, I actually found a way to bypass this due to another careless bug. Right before we got to the above code, there was another earlier check looking for the extension ".exe".

.text:0048A830 lea edx, [esi+1F6Fh]
.text:0048A836 push edx ; s2
.text:0048A837 push ebx ; s1
.text:0048A838 call j__strstr
.text:0048A83D add esp, 8
.text:0048A840 mov edi, eax
.text:0048A842 test edi, edi
.text:0048A844 jz short loc_48A897

It's weird that the developers have somehow forgotten that strstr is case sensitive, in other words, this routine looks for ".exe" but how if let say the filename is all caps i.e SAMPLE.EXE? So this check would fail and we are happily bypassing the next check that otherwise would crash our debugger.

Anyway, failing the check above, the debugger will check if the file really exist:

.text:0048A897 loc_48A897:
.text:0048A897 lea edx, [ebp+FilePart]
.text:0048A89A lea ecx, [ebp+String]
.text:0048A8A0 push edx ; lpFilePart
.text:0048A8A1 push ecx ; lpBuffer
.text:0048A8A2 lea eax, [esi+1F6Fh]
.text:0048A8A8 push 104h ; nBufferLength
.text:0048A8AD push eax ; lpExtension
.text:0048A8AE push ebx ; lpFileName
.text:0048A8AF push 0 ; lpPath
.text:0048A8B1 call SearchPathA
.text:0048A8B6 test eax, eax
.text:0048A8B8 jnz short loc_48A8D2

If the result is nonzero, the debugger then check if this file has the extension ".SYS"


.text:0048A8D2 loc_48A8D2:
.text:0048A8D2 lea edx, [esi+23B4h]
.text:0048A8D8 push edx ; s2
.text:0048A8D9 lea ecx, [ebp+s1]
.text:0048A8DF push ecx ; s1
.text:0048A8E0 call _stricmp
.text:0048A8E5 add esp, 8
.text:0048A8E8 test eax, eax
.text:0048A8EA jnz short loc_48A960

It's quite interesting that this time they decided to use "stricmp" which is the case insensitive version of strcmp (I mean, they could have done that earlier).

Failing this check, the debugger then goes for plan B which is to parse the target application to see if it's indeed an executable file. But of course for the sake of getting this post short, I won't go any further.

Wednesday, February 17, 2010

Fun with functions

As I mentioned in my earlier post, I will try to share some useful tips and tricks on improving reverse engineering experience whenever possible. For today, I would like to start by pointing the readers to a post made by Cody Pierce on TippingPoint's website over a year ago:

http://dvlabs.tippingpoint.com/blog/2008/10/09/mindshare-first-things-first

In that post he discussed about the top down approach taken by them when analyzing binaries. Even though the post is more towards finding vulnerabilities, the same approach could also be applied when analyzing malicious samples. Anyway, this is the part of the post which we will be discussing today:

"With that said, one of the first things we do is look at the most cross referenced functions. By doing so, our efforts will always help future endeavors. Let's say we have a function that is called 4000 times throughout the binary. If we identify that it is a memory allocation routine, we have just made those other 4000 functions that much easier to understand. These common functions are the building blocks for more complex code we may encounter later."

This is very true especially to new analysts as I still remember how I was struggling the first time I analyzed a live sample, not knowing where to start analyzing wasn't a fun experience to me. Another interesting post is made by Dion with the subject "Differential Reversing" which was posted on his website not too long ago. You can find the link below:

http://dion.t-rexin.org/notes/2009/09/29/differential-reversing/

But we will leave that for future discussion.

The ability to cross reference function calls is one of the great features being offered by IDA Pro, It's really helpful to analysts especially when it comes to deciding which functions should be prioritized for analysis. Combine this with differential analysis, it becomes even more powerful to help analysts do their work faster.



My only problem is that sometimes I just prefer, instead of going through the functions list in the dialogue box manually counting how many cross-references it has, to have IDA just give me the number (total). It will be even better if I can somehow have control over what kind of information related to a function I want IDA to display. I tried to Google for a plugin that will do such thing for me but couldn't find any (Maybe I just didn't try hard enough) The good news is, this can all be done with the provided IDA SDK and Gergely Erdélyi's IDAPython just make things even easier and more fun.

Let me first list down the features that I would like to have in this plugin:

1) Print me some basics info about the function, its address, name and the segment where it's located.
2) Tell me how many locations are referencing to this functions. I would like to get the total of the code and data references and also the total for both types.

Ok now let's take a look at the quick hack I put together:

Warning : This is a quick hack that I put together for this post and might not be as clean as you expected (After all I am not a good coder :p).



# Zarul Shahrin 2010, zarulshahrin - at - hackinthebox.org

from idaapi import *
from idautils import *

def reportRefsTo(func):

funcName = get_func_name(func.startEA) # Get the name of the function from the given address
segName = get_segm_name(func.startEA) # Get the segment it's located

print "=" * 72
print ''
print "Function Name : ", funcName
print "Address : 0x%x" % func.startEA
print "Segment : ", segName

print "Total code references to this function : ",

cTotal = sum(1 for ref in CodeRefsTo(func.startEA, 1)) # Get the total of code reference to this function
print cTotal

print "Total data references to this function : ",

dTotal = sum(1 for ref in DataRefsTo(func.startEA)) # Get the total of data reference to this function
print dTotal

print "Total code + data references to this function : ", cTotal + dTotal
print "\n"


def getFuncInfo(dOption = "all",fImport = 0):


if dOption == "all":

qty = get_func_qty() # Get the number of functions
fCount = 0

while ( fCount < qty):
func = getn_func(fCount) # Get pointer to function structure

reportRefsTo(func)
fCount += 1
else:

func = get_func(dOption) # Get pointer to function structure
reportRefsTo(func) # Print the report

def main():
getFuncInfo()

if __name__ == "__main__":
main()






(Blogger messed up with the code .You can get the final and updated code here)

Below is the sample results for the above IDAPython script (Not full listing. The target application is the GNU Patch.exe):

========================================================================

Function Name : sub_415510
Address : 0x415510
Segment : _text
Total code references to this function : 0
Total data references to this function : 2
Total code + data references to this function : 2


========================================================================

Function Name : sub_4155A0
Address : 0x4155a0
Segment : _text
Total code references to this function : 1
Total data references to this function : 0
Total code + data references to this function : 1


========================================================================

Function Name : sub_415610
Address : 0x415610
Segment : _text
Total code references to this function : 6
Total data references to this function : 0
Total code + data references to this function : 6


========================================================================

Function Name : sub_415810
Address : 0x415810
Segment : _text
Total code references to this function : 15
Total data references to this function : 0
Total code + data references to this function : 15


========================================================================

Function Name : sub_415970
Address : 0x415970
Segment : _text
Total code references to this function : 1
Total data references to this function : 0
Total code + data references to this function : 1


========================================================================

Function Name : _write
Address : 0x4159a0
Segment : _text
Total code references to this function : 4
Total data references to this function : 0
Total code + data references to this function : 4


========================================================================

Function Name : _read
Address : 0x4159b0
Segment : _text
Total code references to this function : 5
Total data references to this function : 0
Total code + data references to this function : 5


========================================================================

Function Name : _open
Address : 0x4159c0
Segment : _text
Total code references to this function : 7
Total data references to this function : 0
Total code + data references to this function : 7


========================================================================

Great! So it does what I wanted it to do. My only problem with the above result is, I would like to get rid of the functions that do nothing more than jumping to a system API. In order to do this I added a quick filter to the original code:


# Zarul Shahrin 2010, zarulshahrin - at - hackinthebox.org

from idaapi import *
from idautils import *

def isImport(ea):

ref = get_first_dref_from(ea) # Get the first data reference to this function

if get_segm_name(ref) == "_idata": # Check if it's a reference to an entry in the import section
return True
else:
return False


def reportRefsTo(func):

funcName = get_func_name(func.startEA) # Get the name of the function from the given address
segName = get_segm_name(func.startEA) # Get the segment it's located

print "=" * 72
print ''
print "Function Name : ", funcName
print "Address : 0x%x" % func.startEA
print "Segment : ", segName

print "Total code references to this function : ",

cTotal = sum(1 for ref in CodeRefsTo(func.startEA, 1)) # Get the total of code reference to this function
print cTotal

print "Total data references to this function : ",

dTotal = sum(1 for ref in DataRefsTo(func.startEA)) # Get the total of data reference to this function
print dTotal

print "Total code + data references to this function : ", cTotal + dTotal
print "\n"


def getFuncInfo(dOption = "all",fImport = 0):


if dOption == "all":

qty = get_func_qty() # Get the number of functions
fCount = 0

while ( fCount < qty):
func = getn_func(fCount) # Get pointer to function structure

if (fImport):
if isImport(func.startEA): # Check if it's in the import sections
fCount += 1
continue

reportRefsTo(func)
fCount += 1
else:

func = get_func(dOption) # Get pointer to function structure
reportRefsTo(func) # Print the report

def main():
getFuncInfo()

if __name__ == "__main__":
main()



(Blogger messed up with the code .You can get the final and updated code here)

So now we have successfully get rid of those functions from our result :

========================================================================

Function Name : sub_4144A4
Address : 0x4144a4
Segment : _text
Total code references to this function : 16
Total data references to this function : 0
Total code + data references to this function : 16


========================================================================

Function Name : sub_4144EC
Address : 0x4144ec
Segment : _text
Total code references to this function : 2
Total data references to this function : 0
Total code + data references to this function : 2


========================================================================

Function Name : sub_4146F4
Address : 0x4146f4
Segment : _text
Total code references to this function : 1
Total data references to this function : 0
Total code + data references to this function : 1


========================================================================

Function Name : sub_4148C4
Address : 0x4148c4
Segment : _text
Total code references to this function : 2
Total data references to this function : 0
Total code + data references to this function : 2


========================================================================

Function Name : sub_414C24
Address : 0x414c24
Segment : _text
Total code references to this function : 2
Total data references to this function : 0
Total code + data references to this function : 2


========================================================================

Function Name : sub_414D80
Address : 0x414d80
Segment : _text
Total code references to this function : 1
Total data references to this function : 0
Total code + data references to this function : 1


========================================================================

Function Name : sub_4152B0
Address : 0x4152b0
Segment : _text
Total code references to this function : 12
Total data references to this function : 0
Total code + data references to this function : 12


========================================================================

From the results, you can clearly see the most cross referenced function. This is very helpful in deciding which function to be analyzed first.

This is just a simple quick hack to demonstrate the kind of powerful things you can do with IDA and its SDK which help speeding up the reverse engineering process (Even if it only save 10 minutes of analysis time, it is more than good enough). Of course you can add more things to it so that it will give you more information about a function. Some of them included:

- Check if a function contains any loop within it (loops detection), if it does, check for the signs of it being an encryption or decryption routine.
- Combine with differential analysis, we can create a filter to only output the functions being called within the execution flow.

I will try to discuss more about this in the future.

I really hope those who are new to reverse engineering will find this post to be useful. The codes are not perfect and there are many ways they can be improved. Feel free to make improvement to it and share with us if you wish :-D

Finally, I would like to wish everyone who are celebrating the Chinese New Year Gong Xi Fa Cai!!

P/S: I would like to thank Esteban Guillardoy from RibadeoHacklab for the help with proof-reading. Thanks Bro!

Back Posting

When I first created this blog, my goal was to share my knowledge with the public. But due the nature of my job (which put limitation to what I was able to blog) and my commitment to my previous employer, it didn't happen. Since a couple of weeks ago, I am pretty much a freeman for now and thus I am back to posting here again. I really hope this will continue or I will do my best to share as much as possible before the door is close again.