Jean Flynn wrote a great essay about how he reached the advanced stego page, but he didn't mention some details about Steganos that are very useful to know. The program calculates a checksum of the password. This'll be the target of my brute force attack.
You should read Jean Flinn's essay about how he reached the advanced stego page. He explains the encryption routine itself, so i don't want to do this again. Finally, he used a dictionary derived from +Fravia's page. If we want to reverse engineer Steganos images in general, we'll not have the knowledge to build such a dictionary of probable words. Thus, we have to think about a brute force attack. I was not able to find a serious weakness in Steganos, as well, but i found something that helps a little bit. Jean Flinn did not mention that Steganos calculates a checksum of the password, which is stored within the image. You should find the algorithm easily: Enter a password, bpx hmemcpy and notice what's going on...:00407053 8A0439 mov al, byte ptr [ecx+edi] - store char in al :00407056 8AD1 mov dl, cl - store counter in dl :00407058 FEC2 inc dl - +1 :0040705A F6EA imul dl - * al (char) :0040705C 32D8 xor bl, al - xor checksum with al :0040705E 41 inc ecx - inc counter :0040705F 3BCE cmp ecx, esi - finished ? :00407061 7EF0 jle 00407053 - no, do it again :00407063 80FB2A cmp bl, 2A :00407066 7502 jne 0040706A :00407068 B301 mov bl, 01 :0040706A A1041B4400 mov eax, dword ptr [00441B04] :0040706F 85C0 test eax, eax :00407071 7502 jne 00407075 :00407073 B32A mov bl, 2A :00407075 8D4C2410 lea ecx, dword ptr [esp+10] :00407079 E8BED50100 call 0042463C :0040707E 5F pop edi :0040707F 8AC3 mov al, bl :00407081 5E pop esi :00407082 5B pop ebx :00407083 C3 ret This is the code translated into Pascal.function Checksum (passw: string): byte; var i: integer; b: byte; begin b:=0; for i:=1 to length (passw) do b:=b xor (ord(passw[i])*i); if b=42 then b:=1; Checksum:=b; end; Steganos Decryptor compares the original checksum with the checksum of the password at 402bf0.:00402A28 E813460000 call calcPasswdChksum (00407040) :00402A2D 8A4C2418 mov cl, byte ptr [esp+18] :00402A31 83C404 add esp, 00000004 :00402A34 3AC1 cmp al, cl :00402A36 0F84A1000000 je 00402ADD By setting a bpx 402a34, you'll find the original checksum in cl (8a for +Fravia's image). This is a good target for our attack, because it will sort out lots of wrong passwords. +Fravia told us that his password has 8 chars and i guessed that all of them were lower case (very probable for +Fravia's passwords :-). Thus, I was able to sped up the algorithm a lot.checksum:=0; for a:=1 to 26 do begin chksum:=fe[a] xor chksum; for b:=1 to 26 do begin chksum:=(fe[b] shl 1) xor chksum; for c:=1 to 26 do begin chksum:=(fe[c]*3) xor chksum; for d:=1 to 26 do begin chksum:=(fe[d] shl 2) xor chksum; for e:=1 to 26 do begin chksum:=(fe[e]*5) xor chksum; for f:=1 to 26 do begin chksum:=(fe[f]*6) xor chksum; for g:=1 to 26 do begin chksum:=(fe[g]*7) xor chksum; for h:=1 to 26 do begin chksum:=(fe[h] shl 3) xor chksum; if (chksum=$8a) then ChkIfRightPwd; chksum:=(fe[h] shl 3) xor chksum; end; chksum:=(fe[g]*7) xor chksum; end; chksum:=(fe[f]*6) xor chksum; end; chksum:=(fe[e]*5) xor chksum; end; chksum:=(fe[d] shl 2) xor chksum; end; chksum:=(fe[c]*3) xor chksum; end; chksum:=(fe[b] shl 1) xor chksum; end; chksum:=fe[a] xor chksum; end; Note: fe[1..26] contains 'a'..'z' It looks complicated, but is really fast. But there are still several thousand "right" passwords. A serious weakness is that Steganos hides and encrypts the filename (+00h) of the text. Thus, I wrote a procedure which ckecks if the first part of the decrypted text is a filename (very similar to Jean Flinn's approach). My whole program could check all possibilities in about 80 hours (but i guessed the first letter was 's' for something like 'stego'... it took not more than two hours :-)
Here are my procedures for Steganos in Pascal:procedure makeCodeTable (passw: string;var codetable: array of byte); var i,old,temp: byte; ba: array [0..255] of byte; begin for i:=0 to 255 do codetable[i]:=i; for i:=0 to 255 do begin ba[i]:=ord (passw[1+(i mod length(passw))]); end; old:=0; for i:=0 to 255 do begin old:=(codetable[i]+ba[i]+old) and 255; temp:=codetable[i]; codetable[i]:=codetable[old]; codetable[old]:=temp; end; end; Notes
passw is the password...
codetable[0..255] is used for decryption later on
the whole procedure is located at :00407090
{ function decode (b:byte; var codetable:array of byte;var count:byte;var co:byte; var old: byte):byte; var temp: byte; begin inc (count); old:=codetable[count]+old; temp:=codetable[count]; codetable[count]:=codetable[old]; codetable[old]:=temp; temp:=codetable[count]+temp; temp:=codetable[temp]; co:=co+$0d; b:=b xor temp xor co; decode:=b; end;
b is the byte to decrypt
codetable[0..255] (see above)
count is a counter (starts at 0)
co is a constant (starts at 28h)
old starts at 0
the whole procedure is located at :00406fa0
This is only the first step to decrypt steganography. It helps a little bit, but is far from being good. It makes brute force attacks faster, but if someone uses numbers, lower and upper case characters, a brute force attack is still too slow. Note for all steganography authors: Don't encrypt additional information such as a filename. This helps us a lot...